From 87c50e529d82f272facb3efa7e3c6101d134d758 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 16:51:45 +0800 Subject: [PATCH 001/965] Update mw.tpl --- scripts/init.d/mw.tpl | 118 +++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index ee4c491d9..f7ff10779 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -28,13 +28,13 @@ WORKING='[\033[34m*\033[0m]' PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export LANG=en_US.UTF-8 -mw_path={$SERVER_PATH} -ROOT_PATH=$(dirname "$mw_path") +PANEL_DIR={$SERVER_PATH} +ROOT_PATH=$(dirname "$PANEL_DIR") PATH=$PATH:$mw_path/bin -if [ -f $mw_path/bin/activate ];then - source $mw_path/bin/activate +if [ -f ${PANEL_DIR}/bin/activate ];then + source ${PANEL_DIR}/bin/activate if [ "$?" != "0" ];then echo "load local python env fail!" fi @@ -45,8 +45,8 @@ mw_start_panel() isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'` if [ "$isStart" == '' ];then echo -e "starting mw-panel... \c" - cd $mw_path && gunicorn -c setting.py app:app - port=$(cat ${mw_path}/data/port.pl) + cd ${PANEL_DIR}/web && gunicorn -c setting.py app:app + port=$(cat ${PANEL_DIR}/data/port.pl) isStart="" while [[ "$isStart" == "" ]]; do @@ -61,7 +61,7 @@ mw_start_panel() if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' - tail -n 20 ${mw_path}/logs/error.log + tail -n 20 ${PANEL_DIR}/logs/panel_error.log echo '------------------------------------------------------' echo -e "\033[31mError: mw-panel service startup failed.\033[0m" return; @@ -75,16 +75,16 @@ mw_start_panel() mw_start_task() { - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "starting mw-tasks... \c" - cd $mw_path && python3 task.py >> ${mw_path}/logs/task.log 2>&1 & + cd ${PANEL_DIR} && python3 panel_task.py >> ${PANEL_DIR}/logs/panel_task.log 2>&1 & sleep 0.3 - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' - tail -n 20 $mw_path/logs/task.log + tail -n 20 ${PANEL_DIR}/logs/panel_task.log echo '------------------------------------------------------' echo -e "\033[31mError: mw-tasks service startup failed.\033[0m" return; @@ -110,7 +110,7 @@ mw_stop_task() fi echo -e "stopping mw-tasks... \c"; - pids=$(ps aux | grep 'task.py'|grep -v grep|awk '{print $2}') + pids=$(ps aux | grep 'panel_task.py'|grep -v grep|awk '{print $2}') arr=($pids) for p in ${arr[@]} do @@ -154,7 +154,7 @@ mw_status() echo -e "\033[31mmw not running\033[0m" fi - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" != '' ];then echo -e "\033[32mmw-task (pid $isStart) already running\033[0m" else @@ -174,7 +174,7 @@ mw_reload() do kill -9 $p done - cd $mw_path && gunicorn -c setting.py app:app + cd ${PANEL_DIR}/web && gunicorn -c setting.py app:app isStart=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'` if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" @@ -192,27 +192,27 @@ mw_reload() } mw_close(){ - echo 'True' > $mw_path/data/close.pl + echo 'True' > ${PANEL_DIR}/data/close.pl } mw_open() { - if [ -f $mw_path/data/close.pl ];then - rm -rf $mw_path/data/close.pl + if [ -f ${PANEL_DIR}/data/close.pl ];then + rm -rf ${PANEL_DIR}/data/close.pl fi } mw_unbind_domain() { - if [ -f $mw_path/data/bind_domain.pl ];then - rm -rf $mw_path/data/bind_domain.pl + if [ -f ${PANEL_DIR}/data/bind_domain.pl ];then + rm -rf ${PANEL_DIR}/data/bind_domain.pl fi } mw_unbind_ssl() { - if [ -f $mw_path/local ];then - rm -rf $mw_path/local + if [ -f ${PANEL_DIR}/local ];then + rm -rf ${PANEL_DIR}/local fi if [ -f $mw_path/nginx ];then @@ -226,12 +226,12 @@ mw_unbind_ssl() error_logs() { - tail -n 100 $mw_path/logs/error.log + tail -n 100 ${PANEL_DIR}/logs/panel_error.log } mw_update() { - if [ -f $mw_path/task.py ];then + if [ -f ${PANEL_DIR}/task.py ];then echo "与后续版本差异太大,不再提供更新" exit 0 fi @@ -251,7 +251,7 @@ mw_update() mw_update_dev() { - if [ -f $mw_path/task.py ];then + if [ -f ${PANEL_DIR}/task.py ];then echo "与后续版本差异太大,不再提供更新" exit 0 fi @@ -267,14 +267,14 @@ mw_update_dev() else curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash fi - cd /www/server/mdserver-web + cd ${PANEL_DIR} } mw_update_venv() { - rm -rf /www/server/mdserver-web/bin - rm -rf /www/server/mdserver-web/lib64 - rm -rf /www/server/mdserver-web/lib + rm -rf ${PANEL_DIR}/bin + rm -rf ${PANEL_DIR}/lib64 + rm -rf ${PANEL_DIR}/lib LOCAL_ADDR=common cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") @@ -287,7 +287,7 @@ mw_update_venv() else curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash fi - cd /www/server/mdserver-web + cd ${PANEL_DIR} } mw_mirror() @@ -303,7 +303,7 @@ mw_mirror() else bash <(curl --insecure -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh) fi - cd ${ROOT_PATH}/mdserver-web + cd ${ROOT_PATH} } mw_install_app() @@ -338,12 +338,12 @@ mw_debug(){ mw_force_kill port=7200 - if [ -f $mw_path/data/port.pl ];then - port=$(cat $mw_path/data/port.pl) + if [ -f ${PANEL_DIR}/data/port.pl ];then + port=$(cat ${PANEL_DIR}/data/port.pl) fi - if [ -d ${ROOT_PATH}/mdserver-web ];then - cd ${ROOT_PATH}/mdserver-web + if [ -d ${PANEL_DIR}/web ];then + cd ${PANEL_DIR}/web fi gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } @@ -445,7 +445,7 @@ mw_connect_pgdb(){ fi - pwd=$(cd ${ROOT_PATH}/mdserver-web && python3 ${ROOT_PATH}/mdserver-web/plugins/postgresql/index.py root_pwd) + pwd=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/plugins/postgresql/index.py root_pwd) export PGPASSWORD=${pwd} echo "${ROOT_PATH}/postgresql/bin/psql -U postgres -W" ${ROOT_PATH}/postgresql/bin/psql -U postgres -W @@ -464,7 +464,7 @@ mw_mongodb(){ AUTH_STR="" if [[ "$MGDB_AUTH" == "enabled" ]];then - pwd=$(cd ${ROOT_PATH}/mdserver-web && python3 ${ROOT_PATH}/mdserver-web/plugins/mongodb/index.py root_pwd) + pwd=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/plugins/mongodb/index.py root_pwd) AUTH_STR="-u root -p ${pwd}" fi @@ -493,14 +493,14 @@ mw_redis(){ } mw_venv(){ - cd ${ROOT_PATH}/mdserver-web && source bin/activate + cd ${PANEL_DIR} && source bin/activate } mw_clean_lib(){ - cd ${ROOT_PATH}/mdserver-web && rm -rf lib - cd ${ROOT_PATH}/mdserver-web && rm -rf lib64 - cd ${ROOT_PATH}/mdserver-web && rm -rf bin - cd ${ROOT_PATH}/mdserver-web && rm -rf include + cd ${PANEL_DIR} && rm -rf lib + cd ${PANEL_DIR} && rm -rf lib64 + cd ${PANEL_DIR} && rm -rf bin + cd ${PANEL_DIR} && rm -rf include } case "$1" in @@ -540,35 +540,35 @@ case "$1" in port=7200 scheme=http - if [ -f $mw_path/ssl/choose.pl ];then + if [ -f ${PANEL_DIR}/ssl/choose.pl ];then scheme=https fi - if [ -f $mw_path/data/port.pl ];then - port=$(cat $mw_path/data/port.pl) + if [ -f ${PANEL_DIR}/data/port.pl ];then + port=$(cat ${PANEL_DIR}/data/port.pl) fi - if [ ! -f $mw_path/data/default.pl ];then + if [ ! -f ${PANEL_DIR}/data/default.pl ];then echo -e "\033[33mInstall Failed\033[0m" exit 1 fi - password=$(cat $mw_path/data/default.pl) - if [ -f $mw_path/data/domain.conf ];then - address=$(cat $mw_path/data/domain.conf) + password=$(cat ${PANEL_DIR}/data/default.pl) + if [ -f ${PANEL_DIR}/data/domain.conf ];then + address=$(cat ${PANEL_DIR}/data/domain.conf) fi - if [ -f $mw_path/data/admin_path.pl ];then - auth_path=$(cat $mw_path/data/admin_path.pl) + if [ -f ${PANEL_DIR}/data/admin_path.pl ];then + auth_path=$(cat ${PANEL_DIR}/data/admin_path.pl) fi if [ "$address" == "" ];then - v4=$(python3 $mw_path/tools.py getServerIp 4) - v6=$(python3 $mw_path/tools.py getServerIp 6) + v4=$(python3 ${PANEL_DIR}/tools.py getServerIp 4) + v6=$(python3 ${PANEL_DIR}/tools.py getServerIp 6) if [ "$v4" != "" ] && [ "$v6" != "" ]; then - if [ ! -f $mw_path/data/ipv6.pl ];then - echo 'True' > $mw_path/data/ipv6.pl + if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then + echo 'True' > ${PANEL_DIR}/data/ipv6.pl mw_stop mw_start fi @@ -578,9 +578,9 @@ case "$1" in address="MW-Panel-Url: ${scheme}://$v4:$port$auth_path" elif [ "$v6" != "" ]; then - if [ ! -f $mw_path/data/ipv6.pl ];then + if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then # Need to restart ipv6 to take effect - echo 'True' > $mw_path/data/ipv6.pl + echo 'True' > ${PANEL_DIR}/data/ipv6.pl mw_stop mw_start fi @@ -597,8 +597,8 @@ case "$1" in echo -e "\033[32mMW-Panel default info!\033[0m" echo -e "==================================================================" echo -e "$address" - echo -e `python3 $mw_path/tools.py username` - echo -e `python3 $mw_path/tools.py password` + echo -e `python3 ${PANEL_DIR}/panel_tools.py username` + echo -e `python3 ${PANEL_DIR}/panel_tools.py password` # echo -e "password: $password" echo -e "\033[33mWarning:\033[0m" echo -e "\033[33mIf you cannot access the panel. \033[0m" @@ -606,6 +606,6 @@ case "$1" in echo -e "==================================================================" ;; *) - cd $mw_path && python3 $mw_path/tools.py cli $1 + cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py cli $1 ;; esac From c08a2965abbf59b271dab74ddc671ff5988b79be Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 16:54:42 +0800 Subject: [PATCH 002/965] Create update_new.sh --- scripts/update_new.sh | 134 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100755 scripts/update_new.sh diff --git a/scripts/update_new.sh b/scripts/update_new.sh new file mode 100755 index 000000000..1f8caa7aa --- /dev/null +++ b/scripts/update_new.sh @@ -0,0 +1,134 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH +# LANG=en_US.UTF-8 +is64bit=`getconf LONG_BIT` + +startTime=`date +%s` + +if [ -f /www/server/mdserver-web/tools.py ];then + echo -e "存在旧版代码,不能安装!,已知风险的情况下" + echo -e "rm -rf /www/server/mdserver-web" + echo -e "可安装!" + exit 0 +fi + +_os=`uname` +echo "use system: ${_os}" + +if [ "$EUID" -ne 0 ] + then echo "Please run as root!" + exit +fi + +if [ ${_os} != "Darwin" ] && [ ! -d /www/server/mdserver-web/logs ]; then + mkdir -p /www/server/mdserver-web/logs +fi + +LOG_FILE=/var/log/mw-update.log + +{ + +if [ ${_os} == "Darwin" ]; then + OSNAME='macos' +elif grep -Eqi "openSUSE" /etc/*-release; then + OSNAME='opensuse' + zypper refresh +elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then + OSNAME='euler' +elif grep -Eqi "FreeBSD" /etc/*-release; then + OSNAME='freebsd' +elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then + OSNAME='centos' + yum install -y wget zip unzip +elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then + OSNAME='amazon' + yum install -y wget zip unzip +elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then + OSNAME='debian' + apt install -y wget zip unzip +elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then + OSNAME='ubuntu' + apt install -y wget zip unzip +elif grep -Eqi "Raspbian" /etc/issue || grep -Eqi "Raspbian" /etc/*-release; then + OSNAME='raspbian' +else + OSNAME='unknow' +fi + +# HTTP_PREFIX="https://" +# LOCAL_ADDR=common +# ping -c 1 github.com > /dev/null 2>&1 +# if [ "$?" != "0" ];then +# LOCAL_ADDR=cn +# HTTP_PREFIX="https://mirror.ghproxy.com/" +# fi +HTTP_PREFIX="https://" +LOCAL_ADDR=common +cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") +if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then + LOCAL_ADDR=cn + HTTP_PREFIX="https://mirror.ghproxy.com/" +fi + +echo "local:${LOCAL_ADDR}" + +CP_CMD=/usr/bin/cp +if [ -f /bin/cp ];then + CP_CMD=/bin/cp +fi + +echo "update mdserver-web code start" + +if [ "$LOCAL_ADDR" != "common" ];then + wget --no-check-certificate -O /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip + # curl --insecure -sSLo /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip + cd /tmp && unzip /tmp/dev.zip + + $CP_CMD -rf /tmp/mdserver-web/* /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web +else + curl --insecure -sSLo /tmp/dev.zip ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/heads/dev.zip + cd /tmp && unzip /tmp/dev.zip + $CP_CMD -rf /tmp/mdserver-web-dev/* /www/server/mdserver-web + rm -rf /tmp/dev.zip + rm -rf /tmp/mdserver-web-dev +fi + +echo "update mdserver-web code end" + +rm -rf /tmp/dev.zip +rm -rf /tmp/mdserver-web-dev + +#pip uninstall public +echo "use system version: ${OSNAME}" +cd /www/server/mdserver-web && bash scripts/update/${OSNAME}.sh + +bash /etc/rc.d/init.d/mw restart +bash /etc/rc.d/init.d/mw default + +if [ -f /usr/bin/mw ];then + rm -rf /usr/bin/mw +fi + +if [ ! -e /usr/bin/mw ]; then + if [ ! -f /usr/bin/mw ];then + ln -s /etc/rc.d/init.d/mw /usr/bin/mw + fi +fi + +endTime=`date +%s` +((outTime=($endTime-$startTime)/60)) +echo -e "Time consumed:\033[32m $outTime \033[0mMinute!" + +} 1> >(tee $LOG_FILE) 2>&1 \ No newline at end of file From 4bd04eb71d678ec7ae390e93fa6c8faa7050a9bb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 16:55:22 +0800 Subject: [PATCH 003/965] Update update_new.sh --- scripts/update_new.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scripts/update_new.sh b/scripts/update_new.sh index 1f8caa7aa..7c4d2ebd6 100755 --- a/scripts/update_new.sh +++ b/scripts/update_new.sh @@ -6,13 +6,6 @@ is64bit=`getconf LONG_BIT` startTime=`date +%s` -if [ -f /www/server/mdserver-web/tools.py ];then - echo -e "存在旧版代码,不能安装!,已知风险的情况下" - echo -e "rm -rf /www/server/mdserver-web" - echo -e "可安装!" - exit 0 -fi - _os=`uname` echo "use system: ${_os}" From 71ab5920c83adb6a4d5922ebb70ab224b1953255 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 16:55:47 +0800 Subject: [PATCH 004/965] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1107bbb41..fbb8a397e 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev 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/update_new.sh | bash ``` ### 捐赠地址 USDT(TRC20) From b44fec18594d40891ccc380a669fa77a0fc4f9e1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 16:56:45 +0800 Subject: [PATCH 005/965] Update __init__.py --- web/utils/system/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/utils/system/__init__.py b/web/utils/system/__init__.py index 0d5869331..de3430715 100644 --- a/web/utils/system/__init__.py +++ b/web/utils/system/__init__.py @@ -10,6 +10,7 @@ import os import sys +import re import time import math import psutil From 6da09e708a40036a07192cfe05991a4a63378c4c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 16:58:51 +0800 Subject: [PATCH 006/965] Update mw.tpl --- scripts/init.d/mw.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index f7ff10779..09be6cd68 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -562,8 +562,8 @@ case "$1" in fi if [ "$address" == "" ];then - v4=$(python3 ${PANEL_DIR}/tools.py getServerIp 4) - v6=$(python3 ${PANEL_DIR}/tools.py getServerIp 6) + v4=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) + v6=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) if [ "$v4" != "" ] && [ "$v6" != "" ]; then From aa2380939094ac21b20dc95e0df3fd916f670e3e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:01:52 +0800 Subject: [PATCH 007/965] Update mw.tpl --- scripts/init.d/mw.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 09be6cd68..bf0948df6 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -597,8 +597,8 @@ case "$1" in echo -e "\033[32mMW-Panel default info!\033[0m" echo -e "==================================================================" echo -e "$address" - echo -e `python3 ${PANEL_DIR}/panel_tools.py username` - echo -e `python3 ${PANEL_DIR}/panel_tools.py password` + echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py username` + echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py password` # echo -e "password: $password" echo -e "\033[33mWarning:\033[0m" echo -e "\033[33mIf you cannot access the panel. \033[0m" From 328543852cbbc6cec1b684de930d59f383b8fc01 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:13:06 +0800 Subject: [PATCH 008/965] Update mw.tpl --- scripts/init.d/mw.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index bf0948df6..1d2ace922 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -30,7 +30,9 @@ export LANG=en_US.UTF-8 PANEL_DIR={$SERVER_PATH} ROOT_PATH=$(dirname "$PANEL_DIR") -PATH=$PATH:$mw_path/bin +PATH=$PATH:${PANEL_DIR}/bin + +echo ${PANEL_DIR} if [ -f ${PANEL_DIR}/bin/activate ];then From 9fd7d2005106707a0c9991bd332b874bfca96147 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:16:47 +0800 Subject: [PATCH 009/965] Update mw.tpl --- scripts/init.d/mw.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 1d2ace922..c73cbf5cd 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -564,8 +564,8 @@ case "$1" in fi if [ "$address" == "" ];then - v4=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) - v6=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) + v4=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) + v6=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) if [ "$v4" != "" ] && [ "$v6" != "" ]; then From 14ee45918fbf781cad1e44a84f80dc0894bc388b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:18:19 +0800 Subject: [PATCH 010/965] Update panel_tools.py --- panel_tools.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index ff4fa3d79..171201b67 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -235,7 +235,6 @@ def set_panel_pwd(password, ncli=False): def show_panel_pwd(): # 设置面板密码 - import db sql = db.Sql() password = sql.table('users').where('id=?', (1,)).getField('password') @@ -251,7 +250,6 @@ def show_panel_pwd(): def set_panel_username(username=None): # 随机面板用户名 - import db sql = db.Sql() if username: if len(username) < 5: From d5e2f554f11ae03fc3aac6bb3e8865ba12171e2e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:21:19 +0800 Subject: [PATCH 011/965] update --- panel_tools.py | 6 +----- scripts/init.d/mw.tpl | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index 171201b67..558b9ac93 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -23,14 +23,10 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) +from admin import model import core.mw as mw import core.db as db -# 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) - INIT_DIR = "/etc/rc.d/init.d" if mw.isAppleSystem(): INIT_DIR = mw.getPanelDir() + "/scripts/init.d" diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index c73cbf5cd..f57c64f6e 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -32,8 +32,6 @@ PANEL_DIR={$SERVER_PATH} ROOT_PATH=$(dirname "$PANEL_DIR") PATH=$PATH:${PANEL_DIR}/bin -echo ${PANEL_DIR} - if [ -f ${PANEL_DIR}/bin/activate ];then source ${PANEL_DIR}/bin/activate From 9ff26caa5f50d2ef0c0fad66f3af7991fc5c1d2b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:22:32 +0800 Subject: [PATCH 012/965] Update mw.tpl --- scripts/init.d/mw.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index f57c64f6e..cf16e0b40 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -536,7 +536,7 @@ case "$1" in 'venv') mw_update_venv;; 'clean_lib') mw_clean_lib;; 'default') - cd $mw_path + cd ${PANEL_DIR} port=7200 scheme=http From b0e9a13984b09dc9c32a535fb226f51a11414bb5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:26:01 +0800 Subject: [PATCH 013/965] Update mw.tpl --- scripts/init.d/mw.tpl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index cf16e0b40..f662fa7f1 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -573,9 +573,9 @@ case "$1" in mw_start fi - address="MW-Panel-Url-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-Url-Ipv6: ${scheme}://[$v6]:$port$auth_path" + address="MW-PANEL-URL-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-URL-Ipv6: ${scheme}://[$v6]:$port$auth_path" elif [ "$v4" != "" ]; then - address="MW-Panel-Url: ${scheme}://$v4:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://$v4:$port$auth_path" elif [ "$v6" != "" ]; then if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then @@ -584,17 +584,17 @@ case "$1" in mw_stop mw_start fi - address="MW-Panel-Url: ${scheme}://[$v6]:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://[$v6]:$port$auth_path" else - address="MW-Panel-Url: ${scheme}://you-network-ip:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://you-network-ip:$port$auth_path" fi else - address="MW-Panel-Url: ${scheme}://$address:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://$address:$port$auth_path" fi show_panel_ip="$port|" echo -e "==================================================================" - echo -e "\033[32mMW-Panel default info!\033[0m" + echo -e "\033[32mMW-PANEL DEFAULT INFO!\033[0m" echo -e "==================================================================" echo -e "$address" echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py username` From 39c9a260cbc7b616f92ebd3a65aea831cc7f8c52 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:32:30 +0800 Subject: [PATCH 014/965] Update mw.tpl --- scripts/init.d/mw.tpl | 143 +++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 70 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index f662fa7f1..c07de2f6d 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -503,6 +503,78 @@ mw_clean_lib(){ cd ${PANEL_DIR} && rm -rf include } +mw_default(){ + cd ${PANEL_DIR} + port=7200 + scheme=http + + if [ -f ${PANEL_DIR}/ssl/choose.pl ];then + scheme=https + fi + + if [ -f ${PANEL_DIR}/data/port.pl ];then + port=$(cat ${PANEL_DIR}/data/port.pl) + fi + + if [ ! -f ${PANEL_DIR}/data/default.pl ];then + echo -e "\033[33mInstall Failed\033[0m" + exit 1 + fi + + password=$(cat ${PANEL_DIR}/data/default.pl) + if [ -f ${PANEL_DIR}/data/domain.conf ];then + address=$(cat ${PANEL_DIR}/data/domain.conf) + fi + if [ -f ${PANEL_DIR}/data/admin_path.pl ];then + auth_path=$(cat ${PANEL_DIR}/data/admin_path.pl) + fi + + if [ "$address" == "" ];then + v4=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) + v6=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) + + if [ "$v4" != "" ] && [ "$v6" != "" ]; then + + if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then + echo 'True' > ${PANEL_DIR}/data/ipv6.pl + mw_stop + mw_start + fi + + address="MW-PANEL-URL-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-URL-Ipv6: ${scheme}://[$v6]:$port$auth_path" + elif [ "$v4" != "" ]; then + address="MW-PANEL-URL: ${scheme}://$v4:$port$auth_path" + elif [ "$v6" != "" ]; then + + if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then + # Need to restart ipv6 to take effect + echo 'True' > ${PANEL_DIR}/data/ipv6.pl + mw_stop + mw_start + fi + address="MW-PANEL-URL: ${scheme}://[$v6]:$port$auth_path" + else + address="MW-PANEL-URL: ${scheme}://you-network-ip:$port$auth_path" + fi + else + address="MW-PANEL-URL: ${scheme}://$address:$port$auth_path" + fi + + show_panel_ip="$port|" + echo -e "==================================================================" + echo -e "\033[32mMW-PANEL DEFAULT INFO!\033[0m" + echo -e "==================================================================" + echo -e "$address" + echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py username` + echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py password` + # echo -e "password: $password" + echo -e "\033[33mWarning:\033[0m" + echo -e "\033[33mIf you cannot access the panel. \033[0m" + echo -e "\033[33mrelease the following port (${show_panel_ip}80|443|22) in the security group.\033[0m" + echo -e "==================================================================" + ;; +} + case "$1" in 'start') mw_start;; 'stop') mw_stop;; @@ -535,76 +607,7 @@ case "$1" in 'mongodb') mw_mongodb;; 'venv') mw_update_venv;; 'clean_lib') mw_clean_lib;; - 'default') - cd ${PANEL_DIR} - port=7200 - scheme=http - - if [ -f ${PANEL_DIR}/ssl/choose.pl ];then - scheme=https - fi - - if [ -f ${PANEL_DIR}/data/port.pl ];then - port=$(cat ${PANEL_DIR}/data/port.pl) - fi - - if [ ! -f ${PANEL_DIR}/data/default.pl ];then - echo -e "\033[33mInstall Failed\033[0m" - exit 1 - fi - - password=$(cat ${PANEL_DIR}/data/default.pl) - if [ -f ${PANEL_DIR}/data/domain.conf ];then - address=$(cat ${PANEL_DIR}/data/domain.conf) - fi - if [ -f ${PANEL_DIR}/data/admin_path.pl ];then - auth_path=$(cat ${PANEL_DIR}/data/admin_path.pl) - fi - - if [ "$address" == "" ];then - v4=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) - v6=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) - - if [ "$v4" != "" ] && [ "$v6" != "" ]; then - - if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then - echo 'True' > ${PANEL_DIR}/data/ipv6.pl - mw_stop - mw_start - fi - - address="MW-PANEL-URL-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-URL-Ipv6: ${scheme}://[$v6]:$port$auth_path" - elif [ "$v4" != "" ]; then - address="MW-PANEL-URL: ${scheme}://$v4:$port$auth_path" - elif [ "$v6" != "" ]; then - - if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then - # Need to restart ipv6 to take effect - echo 'True' > ${PANEL_DIR}/data/ipv6.pl - mw_stop - mw_start - fi - address="MW-PANEL-URL: ${scheme}://[$v6]:$port$auth_path" - else - address="MW-PANEL-URL: ${scheme}://you-network-ip:$port$auth_path" - fi - else - address="MW-PANEL-URL: ${scheme}://$address:$port$auth_path" - fi - - show_panel_ip="$port|" - echo -e "==================================================================" - echo -e "\033[32mMW-PANEL DEFAULT INFO!\033[0m" - echo -e "==================================================================" - echo -e "$address" - echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py username` - echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py password` - # echo -e "password: $password" - echo -e "\033[33mWarning:\033[0m" - echo -e "\033[33mIf you cannot access the panel. \033[0m" - echo -e "\033[33mrelease the following port (${show_panel_ip}80|443|22) in the security group.\033[0m" - echo -e "==================================================================" - ;; + 'default') mw_default;; *) cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py cli $1 ;; From 3d381b6dfa21c0a9e4078b37b599e581a5462e3f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:33:16 +0800 Subject: [PATCH 015/965] Update mw.tpl --- scripts/init.d/mw.tpl | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index c07de2f6d..3565e4962 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -572,7 +572,6 @@ mw_default(){ echo -e "\033[33mIf you cannot access the panel. \033[0m" echo -e "\033[33mrelease the following port (${show_panel_ip}80|443|22) in the security group.\033[0m" echo -e "==================================================================" - ;; } case "$1" in From 386448d60c6c48239471f8207d635a9561a8050f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:34:15 +0800 Subject: [PATCH 016/965] Update mw.tpl --- scripts/init.d/mw.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 3565e4962..d609da790 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -530,8 +530,8 @@ mw_default(){ fi if [ "$address" == "" ];then - v4=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) - v6=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) + v4=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) + v6=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) if [ "$v4" != "" ] && [ "$v6" != "" ]; then @@ -565,8 +565,8 @@ mw_default(){ echo -e "\033[32mMW-PANEL DEFAULT INFO!\033[0m" echo -e "==================================================================" echo -e "$address" - echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py username` - echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py password` + echo -e `python3 ${PANEL_DIR}/panel_tools.py username` + echo -e `python3 ${PANEL_DIR}/panel_tools.py password` # echo -e "password: $password" echo -e "\033[33mWarning:\033[0m" echo -e "\033[33mIf you cannot access the panel. \033[0m" From 759cbd1065ee0e9918e8ece35bf23490fc78a845 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 17:34:48 +0800 Subject: [PATCH 017/965] Update mw.tpl --- scripts/init.d/mw.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index d609da790..b2830cb77 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -541,7 +541,7 @@ mw_default(){ mw_start fi - address="MW-PANEL-URL-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-URL-Ipv6: ${scheme}://[$v6]:$port$auth_path" + address="MW-PANEL-URL-IPV4: ${scheme}://$v4:$port$auth_path \nMW-PANEL-URL-IPV6: ${scheme}://[$v6]:$port$auth_path" elif [ "$v4" != "" ]; then address="MW-PANEL-URL: ${scheme}://$v4:$port$auth_path" elif [ "$v6" != "" ]; then From d767a4d4a2897a9117c3999806a33279f53d3799 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:14:31 +0800 Subject: [PATCH 018/965] update --- panel_tools.py | 17 ++++++++----- web/admin/model/__init__.py | 4 ++- web/admin/model/user.py | 51 +++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index 558b9ac93..a6547e0c7 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -120,7 +120,7 @@ def mwcli(mw_input=0): input_user = mw_input_cmd("请输入新的面板用户名(>=5位):") set_panel_username(input_user.strip()) elif mw_input == 13: - os.system('tail -100 ' + mw.getPanelDir() + '/logs/error.log') + os.system('tail -100 ' + mw.getPanelDir() + '/logs/panel_error.log') elif mw_input == 20: basic_auth = 'data/basic_auth.json' if os.path.exists(basic_auth): @@ -246,7 +246,6 @@ def show_panel_pwd(): def set_panel_username(username=None): # 随机面板用户名 - sql = db.Sql() if username: if len(username) < 5: print("|-错误,用户名长度不能少于5位") @@ -255,15 +254,15 @@ def set_panel_username(username=None): print("|-错误,不能使用过于简单的用户名") return - sql.table('users').where('id=?', (1,)).setField('username', username) + model.setUserByRoot(name=username) print("|-新用户名: %s" % username) return - username = sql.table('users').where('id=?', (1,)).getField('username') - if username == 'admin': + info = model.getUserByRoot() + if info['name'] == 'admin': username = mw.getRandomString(8).lower() - sql.table('users').where('id=?', (1,)).setField('username', username) - print('username: ' + username) + model.setUserByRoot(name=username) + print('|-用户名: ' + info['name']) def getServerIp(): @@ -276,6 +275,10 @@ def getServerIp(): if __name__ == "__main__": + + if len(sys.argv) == 1: + print('ERROR: Parameter error!') + exit(-2) method = sys.argv[1] if method == 'panel': set_panel_pwd(sys.argv[2]) diff --git a/web/admin/model/__init__.py b/web/admin/model/__init__.py index 45762ecbd..1d1af4dc7 100644 --- a/web/admin/model/__init__.py +++ b/web/admin/model/__init__.py @@ -21,7 +21,9 @@ from .task import addTask from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun from .task import setTaskStatus,setTaskData -from .user import getUserByName,getUserById,isLoginCheck +from .user import isLoginCheck +from .user import getUserByName,getUserById,getUserByRoot +from .user import setUserByRoot from .temp_login import getTempLoginByToken,clearTempLogin diff --git a/web/admin/model/user.py b/web/admin/model/user.py index 241af678e..ac772206a 100644 --- a/web/admin/model/user.py +++ b/web/admin/model/user.py @@ -32,16 +32,6 @@ def getUserByName(name, row['update_time'] = item.update_time return row -def isLoginCheck(username, password) -> bool: - info = getUserByName(data['username']) - if info is None: - return False - - if info['password'] == mw.md5(password): - return True - return False - - def getUserById(id, ) -> None: ''' @@ -62,6 +52,47 @@ def getUserById(id, row['update_time'] = item.update_time return row +def getUserByRoot() -> None: + ''' + 获取用户信息通过用户名 + ''' + return getUserById(1) + +def setUserByRoot( + name: str | None = None, + password: str | None =None, +) -> bool: + ''' + 设置配置的值 + :name -> str 名称 (必填) + :value -> object值 (必填) + :type -> str 类型 (可选|默认common) + ''' + data = {} + + if name is not None: + data['name'] = name + + if name is not None: + data['password'] = mw.md5(password) + + db.session.query(Users).filter_by(Users.id==1).update(data) + db.session.commit() + db.session.close() + return True + +def isLoginCheck(username, password) -> bool: + info = getUserByName(data['username']) + if info is None: + return False + + if info['password'] == mw.md5(password): + return True + return False + + + + def isLoginCheck(username, password) -> bool: info = getUserByName(data['username']) if info is None: From fb8b5314e1d81ec51e81a80db1d9094b1e5bcef3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:18:12 +0800 Subject: [PATCH 019/965] Update panel_tools.py --- panel_tools.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index a6547e0c7..9576d8bd5 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -23,6 +23,8 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) + +from admin import app from admin import model import core.mw as mw import core.db as db @@ -274,8 +276,7 @@ def getServerIp(): print(ip[0]) -if __name__ == "__main__": - +def main(): if len(sys.argv) == 1: print('ERROR: Parameter error!') exit(-2) @@ -301,3 +302,7 @@ if __name__ == "__main__": mwcli(clinum) else: print('ERROR: Parameter error') + +if __name__ == "__main__": + with app.app_context(): + run() From 559e549590bf3e093eafeea6eda811f0f96dd8a8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:18:58 +0800 Subject: [PATCH 020/965] Update panel_tools.py --- panel_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_tools.py b/panel_tools.py index 9576d8bd5..eedf44b38 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -305,4 +305,4 @@ def main(): if __name__ == "__main__": with app.app_context(): - run() + main() From 9b1552269b40b9855b84c01a553d0fc6b3f03cfe Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:30:50 +0800 Subject: [PATCH 021/965] update --- panel_tools.py | 6 ++++++ scripts/init.d/mw.tpl | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index eedf44b38..a0e46a1f7 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -245,6 +245,10 @@ def show_panel_pwd(): return print("password has been changed!") +def show_panel_adminpath(): + admin_path = model.getOption('admin_path') + print(admin_path) + def set_panel_username(username=None): # 随机面板用户名 @@ -290,6 +294,8 @@ def main(): set_panel_username() elif method == 'password': show_panel_pwd() + elif method == 'admin_path': + show_panel_adminpath() elif method == 'getServerIp': getServerIp() elif method == "cli": diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index b2830cb77..cfea01021 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -522,12 +522,12 @@ mw_default(){ fi password=$(cat ${PANEL_DIR}/data/default.pl) + if [ -f ${PANEL_DIR}/data/domain.conf ];then address=$(cat ${PANEL_DIR}/data/domain.conf) fi - if [ -f ${PANEL_DIR}/data/admin_path.pl ];then - auth_path=$(cat ${PANEL_DIR}/data/admin_path.pl) - fi + + admin_path=$(python3 ${PANEL_DIR}/panel_tools.py admin_path) if [ "$address" == "" ];then v4=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) From 669bbfce937a12aa42eb9bbdbd5323306c144d90 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:34:35 +0800 Subject: [PATCH 022/965] Update mw.tpl --- scripts/init.d/mw.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index cfea01021..97e9c83e5 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -527,8 +527,8 @@ mw_default(){ address=$(cat ${PANEL_DIR}/data/domain.conf) fi - admin_path=$(python3 ${PANEL_DIR}/panel_tools.py admin_path) - + admin_path=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py admin_path) + if [ "$address" == "" ];then v4=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) v6=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) From f3552281c551f4ce669be0def2346f98519ea3c0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:35:07 +0800 Subject: [PATCH 023/965] Update mw.tpl --- scripts/init.d/mw.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 97e9c83e5..7bbd1c9aa 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -528,7 +528,7 @@ mw_default(){ fi admin_path=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py admin_path) - + echo "admin_path:$admin_path" if [ "$address" == "" ];then v4=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) v6=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) From 306d69bd3967c3e1b888400cd48bede7852357a9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:36:04 +0800 Subject: [PATCH 024/965] Update mw.tpl --- scripts/init.d/mw.tpl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 7bbd1c9aa..4a44f97ed 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -528,7 +528,6 @@ mw_default(){ fi admin_path=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py admin_path) - echo "admin_path:$admin_path" if [ "$address" == "" ];then v4=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) v6=$(python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) @@ -541,9 +540,9 @@ mw_default(){ mw_start fi - address="MW-PANEL-URL-IPV4: ${scheme}://$v4:$port$auth_path \nMW-PANEL-URL-IPV6: ${scheme}://[$v6]:$port$auth_path" + address="MW-PANEL-URL-IPV4: ${scheme}://$v4:$port$admin_path \nMW-PANEL-URL-IPV6: ${scheme}://[$v6]:$port$admin_path" elif [ "$v4" != "" ]; then - address="MW-PANEL-URL: ${scheme}://$v4:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://$v4:$port$admin_path" elif [ "$v6" != "" ]; then if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then @@ -552,12 +551,12 @@ mw_default(){ mw_stop mw_start fi - address="MW-PANEL-URL: ${scheme}://[$v6]:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://[$v6]:$port$admin_path" else - address="MW-PANEL-URL: ${scheme}://you-network-ip:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://you-network-ip:$port$admin_path" fi else - address="MW-PANEL-URL: ${scheme}://$address:$port$auth_path" + address="MW-PANEL-URL: ${scheme}://$address:$port$admin_path" fi show_panel_ip="$port|" From 671859ad3c8fd7e627d33a656db82c3bd69b7d6d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:37:00 +0800 Subject: [PATCH 025/965] Update panel_tools.py --- panel_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_tools.py b/panel_tools.py index a0e46a1f7..1a77d62f7 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -247,7 +247,7 @@ def show_panel_pwd(): def show_panel_adminpath(): admin_path = model.getOption('admin_path') - print(admin_path) + print('/'+admin_path) def set_panel_username(username=None): From 90a406e5e9e099cf330f616b12b3541ed3bcb2b8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:43:07 +0800 Subject: [PATCH 026/965] Update panel_tools.py --- panel_tools.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index 1a77d62f7..6e4c69bed 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -218,14 +218,11 @@ def open_ssh_port(): def set_panel_pwd(password, ncli=False): - # 设置面板密码 - import db - sql = db.Sql() - result = sql.table('users').where('id=?', (1,)).setField( - 'password', mw.md5(password)) + info = model.getUserByRoot() username = sql.table('users').where('id=?', (1,)).getField('username') + model.setUserByRoot(password=password) if ncli: - print("|-用户名: " + username) + print("|-用户名: " + info['name']) print("|-新密码: " + password) else: print(username) From 0eea41ba85269f728bd8f9cd4d34fe625b0ac91a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:45:07 +0800 Subject: [PATCH 027/965] Update panel_tools.py --- panel_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/panel_tools.py b/panel_tools.py index 6e4c69bed..a6a28cfb3 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -219,7 +219,6 @@ def open_ssh_port(): def set_panel_pwd(password, ncli=False): info = model.getUserByRoot() - username = sql.table('users').where('id=?', (1,)).getField('username') model.setUserByRoot(password=password) if ncli: print("|-用户名: " + info['name']) From 3f3221a3df2314ad91a4332070700032cdd4c234 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:48:04 +0800 Subject: [PATCH 028/965] Update user.py --- web/admin/model/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/admin/model/user.py b/web/admin/model/user.py index ac772206a..2592cc85d 100644 --- a/web/admin/model/user.py +++ b/web/admin/model/user.py @@ -76,7 +76,7 @@ def setUserByRoot( if name is not None: data['password'] = mw.md5(password) - db.session.query(Users).filter_by(Users.id==1).update(data) + db.session.query(Users).filter(Users.id==1).update(data) db.session.commit() db.session.close() return True From b2f595dfb2419be683750d9a0b5408e0890e1bfa Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:52:46 +0800 Subject: [PATCH 029/965] Update user.py --- web/admin/model/user.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/admin/model/user.py b/web/admin/model/user.py index 2592cc85d..e41b85a5a 100644 --- a/web/admin/model/user.py +++ b/web/admin/model/user.py @@ -60,7 +60,7 @@ def getUserByRoot() -> None: def setUserByRoot( name: str | None = None, - password: str | None =None, + password: str | None = None, ) -> bool: ''' 设置配置的值 @@ -76,7 +76,7 @@ def setUserByRoot( if name is not None: data['password'] = mw.md5(password) - db.session.query(Users).filter(Users.id==1).update(data) + Users.query.filter(Users.id==1).update(data) db.session.commit() db.session.close() return True From f8f152affbb113ebd81ae8bba112cb91c488d9bf Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:56:49 +0800 Subject: [PATCH 030/965] Update user.py --- web/admin/model/user.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/admin/model/user.py b/web/admin/model/user.py index e41b85a5a..fac67858e 100644 --- a/web/admin/model/user.py +++ b/web/admin/model/user.py @@ -75,10 +75,11 @@ def setUserByRoot( if name is not None: data['password'] = mw.md5(password) + + print(data) Users.query.filter(Users.id==1).update(data) db.session.commit() - db.session.close() return True def isLoginCheck(username, password) -> bool: From 0467d39c90637a8cfe711f6078b239b6bb5d0046 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:57:12 +0800 Subject: [PATCH 031/965] Update user.py --- web/admin/model/user.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/web/admin/model/user.py b/web/admin/model/user.py index fac67858e..6fa1ec3e7 100644 --- a/web/admin/model/user.py +++ b/web/admin/model/user.py @@ -90,15 +90,3 @@ def isLoginCheck(username, password) -> bool: if info['password'] == mw.md5(password): return True return False - - - - -def isLoginCheck(username, password) -> bool: - info = getUserByName(data['username']) - if info is None: - return False - - if info['password'] == mw.md5(password): - return True - return False From a4cdc1e03b9a4e0c3e9652a90a6c8df1834dea27 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 18:59:08 +0800 Subject: [PATCH 032/965] Update user.py --- web/admin/model/user.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/admin/model/user.py b/web/admin/model/user.py index 6fa1ec3e7..5e9918736 100644 --- a/web/admin/model/user.py +++ b/web/admin/model/user.py @@ -73,10 +73,8 @@ def setUserByRoot( if name is not None: data['name'] = name - if name is not None: + if password is not None: data['password'] = mw.md5(password) - - print(data) Users.query.filter(Users.id==1).update(data) db.session.commit() From 205bb471016979eda5a50b4516f99b913e656265 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 19:00:21 +0800 Subject: [PATCH 033/965] Update user.py --- web/admin/model/user.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/admin/model/user.py b/web/admin/model/user.py index 5e9918736..058e8fbd1 100644 --- a/web/admin/model/user.py +++ b/web/admin/model/user.py @@ -75,6 +75,9 @@ def setUserByRoot( if password is not None: data['password'] = mw.md5(password) + + if not data: + return False Users.query.filter(Users.id==1).update(data) db.session.commit() From 39b3936eca7618a981f5f51384626e2511a8672a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 19:04:14 +0800 Subject: [PATCH 034/965] Update adult_log.py --- web/utils/adult_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/adult_log.py b/web/utils/adult_log.py index f1b4e4a80..303a99417 100644 --- a/web/utils/adult_log.py +++ b/web/utils/adult_log.py @@ -134,7 +134,7 @@ def parseAuditFile(log_name, result): log_list.append(_line) return log_list -def getAuditLast(self, log_name): +def getAuditLast(log_name): # 获取日志 cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format( '/var/log/' + log_name) From 1bae404c5e12bb2941ed6b1703397da0cdef5851 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 19:06:54 +0800 Subject: [PATCH 035/965] Update adult_log.py --- web/utils/adult_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/adult_log.py b/web/utils/adult_log.py index 303a99417..d69d1ccc5 100644 --- a/web/utils/adult_log.py +++ b/web/utils/adult_log.py @@ -173,7 +173,7 @@ def getAuditLast(log_name): # lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True) return mw.returnData(True, 'ok!', lastlog_list) -def getAuditLastLog(self): +def getAuditLastLog(): cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username''' result = mw.execShell(cmd) lastlog_list = [] From 1dc21b3c0ba8c2e0c5fd50a59ab9898d6040b646 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 21:14:26 +0800 Subject: [PATCH 036/965] Update __init__.py --- web/admin/site/__init__.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 12ad0468f..42c3fddf1 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -59,4 +59,26 @@ def list(): @blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) @panel_login_required def get_site_types(): - return [] \ No newline at end of file + return [] + +@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) +@panel_login_required +def get_cli_php_version(): + php_dir = mw.getServerDir() + '/php' + if not os.path.exists(php_dir): + return mw.returnData(False, '未安装PHP,无法设置') + + php_bin = '/usr/bin/php' + php_versions = self.getPhpVersion() + php_versions = php_versions[1:] + + if len(php_versions) < 1: + return mw.returnData(False, '未安装PHP,无法设置') + + if os.path.exists(php_bin) and os.path.islink(php_bin): + link_re = os.readlink(php_bin) + for v in php_versions: + if link_re.find(v['version']) != -1: + return mw.returnData({"select": v, "versions": php_versions}) + + return mw.getJson({"select": php_versions[0],"versions": php_versions}) \ No newline at end of file From e6598ff57d5ba87a5267265e9b215234ec5f144e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 21:16:25 +0800 Subject: [PATCH 037/965] update --- web/admin/site/__init__.py | 3 +- web/utils/site.py | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 web/utils/site.py diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 42c3fddf1..84389b84f 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -16,6 +16,7 @@ from admin.model import Sites from admin.user_login_check import panel_login_required from utils.mwplugin import MwPlugin +from utils.site import site import core.mw as mw blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') @@ -69,7 +70,7 @@ def get_cli_php_version(): return mw.returnData(False, '未安装PHP,无法设置') php_bin = '/usr/bin/php' - php_versions = self.getPhpVersion() + php_versions = site.getPhpVersion() php_versions = php_versions[1:] if len(php_versions) < 1: diff --git a/web/utils/site.py b/web/utils/site.py new file mode 100644 index 000000000..a9a5b0c24 --- /dev/null +++ b/web/utils/site.py @@ -0,0 +1,60 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import json +import threading +import multiprocessing + +from admin import model + +import core.mw as mw + +def getPhpVersion(): + 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 \ No newline at end of file From 210dbfe164b090846cb3412900684694cd8a71ce Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 21:18:49 +0800 Subject: [PATCH 038/965] Update __init__.py --- web/admin/site/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 84389b84f..566135b19 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -16,7 +16,7 @@ from admin.model import Sites from admin.user_login_check import panel_login_required from utils.mwplugin import MwPlugin -from utils.site import site +import utils.site as site import core.mw as mw blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') From 580235b105bfd667f91027d5f7614a3fa62f4967 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 21:24:14 +0800 Subject: [PATCH 039/965] Update __init__.py --- web/admin/site/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 566135b19..de1ab88aa 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -8,6 +8,7 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os from flask import Blueprint, render_template from flask import request From c56c1c7274fa082eefc4e6f8dc9cecff14470d51 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 21:28:39 +0800 Subject: [PATCH 040/965] Update __init__.py --- web/admin/site/__init__.py | 40 +++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index de1ab88aa..9760ebc7d 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -83,4 +83,42 @@ def get_cli_php_version(): if link_re.find(v['version']) != -1: return mw.returnData({"select": v, "versions": php_versions}) - return mw.getJson({"select": php_versions[0],"versions": php_versions}) \ No newline at end of file + return mw.getJson({"select": php_versions[0],"versions": php_versions}) + +@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) +@panel_login_required +def get_cli_php_version(): + if mw.isAppleSystem(): + return mw.returnData(False, "开发机不可设置!") + + version = request.form.get('version', '') + + php_bin = '/usr/bin/php' + php_bin_src = "/www/server/php/%s/bin/php" % version + php_ize = '/usr/bin/phpize' + php_ize_src = "/www/server/php/%s/bin/phpize" % version + php_fpm = '/usr/bin/php-fpm' + php_fpm_src = "/www/server/php/%s/sbin/php-fpm" % version + php_pecl = '/usr/bin/pecl' + php_pecl_src = "/www/server/php/%s/bin/pecl" % version + php_pear = '/usr/bin/pear' + php_pear_src = "/www/server/php/%s/bin/pear" % version + if not os.path.exists(php_bin_src): + return mw.returnData(False, '指定PHP版本未安装!') + + is_chattr = mw.execShell('lsattr /usr|grep /usr/bin')[0].find('-i-') + if is_chattr != -1: + mw.execShell('chattr -i /usr/bin') + mw.execShell("rm -f " + php_bin + ' ' + php_ize + ' ' + + php_fpm + ' ' + php_pecl + ' ' + php_pear) + mw.execShell("ln -sf %s %s" % (php_bin_src, php_bin)) + mw.execShell("ln -sf %s %s" % (php_ize_src, php_ize)) + mw.execShell("ln -sf %s %s" % (php_fpm_src, php_fpm)) + mw.execShell("ln -sf %s %s" % (php_pecl_src, php_pecl)) + mw.execShell("ln -sf %s %s" % (php_pear_src, php_pear)) + if is_chattr != -1: + mw.execShell('chattr +i /usr/bin') + mw.writeLog('面板设置', '设置PHP-CLI版本为: %s' % version) + return mw.returnData(True, '设置成功!') + + From d81764cc789920113ee557f171df2d75c969c5d1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 21:30:18 +0800 Subject: [PATCH 041/965] Update __init__.py --- web/admin/site/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 9760ebc7d..4ec0f7db6 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -85,9 +85,9 @@ def get_cli_php_version(): return mw.getJson({"select": php_versions[0],"versions": php_versions}) -@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) +@blueprint.route('/set_cli_php_version', endpoint='set_cli_php_version',methods=['POST']) @panel_login_required -def get_cli_php_version(): +def set_cli_php_version(): if mw.isAppleSystem(): return mw.returnData(False, "开发机不可设置!") From 8803d3ee03c53a31b67032562937a71806b011d1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 1 Nov 2024 21:30:24 +0800 Subject: [PATCH 042/965] Update new_cli.sh --- new_cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new_cli.sh b/new_cli.sh index c5dd43844..63f4795c7 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -61,7 +61,7 @@ mw_start_debug(){ port=$(cat ${DIR}/data/port.pl) fi # gunicorn -b :${port} -k gevent -w 1 app:app - cd web && gunicorn -b :${port} -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -w 1 app:app } mw_start_debug2(){ From 31571a60e30917c69706add72390944d7de53b0d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 02:19:58 +0800 Subject: [PATCH 043/965] update --- new_cli.sh | 5 +- panel_task.py | 27 ++- panel_tools.py | 17 +- scripts/init.d/mw.tpl | 4 +- web/admin/crontab/__init__.py | 4 +- web/admin/dashboard/__init__.py | 25 ++- web/admin/firewall/__init__.py | 17 +- web/admin/model/__init__.py | 7 + web/admin/model/firewall.py | 41 +++++ web/admin/model/logs.py | 12 +- web/admin/setting/setting.py | 29 ++- web/admin/system/__init__.py | 167 +---------------- web/admin/system/system.py | 158 ++++++++++++++++ web/admin/system/upgrade.py | 34 ++++ web/core/mw.py | 10 +- web/setting.py | 16 +- web/templates/default/layout.html | 24 +-- web/utils/firewall.py | 108 ++++++++++- web/utils/system/__init__.py | 287 +----------------------------- web/utils/system/main.py | 153 ++++++++++++++++ web/utils/system/monitor.py | 21 +++ web/utils/system/query.py | 103 +++++++++++ web/utils/system/stats.py | 161 +++++++++++++++++ 23 files changed, 896 insertions(+), 534 deletions(-) create mode 100644 web/admin/model/firewall.py create mode 100644 web/admin/system/system.py create mode 100644 web/admin/system/upgrade.py create mode 100644 web/utils/system/main.py create mode 100644 web/utils/system/monitor.py create mode 100644 web/utils/system/query.py create mode 100644 web/utils/system/stats.py diff --git a/new_cli.sh b/new_cli.sh index 63f4795c7..8dcd7cd3f 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -3,6 +3,8 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/hom DIR=$(cd "$(dirname "$0")"; pwd) MDIR=$(dirname "$DIR") +export LC_ALL="en_US.UTF-8" + # echo $DIR PATH=$PATH:$DIR/bin @@ -14,9 +16,6 @@ if [ -f ${DIR}/bin/activate ];then fi fi -export LC_ALL="en_US.UTF-8" - - mw_start_task() { isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') diff --git a/panel_task.py b/panel_task.py index b23038993..c5d18a68a 100755 --- a/panel_task.py +++ b/panel_task.py @@ -31,16 +31,6 @@ import core.mw as mw import core.db as db -# print(mw.getPanelDir()) - -# print sys.path - -# 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) - - global pre, timeoutCount, logPath, isTask, oldEdate, isCheck pre = 0 timeoutCount = 0 @@ -85,9 +75,12 @@ def service_cmd(method): execShell(cmd + ' ' + method) return - cmd = mw.getRunDir() + '/scripts/init.d/mw' + cmd = mw.getPanelDir() + '/scripts/init.d/mw' + print(cmd) if os.path.exists(cmd): - execShell(cmd + ' ' + method) + print(cmd + ' ' + method) + data = execShell(cmd + ' ' + method) + print(data) return @@ -573,7 +566,7 @@ def openrestyRestartAtOnce(): # --------------------------------------Panel Restart Start --------------------------------------------- # def restartPanelService(): - restartTip = 'data/restart.pl' + restartTip = mw.getPanelDir()+'/data/restart.pl' while True: if os.path.exists(restartTip): os.remove(restartTip) @@ -612,10 +605,10 @@ def run(): # oar.start() - # # Panel Restart Start - # rps = threading.Thread(target=restartPanelService) - # rps = setDaemon(rps) - # rps.start() + # Panel Restart Start + rps = threading.Thread(target=restartPanelService) + rps = setDaemon(rps) + rps.start() # 面板后台任务 startPanelTask() diff --git a/panel_tools.py b/panel_tools.py index a6a28cfb3..a56444a8f 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -228,16 +228,15 @@ def set_panel_pwd(password, ncli=False): def show_panel_pwd(): - # 设置面板密码 - sql = db.Sql() - password = sql.table('users').where('id=?', (1,)).getField('password') + # 面板密码展示 + info = model.getUserByRoot() + defailt_pwd_file = mw.getPanelDir()+'/data/default.pl' + pwd = '' + if os.path.exists(defailt_pwd_file): + pwd = mw.readFile(defailt_pwd_file).strip() - file_pwd = '' - if os.path.exists('data/default.pl'): - file_pwd = mw.readFile('data/default.pl').strip() - - if mw.md5(file_pwd) == password: - print('password: ' + file_pwd) + if mw.md5(pwd) == info['password']: + print('password: ' + pwd) return print("password has been changed!") diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 4a44f97ed..236c57a63 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -104,7 +104,7 @@ mw_start() # /www/server/mdserver-web/tmp/panelTask.pl && service mw restart_task mw_stop_task() { - if [ -f $mw_path/tmp/panelTask.pl ];then + if [ -f ${PANEL_DIR}/tmp/panelTask.pl ];then echo -e "\033[32mthe task is running and cannot be stopped\033[0m" exit 0 fi @@ -123,7 +123,7 @@ mw_stop_panel() { echo -e "stopping mw-panel... \c"; - pidfile=${mw_path}/logs/mw.pid + pidfile=${PANEL_DIR}/logs/mw.pid if [ -f $pidfile ];then pid=`cat $pidfile` kill -9 $pid > /dev/null 2>&1 diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index 6162e7272..f10762d03 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -27,8 +27,8 @@ def list(): page = request.args.get('p', 1) size = 10 count = Crontab.query.count() - print(count) + # print(count) clist = Crontab.query.paginate(page=int(page), per_page=size) - print(clist) + # print(clist) return [] \ No newline at end of file diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py index 0bf9fdee3..e3fd25933 100644 --- a/web/admin/dashboard/__init__.py +++ b/web/admin/dashboard/__init__.py @@ -32,6 +32,18 @@ blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='.. def index(): return render_template('default/index.html') +# 安全路径 +@blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) +def admin_safe_path(path): + db_path = model.getOption('admin_path') + if db_path == path: + return render_template('default/login.html') + + unauthorized_status = model.getOption('unauthorized_status') + if unauthorized_status == '0': + return render_template('default/path.html') + return Response(status=int(unauthorized_status)) + # 仅针对webhook插件 @blueprint.route("/hook", methods=['POST', 'GET']) def webhook(): @@ -63,19 +75,6 @@ def webhook(): except Exception as e: return str(e) -# 安全路径 -@blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) -def admin_safe_path(path): - db_path = model.getOption('admin_path') - if db_path == path: - return render_template('default/login.html') - - unauthorized_status = model.getOption('unauthorized_status') - if unauthorized_status == '0': - return render_template('default/path.html') - return Response(status=int(unauthorized_status)) - - # --------------------------------------------------------------------------------- # 定义登录入口相关方法 # --------------------------------------------------------------------------------- diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 182390ce4..a14c0dff7 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -75,8 +75,21 @@ def set_ping(): mf = MwFirewall.instance() return mf.setPing() - - +# 添加放行端口 +@blueprint.route('/add_accept_port', endpoint='add_accept_port', methods=['POST']) +@panel_login_required +def add_accept_port(): + mf = MwFirewall.instance() + if not mf.getFwStatus(): + return mw.returnData(False, '防火墙启动时,才能添加规则!') + + port = request.form.get('port', '').strip() + ps = request.form.get('ps', '').strip() + protocol = request.form.get('protocol', '').strip() + stype = request.form.get('type', '').strip() + + data = mf.addAcceptPort(port, ps, stype, protocol=protocol) + return mw.getJson(data) diff --git a/web/admin/model/__init__.py b/web/admin/model/__init__.py index 1d1af4dc7..b0821ac6d 100644 --- a/web/admin/model/__init__.py +++ b/web/admin/model/__init__.py @@ -8,6 +8,7 @@ # Author: midoks # --------------------------------------------------------------------------------- + from .initdb import * from .logs import addLog,clearLog @@ -25,5 +26,11 @@ from .user import isLoginCheck from .user import getUserByName,getUserById,getUserByRoot from .user import setUserByRoot +from .firewall import addFirewall + from .temp_login import getTempLoginByToken,clearTempLogin + + + + diff --git a/web/admin/model/firewall.py b/web/admin/model/firewall.py new file mode 100644 index 000000000..db7c36939 --- /dev/null +++ b/web/admin/model/firewall.py @@ -0,0 +1,41 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from admin.model import db, Firewall + +import core.mw as mw + + + +def addFirewall(port, + protocol: str | None = 'tcp', + ps: str | None = '备注' +) -> bool: + ''' + 设置配置的值 + :port -> str 端口 (必填) + :protocol -> str 协议 (可选|tcp,udp,tcp/udp) + :ps -> str 备注 (可选) + ''' + now_time = mw.formatDate() + add_data = Firewall( + port=port, + protocol=protocol, + add_time=now_time, + update_time=now_time) + db.session.add(add_data) + db.session.commit() + return True + + +def getFirewallCountByPort(port): + return Firewall.query.filter(Tasks.port==port).count() + + diff --git a/web/admin/model/logs.py b/web/admin/model/logs.py index 94f911966..54cda769f 100644 --- a/web/admin/model/logs.py +++ b/web/admin/model/logs.py @@ -13,13 +13,7 @@ import json import time from admin.model import db, Logs - -def formatDate(format="%Y-%m-%d %H:%M:%S", times=None): - # 格式化指定时间戳 - if not times: - times = int(time.time()) - time_local = time.localtime(times) - return time.strftime(format, time_local) +import core.mw as mw def clearLog(): try: @@ -42,12 +36,12 @@ def addLog(type, log, :log -> str 日志内容 (必填) :uid -> int 用户ID ''' - add_time = formatDate() + now_time = mw.formatDate() add_logs = Logs( uid=uid, log=log, type=type, - add_time=add_time) + add_time=now_time) db.session.add(add_logs) db.session.commit() db.session.close() diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 0e3b1a26b..05717339f 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -142,7 +142,7 @@ def set_basic_auth(): return mw.returnData(True, '设置成功!') -# 设置站点状态 +# 设置面板未登录状态 @blueprint.route('/set_status_code', endpoint='set_status_code', methods=['POST']) @panel_login_required def set_status_code(): @@ -159,7 +159,32 @@ def set_status_code(): model.setOption('unauthorized_status', str(status_code)) mw.writeLog('面板设置', '将未授权响应状态码设置为:{0}:{1}'.format(status_code,info['text'])) return mw.returnData(True, '设置成功!') - +# 设置站点状态 +@blueprint.route('/set_port', endpoint='set_port', methods=['POST']) +@panel_login_required +def set_port(): + port = request.form.get('port', '') + if port != mw.getHostPort(): + import system_api + import firewall_api + sysCfgDir = mw.systemdCfgDir() + if os.path.exists(sysCfgDir + "/firewalld.service"): + if not firewall_api.firewall_api().getFwStatus(): + return mw.returnData(False, 'firewalld必须先启动!') + + # mw.setHostPort(port) + + msg = mw.getInfo('放行端口[{1}]成功', (port,)) + mw.writeLog("防火墙管理", msg) + addtime = time.strftime('%Y-%m-%d %X', time.localtime()) + mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime)) + + # firewall_api.firewall_api().addAcceptPort(port) + # firewall_api.firewall_api().firewallReload() + + # system_api.system_api().restartMw() + + return mw.returnJson(True, '端口保存成功!') \ No newline at end of file diff --git a/web/admin/system/__init__.py b/web/admin/system/__init__.py index 840978ec2..47d29fb71 100644 --- a/web/admin/system/__init__.py +++ b/web/admin/system/__init__.py @@ -8,169 +8,6 @@ # Author: midoks # --------------------------------------------------------------------------------- - -from flask import Blueprint, render_template -from flask import request - -from admin.user_login_check import panel_login_required -import admin.model.option as option - -import core.mw as mw -import utils.system as sys - - -blueprint = Blueprint('system', __name__, url_prefix='/system', template_folder='../../templates') - -# 获取系统的统计信息 -@blueprint.route('/system_total', endpoint='system_total') -@panel_login_required -def system_total(): - data = sys.getMemInfo() - cpu = sys.getCpuInfo(interval=1) - data['cpuNum'] = cpu[1] - data['cpuRealUsed'] = cpu[0] - data['time'] = sys.getBootTime() - data['system'] = sys.getSystemVersion() - data['version'] = '0.0.1' - return data - -# 获取系统的网络流量信息 -@blueprint.route('/network', endpoint='network') -@panel_login_required -def network(): - stat = {} - stat['cpu'] = sys.getCpuInfo() - stat['load'] = sys.getLoadAverage() - stat['mem'] = sys.getMemInfo() - stat['iostat'] = sys.stats().disk() - stat['network'] = sys.stats().network() - return stat - -# 获取系统的磁盘信息 -@blueprint.route('/disk_info', endpoint='disk_info') -@panel_login_required -def disk_info(): - data = sys.getDiskInfo() - return data - -# 获取系统的负载统计信息 -@blueprint.route('/get_load_average', endpoint='get_load_average', methods=['GET']) -@panel_login_required -def get_load_average(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('load_average').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?", (start, end,))\ - .field('id,pro,one,five,fifteen,addtime')\ - .order('id asc').select() - # return self.toAddtime(data) - # print(data) - return data - -# 获取系统的磁盘IO统计信息 -@blueprint.route('/get_disk_io', endpoint='get_disk_io', methods=['GET']) -@panel_login_required -def get_disk_io(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('diskio').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?", (start, end))\ - .field('id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime')\ - .order('id asc').select() - return data - -# 获取系统的CPU/IO统计信息 -@blueprint.route('/get_cpu_io', endpoint='get_cpu_io', methods=['GET']) -@panel_login_required -def get_cpu_io(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('cpuio').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?",(start, end))\ - .field('id,pro,mem,addtime')\ - .order('id asc').select() - # return self.toAddtime(data) - # print(data) - return data - - -# 获取系统网络IO统计信息 -@blueprint.route('/get_network_io', endpoint='get_network_io', methods=['GET']) -@panel_login_required -def get_network_io(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('network').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?", (start, end))\ - .field('id,up,down,total_up,total_down,down_packets,up_packets,addtime')\ - .order('id asc').select() - # return self.toAddtime(data) - # print(data) - return data - -# 获取系统网络IO统计信息 -@blueprint.route('/set_control', endpoint='set_control', methods=['POST']) -@panel_login_required -def set_control(): - stype = request.form.get('type', '') - day = request.form.get('day', '') - - if stype == '0': - option.setOption('monitor_status', 'close', type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == '1': - _day = int(day) - if _day < 1: - return mw.returnData(False, "设置失败!") - option.setOption('monitor_day', day, type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == '2': - option.setOption('monitor_only_netio', 'close', type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == '3': - option.setOption('monitor_only_netio', 'open', type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == 'del': - if not mw.isRestart(): - return mw.returnData(False, '请等待所有安装任务完成再执行') - os.remove("data/system.db") - - sql = db.Sql().dbfile('system') - csql = mw.readFile('data/sql/system.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - return mw.returnData(True, "监控服务已关闭") - else: - monitor_status = option.getOption('monitor_status', default='open', type='monitor') - monitor_day = option.getOption('monitor_day', default='30', type='monitor') - monitor_only_netio = option.getOption('monitor_only_netio', default='open', type='monitor') - data = {} - data['day'] = monitor_day - if monitor_status == 'open': - data['status'] = True - else: - data['status'] = False - if monitor_only_netio == 'open': - data['stat_all_status'] = True - else: - data['stat_all_status'] = False - - return data - - return mw.returnData(False, "异常!") - - - - -# 升级检测 -@blueprint.route('/update_server', endpoint='update_server') -@panel_login_required -def update_server(): - panel_type = request.args.get('type', 'check') - version = request.args.get('version', '') - - return mw.returnData(False, '已经是最新,无需更新!') - - +from .system import * +from .upgrade import * diff --git a/web/admin/system/system.py b/web/admin/system/system.py new file mode 100644 index 000000000..cd6e8a578 --- /dev/null +++ b/web/admin/system/system.py @@ -0,0 +1,158 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required +import admin.model.option as option + +import core.mw as mw +import utils.system as sys + + +blueprint = Blueprint('system', __name__, url_prefix='/system', template_folder='../../templates') + +# 获取系统的统计信息 +@blueprint.route('/system_total', endpoint='system_total') +@panel_login_required +def system_total(): + data = sys.getMemInfo() + cpu = sys.getCpuInfo(interval=1) + data['cpuNum'] = cpu[1] + data['cpuRealUsed'] = cpu[0] + data['time'] = sys.getBootTime() + data['system'] = sys.getSystemVersion() + data['version'] = '0.0.1' + return data + +# 获取系统的网络流量信息 +@blueprint.route('/network', endpoint='network') +@panel_login_required +def network(): + stat = {} + stat['cpu'] = sys.getCpuInfo() + stat['load'] = sys.getLoadAverage() + stat['mem'] = sys.getMemInfo() + stat['iostat'] = sys.stats().disk() + stat['network'] = sys.stats().network() + return stat + +# 获取系统的磁盘信息 +@blueprint.route('/disk_info', endpoint='disk_info') +@panel_login_required +def disk_info(): + data = sys.getDiskInfo() + return data + +# 获取系统的负载统计信息 +@blueprint.route('/get_load_average', endpoint='get_load_average', methods=['GET']) +@panel_login_required +def get_load_average(): + start = request.args.get('start', '') + end = request.args.get('end', '') + return sys.getLoadAverageByDB(start, end) + +# 获取系统的磁盘IO统计信息 +@blueprint.route('/get_disk_io', endpoint='get_disk_io', methods=['GET']) +@panel_login_required +def get_disk_io(): + start = request.args.get('start', '') + end = request.args.get('end', '') + return sys.getDiskIoByDB(start, end) + +# 获取系统的CPU/IO统计信息 +@blueprint.route('/get_cpu_io', endpoint='get_cpu_io', methods=['GET']) +@panel_login_required +def get_cpu_io(): + start = request.args.get('start', '') + end = request.args.get('end', '') + return sys.getCpuIoByDB(start, end) + +# 获取系统网络IO统计信息 +@blueprint.route('/get_network_io', endpoint='get_network_io', methods=['GET']) +@panel_login_required +def get_network_io(): + start = request.args.get('start', '') + end = request.args.get('end', '') + return sys.getNetworkIoByDB(start, end) + +# 重启面板 +@blueprint.route('/restart', endpoint='restart', methods=['POST']) +@panel_login_required +def restart(): + mw.restartMw() + return mw.returnData(True, '面板已重启!') + +# 重启面板 +@blueprint.route('/restart_server', endpoint='restart_server', methods=['POST']) +@panel_login_required +def restart_server(): + mw.restartMw() + return mw.returnData(True, '面板已重启!') + + + +# 设置 +@blueprint.route('/set_control', endpoint='set_control', methods=['POST']) +@panel_login_required +def set_control(): + stype = request.form.get('type', '') + day = request.form.get('day', '') + + if stype == '0': + option.setOption('monitor_status', 'close', type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == '1': + _day = int(day) + if _day < 1: + return mw.returnData(False, "设置失败!") + option.setOption('monitor_day', day, type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == '2': + option.setOption('monitor_only_netio', 'close', type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == '3': + option.setOption('monitor_only_netio', 'open', type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == 'del': + if not mw.isRestart(): + return mw.returnData(False, '请等待所有安装任务完成再执行') + os.remove("data/system.db") + + sql = db.Sql().dbfile('system') + csql = mw.readFile('data/sql/system.sql') + csql_list = csql.split(';') + for index in range(len(csql_list)): + sql.execute(csql_list[index], ()) + return mw.returnData(True, "监控服务已关闭") + else: + monitor_status = option.getOption('monitor_status', default='open', type='monitor') + monitor_day = option.getOption('monitor_day', default='30', type='monitor') + monitor_only_netio = option.getOption('monitor_only_netio', default='open', type='monitor') + data = {} + data['day'] = monitor_day + if monitor_status == 'open': + data['status'] = True + else: + data['status'] = False + if monitor_only_netio == 'open': + data['stat_all_status'] = True + else: + data['stat_all_status'] = False + + return data + + return mw.returnData(False, "异常!") + + + + diff --git a/web/admin/system/upgrade.py b/web/admin/system/upgrade.py new file mode 100644 index 000000000..82a00684d --- /dev/null +++ b/web/admin/system/upgrade.py @@ -0,0 +1,34 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from admin import model + +import core.mw as mw +import utils.system as sys + +from .system import blueprint + +# 升级检测 +@blueprint.route('/update_server', endpoint='update_server') +@panel_login_required +def update_server(): + panel_type = request.args.get('type', 'check') + version = request.args.get('version', '') + + return mw.returnData(False, '已经是最新,无需更新!') + + + diff --git a/web/core/mw.py b/web/core/mw.py index 76693f045..94456abfb 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -487,8 +487,9 @@ def getPageObject(args, result='1,2,3,4,5,8'): def getHostPort(): - if os.path.exists('data/port.pl'): - return readFile('data/port.pl').strip() + port_file = getPanelDir() + '/data/port.pl' + if os.path.exists(port_file): + return readFile(port_file).strip() return '7200' @@ -627,6 +628,11 @@ def deDoubleCrypt(key, strings): # ------------------------------ openresty start ----------------------------- +def restartMw(): + restart_file = getPanelDir()+'/data/restart.pl' + writeFile(restart_file, 'True') + return True + def restartWeb(): return opWeb("reload") diff --git a/web/setting.py b/web/setting.py index 1d6961488..d914c71a9 100755 --- a/web/setting.py +++ b/web/setting.py @@ -20,9 +20,8 @@ import os import core.mw as mw - - import utils.system as system + cpu_info = system.getCpuInfo() workers = cpu_info[1] @@ -35,8 +34,9 @@ if not os.path.exists(log_dir): # default port mw_port = '7200' -if os.path.exists(panel_dir+'/data/port.pl'): - mw_port = mw.readFile(panel_dir+'/data/port.pl') +default_port_file = panel_dir+'/data/port.pl' +if os.path.exists(default_port_file): + mw_port = mw.readFile(default_port_file) mw_port.strip() # else: # import firewall_api @@ -47,21 +47,21 @@ if os.path.exists(panel_dir+'/data/port.pl'): # mw.writeFile('data/port.pl', mw_port) bind = [] -if os.path.exists('data/ipv6.pl'): +default_ipv6_file = panel_dir+'/data/ipv6.pl' +if os.path.exists(default_ipv6_file): bind.append('[0:0:0:0:0:0:0:0]:%s' % mw_port) else: bind.append('0.0.0.0:%s' % mw_port) -print(mw_port) if workers > 2: - workers = 2 + workers = 1 threads = workers * 1 backlog = 512 reload = False daemon = True # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -timeout = 7200 +timeout = 600 keepalive = 60 preload_app = True capture_output = True diff --git a/web/templates/default/layout.html b/web/templates/default/layout.html index bf99ad9ec..b02f7540a 100755 --- a/web/templates/default/layout.html +++ b/web/templates/default/layout.html @@ -41,7 +41,7 @@
@@ -137,26 +137,18 @@ $('.panel_ad_list_mode').click(function(){ }); -function getUrlPath(){ - var thisUrl = window.location.pathname; - console.log(thisUrl); - var pathArr = thisUrl.split('/'); - return pathArr[pathArr.length - 1]; -} -var thisPath = getUrlPath(); -// console.log(thisPath); +var thisPath = window.location.pathname.trim()+window.location.search; if (thisPath.indexOf('?')>-1){ var tag = $.getUrlParam('tag'); if(tag != undefined){ $('#memuAplugins_'+tag).css('background-color','#2c3138'); - } else { - $('#memuA').css('background-color','#f2f2f2'); } } else{ - if(thisPath != undefined){ - $('#memuA'+thisPath).css('background-color','#2c3138'); - } else { - $('#memuA').css('background-color','#f2f2f2'); + if(thisPath != '/'){ + var pathArr = thisPath.substring(1).split('/'); + $('#memuA'+pathArr[0]).css('background-color','#2c3138'); + } else{ + $('#memuA').css('background-color','#2c3138'); } } diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 086f14d78..509b17ae6 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -11,6 +11,10 @@ import os import re import threading +import re +import time + +from admin import model import core.mw as mw @@ -43,6 +47,18 @@ class Firewall(object): elif mw.isAppleSystem(): self.__isMac = True + def reload(self): + if self.__isUfw: + mw.execShell('/usr/sbin/ufw reload') + return + elif self.__isIptables: + mw.execShell('service iptables save') + mw.execShell('service iptables restart') + elif self.__isFirewalld: + mw.execShell('firewall-cmd --reload') + else: + pass + def getFwStatus(self): if self.__isUfw: cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'" @@ -134,4 +150,94 @@ class Firewall(object): mw.writeFile(filename, conf) mw.execShell('sysctl -p') - return mw.returnData(True, '设置成功!') \ No newline at end of file + return mw.returnData(True, '设置成功!') + + def setFw(self, status): + if self.__isIptables: + self.setFwIptables(status) + return mw.returnData(True, '设置成功!') + + if status == '1': + if self.__isUfw: + mw.execShell('/usr/sbin/ufw disable') + elif self.__isFirewalld: + mw.execShell('systemctl stop firewalld.service') + mw.execShell('systemctl disable firewalld.service') + else: + pass + else: + if self.__isUfw: + mw.execShell("echo 'y'| ufw enable") + elif self.__isFirewalld: + mw.execShell('systemctl start firewalld.service') + mw.execShell('systemctl enable firewalld.service') + else: + pass + return mw.returnData(True, '设置成功!') + + def addAcceptPortCmd(self, port, + protocol:str | None ='tcp' + ): + if self.__isUfw: + if protocol == 'tcp': + mw.execShell('ufw allow ' + port + '/tcp') + if protocol == 'udp': + mw.execShell('ufw allow ' + port + '/udp') + if protocol == 'tcp/udp': + mw.execShell('ufw allow ' + port + '/tcp') + mw.execShell('ufw allow ' + port + '/udp') + elif self.__isFirewalld: + port = port.replace(':', '-') + if protocol == 'tcp': + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' + mw.execShell(cmd) + if protocol == 'udp': + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' + mw.execShell(cmd) + if protocol == 'tcp/udp': + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' + mw.execShell(cmd) + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' + mw.execShell(cmd) + elif self.__isIptables: + if protocol == 'tcp': + cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + if protocol == 'udp': + cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + if protocol == 'tcp/udp': + cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + else: + pass + return True + + # 添加放行端口 + def addAcceptPort(self, port, ps, stype, + protocol: str | None ='tcp' + ): + if not self.getFwStatus(): + self.setFw(0) + + + rep = r"^\d{1,5}(:\d{1,5})?$" + if not re.search(rep, port): + return mw.returnData(False, '端口范围不正确!') + + if model.getFirewallCountByPort(port) > 0: + return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') + + model.addFirewall(port,ps=ps,protocol=protocol) + self.addAcceptPortCmd(port, protocol=protocol) + self.reload() + + msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,)) + mw.writeLog("防火墙管理", msg) + + return mw.returnData(True, '添加放行(' + port + ')端口成功!') + + + diff --git a/web/utils/system/__init__.py b/web/utils/system/__init__.py index de3430715..7f612c99c 100644 --- a/web/utils/system/__init__.py +++ b/web/utils/system/__init__.py @@ -8,286 +8,7 @@ # Author: midoks # --------------------------------------------------------------------------------- -import os -import sys -import re -import time -import math -import psutil - - -import core.mw as mw - -def getDiskInfo(): - # 取磁盘分区信息 - temp = mw.execShell("df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - tempInodes = mw.execShell("df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - temp1 = temp.split('\n') - tempInodes1 = tempInodes.split('\n') - diskInfo = [] - n = 0 - cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', - '/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user'] - for tmp in temp1: - n += 1 - inodes = tempInodes1[n - 1].split() - disk = tmp.split() - if len(disk) < 5: - continue - if disk[1].find('M') != -1: - continue - if disk[1].find('K') != -1: - continue - if len(disk[5].split('/')) > 4: - continue - if disk[5] in cuts: - continue - arr = {} - arr['path'] = disk[5] - tmp1 = [disk[1], disk[2], disk[3], disk[4]] - arr['size'] = tmp1 - arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]] - diskInfo.append(arr) - return diskInfo - -class stats: - cache = {} - - # 磁盘统计 - def disk(self): - iokey = 'disk_stat' - diskInfo = {} - diskInfo['ALL'] = {} - diskInfo['ALL']['read_count'] = 0 - diskInfo['ALL']['write_count'] = 0 - diskInfo['ALL']['read_bytes'] = 0 - diskInfo['ALL']['write_bytes'] = 0 - diskInfo['ALL']['read_time'] = 0 - diskInfo['ALL']['write_time'] = 0 - diskInfo['ALL']['read_merged_count'] = 0 - diskInfo['ALL']['write_merged_count'] = 0 - - try: - diskio = None - if iokey in self.cache: - diskio = self.cache[iokey] - - mtime = int(time.time()) - if not diskio: - diskio = {} - diskio['info'] = None - diskio['time'] = mtime - - diskio_cache = diskio['info'] - stime = mtime - diskio['time'] - if not stime: stime = 1 - - diskio_group = psutil.disk_io_counters(perdisk=True) - if not diskio_cache: - diskio_cache = diskio_group - - for disk_name in diskio_group.keys(): - diskInfo[disk_name] = {} - # print('disk_name',disk_name) - # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time) - # print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count) - - diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime) - diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime) - diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime) - diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime) - diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime) - diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime) - - if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime) - if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime) - - diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count'] - diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count'] - diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes'] - diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes'] - if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']: - diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time'] - if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']: - diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time'] - - if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count'] - if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count'] - - self.cache[iokey] = {'info':diskio_group,'time':mtime} - except Exception as e: - pass - - return diskInfo - - # 网络统计 - def network(self): - netInfo = {} - - netInfo['ALL'] = {} - netInfo['ALL']['up'] = 0 - netInfo['ALL']['down'] = 0 - netInfo['ALL']['upTotal'] = 0 - netInfo['ALL']['downTotal'] = 0 - netInfo['ALL']['upPackets'] = 0 - netInfo['ALL']['downPackets'] = 0 - - mtime = time.time() - iokey = 'net_stat' - netio = None - if iokey in self.cache: - netio = self.cache[iokey] - - if not netio: - netio = {} - netio['info'] = None - netio['all_io'] = None - netio['time'] = mtime - - stime = mtime - netio['time'] - if not stime: stime = 1 - - # print("new:",stime) - netio_group = psutil.net_io_counters(pernic=True).keys() - - netio_cache = netio['info'] - allio_cache = netio['all_io'] - if not netio_cache: - netio_cache = {} - - netio_group_t = {} - for name in netio_group: - netInfo[name] = {} - - io_data = psutil.net_io_counters(pernic=True).get(name) - if not name in netio_cache: - netio_cache[name] = io_data - - netio_group_t[name] = io_data - - netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2) - netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2) - - netInfo[name]['upTotal'] = io_data[0] - netInfo[name]['downTotal'] = io_data[1] - netInfo[name]['upPackets'] = io_data[2] - netInfo[name]['downPackets'] = io_data[3] - - all_io = psutil.net_io_counters()[:4] - if not allio_cache: - allio_cache = all_io - - netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2) - netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2) - netInfo['ALL']['upTotal'] = all_io[0] - netInfo['ALL']['downTotal'] = all_io[1] - netInfo['ALL']['upPackets'] = all_io[2] - netInfo['ALL']['downPackets'] = all_io[3] - - self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime} - return netInfo - - - -def getLoadAverage(): - c = os.getloadavg() - data = {} - data['one'] = round(float(c[0]), 2) - data['five'] = round(float(c[1]), 2) - data['fifteen'] = round(float(c[2]), 2) - data['max'] = psutil.cpu_count() * 2 - data['limit'] = data['max'] - data['safe'] = data['max'] * 0.75 - return data - -def getSystemVersion(): - # 取操作系统版本 - current_os = mw.getOs() - # sys_temper = self.getSystemDeviceTemperature() - # print(sys_temper) - # mac - if current_os == 'darwin': - data = mw.execShell('sw_vers')[0] - data_list = data.strip().split("\n") - mac_version = '' - for x in data_list: - xlist = x.split("\t") - mac_version += xlist[len(xlist)-1] + ' ' - - arch_ver = mw.execShell("arch") - return mac_version + " (" + arch_ver[0].strip() + ")" - - # freebsd - if current_os.startswith('freebsd'): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - arch_ver = mw.execShell( - "sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - - redhat_series = '/etc/redhat-release' - if os.path.exists(redhat_series): - version = mw.readFile('/etc/redhat-release') - version = version.replace('release ', '').strip() - - arch_ver = mw.execShell("arch") - return version + " (" + arch_ver[0].strip() + ")" - - os_series = '/etc/os-release' - if os.path.exists(os_series): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - - arch_ver = mw.execShell("arch") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - -def getBootTime(): - # 取系统启动时间 - uptime = mw.readFile('/proc/uptime') - if uptime == False: - start_time = psutil.boot_time() - run_time = time.time() - start_time - else: - run_time = uptime.split()[0] - tStr = float(run_time) - min = tStr / 60 - hours = min / 60 - days = math.floor(hours / 24) - hours = math.floor(hours - (days * 24)) - min = math.floor(min - (days * 60 * 24) - (hours * 60)) - return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min)))) - -def getCpuInfo(interval=1): - # 取CPU信息 - cpuCount = psutil.cpu_count() - cpuLogicalNum = psutil.cpu_count(logical=False) - used = psutil.cpu_percent(interval=interval) - cpuLogicalNum = 0 - if os.path.exists('/proc/cpuinfo'): - c_tmp = mw.readFile('/proc/cpuinfo') - d_tmp = re.findall("physical id.+", c_tmp) - cpuLogicalNum = len(set(d_tmp)) - - used_all = psutil.cpu_percent(percpu=True) - cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum) - return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum - -def getMemInfo(): - # 取内存信息 - mem = psutil.virtual_memory() - if sys.platform == 'darwin': - memInfo = {'memTotal': mem.total} - memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) - else: - memInfo = { - 'memTotal': mem.total, - 'memFree': mem.free, - 'memBuffers': mem.buffers, - 'memCached': mem.cached - } - memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached'] - return memInfo \ No newline at end of file +from .main import * +from .query import * +from .stats import * +from .monitor import * \ No newline at end of file diff --git a/web/utils/system/main.py b/web/utils/system/main.py new file mode 100644 index 000000000..4e7b8a7b4 --- /dev/null +++ b/web/utils/system/main.py @@ -0,0 +1,153 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil + + +import core.mw as mw + +def getDiskInfo(): + # 取磁盘分区信息 + temp = mw.execShell("df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] + tempInodes = mw.execShell("df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] + temp1 = temp.split('\n') + tempInodes1 = tempInodes.split('\n') + diskInfo = [] + n = 0 + cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', + '/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user'] + for tmp in temp1: + n += 1 + inodes = tempInodes1[n - 1].split() + disk = tmp.split() + if len(disk) < 5: + continue + if disk[1].find('M') != -1: + continue + if disk[1].find('K') != -1: + continue + if len(disk[5].split('/')) > 4: + continue + if disk[5] in cuts: + continue + arr = {} + arr['path'] = disk[5] + tmp1 = [disk[1], disk[2], disk[3], disk[4]] + arr['size'] = tmp1 + arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]] + diskInfo.append(arr) + return diskInfo + + +def getLoadAverage(): + c = os.getloadavg() + data = {} + data['one'] = round(float(c[0]), 2) + data['five'] = round(float(c[1]), 2) + data['fifteen'] = round(float(c[2]), 2) + data['max'] = psutil.cpu_count() * 2 + data['limit'] = data['max'] + data['safe'] = data['max'] * 0.75 + return data + +def getSystemVersion(): + # 取操作系统版本 + current_os = mw.getOs() + # sys_temper = self.getSystemDeviceTemperature() + # print(sys_temper) + # mac + if current_os == 'darwin': + data = mw.execShell('sw_vers')[0] + data_list = data.strip().split("\n") + mac_version = '' + for x in data_list: + xlist = x.split("\t") + mac_version += xlist[len(xlist)-1] + ' ' + + arch_ver = mw.execShell("arch") + return mac_version + " (" + arch_ver[0].strip() + ")" + + # freebsd + if current_os.startswith('freebsd'): + version = mw.execShell( + "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + arch_ver = mw.execShell( + "sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'") + return version[0].strip() + " (" + arch_ver[0].strip() + ")" + + redhat_series = '/etc/redhat-release' + if os.path.exists(redhat_series): + version = mw.readFile('/etc/redhat-release') + version = version.replace('release ', '').strip() + + arch_ver = mw.execShell("arch") + return version + " (" + arch_ver[0].strip() + ")" + + os_series = '/etc/os-release' + if os.path.exists(os_series): + version = mw.execShell( + "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + + arch_ver = mw.execShell("arch") + return version[0].strip() + " (" + arch_ver[0].strip() + ")" + +def getBootTime(): + # 取系统启动时间 + uptime = mw.readFile('/proc/uptime') + if uptime == False: + start_time = psutil.boot_time() + run_time = time.time() - start_time + else: + run_time = uptime.split()[0] + tStr = float(run_time) + min = tStr / 60 + hours = min / 60 + days = math.floor(hours / 24) + hours = math.floor(hours - (days * 24)) + min = math.floor(min - (days * 60 * 24) - (hours * 60)) + return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min)))) + +def getCpuInfo(interval=1): + # 取CPU信息 + cpuCount = psutil.cpu_count() + cpuLogicalNum = psutil.cpu_count(logical=False) + used = psutil.cpu_percent(interval=interval) + cpuLogicalNum = 0 + if os.path.exists('/proc/cpuinfo'): + c_tmp = mw.readFile('/proc/cpuinfo') + d_tmp = re.findall("physical id.+", c_tmp) + cpuLogicalNum = len(set(d_tmp)) + + used_all = psutil.cpu_percent(percpu=True) + cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum) + return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum + +def getMemInfo(): + # 取内存信息 + mem = psutil.virtual_memory() + if sys.platform == 'darwin': + memInfo = {'memTotal': mem.total} + memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) + else: + memInfo = { + 'memTotal': mem.total, + 'memFree': mem.free, + 'memBuffers': mem.buffers, + 'memCached': mem.cached + } + memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached'] + return memInfo + + diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py new file mode 100644 index 000000000..2f33b438a --- /dev/null +++ b/web/utils/system/monitor.py @@ -0,0 +1,21 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil + +import core.mw as mw + + + diff --git a/web/utils/system/query.py b/web/utils/system/query.py new file mode 100644 index 000000000..9d2710070 --- /dev/null +++ b/web/utils/system/query.py @@ -0,0 +1,103 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil + + +import core.mw as mw + +# -------------------------------------------- 数据查询相关 -------------------------------------------- +# 格式化addtime列 +def toAddtime(data, tomem=False): + import time + if tomem: + import psutil + mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100 + length = len(data) + he = 1 + if length > 100: + he = 1 + if length > 1000: + he = 3 + if length > 10000: + he = 15 + if he == 1: + for i in range(length): + data[i]['addtime'] = time.strftime( + '%m/%d %H:%M', time.localtime(float(data[i]['addtime']))) + if tomem and data[i]['mem'] > 100: + data[i]['mem'] = data[i]['mem'] / mPre + + return data + else: + count = 0 + tmp = [] + for value in data: + if count < he: + count += 1 + continue + value['addtime'] = time.strftime( + '%m/%d %H:%M', time.localtime(float(value['addtime']))) + if tomem and value['mem'] > 100: + value['mem'] = value['mem'] / mPre + tmp.append(value) + count = 0 + return tmp + +# 格式化addtime列 +def toUseAddtime(data): + dlen = len(data) + for i in range(dlen): + data[i]['addtime'] = time.strftime('%m/%d %H:%M', time.localtime(float(data[i]['addtime']))) + return data + +def getLoadAverageByDB(start, end): + # 获取系统的负载统计信息 + data = mw.M('load_average').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?", (start, end,))\ + .field('pro,one,five,fifteen,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +def getDiskIoByDB(start, end): + # 获取系统的磁盘IO统计信息 + data = mw.M('diskio').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?", (start, end))\ + .field('read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +def getCpuIoByDB(start, end): + # 获取系统的CPU/IO统计信息 + data = mw.M('cpuio').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?",(start, end))\ + .field('pro,mem,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +def getNetworkIoByDB(start, end): + # 获取系统网络IO统计信息 + # id, + data = mw.M('network').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?", (start, end))\ + .field('up,down,total_up,total_down,down_packets,up_packets,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +# -------------------------------------------- 数据查询相关 -------------------------------------------- diff --git a/web/utils/system/stats.py b/web/utils/system/stats.py new file mode 100644 index 000000000..123e5e80b --- /dev/null +++ b/web/utils/system/stats.py @@ -0,0 +1,161 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil + +import core.mw as mw + +class stats: + cache = {} + + # 磁盘统计 + def disk(self): + iokey = 'disk_stat' + diskInfo = {} + diskInfo['ALL'] = {} + diskInfo['ALL']['read_count'] = 0 + diskInfo['ALL']['write_count'] = 0 + diskInfo['ALL']['read_bytes'] = 0 + diskInfo['ALL']['write_bytes'] = 0 + diskInfo['ALL']['read_time'] = 0 + diskInfo['ALL']['write_time'] = 0 + diskInfo['ALL']['read_merged_count'] = 0 + diskInfo['ALL']['write_merged_count'] = 0 + + try: + diskio = None + if iokey in self.cache: + diskio = self.cache[iokey] + + mtime = int(time.time()) + if not diskio: + diskio = {} + diskio['info'] = None + diskio['time'] = mtime + + diskio_cache = diskio['info'] + stime = mtime - diskio['time'] + if not stime: stime = 1 + + diskio_group = psutil.disk_io_counters(perdisk=True) + if not diskio_cache: + diskio_cache = diskio_group + + for disk_name in diskio_group.keys(): + diskInfo[disk_name] = {} + # print('disk_name',disk_name) + # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time) + # print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count) + + diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime) + diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime) + diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime) + diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime) + diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime) + diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime) + + if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]: + diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime) + if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]: + diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime) + + diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count'] + diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count'] + diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes'] + diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes'] + if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']: + diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time'] + if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']: + diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time'] + + if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]: + diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count'] + if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]: + diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count'] + + self.cache[iokey] = {'info':diskio_group,'time':mtime} + except Exception as e: + pass + + return diskInfo + + # 网络统计 + def network(self): + netInfo = {} + + netInfo['ALL'] = {} + netInfo['ALL']['up'] = 0 + netInfo['ALL']['down'] = 0 + netInfo['ALL']['upTotal'] = 0 + netInfo['ALL']['downTotal'] = 0 + netInfo['ALL']['upPackets'] = 0 + netInfo['ALL']['downPackets'] = 0 + + mtime = time.time() + iokey = 'net_stat' + netio = None + if iokey in self.cache: + netio = self.cache[iokey] + + if not netio: + netio = {} + netio['info'] = None + netio['all_io'] = None + netio['time'] = mtime + + stime = mtime - netio['time'] + if not stime: stime = 1 + + # print("new:",stime) + netio_group = psutil.net_io_counters(pernic=True).keys() + + netio_cache = netio['info'] + allio_cache = netio['all_io'] + if not netio_cache: + netio_cache = {} + + netio_group_t = {} + for name in netio_group: + netInfo[name] = {} + + io_data = psutil.net_io_counters(pernic=True).get(name) + if not name in netio_cache: + netio_cache[name] = io_data + + netio_group_t[name] = io_data + + netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2) + netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2) + + netInfo[name]['upTotal'] = io_data[0] + netInfo[name]['downTotal'] = io_data[1] + netInfo[name]['upPackets'] = io_data[2] + netInfo[name]['downPackets'] = io_data[3] + + all_io = psutil.net_io_counters()[:4] + if not allio_cache: + allio_cache = all_io + + netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2) + netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2) + netInfo['ALL']['upTotal'] = all_io[0] + netInfo['ALL']['downTotal'] = all_io[1] + netInfo['ALL']['upPackets'] = all_io[2] + netInfo['ALL']['downPackets'] = all_io[3] + + self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime} + return netInfo + + From 472f441c0e3e195efeed046778565c03a5f11c61 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 02:47:47 +0800 Subject: [PATCH 044/965] update --- new_cli.sh | 6 +++--- panel_task.py | 6 +++--- scripts/rememory.sh | 2 +- web/app.py | 1 - web/utils/system/monitor.py | 6 ++++++ 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/new_cli.sh b/new_cli.sh index 8dcd7cd3f..33bfe5bd5 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -65,12 +65,12 @@ mw_start_debug(){ mw_start_debug2(){ python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & - gunicorn -b :7200 -w 1 app:app + cd ${DIR}/web && gunicorn -b :7200 -w 1 app:app } mw_start_debug3(){ - gunicorn -c setting.py app:app - python3 panel_task.py + cd ${DIR}/web && gunicorn -c setting.py app:app + cd ${DIR} && python3 panel_task.py } diff --git a/panel_task.py b/panel_task.py index c5d18a68a..8382b29bb 100755 --- a/panel_task.py +++ b/panel_task.py @@ -584,9 +584,9 @@ def setDaemon(t): def run(): # # 系统监控 - # sysTask = threading.Thread(target=systemTask) - # sysTask = setDaemon(sysTask) - # sysTask.start() + sysTask = threading.Thread(target=systemTask) + sysTask = setDaemon(sysTask) + sysTask.start() # # PHP 502错误检查线程 # php502 = threading.Thread(target=check502Task) diff --git a/scripts/rememory.sh b/scripts/rememory.sh index 933f7eff2..5311e7aa6 100755 --- a/scripts/rememory.sh +++ b/scripts/rememory.sh @@ -32,7 +32,7 @@ fi echo "OpenResty -- END" -PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81) +PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84) for PHP_VER in ${PHP_VER_LIST[@]}; do echo "PHP${PHP_VER} -- START" if [ -f /usr/lib/systemd/system/php${PHP_VER}.service ];then diff --git a/web/app.py b/web/app.py index b51d018a1..aa0fe2680 100644 --- a/web/app.py +++ b/web/app.py @@ -20,7 +20,6 @@ import os if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) -# print(sys.path) import config from admin import app, socketio diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index 2f33b438a..1abb5db39 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -18,4 +18,10 @@ import psutil import core.mw as mw +class monitor: + + def initDBFile(): + pass + + From 01b6ede03e920e3a3bcbbd12aa056f06af072290 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 15:41:33 +0800 Subject: [PATCH 045/965] update --- plugins/openresty/info.json | 2 +- plugins/openresty/versions/1.27.1/install.sh | 180 +++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 plugins/openresty/versions/1.27.1/install.sh diff --git a/plugins/openresty/info.json b/plugins/openresty/info.json index 8ddc42b42..139160771 100755 --- a/plugins/openresty/info.json +++ b/plugins/openresty/info.json @@ -12,6 +12,6 @@ "home":"http://openresty.org", "date":"2017-11-24", "pid": "1", - "versions": ["1.17.8","1.19.3","1.21.4","1.25.3"], + "versions": ["1.17.8","1.19.3","1.21.4","1.25.3","1.27.1"], "updates": ["1.17.8.2","1.19.3.1","1.21.4.2","1.25.3.2"] } \ No newline at end of file diff --git a/plugins/openresty/versions/1.27.1/install.sh b/plugins/openresty/versions/1.27.1/install.sh new file mode 100644 index 000000000..2183be696 --- /dev/null +++ b/plugins/openresty/versions/1.27.1/install.sh @@ -0,0 +1,180 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 +# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +sysName=`uname` +action=$1 +type=$2 + +VERSION=1.27.1.1 + +install_tmp=${rootPath}/tmp/mw_install.pl +openrestyDir=${serverPath}/source/openresty + +Install_openresty() +{ + if [ -d $serverPath/openresty ];then + exit 0 + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + if [ "$cpuCore" -gt "2" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + else + cpuCore="1" + fi + # ----- cpu end ------ + + mkdir -p ${openrestyDir} + echo '正在安装脚本文件...' + + # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3 + fi + + DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'` + if [ "$DOWNLOAD_SIZE" == "0" ];then + echo 'download failed, download again' + rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz + fi + + # Last Download Method + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3 + fi + + cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz + + OPTIONS='' + + opensslVersion="1.1.1p" + libresslVersion="3.9.1" + pcreVersion='8.38' + if [ "$sysName" == "Darwin" ];then + + if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then + cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}" + + + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + # BREW_DIR=`which brew` + # BREW_DIR=${BREW_DIR/\/bin\/brew/} + + # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}' + # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'` + # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}" + else + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + fi + + if [[ "$VERSION" =~ "1.25.3" ]]; then + OPTIONS="${OPTIONS} --with-http_v3_module" + + if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then + cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz + fi + + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include" + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib" + fi + + + cd ${openrestyDir}/openresty-${VERSION} && ./configure \ + --prefix=$serverPath/openresty \ + $OPTIONS \ + --with-stream \ + --with-http_v2_module \ + --with-http_ssl_module \ + --with-http_slice_module \ + --with-http_stub_status_module \ + --with-http_sub_module \ + --with-http_realip_module + # --without-luajit-gc64 + # --with-debug + # 用于调式 + + CMD_MAKE=`which gmake` + if [ "$?" == "0" ];then + gmake -j${cpuCore} && gmake install && gmake clean + else + make -j${cpuCore} && make install && make clean + fi + + + if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then + rm -rf ${openrestyDir}/pcre-${pcreVersion} + fi + + if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then + rm -rf ${openrestyDir}/openssl-${opensslVersion} + fi + + if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then + rm -rf ${openrestyDir}/libressl-${libresslVersion} + fi + echo '安装完成' +} + +Uninstall_openresty() +{ + echo '卸载完成' +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_openresty +else + Uninstall_openresty +fi From 07bca6cac8793b5e37427d2b2102875e4d808d82 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 15:54:37 +0800 Subject: [PATCH 046/965] update --- plugins/openresty/info.json | 2 +- plugins/openresty/versions/rmtp/install.sh | 180 +++++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 plugins/openresty/versions/rmtp/install.sh diff --git a/plugins/openresty/info.json b/plugins/openresty/info.json index 139160771..dbb697869 100755 --- a/plugins/openresty/info.json +++ b/plugins/openresty/info.json @@ -12,6 +12,6 @@ "home":"http://openresty.org", "date":"2017-11-24", "pid": "1", - "versions": ["1.17.8","1.19.3","1.21.4","1.25.3","1.27.1"], + "versions": ["1.17.8","1.19.3","1.21.4","1.25.3","1.27.1","rmtp"], "updates": ["1.17.8.2","1.19.3.1","1.21.4.2","1.25.3.2"] } \ No newline at end of file diff --git a/plugins/openresty/versions/rmtp/install.sh b/plugins/openresty/versions/rmtp/install.sh new file mode 100644 index 000000000..f488be01b --- /dev/null +++ b/plugins/openresty/versions/rmtp/install.sh @@ -0,0 +1,180 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.27.1.1 +# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.27.1.1 + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +sysName=`uname` +action=$1 +type=$2 + +VERSION=1.27.1.1 + +install_tmp=${rootPath}/tmp/mw_install.pl +openrestyDir=${serverPath}/source/openresty + +Install_openresty() +{ + if [ -d $serverPath/openresty ];then + exit 0 + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + if [ "$cpuCore" -gt "2" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + else + cpuCore="1" + fi + # ----- cpu end ------ + + mkdir -p ${openrestyDir} + echo '正在安装脚本文件...' + + # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3 + fi + + DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'` + if [ "$DOWNLOAD_SIZE" == "0" ];then + echo 'download failed, download again' + rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz + fi + + # Last Download Method + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3 + fi + + cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz + + OPTIONS='' + + opensslVersion="1.1.1p" + libresslVersion="3.9.1" + pcreVersion='8.38' + if [ "$sysName" == "Darwin" ];then + + if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then + cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}" + + + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + # BREW_DIR=`which brew` + # BREW_DIR=${BREW_DIR/\/bin\/brew/} + + # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}' + # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'` + # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}" + else + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + fi + + if [[ "$VERSION" =~ "1.25.3" ]]; then + OPTIONS="${OPTIONS} --with-http_v3_module" + + if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then + cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz + fi + + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include" + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib" + fi + + + cd ${openrestyDir}/openresty-${VERSION} && ./configure \ + --prefix=$serverPath/openresty \ + $OPTIONS \ + --with-stream \ + --with-http_v2_module \ + --with-http_ssl_module \ + --with-http_slice_module \ + --with-http_stub_status_module \ + --with-http_sub_module \ + --with-http_realip_module + # --without-luajit-gc64 + # --with-debug + # 用于调式 + + CMD_MAKE=`which gmake` + if [ "$?" == "0" ];then + gmake -j${cpuCore} && gmake install && gmake clean + else + make -j${cpuCore} && make install && make clean + fi + + + if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then + rm -rf ${openrestyDir}/pcre-${pcreVersion} + fi + + if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then + rm -rf ${openrestyDir}/openssl-${opensslVersion} + fi + + if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then + rm -rf ${openrestyDir}/libressl-${libresslVersion} + fi + echo '安装完成' +} + +Uninstall_openresty() +{ + echo '卸载完成' +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_openresty +else + Uninstall_openresty +fi From b66ab079ed58873fd018462c665ff5294ea427d2 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 15:58:50 +0800 Subject: [PATCH 047/965] Update install.sh --- plugins/openresty/versions/rmtp/install.sh | 64 +++++++++++++--------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/plugins/openresty/versions/rmtp/install.sh b/plugins/openresty/versions/rmtp/install.sh index f488be01b..6c950453d 100644 --- a/plugins/openresty/versions/rmtp/install.sh +++ b/plugins/openresty/versions/rmtp/install.sh @@ -27,7 +27,7 @@ Install_openresty() # ----- cpu start ------ if [ -z "${cpuCore}" ]; then - cpuCore="1" + cpuCore="1" fi if [ -f /proc/cpuinfo ];then @@ -36,11 +36,11 @@ Install_openresty() MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then - if [ "${cpuCore}" -gt "${MEM_INFO}" ];then - cpuCore="${MEM_INFO}" - fi + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi else - cpuCore="1" + cpuCore="1" fi if [ "$cpuCore" -gt "2" ];then @@ -89,13 +89,13 @@ Install_openresty() if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then - wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz - fi + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi - if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz fi - OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" # BREW_DIR=`which brew` # BREW_DIR=${BREW_DIR/\/bin\/brew/} @@ -105,10 +105,10 @@ Install_openresty() # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}" else if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then - wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz - fi + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi - if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz fi OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" @@ -119,17 +119,27 @@ Install_openresty() OPTIONS="${OPTIONS} --with-http_v3_module" if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then - wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz - fi + wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz + fi - if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then + if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz fi - - OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include" - OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib" + + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include" + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib" fi + # rtmp推流功能 + if [ ! -f ${openrestyDir}/nginx-rtmp-module.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/nginx-rtmp-module.tar.gz https://github.com/arut/nginx-rtmp-module/archive/refs/tags/v1.2.2.tar.gz + fi + + if [ ! -d ${openrestyDir}/nginx-rtmp-module.tar.gz ];then + cd ${openrestyDir} && tar -zxvf nginx-rtmp-module.tar.gz + fi + OPTIONS="${OPTIONS} --add-module=${openrestyDir}/nginx-rtmp-module" + cd ${openrestyDir}/openresty-${VERSION} && ./configure \ --prefix=$serverPath/openresty \ @@ -153,17 +163,17 @@ Install_openresty() fi - if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then - rm -rf ${openrestyDir}/pcre-${pcreVersion} - fi + if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then + rm -rf ${openrestyDir}/pcre-${pcreVersion} + fi - if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then - rm -rf ${openrestyDir}/openssl-${opensslVersion} - fi + if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then + rm -rf ${openrestyDir}/openssl-${opensslVersion} + fi - if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then - rm -rf ${openrestyDir}/libressl-${libresslVersion} - fi + if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then + rm -rf ${openrestyDir}/libressl-${libresslVersion} + fi echo '安装完成' } From c6418069fbb49f669cd7b5c7efe9c99d78f0a291 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 16:01:48 +0800 Subject: [PATCH 048/965] Update install.sh --- plugins/openresty/versions/rmtp/install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/openresty/versions/rmtp/install.sh b/plugins/openresty/versions/rmtp/install.sh index 6c950453d..9050814de 100644 --- a/plugins/openresty/versions/rmtp/install.sh +++ b/plugins/openresty/versions/rmtp/install.sh @@ -131,14 +131,15 @@ Install_openresty() fi # rtmp推流功能 + nginx_rtmp_ver=1.2.2 if [ ! -f ${openrestyDir}/nginx-rtmp-module.tar.gz ];then - wget --no-check-certificate -O ${openrestyDir}/nginx-rtmp-module.tar.gz https://github.com/arut/nginx-rtmp-module/archive/refs/tags/v1.2.2.tar.gz + wget --no-check-certificate -O ${openrestyDir}/nginx-rtmp-module.tar.gz https://github.com/arut/nginx-rtmp-module/archive/refs/tags/v${nginx_rtmp_ver}.tar.gz fi if [ ! -d ${openrestyDir}/nginx-rtmp-module.tar.gz ];then cd ${openrestyDir} && tar -zxvf nginx-rtmp-module.tar.gz fi - OPTIONS="${OPTIONS} --add-module=${openrestyDir}/nginx-rtmp-module" + OPTIONS="${OPTIONS} --add-module=${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver}" cd ${openrestyDir}/openresty-${VERSION} && ./configure \ From 2a19a8f93d7ac934965240d064dadfbbceff9beb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 16:03:58 +0800 Subject: [PATCH 049/965] Update install.sh --- plugins/openresty/versions/rmtp/install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/openresty/versions/rmtp/install.sh b/plugins/openresty/versions/rmtp/install.sh index 9050814de..93b0f2a0f 100644 --- a/plugins/openresty/versions/rmtp/install.sh +++ b/plugins/openresty/versions/rmtp/install.sh @@ -175,6 +175,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver} ];then + rm -rf ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver} + fi echo '安装完成' } From 6b378a9712d91c96d712135f74d5a5301799a906 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 16:12:55 +0800 Subject: [PATCH 050/965] update --- plugins/openresty/info.json | 2 +- plugins/openresty/versions/{rmtp => rtmp}/install.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename plugins/openresty/versions/{rmtp => rtmp}/install.sh (100%) diff --git a/plugins/openresty/info.json b/plugins/openresty/info.json index dbb697869..5f9e808aa 100755 --- a/plugins/openresty/info.json +++ b/plugins/openresty/info.json @@ -12,6 +12,6 @@ "home":"http://openresty.org", "date":"2017-11-24", "pid": "1", - "versions": ["1.17.8","1.19.3","1.21.4","1.25.3","1.27.1","rmtp"], + "versions": ["1.17.8","1.19.3","1.21.4","1.25.3","1.27.1","rtmp"], "updates": ["1.17.8.2","1.19.3.1","1.21.4.2","1.25.3.2"] } \ No newline at end of file diff --git a/plugins/openresty/versions/rmtp/install.sh b/plugins/openresty/versions/rtmp/install.sh similarity index 100% rename from plugins/openresty/versions/rmtp/install.sh rename to plugins/openresty/versions/rtmp/install.sh From 083f5e22710f8b922bd2a305825b7b33b77d6a65 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 16:13:16 +0800 Subject: [PATCH 051/965] update --- web/admin/__init__.py | 1 + web/admin/setup/__init__.py | 1 + web/admin/setup/init_db_system.py | 16 ++++++++++++++++ 3 files changed, 18 insertions(+) create mode 100644 web/admin/setup/init_db_system.py diff --git a/web/admin/__init__.py b/web/admin/__init__.py index d8f3ff159..87dd08ffd 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -93,6 +93,7 @@ with app.app_context(): if setup_db_required: setup.init_admin_user() setup.init_option() +setup.init_db_system() app.config['BASIC_AUTH_OPEN'] = False with app.app_context(): diff --git a/web/admin/setup/__init__.py b/web/admin/setup/__init__.py index 234f2f7de..7cd824ba3 100644 --- a/web/admin/setup/__init__.py +++ b/web/admin/setup/__init__.py @@ -10,4 +10,5 @@ from .user import init_admin_user from .option import init_option +from .init_db_system import init_db_system diff --git a/web/admin/setup/init_db_system.py b/web/admin/setup/init_db_system.py new file mode 100644 index 000000000..bcce3db89 --- /dev/null +++ b/web/admin/setup/init_db_system.py @@ -0,0 +1,16 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from utils.system import monitor + + +def init_db_system(): + monitor.instance().initDBFile() + return True \ No newline at end of file From 5788693e6cc2657103ac19110869d97c7d877bba Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 16:14:29 +0800 Subject: [PATCH 052/965] Update install.sh --- plugins/openresty/versions/rtmp/install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/openresty/versions/rtmp/install.sh b/plugins/openresty/versions/rtmp/install.sh index 93b0f2a0f..eab364971 100644 --- a/plugins/openresty/versions/rtmp/install.sh +++ b/plugins/openresty/versions/rtmp/install.sh @@ -179,6 +179,11 @@ Install_openresty() if [ -d ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver} ];then rm -rf ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi + echo '安装完成' } From fa64dfb89b77d8b79d56b3aa525c6ba51f40a8e0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 16:15:21 +0800 Subject: [PATCH 053/965] update --- plugins/openresty/install.sh | 1 - plugins/openresty/versions/1.17.8/install.sh | 5 +++++ plugins/openresty/versions/1.19.3/install.sh | 4 ++++ plugins/openresty/versions/1.21.4/install.sh | 4 ++++ plugins/openresty/versions/1.25.3/install.sh | 4 ++++ plugins/openresty/versions/1.27.1/install.sh | 4 ++++ 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/plugins/openresty/install.sh b/plugins/openresty/install.sh index 3c72e5451..35680f32d 100755 --- a/plugins/openresty/install.sh +++ b/plugins/openresty/install.sh @@ -57,5 +57,4 @@ if [ "${action}" == "install" ] && [ -d $serverPath/openresty ];then #初始化 cd ${rootPath} && python3 ${rootPath}/plugins/openresty/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/openresty/index.py initd_install - rm -rf $openrestyDir/openresty-${VERSION} fi diff --git a/plugins/openresty/versions/1.17.8/install.sh b/plugins/openresty/versions/1.17.8/install.sh index d3bf95aab..4259f71ea 100644 --- a/plugins/openresty/versions/1.17.8/install.sh +++ b/plugins/openresty/versions/1.17.8/install.sh @@ -149,6 +149,11 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi + echo '安装完成' } diff --git a/plugins/openresty/versions/1.19.3/install.sh b/plugins/openresty/versions/1.19.3/install.sh index 9934e2a6c..555a7a609 100644 --- a/plugins/openresty/versions/1.19.3/install.sh +++ b/plugins/openresty/versions/1.19.3/install.sh @@ -150,6 +150,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi echo '安装完成' } diff --git a/plugins/openresty/versions/1.21.4/install.sh b/plugins/openresty/versions/1.21.4/install.sh index 531e38368..817699157 100644 --- a/plugins/openresty/versions/1.21.4/install.sh +++ b/plugins/openresty/versions/1.21.4/install.sh @@ -149,6 +149,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi echo '安装完成' } diff --git a/plugins/openresty/versions/1.25.3/install.sh b/plugins/openresty/versions/1.25.3/install.sh index 9c5224662..473fe1938 100644 --- a/plugins/openresty/versions/1.25.3/install.sh +++ b/plugins/openresty/versions/1.25.3/install.sh @@ -164,6 +164,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi echo '安装完成' } diff --git a/plugins/openresty/versions/1.27.1/install.sh b/plugins/openresty/versions/1.27.1/install.sh index 2183be696..8e4d01ca6 100644 --- a/plugins/openresty/versions/1.27.1/install.sh +++ b/plugins/openresty/versions/1.27.1/install.sh @@ -164,6 +164,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi echo '安装完成' } From 18e59ef377f064f62b5b890743797975c78a5954 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 2 Nov 2024 16:16:31 +0800 Subject: [PATCH 054/965] up --- web/core/db.py | 24 +++++++----- web/core/mw.py | 4 +- web/utils/system/__init__.py | 2 +- web/utils/system/main.py | 17 +++++++++ web/utils/system/monitor.py | 72 +++++++++++++++++++++++++++++++++++- 5 files changed, 105 insertions(+), 14 deletions(-) diff --git a/web/core/db.py b/web/core/db.py index 10bddcc09..e5c1107a8 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -12,11 +12,18 @@ # sqlite3操作 # --------------------------------------------------------------------------------- - -import sqlite3 import os import sys +import sqlite3 +import core.mw as mw + +def getPanelDir(): + return os.path.dirname(os.getcwd()) + +def getTracebackInfo(): + import traceback + return traceback.format_exc() class Sql(): #------------------------------ @@ -33,7 +40,7 @@ class Sql(): __OPT_PARAM = () # where值 def __init__(self): - self.__DB_FILE = '../data/default.db' + self.__DB_FILE = getPanelDir()+'/data/panel.db' def __getConn(self): # 取数据库对象 @@ -42,7 +49,7 @@ class Sql(): self.__DB_CONN = sqlite3.connect(self.__DB_FILE) self.__DB_CONN.text_factory = str except Exception as ex: - # print(mw.getTracebackInfo()) + print(getTracebackInfo()) return "error: " + str(ex) def changeTextFactoryToBytes(self): @@ -51,14 +58,12 @@ class Sql(): def autoTextFactory(self): if sys.version_info[0] == 3: - self.__DB_CONN.text_factory = lambda x: str( - x, encoding="utf-8", errors='ignore') + self.__DB_CONN.text_factory = lambda x: str(x, encoding="utf-8", errors='ignore') else: - self.__DB_CONN.text_factory = lambda x: unicode( - x, "utf-8", "ignore") + self.__DB_CONN.text_factory = lambda x: unicode(x, "utf-8", "ignore") def dbfile(self, name): - self.__DB_FILE = 'data/' + name + '.db' + self.__DB_FILE = getPanelDir()+'/data/' + name + '.db' return self def dbPos(self, path, name, suffix_name = 'db'): @@ -192,7 +197,6 @@ class Sql(): def getField(self, keyName): # 取回指定字段 result = self.field(keyName).select() - # print(result) if len(result) == 1: return result[0][keyName] return result diff --git a/web/core/mw.py b/web/core/mw.py index 94456abfb..2350a22e7 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -584,9 +584,11 @@ def writeDbLog(stype, msg, args=(), uid=1): return False -def M(table): +def M(table=''): import core.db as db sql = db.Sql() + if table == '': + return sql return sql.table(table) diff --git a/web/utils/system/__init__.py b/web/utils/system/__init__.py index 7f612c99c..d631fb713 100644 --- a/web/utils/system/__init__.py +++ b/web/utils/system/__init__.py @@ -11,4 +11,4 @@ from .main import * from .query import * from .stats import * -from .monitor import * \ No newline at end of file +from .monitor import monitor \ No newline at end of file diff --git a/web/utils/system/main.py b/web/utils/system/main.py index 4e7b8a7b4..f2a29203b 100644 --- a/web/utils/system/main.py +++ b/web/utils/system/main.py @@ -150,4 +150,21 @@ def getMemInfo(): memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached'] return memInfo +def getMemUsed(): + # 取内存使用率 + try: + import psutil + mem = psutil.virtual_memory() + + if mw.getOs() == 'darwin': + return mem.percent + + memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024, + 'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024} + tmp = memInfo['memTotal'] - memInfo['memFree'] - \ + memInfo['memBuffers'] - memInfo['memCached'] + tmp1 = memInfo['memTotal'] / 100 + return (tmp / tmp1) + except Exception as ex: + return 1 diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index 1abb5db39..45dd57912 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -14,14 +14,82 @@ import re import time import math import psutil +import threading + + +from .main import getMemUsed import core.mw as mw +import core.db as db + +# 监控系统数据入库 class monitor: - def initDBFile(): - pass + _dbfile = mw.getPanelDataDir() + '/system.db' + _diskinfo = None + + def __init__(self): + pass + + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(monitor, "_instance"): + with monitor._instance_lock: + if not hasattr(monitor, "_instance"): + monitor._instance = monitor(*args, **kwargs) + return monitor._instance + + def initDBFile(self): + if os.path.exists(self._dbfile): + return True + sql_file = mw.getPanelDataDir() + '/sql/system.sql' + sql = db.Sql().dbPos(mw.getPanelDataDir(),'system') + csql = mw.readFile(sql_file) + csql_list = csql.split(';') + for index in range(len(csql_list)): + sql.execute(csql_list[index], ()) + return True + + # def safeRun(self): + # self.initDBFile() + + # monitor_day =mw.M('option').field('name').where('name=?',('monitor_day',)).getField('value') + # print(monitor_day) + # print("monitor data", mw.formatDate()) + + def getDiskInfo(self): + if self._diskinfo is None: + self._diskinfo = psutil.disk_io_counters() + + def run(self): + self.initDBFile() + monitor_day = mw.M('option').field('name').where('name=?',('monitor_day',)).getField('value') + print(monitor_day) + print("monitor data", mw.formatDate()) + + now_info = {} + # 取当前CPU Io + now_info['used'] = psutil.cpu_percent(interval=1) + now_info['mem'] = getMemUsed() + + network_io = psutil.net_io_counters()[:4] + diskio = psutil.disk_io_counters() + + print(network_io) + print(now_info) + print(diskio) + # if tmp['used'] > 80: + # panel_title = mw.getConfig('title') + # ip = mw.getHostAddr() + # now_time = mw.getDateFromNow() + # msg = now_time + '|节点[' + panel_title + ':' + ip + \ + # ']处于高负载[' + str(tmp['used']) + '],请排查原因!' + # mw.notifyMessage(msg, '面板监控', 600) From d7b7c572b0226cdd57a469f44bb835a8ed671ca0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 00:59:26 +0800 Subject: [PATCH 055/965] update --- panel_task.py | 22 ++++-- web/static/app/control.js | 3 +- web/utils/system/monitor.py | 131 ++++++++++++++++++++++++++++++------ web/utils/system/query.py | 2 +- 4 files changed, 131 insertions(+), 27 deletions(-) diff --git a/panel_task.py b/panel_task.py index 8382b29bb..84d39bd4f 100755 --- a/panel_task.py +++ b/panel_task.py @@ -346,12 +346,10 @@ def systemTask(): # LoadAverage load_average = sm.getLoadAverage() - lpro = round( - (load_average['one'] / load_average['max']) * 100, 2) + lpro = round((load_average['one'] / load_average['max']) * 100, 2) if lpro > 100: lpro = 100 - sql.table('load_average').add('pro,one,five,fifteen,addtime', (lpro, load_average[ - 'one'], load_average['five'], load_average['fifteen'], addtime)) + sql.table('load_average').add('pro,one,five,fifteen,addtime', (lpro, load_average['one'], load_average['five'], load_average['fifteen'], addtime)) lpro = None load_average = None @@ -381,6 +379,20 @@ def systemTask(): time.sleep(30) systemTask() +def systemTask2(): + # 系统监控任务 + try: + from utils.system import monitor + while True: + monitor.instance().run() + time.sleep(5) + except Exception as ex: + print(str(ex)) + mw.writeFile('logs/sys_interrupt.pl', str(ex)) + restartMw() + time.sleep(30) + systemTask2() + # -------------------------------------- PHP监控 start --------------------------------------------- # # 502错误检查线程 @@ -584,7 +596,7 @@ def setDaemon(t): def run(): # # 系统监控 - sysTask = threading.Thread(target=systemTask) + sysTask = threading.Thread(target=systemTask2) sysTask = setDaemon(sysTask) sysTask.start() diff --git a/web/static/app/control.js b/web/static/app/control.js index 4b68cd96b..9ee2e8807 100755 --- a/web/static/app/control.js +++ b/web/static/app/control.js @@ -490,7 +490,8 @@ function network(b,e){ option = { tooltip: { trigger: 'axis', - axisPointer: { type: 'cross' } + axisPointer: { type: 'cross' }, + formatter:"时间:{b0}
{a0}: {c0} Kb/s
{a1}: {c1} Kb/s", }, legend: { data:[lan.index.net_up,lan.index.net_down] diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index 45dd57912..ea5ba96c8 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -29,6 +29,7 @@ class monitor: _dbfile = mw.getPanelDataDir() + '/system.db' _diskinfo = None + _netinfo = None def __init__(self): pass @@ -55,41 +56,131 @@ class monitor: sql.execute(csql_list[index], ()) return True + def getMonitorDay(self): + monitor_day = mw.M('option').field('name').where('name=?',('monitor_day',)).getField('value') + return int(monitor_day) + + def isOnlyNetIoStats(self): + monitor_only_netio = mw.M('option').field('name').where('name=?',('monitor_only_netio',)).getField('value') + if monitor_only_netio == 'open': + return True + return False + # def safeRun(self): # self.initDBFile() # monitor_day =mw.M('option').field('name').where('name=?',('monitor_day',)).getField('value') # print(monitor_day) # print("monitor data", mw.formatDate()) + def psutilNetIoCounters(self): + ''' + 统计网卡流量 + ''' + if self.isOnlyNetIoStats(): + local_lo = (0, 0, 0, 0) + ioName = psutil.net_io_counters(pernic=True).keys() + for x in ioName: + + if x.find("lo") > -1: + local_lo = psutil.net_io_counters( + pernic=True).get(x)[:4] + + all_io = psutil.net_io_counters()[:4] + result_io = tuple([all_io[i] - local_lo[i] + for i in range(0, len(all_io))]) + + # print(local_lo) + # print(all_io) + # print(result_io) + return result_io + return psutil.net_io_counters()[:4] def getDiskInfo(self): + now_diskinfo = psutil.disk_io_counters() if self._diskinfo is None: - self._diskinfo = psutil.disk_io_counters() + self._diskinfo = now_diskinfo + + info = {} + info['read_count'] = now_diskinfo.read_count - self._diskinfo.read_count + info['write_count'] = now_diskinfo.write_count - self._diskinfo.write_count + info['read_bytes'] = now_diskinfo.read_bytes - self._diskinfo.read_bytes + info['write_bytes'] = now_diskinfo.write_bytes - self._diskinfo.write_bytes + info['read_time'] = now_diskinfo.read_time - self._diskinfo.read_time + info['write_time'] = now_diskinfo.write_time - self._diskinfo.write_time + + self._diskinfo = now_diskinfo + return info + + def getNetIoInfo(self): + # 取当前网络Io + net_io = self.psutilNetIoCounters() + if not self._netinfo: + self._netinfo = net_io + info = {} + info['upTotal'] = net_io[0] + info['downTotal'] = net_io[1] + info['up'] = round(float((net_io[0] - self._netinfo[0]) / 1024), 2) + info['down'] = round(float((net_io[1] - self._netinfo[1]) / 1024), 2) + info['downPackets'] = net_io[3] + info['upPackets'] = net_io[2] + + self._netinfo = net_io + return info + + def getLoadAverage(self): + c = os.getloadavg() + data = {} + data['one'] = round(float(c[0]), 2) + data['five'] = round(float(c[1]), 2) + data['fifteen'] = round(float(c[2]), 2) + data['max'] = psutil.cpu_count() * 2 + data['limit'] = data['max'] + data['safe'] = data['max'] * 0.75 + return data def run(self): self.initDBFile() - monitor_day = mw.M('option').field('name').where('name=?',('monitor_day',)).getField('value') - print(monitor_day) - print("monitor data", mw.formatDate()) - - now_info = {} + monitor_day = self.getMonitorDay() + + info = {} # 取当前CPU Io - now_info['used'] = psutil.cpu_percent(interval=1) - now_info['mem'] = getMemUsed() + info['used'] = psutil.cpu_percent(interval=1) + info['mem'] = getMemUsed() - network_io = psutil.net_io_counters()[:4] - diskio = psutil.disk_io_counters() + netio = self.getNetIoInfo() + diskio = self.getDiskInfo() + + addtime = int(time.time()) + deltime = addtime - (monitor_day * 86400) + + # CPU/内存数据入库 + cpu_mem_data = (info['used'], info['mem'], addtime) + cmd_objm = mw.M('cpuio').dbPos(mw.getPanelDataDir(),'system') + cmd_objm.add('pro,mem,addtime', cpu_mem_data) + cmd_objm.where("addtime 100: + lpro = 100 + load_objm = mw.M('load_average').dbPos(mw.getPanelDataDir(),'system') + load_objm.add('pro,one,five,fifteen,addtime', (lpro, load_data['one'], load_data['five'], load_data['fifteen'], addtime)) + load_objm.where("addtime 80: - # panel_title = mw.getConfig('title') - # ip = mw.getHostAddr() - # now_time = mw.getDateFromNow() - # msg = now_time + '|节点[' + panel_title + ':' + ip + \ - # ']处于高负载[' + str(tmp['used']) + '],请排查原因!' - # mw.notifyMessage(msg, '面板监控', 600) diff --git a/web/utils/system/query.py b/web/utils/system/query.py index 9d2710070..71ddec03b 100644 --- a/web/utils/system/query.py +++ b/web/utils/system/query.py @@ -60,7 +60,7 @@ def toAddtime(data, tomem=False): def toUseAddtime(data): dlen = len(data) for i in range(dlen): - data[i]['addtime'] = time.strftime('%m/%d %H:%M', time.localtime(float(data[i]['addtime']))) + data[i]['addtime'] = time.strftime('%m/%d %H:%M:%S', time.localtime(float(data[i]['addtime']))) return data def getLoadAverageByDB(start, end): From 5e53c637c3b0e2c8dbd892870f2cb7a51ec83c82 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 01:28:19 +0800 Subject: [PATCH 056/965] update --- web/admin/setting/setting.py | 12 ++++++++++++ web/static/app/control.js | 2 +- web/templates/default/setting.html | 2 +- web/utils/config.py | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 05717339f..e0419fc77 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -160,6 +160,18 @@ def set_status_code(): mw.writeLog('面板设置', '将未授权响应状态码设置为:{0}:{1}'.format(status_code,info['text'])) return mw.returnData(True, '设置成功!') +# 设置面板调式模式 +@blueprint.route('/open_debug', endpoint='open_debug', methods=['POST']) +@panel_login_required +def open_debug(): + debug = model.getOption('debug',default='close') + if debug == 'open': + model.setOption('debug','close') + return mw.returnData(True, '开发模式关闭!') + model.setOption('debug','open') + return mw.returnData(True, '开发模式开启!') + + # 设置站点状态 @blueprint.route('/set_port', endpoint='set_port', methods=['POST']) @panel_login_required diff --git a/web/static/app/control.js b/web/static/app/control.js index 9ee2e8807..9737dac81 100755 --- a/web/static/app/control.js +++ b/web/static/app/control.js @@ -504,7 +504,7 @@ function network(b,e){ }, yAxis: { type: 'value', - name: lan.index.unit+':KB/s', + name: '单位:KB/s', boundaryGap: [0, '100%'], splitLine:{ lineStyle:{ color:"#ddd" } }, axisLine:{ lineStyle:{ color:"#666" } } diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index f99eca262..88ae611bc 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -17,7 +17,7 @@
开发模式
- +
diff --git a/web/utils/config.py b/web/utils/config.py index 91bed8319..da4fd00f6 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -48,6 +48,7 @@ def getGlobalVar(): data['site_path'] = model.getOption('site_path', default=mw.getFatherDir()+'/wwwroot') data['backup_path'] = model.getOption('backup_path', default=mw.getFatherDir()+'/backup') data['admin_path'] = '/'+model.getOption('admin_path', default='') + data['debug'] = model.getOption('debug', default='close') data['site_count'] = model.getSitesCount() data['port'] = mw.getHostPort() From 179bc05e09768a5d9d5d98a7a8c03da4bdcb48a5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 01:34:10 +0800 Subject: [PATCH 057/965] Update mw.py --- web/core/mw.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 2350a22e7..37eb5307b 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -520,10 +520,9 @@ def isDebugMode(): if isAppleSystem(): return True - debugPath = getPanelDir() + "/data/debug.pl" - if os.path.exists(debugPath): + debug = M('option').field('name').where('name=?',('debug',)).getField('value') + if debug == 'open': return True - return False def isNumber(s): From 7f5b45fbdc450f9c7e7c1e3a12c828c4a0770931 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 02:01:20 +0800 Subject: [PATCH 058/965] Update monitor.py --- web/utils/system/monitor.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index ea5ba96c8..92db6abe6 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -66,12 +66,6 @@ class monitor: return True return False - # def safeRun(self): - # self.initDBFile() - - # monitor_day =mw.M('option').field('name').where('name=?',('monitor_day',)).getField('value') - # print(monitor_day) - # print("monitor data", mw.formatDate()) def psutilNetIoCounters(self): ''' 统计网卡流量 @@ -80,18 +74,12 @@ class monitor: local_lo = (0, 0, 0, 0) ioName = psutil.net_io_counters(pernic=True).keys() for x in ioName: - if x.find("lo") > -1: - local_lo = psutil.net_io_counters( - pernic=True).get(x)[:4] + local_lo = psutil.net_io_counters(pernic=True).get(x)[:4] all_io = psutil.net_io_counters()[:4] - result_io = tuple([all_io[i] - local_lo[i] - for i in range(0, len(all_io))]) + result_io = tuple([all_io[i] - local_lo[i] for i in range(0, len(all_io))]) - # print(local_lo) - # print(all_io) - # print(result_io) return result_io return psutil.net_io_counters()[:4] From 745d95bbcb4779c4644b3f4b20449d7218932ae8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 02:20:52 +0800 Subject: [PATCH 059/965] Update __init__.py --- web/admin/files/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py index bd14375c8..f7baeeb0a 100644 --- a/web/admin/files/__init__.py +++ b/web/admin/files/__init__.py @@ -12,6 +12,9 @@ import os from flask import Blueprint, render_template from flask import request +from flask import make_response +from flask import send_file +from flask import send_from_directory from admin import model @@ -119,6 +122,19 @@ def delete_dir(): path = request.form.get('path', '') return file.dirDelete(path) +# 删除文件 +@blueprint.route('/download', endpoint='download', methods=['GET']) +@panel_login_required +def download(): + filename = request.args.get('filename', '') + if not os.path.exists(filename): + return '' + is_attachment = True + if filename.endswith(".svg"): + is_attachment = False + + response = make_response(send_from_directory(os.path.dirname(filename), os.path.basename(filename), as_attachment=is_attachment)) + return response # 回收站文件 @blueprint.route('/get_recycle_bin', endpoint='get_recycle_bin', methods=['POST']) From 06d43efd7f50ec2a37046aad8c1a762b0f6ae9dd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 03:06:40 +0800 Subject: [PATCH 060/965] update --- data/sql/system.sql | 4 ++-- web/admin/setting/setting.py | 12 ++++++++++++ web/templates/default/setting.html | 2 +- web/utils/config.py | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/data/sql/system.sql b/data/sql/system.sql index 06ef0a595..ccade955a 100755 --- a/data/sql/system.sql +++ b/data/sql/system.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS `network` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `up` INTEGER, - `down` INTEGER, + `up` REAL, + `down` REAL, `total_up` INTEGER, `total_down` INTEGER, `down_packets` INTEGER, diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index e0419fc77..1019daa07 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -172,6 +172,18 @@ def open_debug(): return mw.returnData(True, '开发模式开启!') +# 设置面板开关 +@blueprint.route('/close_panel', endpoint='close_panel', methods=['POST']) +@panel_login_required +def close_panel(): + admin_close = model.getOption('admin_close',default='no') + if admin_close == 'no': + model.setOption('admin_close','yes') + return mw.returnData(True, '开启面板成功!') + model.setOption('admin_close','no') + return mw.returnData(True, '关闭面板成功!') + + # 设置站点状态 @blueprint.route('/set_port', endpoint='set_port', methods=['POST']) @panel_login_required diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index 88ae611bc..c001f7375 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -10,7 +10,7 @@
关闭面板
- +
diff --git a/web/utils/config.py b/web/utils/config.py index da4fd00f6..e11857bf5 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -49,6 +49,7 @@ def getGlobalVar(): data['backup_path'] = model.getOption('backup_path', default=mw.getFatherDir()+'/backup') data['admin_path'] = '/'+model.getOption('admin_path', default='') data['debug'] = model.getOption('debug', default='close') + data['admin_close'] = model.getOption('admin_close', default='no') data['site_count'] = model.getSitesCount() data['port'] = mw.getHostPort() From 7f68067f679ffe15902c43fbbe9db7902e042c9a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 03:17:02 +0800 Subject: [PATCH 061/965] update --- web/admin/system/system.py | 15 ++++++--------- web/core/mw.py | 8 +++++++- web/utils/system/monitor.py | 5 +++++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/web/admin/system/system.py b/web/admin/system/system.py index cd6e8a578..1198a5227 100644 --- a/web/admin/system/system.py +++ b/web/admin/system/system.py @@ -8,17 +8,20 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os from flask import Blueprint, render_template from flask import request from admin.user_login_check import panel_login_required -import admin.model.option as option +from utils.system import monitor +import admin.model.option as option import core.mw as mw import utils.system as sys + blueprint = Blueprint('system', __name__, url_prefix='/system', template_folder='../../templates') # 获取系统的统计信息 @@ -126,14 +129,8 @@ def set_control(): elif stype == 'del': if not mw.isRestart(): return mw.returnData(False, '请等待所有安装任务完成再执行') - os.remove("data/system.db") - - sql = db.Sql().dbfile('system') - csql = mw.readFile('data/sql/system.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - return mw.returnData(True, "监控服务已关闭") + monitor.instance().clearDbFile() + return mw.returnData(True, "清空记录成功!") else: monitor_status = option.getOption('monitor_status', default='open', type='monitor') monitor_day = option.getOption('monitor_day', default='30', type='monitor') diff --git a/web/core/mw.py b/web/core/mw.py index 37eb5307b..840595c44 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -541,7 +541,13 @@ def isNumber(s): return False - +def isRestart(): + # 检查是否允许重启 + num = M('tasks').where('status!=?', ('1',)).count() + if num > 0: + return False + return True + # 检查端口是否占用 def isOpenPort(port): import socket diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index 92db6abe6..224806a0d 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -45,6 +45,11 @@ class monitor: monitor._instance = monitor(*args, **kwargs) return monitor._instance + def clearDbFile(self): + os.remove(self._dbfile) + self.initDBFile() + return True + def initDBFile(self): if os.path.exists(self._dbfile): return True From bed1d7ee85d69dd76085e6ccce1cb8ae731c4308 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 03:25:08 +0800 Subject: [PATCH 062/965] Update system.sql --- data/sql/system.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/sql/system.sql b/data/sql/system.sql index ccade955a..2258520fb 100755 --- a/data/sql/system.sql +++ b/data/sql/system.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS `network` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `up` REAL, - `down` REAL, + `up` TEXT, + `down` TEXT, `total_up` INTEGER, `total_down` INTEGER, `down_packets` INTEGER, From 122bef53fff44e1349211ee2748814ab1bcc3633 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 03:25:21 +0800 Subject: [PATCH 063/965] Update system.sql --- data/sql/system.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/sql/system.sql b/data/sql/system.sql index 2258520fb..f16ececbd 100755 --- a/data/sql/system.sql +++ b/data/sql/system.sql @@ -11,8 +11,8 @@ CREATE TABLE IF NOT EXISTS `network` ( CREATE TABLE IF NOT EXISTS `cpuio` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pro` INTEGER, - `mem` INTEGER, + `pro` TEXT, + `mem` TEXT, `addtime` INTEGER ); From b020f4c502436d6f7c5e159467882c6124aaa774 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 03:31:47 +0800 Subject: [PATCH 064/965] Update system.py --- web/admin/system/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/admin/system/system.py b/web/admin/system/system.py index 1198a5227..125563e2b 100644 --- a/web/admin/system/system.py +++ b/web/admin/system/system.py @@ -130,7 +130,7 @@ def set_control(): if not mw.isRestart(): return mw.returnData(False, '请等待所有安装任务完成再执行') monitor.instance().clearDbFile() - return mw.returnData(True, "清空记录成功!") + return mw.returnData(True, "清空监控记录成功!") else: monitor_status = option.getOption('monitor_status', default='open', type='monitor') monitor_day = option.getOption('monitor_day', default='30', type='monitor') From fa6900a937dee0cd800b3b6c28aa44459b62f9ab Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 03:34:21 +0800 Subject: [PATCH 065/965] Update monitor.py --- web/utils/system/monitor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index 224806a0d..3a4f05173 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -138,6 +138,8 @@ class monitor: info = {} # 取当前CPU Io info['used'] = psutil.cpu_percent(interval=1) + info['used'] = round(info['used'], 2) + info['mem'] = getMemUsed() netio = self.getNetIoInfo() From 9a9faa6a3355b158ac26bd2d0dbe87834740322d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 03:38:07 +0800 Subject: [PATCH 066/965] Update monitor.py --- web/utils/system/monitor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index 3a4f05173..95a8d6e1c 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -141,6 +141,7 @@ class monitor: info['used'] = round(info['used'], 2) info['mem'] = getMemUsed() + info['mem'] = round(info['mem'], 2) netio = self.getNetIoInfo() diskio = self.getDiskInfo() From ed90f9a0875fe55a1174f7be7f1f7743a17dc2a9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 14:57:20 +0800 Subject: [PATCH 067/965] Update system.sql --- data/sql/system.sql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/sql/system.sql b/data/sql/system.sql index f16ececbd..75fe9557f 100755 --- a/data/sql/system.sql +++ b/data/sql/system.sql @@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS `network` ( `up_packets` INTEGER, `addtime` INTEGER ); +CREATE INDEX addtime_idx ON network(`addtime`); CREATE TABLE IF NOT EXISTS `cpuio` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -15,6 +16,7 @@ CREATE TABLE IF NOT EXISTS `cpuio` ( `mem` TEXT, `addtime` INTEGER ); +CREATE INDEX addtime_idx ON cpuio(`addtime`); CREATE TABLE IF NOT EXISTS `diskio` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -26,6 +28,7 @@ CREATE TABLE IF NOT EXISTS `diskio` ( `write_time` INTEGER, `addtime` INTEGER ); +CREATE INDEX addtime_idx ON diskio(`addtime`); CREATE TABLE IF NOT EXISTS `load_average` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -34,4 +37,5 @@ CREATE TABLE IF NOT EXISTS `load_average` ( `five` REAL, `fifteen` REAL, `addtime` INTEGER -); \ No newline at end of file +); +CREATE INDEX addtime_idx ON load_average(`addtime`); \ No newline at end of file From 3ce1289c441d488397f6688e76c691e328d46e7f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 18:14:25 +0800 Subject: [PATCH 068/965] update --- panel_task.py | 166 +------------------------------- panel_tools.py | 8 +- web/admin/__init__.py | 6 ++ web/admin/dashboard/__init__.py | 6 +- web/admin/model/__init__.py | 2 +- web/admin/model/option.py | 1 - web/admin/model/task.py | 20 ++++ web/admin/setup/__init__.py | 1 + web/admin/setup/init_cmd.py | 54 +++++++++++ web/admin/setup/option.py | 8 +- web/admin/task/__init__.py | 12 ++- web/core/mw.py | 67 ++++++++++--- web/static/app/public.js | 2 +- web/thisdb/__init__.py | 11 +++ web/thisdb/option.py | 28 ++++++ web/utils/file.py | 2 +- web/utils/task.py | 54 +++++++++++ 17 files changed, 260 insertions(+), 188 deletions(-) create mode 100644 web/admin/setup/init_cmd.py create mode 100644 web/thisdb/__init__.py create mode 100644 web/thisdb/option.py create mode 100644 web/utils/task.py diff --git a/panel_task.py b/panel_task.py index 84d39bd4f..263e9366f 100755 --- a/panel_task.py +++ b/panel_task.py @@ -117,7 +117,7 @@ def mw_async(f): @mw_async def restartMw(): time.sleep(1) - cmd = mw.getRunDir() + '/scripts/init.d/mw reload &' + cmd = mw.getPanelDir() + '/scripts/init.d/mw reload &' mw.execShell(cmd) @@ -225,173 +225,15 @@ def siteEdate(): def systemTask(): + from utils.system import monitor # 系统监控任务 try: - import system_api - import psutil - sm = system_api.system_api() - filename = 'data/control.conf' - - sql = db.Sql().dbfile('system') - csql = mw.readFile('data/sql/system.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - - cpuIo = cpu = {} - cpuCount = psutil.cpu_count() - used = count = 0 - reloadNum = 0 - network_up = network_down = diskio_1 = diskio_2 = networkInfo = cpuInfo = diskInfo = None - while True: - if not os.path.exists(filename): - time.sleep(10) - continue - - day = 30 - try: - day = int(mw.readFile(filename)) - if day < 1: - time.sleep(10) - continue - except: - day = 30 - - tmp = {} - # 取当前CPU Io - tmp['used'] = psutil.cpu_percent(interval=1) - if tmp['used'] > 80: - panel_title = mw.getConfig('title') - ip = mw.getHostAddr() - now_time = mw.getDateFromNow() - msg = now_time + '|节点[' + panel_title + ':' + ip + \ - ']处于高负载[' + str(tmp['used']) + '],请排查原因!' - mw.notifyMessage(msg, '面板监控', 600) - - if not cpuInfo: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - if cpuInfo['used'] < tmp['used']: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - # 取当前网络Io - networkIo = sm.psutilNetIoCounters() - if not network_up: - network_up = networkIo[0] - network_down = networkIo[1] - tmp = {} - tmp['upTotal'] = networkIo[0] - tmp['downTotal'] = networkIo[1] - tmp['up'] = round(float((networkIo[0] - network_up) / 1024), 2) - tmp['down'] = round(float((networkIo[1] - network_down) / 1024), 2) - tmp['downPackets'] = networkIo[3] - tmp['upPackets'] = networkIo[2] - - network_up = networkIo[0] - network_down = networkIo[1] - - if not networkInfo: - networkInfo = tmp - if (tmp['up'] + tmp['down']) > (networkInfo['up'] + networkInfo['down']): - networkInfo = tmp - # 取磁盘Io - # if os.path.exists('/proc/diskstats'): - diskio_2 = psutil.disk_io_counters() - if not diskio_1: - diskio_1 = diskio_2 - tmp = {} - tmp['read_count'] = diskio_2.read_count - diskio_1.read_count - tmp['write_count'] = diskio_2.write_count - diskio_1.write_count - tmp['read_bytes'] = diskio_2.read_bytes - diskio_1.read_bytes - tmp['write_bytes'] = diskio_2.write_bytes - diskio_1.write_bytes - tmp['read_time'] = diskio_2.read_time - diskio_1.read_time - tmp['write_time'] = diskio_2.write_time - diskio_1.write_time - - if not diskInfo: - diskInfo = tmp - else: - diskInfo['read_count'] += tmp['read_count'] - diskInfo['write_count'] += tmp['write_count'] - diskInfo['read_bytes'] += tmp['read_bytes'] - diskInfo['write_bytes'] += tmp['write_bytes'] - diskInfo['read_time'] += tmp['read_time'] - diskInfo['write_time'] += tmp['write_time'] - diskio_1 = diskio_2 - - # print diskInfo - if count >= 12: - try: - addtime = int(time.time()) - deltime = addtime - (day * 86400) - - data = (cpuInfo['used'], cpuInfo['mem'], addtime) - sql.table('cpuio').add('pro,mem,addtime', data) - sql.table('cpuio').where("addtime 100: - lpro = 100 - sql.table('load_average').add('pro,one,five,fifteen,addtime', (lpro, load_average['one'], load_average['five'], load_average['fifteen'], addtime)) - - lpro = None - load_average = None - cpuInfo = None - networkInfo = None - diskInfo = None - count = 0 - reloadNum += 1 - if reloadNum > 1440: - reloadNum = 0 - mw.writeFile('logs/sys_interrupt.pl', - "reload num:" + str(reloadNum)) - restartMw() - except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - del(tmp) - time.sleep(5) - count += 1 - except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - restartMw() - - time.sleep(30) - systemTask() - -def systemTask2(): - # 系统监控任务 - try: - from utils.system import monitor while True: monitor.instance().run() time.sleep(5) except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - restartMw() time.sleep(30) - systemTask2() + systemTask() # -------------------------------------- PHP监控 start --------------------------------------------- # @@ -596,7 +438,7 @@ def setDaemon(t): def run(): # # 系统监控 - sysTask = threading.Thread(target=systemTask2) + sysTask = threading.Thread(target=systemTask) sysTask = setDaemon(sysTask) sysTask.start() diff --git a/panel_tools.py b/panel_tools.py index a56444a8f..89208170a 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -26,6 +26,7 @@ sys.path.append(web_dir) from admin import app from admin import model +import thisdb import core.mw as mw import core.db as db @@ -241,7 +242,7 @@ def show_panel_pwd(): print("password has been changed!") def show_panel_adminpath(): - admin_path = model.getOption('admin_path') + admin_path = thisdb.getOption('admin_path') print('/'+admin_path) @@ -289,6 +290,8 @@ def main(): set_panel_username() elif method == 'password': show_panel_pwd() + elif method == 'test': + thisdb.getOption('admin_path') elif method == 'admin_path': show_panel_adminpath() elif method == 'getServerIp': @@ -305,5 +308,4 @@ def main(): print('ERROR: Parameter error') if __name__ == "__main__": - with app.app_context(): - main() + main() diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 87dd08ffd..55b5d89ba 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -93,8 +93,10 @@ with app.app_context(): if setup_db_required: setup.init_admin_user() setup.init_option() +setup.init_cmd() setup.init_db_system() + app.config['BASIC_AUTH_OPEN'] = False with app.app_context(): basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) @@ -155,6 +157,7 @@ def page_unauthorized(error): # 设置模板全局变量 @app.context_processor def inject_global_variables(): + start_t = time.time() ver = config.APP_VERSION; if mw.isDebugMode(): ver = ver + str(time.time()) @@ -165,6 +168,9 @@ def inject_global_variables(): 'title' : 'MW面板', 'ip' : '127.0.0.1' } + + end_t = time.time() + print("cos:"+str(end_t-start_t)) return dict(config=g_config, data=data) diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py index e3fd25933..c77e84623 100644 --- a/web/admin/dashboard/__init__.py +++ b/web/admin/dashboard/__init__.py @@ -36,6 +36,10 @@ def index(): @blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) def admin_safe_path(path): db_path = model.getOption('admin_path') + + if isLogined(): + return redirect('/') + if db_path == path: return render_template('default/login.html') @@ -63,7 +67,7 @@ def webhook(): wh_install_path = mw.getServerDir() + '/webhook' if not os.path.exists(wh_install_path): - return mw.returnJson(False, '请先安装WebHook插件!') + return mw.returnData(False, '请先安装WebHook插件!') package = mw.getPanelDir() + "/plugins/webhook" if not package in sys.path: diff --git a/web/admin/model/__init__.py b/web/admin/model/__init__.py index b0821ac6d..e2bb5f82e 100644 --- a/web/admin/model/__init__.py +++ b/web/admin/model/__init__.py @@ -19,7 +19,7 @@ from .option import setOption from .sites import getSitesCount from .task import addTask -from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun +from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun,getTaskRunList from .task import setTaskStatus,setTaskData from .user import isLoginCheck diff --git a/web/admin/model/option.py b/web/admin/model/option.py index 98a229217..cbb277be5 100644 --- a/web/admin/model/option.py +++ b/web/admin/model/option.py @@ -71,5 +71,4 @@ def setOption(name, value, db.session.query(Option).filter_by(name=name, type=type).update({"value":value}) db.session.commit() - db.session.close() return True \ No newline at end of file diff --git a/web/admin/model/task.py b/web/admin/model/task.py index 66edfd33c..c3ba9ccc7 100644 --- a/web/admin/model/task.py +++ b/web/admin/model/task.py @@ -88,6 +88,26 @@ def getTaskList( rows.append(t) return rows +def getTaskRunList( + page: int | None = 1, + size: int | None = 10, +): + pagination = Tasks.query.filter(Tasks.status!=1).order_by(Tasks.id.asc()).paginate(page=int(page), per_page=int(size)) + + rows = [] + for item in pagination.items: + t = {} + t['id'] = item.id + t['name'] = item.name + t['type'] = item.type + t['cmd'] = item.cmd + t['start'] = item.start + t['end'] = item.end + t['status'] = item.status + t['add_time'] = item.add_time + rows.append(t) + return rows + diff --git a/web/admin/setup/__init__.py b/web/admin/setup/__init__.py index 7cd824ba3..e8ab0677a 100644 --- a/web/admin/setup/__init__.py +++ b/web/admin/setup/__init__.py @@ -11,4 +11,5 @@ from .user import init_admin_user from .option import init_option from .init_db_system import init_db_system +from .init_cmd import init_cmd diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py new file mode 100644 index 000000000..434528a09 --- /dev/null +++ b/web/admin/setup/init_cmd.py @@ -0,0 +1,54 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import shutil + +import core.mw as mw + +def contentReplace(src, dst): + content = mw.readFile(src) + content = content.replace("{$SERVER_PATH}", mw.getFatherDir()) + + content += "\n# make:{0}".format(mw.formatDate()) + mw.writeFile(dst, content) + + +def init_cmd(): + script = mw.getPanelDir() + '/scripts/init.d/mw.tpl' + script_bin = mw.getPanelDir() + '/scripts/init.d/mw' + contentReplace(script, script_bin) + mw.execShell('chmod +x ' + script_bin) + + # 在linux系统中,确保/etc/init.d存在 + if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"): + mw.execShell('mkdir -p /etc/rc.d/init.d') + + if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"): + mw.execShell('mkdir -p /etc/init.d') + + # initd + if os.path.exists('/etc/rc.d/init.d'): + initd_bin = '/etc/rc.d/init.d/mw' + if not os.path.exists(initd_bin): + shutil.copyfile(script_bin, initd_bin) + mw.execShell('chmod +x ' + initd_bin) + # 加入自启动 + mw.execShell('which chkconfig && chkconfig --add mw') + + if os.path.exists('/etc/init.d'): + initd_bin = '/etc/init.d/mw' + if not os.path.exists(initd_bin): + import shutil + shutil.copyfile(script_bin, initd_bin) + mw.execShell('chmod +x ' + initd_bin) + # 加入自启动 + mw.execShell('which update-rc.d && update-rc.d -f mw defaults') + return True \ No newline at end of file diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 10d739875..87f691471 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -34,9 +34,6 @@ def init_option(): # basic auth 配置 model.setOption('basic_auth', json.dumps({'open':False})) - - - # 开启后台任务 # model.setOption('run_bg_task', 'close') @@ -50,7 +47,10 @@ def init_option(): # 初始化安全路径 model.setOption('admin_path', mw.getRandomString(8)) - model.setOption('server_ip', '127.0.0.1') + + + ip = mw.getLocalIp() + model.setOption('server_ip', ip) # 默认备份目录 model.setOption('backup_path', mw.getFatherDir()+'/backup') diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index 2cb6ea08e..e90f33a89 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -17,6 +17,7 @@ from admin.model import db,Tasks from admin.user_login_check import panel_login_required import core.mw as mw +import utils.task as MwTasks blueprint = Blueprint('task', __name__, url_prefix='/task', template_folder='../../templates/default') @@ -98,9 +99,16 @@ def get_task_speed(): data['isDownload'] = False - data['task'] = model.getTaskList(status=-1) + data['task'] = model.getTaskRunList() return data - + +@blueprint.route('/remove_task', endpoint='remove_task', methods=['POST']) +@panel_login_required +def remove_task(): + task_id = request.form.get('id', '') + if task_id == '': + return mw.returnData(False, '任务ID不能为空!') + return MwTasks.removeTask(task_id) \ No newline at end of file diff --git a/web/core/mw.py b/web/core/mw.py index 840595c44..287c9bae7 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -199,10 +199,6 @@ def writeFile(filename, content, mode='w+'): except Exception as e: return False -def triggerTask(): - isTask = getPanelDir() + '/tmp/panelTask.pl' - writeFile(isTask, 'True') - def systemdCfgDir(): # ubuntu @@ -263,6 +259,39 @@ def getClientIp(): from flask import request return request.remote_addr.replace('::ffff:', '') +def getLocalIp(): + filename = getPanelDir()+'/data/iplist.txt' + try: + ipaddress = readFile(filename) + if not ipaddress or ipaddress == '127.0.0.1': + cmd = "curl --insecure -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" + ip = execShell(cmd) + result = ip[0].strip() + if result == '': + raise Exception("ipv4 is empty!") + writeFile(filename, result) + return result + return ipaddress + except Exception as e: + cmd = "curl --insecure -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" + ip = execShell(cmd) + result = ip[0].strip() + if result == '': + return '127.0.0.1' + writeFile(filename, result) + return result + finally: + pass + return '127.0.0.1' + + +def inArray(arrays, searchStr): + # 搜索数据中是否存在 + for key in arrays: + if key == searchStr: + return True + + return False def getJson(data): import json @@ -541,13 +570,6 @@ def isNumber(s): return False -def isRestart(): - # 检查是否允许重启 - num = M('tasks').where('status!=?', ('1',)).count() - if num > 0: - return False - return True - # 检查端口是否占用 def isOpenPort(port): import socket @@ -633,13 +655,33 @@ def deDoubleCrypt(key, strings): writeFileLog(getTracebackInfo()) return strings -# ------------------------------ openresty start ----------------------------- +# ------------------------------ panel start ----------------------------- + +def isRestart(): + # 检查是否允许重启 + num = M('tasks').where('status!=?', ('1',)).count() + if num > 0: + return False + return True + +def triggerTask(): + isTask = getPanelDir() + '/tmp/panelTask.pl' + writeFile(isTask, 'True') + +def restartTask(): + initd = getPanelDir() + '/scripts/init.d/mw' + if os.path.exists(initd): + cmd = initd + ' ' + 'restart_task' + os.system(cmd) + return True def restartMw(): restart_file = getPanelDir()+'/data/restart.pl' writeFile(restart_file, 'True') return True +# ------------------------------ panel end ----------------------------- +# ------------------------------ openresty start ----------------------------- def restartWeb(): return opWeb("reload") @@ -667,6 +709,7 @@ def opWeb(method): return True return False + def opLuaMake(cmd_name): path = getServerDir() + '/web_conf/nginx/lua/lua.conf' root_dir = getServerDir() + '/web_conf/nginx/lua/' + cmd_name diff --git a/web/static/app/public.js b/web/static/app/public.js index cb8eaae70..8516508d4 100755 --- a/web/static/app/public.js +++ b/web/static/app/public.js @@ -941,7 +941,7 @@ function removeTask(b) { time: 0, shade: [0.3, "#000"] }); - $.post("/files/remove_task", "id=" + b, function(c) { + $.post("/task/remove_task", "id=" + b, function(c) { layer.close(a); layer.msg(c.msg, { icon: c.status ? 1 : 5 diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py new file mode 100644 index 000000000..ebaf86032 --- /dev/null +++ b/web/thisdb/__init__.py @@ -0,0 +1,11 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from .option import * \ No newline at end of file diff --git a/web/thisdb/option.py b/web/thisdb/option.py new file mode 100644 index 000000000..1c197d1ce --- /dev/null +++ b/web/thisdb/option.py @@ -0,0 +1,28 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +import core.mw as mw + +def getOption(name, + type: str | None = 'common', + default : str | None = None +) -> str: + ''' + 获取配置的值 + :name -> str 名称 (必填) + :type -> str 类型 (可选|默认common) + :default -> str 默认值 (可选) + ''' + data = mw.M('option').field('name').where('name=? and type=?',(name, type,)).getField('value') + + if len(data) == 0: + return default + return data \ No newline at end of file diff --git a/web/utils/file.py b/web/utils/file.py index 4638252ef..56aa0a13a 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -397,4 +397,4 @@ def setMode(path): s_path = os.path.dirname(path) p_stat = os.stat(s_path) os.chown(path, p_stat.st_uid, p_stat.st_gid) - os.chmod(path, p_stat.st_mode) \ No newline at end of file + os.chmod(path, p_stat.st_mode) diff --git a/web/utils/task.py b/web/utils/task.py new file mode 100644 index 000000000..49e269647 --- /dev/null +++ b/web/utils/task.py @@ -0,0 +1,54 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import pwd +import time + +import core.mw as mw + +# 删除进程下的所有进程 +def removeTaskRecursion(pid): + cmd = "ps -ef|grep %s | grep -v grep |sed -n '2,1p' | awk '{print $2}'" % pid + sub_pid = mw.execShell(cmd) + if sub_pid[0].strip() == '': + return 'ok' + + if sub_pid[0].strip() == pid : + return 'ok' + + removeTaskRecursion(sub_pid[0].strip()) + mw.execShell('kill -9 ' + sub_pid[0].strip()) + return sub_pid[0].strip() + +# 删除任务 +def removeTask(task_id): + try: + name = mw.M('tasks').where('id=?', (task_id,)).getField('name') + status = mw.M('tasks').where('id=?', (task_id,)).getField('status') + mw.M('tasks').delete(task_id) + if str(status) == '-1': + cmd = "ps aux | grep 'panel_task.py' | grep -v grep |awk '{print $2}'" + task_pid = mw.execShell(cmd) + task_list = task_pid[0].strip().split("\n") + for i in range(len(task_list)): + removeTaskRecursion(task_list[i]) + t = mw.execShell('kill -9 ' + task_list[i]) + print(t) + mw.triggerTask() + mw.restartTask() + except Exception as e: + mw.restartTask() + + # 删除日志 + task_log = mw.getPanelDir() + "/tmp/panelTask.pl" + if os.path.exists(task_log): + os.remove(task_log) + return mw.returnData(True, '任务已删除!') \ No newline at end of file From 514518e278dc12d3486c5dce7357b5615b64c3c9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 18:36:16 +0800 Subject: [PATCH 069/965] Update user.py --- web/admin/setup/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/admin/setup/user.py b/web/admin/setup/user.py index f31f85a27..e18008d30 100644 --- a/web/admin/setup/user.py +++ b/web/admin/setup/user.py @@ -21,7 +21,7 @@ def init_admin_user(): if not data: name = mw.getRandomString(8).lower() password = mw.getRandomString(8).lower() - file_pass_pl = mw.getPanelDataDir() + '/default_new.pl' + file_pass_pl = mw.getPanelDataDir() + '/default.pl' mw.writeFile(file_pass_pl, password) insert_time = mw.formatDate() login_ip = '127.0.0.1' From 063263d924fb03b8cb95652f5d09e47398acf331 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 18:45:36 +0800 Subject: [PATCH 070/965] update --- panel_tools.py | 6 ++-- web/thisdb/__init__.py | 3 +- web/thisdb/user.py | 62 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 web/thisdb/user.py diff --git a/panel_tools.py b/panel_tools.py index 89208170a..c8491bbcc 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -256,14 +256,14 @@ def set_panel_username(username=None): print("|-错误,不能使用过于简单的用户名") return - model.setUserByRoot(name=username) + thisdb.setUserByRoot(name=username) print("|-新用户名: %s" % username) return - info = model.getUserByRoot() + info = thisdb.getUserByRoot() if info['name'] == 'admin': username = mw.getRandomString(8).lower() - model.setUserByRoot(name=username) + thisdb.setUserByRoot(name=username) print('|-用户名: ' + info['name']) diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index ebaf86032..8892e9129 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -8,4 +8,5 @@ # Author: midoks # --------------------------------------------------------------------------------- -from .option import * \ No newline at end of file +from .option import * +from .user import * \ No newline at end of file diff --git a/web/thisdb/user.py b/web/thisdb/user.py new file mode 100644 index 000000000..0270a9021 --- /dev/null +++ b/web/thisdb/user.py @@ -0,0 +1,62 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + + +def getUserById(id, +) -> None: + ''' + 获取用户信息通过用户名 + ''' + users_field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' + item = mw.M('users').field(users_field).where('id=?', (1,)).select() + if len(item) == 0: + return None + row = {} + row['id'] = item[0]['id'] + row['name'] = item[0]['name'] + row['password'] = item[0]['password'] + row['login_ip'] = item[0]['login_ip'] + row['login_time'] = item[0]['login_time'] + row['phone'] = item[0]['phone'] + row['email'] = item[0]['email'] + row['add_time'] = item[0]['add_time'] + row['update_time'] = item[0]['update_time'] + return row + + +def getUserByRoot() -> None: + ''' + 获取用户信息通过用户名 + ''' + return getUserById(1) + +def setUserByRoot( + name: str | None = None, + password: str | None = None, +) -> bool: + ''' + 设置配置的值 + :name -> str 名称 (必填) + :value -> object值 (必填) + :type -> str 类型 (可选|默认common) + ''' + data = {} + if name is not None: + mw.M('users').where('id=?', (1,)).setField('username', name) + + if password is not None: + pwd = mw.md5(password) + mw.M('users').where('id=?', (1,)).setField('password', pwd) + + if not data: + return False + return True \ No newline at end of file From 91612a84657ef3295e7f4c4edc2a3e9236f0a936 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 18:46:41 +0800 Subject: [PATCH 071/965] Update user.py --- web/thisdb/user.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/web/thisdb/user.py b/web/thisdb/user.py index 0270a9021..e95ab8975 100644 --- a/web/thisdb/user.py +++ b/web/thisdb/user.py @@ -10,6 +10,26 @@ import core.mw as mw +def getUserByName(name, +) -> None: + ''' + 获取用户信息通过用户名 + ''' + users_field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' + item = mw.M('users').field(users_field).where('name=?', (name,)).select() + if len(item) == 0: + return None + row = {} + row['id'] = item[0]['id'] + row['name'] = item[0]['name'] + row['password'] = item[0]['password'] + row['login_ip'] = item[0]['login_ip'] + row['login_time'] = item[0]['login_time'] + row['phone'] = item[0]['phone'] + row['email'] = item[0]['email'] + row['add_time'] = item[0]['add_time'] + row['update_time'] = item[0]['update_time'] + return row def getUserById(id, ) -> None: From 7c696c24f3ac9a1d877cf91463c8bb788a79cf2a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 18:48:17 +0800 Subject: [PATCH 072/965] Update panel_tools.py --- panel_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_tools.py b/panel_tools.py index c8491bbcc..413d39917 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -230,7 +230,7 @@ def set_panel_pwd(password, ncli=False): def show_panel_pwd(): # 面板密码展示 - info = model.getUserByRoot() + info = thisdb.getUserByRoot() defailt_pwd_file = mw.getPanelDir()+'/data/default.pl' pwd = '' if os.path.exists(defailt_pwd_file): From 0c6fb48989000cac8681b455aa78cecc28e32321 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 18:57:50 +0800 Subject: [PATCH 073/965] update --- scripts/init.d/mw.tpl | 2 +- web/core/mw.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 236c57a63..79a566f59 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -104,7 +104,7 @@ mw_start() # /www/server/mdserver-web/tmp/panelTask.pl && service mw restart_task mw_stop_task() { - if [ -f ${PANEL_DIR}/tmp/panelTask.pl ];then + if [ -f ${PANEL_DIR}/logs/panel_task.lock ];then echo -e "\033[32mthe task is running and cannot be stopped\033[0m" exit 0 fi diff --git a/web/core/mw.py b/web/core/mw.py index 287c9bae7..7f581af31 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -665,7 +665,7 @@ def isRestart(): return True def triggerTask(): - isTask = getPanelDir() + '/tmp/panelTask.pl' + isTask = getPanelDir() + '/logs/panel_task.lock' writeFile(isTask, 'True') def restartTask(): From 52e98f6d4110660e492937d570ab1b98e27f48f7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 19:15:37 +0800 Subject: [PATCH 074/965] update --- web/admin/setting/setting.py | 14 ++++++++++++++ web/utils/config.py | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 1019daa07..ed4910d31 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -183,6 +183,20 @@ def close_panel(): model.setOption('admin_close','no') return mw.returnData(True, '关闭面板成功!') +# 设置IPV6状态 +@blueprint.route('/set_ipv6_status', endpoint='set_ipv6_status', methods=['POST']) +@panel_login_required +def set_ipv6_status(): + __file = mw.getCommonFile() + ipv6_file = __file['ipv6'] + if os.path.exists(ipv6_file): + os.remove(ipv6_file) + mw.returnData('面板设置', '关闭面板IPv6兼容!') + else: + mw.writeFile(ipv6_file, 'True') + mw.writeLog('面板设置', '开启面板IPv6兼容!') + mw.restartMw() + return mw.returnData(True, '设置成功!') # 设置站点状态 @blueprint.route('/set_port', endpoint='set_port', methods=['POST']) diff --git a/web/utils/config.py b/web/utils/config.py index e11857bf5..5bdb8b049 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -8,6 +8,8 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os + from admin import model import core.mw as mw @@ -53,6 +55,12 @@ def getGlobalVar(): data['site_count'] = model.getSitesCount() data['port'] = mw.getHostPort() + __file = mw.getCommonFile() + if os.path.exists(__file['ipv6']): + data['ipv6'] = 'checked' + else: + data['ipv6'] = '' + # 获取未认证状态信息 unauthorized_status = model.getOption('unauthorized_status', default='0') data['unauthorized_status'] = getUnauthStatus(code=unauthorized_status) From 63145c736f03e9fc37cdb37302ea656b85ccea28 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 19:33:16 +0800 Subject: [PATCH 075/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index 434528a09..2818b3a2d 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -37,18 +37,19 @@ def init_cmd(): # initd if os.path.exists('/etc/rc.d/init.d'): initd_bin = '/etc/rc.d/init.d/mw' - if not os.path.exists(initd_bin): - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) + print(script_bin, initd_bin) + shutil.copyfile(script_bin, initd_bin) + mw.execShell('chmod +x ' + initd_bin) + + # 加入自启动 mw.execShell('which chkconfig && chkconfig --add mw') if os.path.exists('/etc/init.d'): initd_bin = '/etc/init.d/mw' - if not os.path.exists(initd_bin): - import shutil - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) + print(script_bin, initd_bin) + shutil.copyfile(script_bin, initd_bin) + mw.execShell('chmod +x ' + initd_bin) # 加入自启动 mw.execShell('which update-rc.d && update-rc.d -f mw defaults') return True \ No newline at end of file From 0f89246b17659c4d785aebddde60bdbca55908e7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 19:36:09 +0800 Subject: [PATCH 076/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index 2818b3a2d..cf674617a 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -37,7 +37,6 @@ def init_cmd(): # initd if os.path.exists('/etc/rc.d/init.d'): initd_bin = '/etc/rc.d/init.d/mw' - print(script_bin, initd_bin) shutil.copyfile(script_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) @@ -47,7 +46,6 @@ def init_cmd(): if os.path.exists('/etc/init.d'): initd_bin = '/etc/init.d/mw' - print(script_bin, initd_bin) shutil.copyfile(script_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) # 加入自启动 From 6114bcd9bcfdab60cb9b10efe9f9e8bf8c804038 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 19:36:17 +0800 Subject: [PATCH 077/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index cf674617a..d13e6785f 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -39,8 +39,6 @@ def init_cmd(): initd_bin = '/etc/rc.d/init.d/mw' shutil.copyfile(script_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) - - # 加入自启动 mw.execShell('which chkconfig && chkconfig --add mw') From 8f0be8a475a6b1858a98af83cf45428c9586ef8a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 19:39:49 +0800 Subject: [PATCH 078/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index d13e6785f..fd75bc2fb 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -15,7 +15,7 @@ import core.mw as mw def contentReplace(src, dst): content = mw.readFile(src) - content = content.replace("{$SERVER_PATH}", mw.getFatherDir()) + content = content.replace("{$SERVER_PATH}", mw.getPanelDir()) content += "\n# make:{0}".format(mw.formatDate()) mw.writeFile(dst, content) From 0b6cdec4ce4d6e899c574fecefb0f85282de2364 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 19:50:58 +0800 Subject: [PATCH 079/965] Update __init__.py --- web/admin/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 55b5d89ba..949734855 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -99,9 +99,13 @@ setup.init_db_system() app.config['BASIC_AUTH_OPEN'] = False with app.app_context(): - basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) - if basic_auth['open']: - app.config['BASIC_AUTH_OPEN'] = True + try: + basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) + if basic_auth['open']: + app.config['BASIC_AUTH_OPEN'] = True + except Exception as e: + pass + # 加载模块 From 044eb2b65fdb2c1d3283799a25f71f90c6987a5e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 20:21:35 +0800 Subject: [PATCH 080/965] up --- panel_tools.py | 6 +++--- web/thisdb/__init__.py | 3 ++- web/thisdb/init.py | 13 +++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 web/thisdb/init.py diff --git a/panel_tools.py b/panel_tools.py index 413d39917..5e66b6458 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -237,9 +237,9 @@ def show_panel_pwd(): pwd = mw.readFile(defailt_pwd_file).strip() if mw.md5(pwd) == info['password']: - print('password: ' + pwd) + print('|-password: ' + pwd) return - print("password has been changed!") + print("* password has been changed!") def show_panel_adminpath(): admin_path = thisdb.getOption('admin_path') @@ -264,7 +264,7 @@ def set_panel_username(username=None): if info['name'] == 'admin': username = mw.getRandomString(8).lower() thisdb.setUserByRoot(name=username) - print('|-用户名: ' + info['name']) + print('|-username: ' + info['name']) def getServerIp(): diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 8892e9129..47539da16 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -8,5 +8,6 @@ # Author: midoks # --------------------------------------------------------------------------------- +from .init import * from .option import * -from .user import * \ No newline at end of file +from .user import * diff --git a/web/thisdb/init.py b/web/thisdb/init.py new file mode 100644 index 000000000..6fa7431ff --- /dev/null +++ b/web/thisdb/init.py @@ -0,0 +1,13 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +def initPanelData(): + pass \ No newline at end of file From 39603f367f3960e29443baff97d1060fa8c2db44 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 20:24:45 +0800 Subject: [PATCH 081/965] Update panel_tools.py --- panel_tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index 5e66b6458..f54266e9a 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -222,8 +222,8 @@ def set_panel_pwd(password, ncli=False): info = model.getUserByRoot() model.setUserByRoot(password=password) if ncli: - print("|-用户名: " + info['name']) - print("|-新密码: " + password) + print("|-username: " + info['name']) + print("|-password: " + password) else: print(username) @@ -257,7 +257,7 @@ def set_panel_username(username=None): return thisdb.setUserByRoot(name=username) - print("|-新用户名: %s" % username) + print("|-username: %s" % username) return info = thisdb.getUserByRoot() From c77d409317ff8abe3f8b575ea541f4b40c09ff0d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 21:16:13 +0800 Subject: [PATCH 082/965] up --- web/admin/setting/setting.py | 3 ++- web/thisdb/__init__.py | 1 + web/thisdb/option.py | 34 ++++++++++++++++++++++++++++++++-- web/thisdb/sites.py | 15 +++++++++++++++ web/utils/config.py | 25 +++++++++++++------------ 5 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 web/thisdb/sites.py diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index ed4910d31..72010d808 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -191,6 +191,7 @@ def set_ipv6_status(): ipv6_file = __file['ipv6'] if os.path.exists(ipv6_file): os.remove(ipv6_file) + mw.writeLog('面板设置', '关闭面板IPv6兼容!') mw.returnData('面板设置', '关闭面板IPv6兼容!') else: mw.writeFile(ipv6_file, 'True') @@ -222,7 +223,7 @@ def set_port(): # firewall_api.firewall_api().addAcceptPort(port) # firewall_api.firewall_api().firewallReload() - # system_api.system_api().restartMw() + mw.restartMw() return mw.returnJson(True, '端口保存成功!') \ No newline at end of file diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 47539da16..fa43ddfe9 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -11,3 +11,4 @@ from .init import * from .option import * from .user import * +from .sites import * diff --git a/web/thisdb/option.py b/web/thisdb/option.py index 1c197d1ce..033a13925 100644 --- a/web/thisdb/option.py +++ b/web/thisdb/option.py @@ -8,7 +8,8 @@ # Author: midoks # --------------------------------------------------------------------------------- - +import os +import json import core.mw as mw def getOption(name, @@ -25,4 +26,33 @@ def getOption(name, if len(data) == 0: return default - return data \ No newline at end of file + return data + + +def getOptionByJson(name, + type: str | None = 'common', + default : object | None = None +) -> object: + ''' + 获取配置的值,返回对象类型 + :name -> str 名称 (必填) + :type -> str 类型 (可选|默认common) + :default -> str 默认值 (可选) + ''' + data = mw.M('option').field('name').where('name=? and type=?',(name, type,)).getField('value') + if len(data) == 0: + return default + if data is not None: + return json.loads(data) + +def setOption(name, value, + type: str | None = 'common' +) -> bool: + ''' + 设置配置的值 + :name -> str 名称 (必填) + :value -> object值 (必填) + :type -> str 类型 (可选|默认common) + ''' + mw.M('option').field('name').where('name=? and type=?',(name, type,)).setField('value', value) + return True \ No newline at end of file diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py new file mode 100644 index 000000000..b1f5a7082 --- /dev/null +++ b/web/thisdb/sites.py @@ -0,0 +1,15 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +def getSitesCount(): + return mw.M('sites').count() + diff --git a/web/utils/config.py b/web/utils/config.py index 5bdb8b049..878dc74d3 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -13,6 +13,7 @@ import os from admin import model import core.mw as mw +import thisdb def getUnauthStatus( code: str | None = '0' @@ -44,15 +45,15 @@ def getGlobalVar(): 获取全局变量 ''' data = {} - data['title'] = model.getOption('title', default='后羿面板') - data['ip'] = model.getOption('server_ip', default='127.0.0.1') + data['title'] = thisdb.getOption('title', default='后羿面板') + data['ip'] = thisdb.getOption('server_ip', default='127.0.0.1') - data['site_path'] = model.getOption('site_path', default=mw.getFatherDir()+'/wwwroot') - data['backup_path'] = model.getOption('backup_path', default=mw.getFatherDir()+'/backup') - data['admin_path'] = '/'+model.getOption('admin_path', default='') - data['debug'] = model.getOption('debug', default='close') - data['admin_close'] = model.getOption('admin_close', default='no') - data['site_count'] = model.getSitesCount() + data['site_path'] = thisdb.getOption('site_path', default=mw.getFatherDir()+'/wwwroot') + data['backup_path'] = thisdb.getOption('backup_path', default=mw.getFatherDir()+'/backup') + data['admin_path'] = '/'+thisdb.getOption('admin_path', default='') + data['debug'] = thisdb.getOption('debug', default='close') + data['admin_close'] = thisdb.getOption('admin_close', default='no') + data['site_count'] = thisdb.getSitesCount() data['port'] = mw.getHostPort() __file = mw.getCommonFile() @@ -62,16 +63,16 @@ def getGlobalVar(): data['ipv6'] = '' # 获取未认证状态信息 - unauthorized_status = model.getOption('unauthorized_status', default='0') + unauthorized_status = thisdb.getOption('unauthorized_status', default='0') data['unauthorized_status'] = getUnauthStatus(code=unauthorized_status) - data['basic_auth'] = model.getOptionByJson('basic_auth', default={'open':False}) + data['basic_auth'] = thisdb.getOptionByJson('basic_auth', default={'open':False}) # 服务器时间 sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"' data['systemdate'] = mw.execShell(sformat)[0].strip() - data['hook_menu'] = model.getOptionByJson('hook_menu',type='hook',default=[]) - data['hook_global_static'] = model.getOptionByJson('hook_global_static',type='hook',default=[]) + data['hook_menu'] = thisdb.getOptionByJson('hook_menu',type='hook',default=[]) + data['hook_global_static'] = thisdb.getOptionByJson('hook_global_static',type='hook',default=[]) return data \ No newline at end of file From 3ae8f0253e3c7e3a0954cb8f3fa956c4b067b65b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 22:21:41 +0800 Subject: [PATCH 083/965] up --- class/core/crontab_api.py | 3 +-- data/sql/default.sql | 33 +++++++++++++---------- web/admin/crontab/__init__.py | 17 +++++++++++- web/admin/plugins/__init__.py | 2 +- web/admin/setting/setting.py | 6 +++-- web/admin/site/__init__.py | 2 +- web/thisdb/__init__.py | 1 + web/thisdb/tasks.py | 39 ++++++++++++++++++++++++++++ web/utils/crontab.py | 9 +++++++ web/utils/firewall.py | 2 +- web/utils/{mwplugin.py => plugin.py} | 19 +++++++------- 11 files changed, 103 insertions(+), 30 deletions(-) create mode 100644 web/thisdb/tasks.py create mode 100644 web/utils/crontab.py rename web/utils/{mwplugin.py => plugin.py} (97%) diff --git a/class/core/crontab_api.py b/class/core/crontab_api.py index c9f7d682d..ba77196a2 100755 --- a/class/core/crontab_api.py +++ b/class/core/crontab_api.py @@ -500,8 +500,7 @@ class crontab_api: # 取任务构造Month def month(self, param): - cuonConfig = "{0} {1} {2} * * ".format( - param['minute'], param['hour'], param['where1']) + cuonConfig = "{0} {1} {2} * * ".format(param['minute'], param['hour'], param['where1']) return cuonConfig # 取执行脚本 diff --git a/data/sql/default.sql b/data/sql/default.sql index daa5e4521..1acdaa795 100755 --- a/data/sql/default.sql +++ b/data/sql/default.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS `backup` ( `pid` INTEGER, `filename` TEXT, `size` INTEGER, - `addtime` TEXT + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `binding` ( @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS `binding` ( `domain` TEXT, `path` TEXT, `port` INTEGER, - `addtime` TEXT + `add_time` TEXT ); @@ -27,7 +27,6 @@ CREATE TABLE IF NOT EXISTS `crontab` ( `where_hour` INTEGER, `where_minute` INTEGER, `echo` TEXT, - `addtime` TEXT, `status` INTEGER DEFAULT '1', `save` INTEGER DEFAULT '3', `backup_to` TEXT DEFAULT 'off', @@ -35,6 +34,7 @@ CREATE TABLE IF NOT EXISTS `crontab` ( `sbody` TEXT, 'stype' TEXT, `urladdress` TEXT + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `firewall` ( @@ -42,12 +42,12 @@ CREATE TABLE IF NOT EXISTS `firewall` ( `port` TEXT, `protocol` TEXT DEFAULT 'tcp', `ps` TEXT, - `addtime` TEXT + `add_time` TEXT ); ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp'; -INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `addtime`) VALUES +INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`) VALUES (1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00'), (2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00'); @@ -58,7 +58,7 @@ CREATE TABLE IF NOT EXISTS `logs` ( `type` TEXT, `log` TEXT, `uid` INTEGER DEFAULT '1', - `addtime` TEXT + `add_time` TEXT ); ALTER TABLE `logs` ADD COLUMN `uid` INTEGER DEFAULT '1'; @@ -73,7 +73,7 @@ CREATE TABLE IF NOT EXISTS `sites` ( `edate` TEXT, `ssl_effective_date` TEXT, `ssl_expiration_date` TEXT, - `addtime` TEXT + `add_time` TEXT ); ALTER TABLE `sites` ADD COLUMN `ssl_effective_date` TEXT DEFAULT ''; @@ -89,7 +89,7 @@ CREATE TABLE IF NOT EXISTS `domain` ( `pid` INTEGER, `name` TEXT, `port` INTEGER, - `addtime` TEXT + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `users` ( @@ -110,11 +110,11 @@ CREATE TABLE IF NOT EXISTS `tasks` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `type` TEXT, - `status` TEXT, - `addtime` TEXT, `start` INTEGER, `end` INTEGER, - `execstr` TEXT + `cmd` TEXT, + `status` INTEGER, + `add_time` INTEGER ); CREATE TABLE IF NOT EXISTS `temp_login` ( @@ -126,7 +126,7 @@ CREATE TABLE IF NOT EXISTS `temp_login` ( `login_addr` REAL, `logout_time` INTEGER, `expire` INTEGER, - `addtime` INTEGER + `add_time` INTEGER ); CREATE TABLE IF NOT EXISTS `panel` ( @@ -136,5 +136,12 @@ CREATE TABLE IF NOT EXISTS `panel` ( `username` TEXT, `password` TEXT, `click` INTEGER, - `addtime` INTEGER + `add_time` INTEGER +); + +CREATE TABLE IF NOT EXISTS `option` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `type` TEXT, + `value` TEXT ); diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index f10762d03..782623ca2 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -31,4 +31,19 @@ def list(): clist = Crontab.query.paginate(page=int(page), per_page=size) # print(clist) - return [] \ No newline at end of file + return [] + + +@blueprint.route('/add', endpoint='add', methods=['GET','POST']) +@panel_login_required +def add(): + page = request.args.get('p', 1) + size = 10 + count = Crontab.query.count() + # print(count) + clist = Crontab.query.paginate(page=int(page), per_page=size) + # print(clist) + + return [] + + diff --git a/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py index 8e8a1a1e2..77baaf6f1 100644 --- a/web/admin/plugins/__init__.py +++ b/web/admin/plugins/__init__.py @@ -14,7 +14,7 @@ import json from flask import Blueprint, render_template from flask import request -from utils.mwplugin import MwPlugin +from utils.plugin import plugin as MwPlugin from admin.user_login_check import panel_login_required from admin import model diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 72010d808..f6e663e27 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -18,9 +18,11 @@ from flask import request from admin import model from admin.user_login_check import panel_login_required + import core.mw as mw import utils.config as utils_config + # 默认页面 blueprint = Blueprint('setting', __name__, url_prefix='/setting', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @@ -218,12 +220,12 @@ def set_port(): msg = mw.getInfo('放行端口[{1}]成功', (port,)) mw.writeLog("防火墙管理", msg) addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime)) + mw.M('firewall').add('port,ps,add_time', (port, "配置修改", add_time)) # firewall_api.firewall_api().addAcceptPort(port) # firewall_api.firewall_api().firewallReload() mw.restartMw() - return mw.returnJson(True, '端口保存成功!') + return mw.returnData(True, '端口保存成功!') \ No newline at end of file diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 4ec0f7db6..d51580a0c 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -16,7 +16,7 @@ from flask import request from admin.model import Sites from admin.user_login_check import panel_login_required -from utils.mwplugin import MwPlugin +from utils.plugin import plugin as MwPlugin import utils.site as site import core.mw as mw diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index fa43ddfe9..9022ba603 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -12,3 +12,4 @@ from .init import * from .option import * from .user import * from .sites import * +from .tasks import * diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py new file mode 100644 index 000000000..7dcab2a21 --- /dev/null +++ b/web/thisdb/tasks.py @@ -0,0 +1,39 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +def addTask( + name: str | None = '常用任务', + cmd: str | None = None, + type: str | None = 'execshell', + status: int | None = 0, +): + ''' + 添加后台任务 + :name -> str 类型 + :cmd -> str 日志内容 (必填) + :type -> str 用户ID + ''' + if cmd is None: + return False + + add_time = mw.formatDate() + insert_data = { + 'name':name, + 'type':type, + 'cmd':cmd, + 'start':0, + 'end':0, + 'status':status, + 'add_time':add_time, + } + mw.M('tasks').insert(insert_data) + return True \ No newline at end of file diff --git a/web/utils/crontab.py b/web/utils/crontab.py new file mode 100644 index 000000000..b15af4bdb --- /dev/null +++ b/web/utils/crontab.py @@ -0,0 +1,9 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 509b17ae6..e100925ad 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -230,7 +230,7 @@ class Firewall(object): if model.getFirewallCountByPort(port) > 0: return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') - model.addFirewall(port,ps=ps,protocol=protocol) + model.addFirewall(port, ps=ps,protocol=protocol) self.addAcceptPortCmd(port, protocol=protocol) self.reload() diff --git a/web/utils/mwplugin.py b/web/utils/plugin.py similarity index 97% rename from web/utils/mwplugin.py rename to web/utils/plugin.py index 6a2ff736b..81eda5a51 100644 --- a/web/utils/mwplugin.py +++ b/web/utils/plugin.py @@ -18,8 +18,9 @@ from admin import model import core.mw as mw import admin.model.option as option +import thisdb -class pa_thread(threading.Thread): +class pg_thread(threading.Thread): def __init__(self, func, args, name=''): threading.Thread.__init__(self) @@ -34,7 +35,7 @@ class pa_thread(threading.Thread): except Exception: return None -class MwPlugin(object): +class plugin(object): def_plugin_type = [ { @@ -88,11 +89,11 @@ class MwPlugin(object): @classmethod def instance(cls, *args, **kwargs): - if not hasattr(MwPlugin, "_instance"): - with MwPlugin._instance_lock: - if not hasattr(MwPlugin, "_instance"): - MwPlugin._instance = MwPlugin(*args, **kwargs) - return MwPlugin._instance + if not hasattr(plugin, "_instance"): + with plugin._instance_lock: + if not hasattr(plugin, "_instance"): + plugin._instance = plugin(*args, **kwargs) + return plugin._instance """插件类初始化""" def __init__(self): @@ -260,7 +261,7 @@ class MwPlugin(object): self.hookInstall(info_data) title = '{0}[{1}-{2}]'.format(msg_head,name,version) - model.addTask(name=title,cmd=exec_bash, status=0) + thisdb.addTask(name=title,cmd=exec_bash, status=0) # 调式日志 mw.debugLog(exec_bash) @@ -494,7 +495,7 @@ class MwPlugin(object): threads = [] ntmp_list = range(len(info)) for i in ntmp_list: - t = pa_thread(self.checkStatusThreads,(info[i], i)) + t = pg_thread(self.checkStatusThreads,(info[i], i)) threads.append(t) for i in ntmp_list: From 06abfd92f28587a1d307e979bde0aea615e7485b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 23:44:05 +0800 Subject: [PATCH 084/965] update --- class/core/mw.py | 19 +- panel_task.py | 163 +++----------- web/core/mw.py | 71 +++++- web/utils/crontab.py | 2 + web/utils/php/fcgi_client.py | 408 +++++++++++++++++++++++++++++++++++ web/utils/php/fpm.py | 209 ++++++++++++++++++ 6 files changed, 731 insertions(+), 141 deletions(-) create mode 100644 web/utils/php/fcgi_client.py create mode 100644 web/utils/php/fpm.py diff --git a/class/core/mw.py b/class/core/mw.py index a4441260e..99fe840ab 100755 --- a/class/core/mw.py +++ b/class/core/mw.py @@ -602,7 +602,7 @@ def writeLog(stype, msg, args=()): def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3): - log_file = getServerDir() + '/mdserver-web/logs/debug.log' + log_file = getPanelDir() + '/logs/debug.log' if path != None: log_file = path @@ -632,14 +632,15 @@ def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3): def writeDbLog(stype, msg, args=(), uid=1): try: - import time - import db - import json - sql = db.Sql() - mdate = time.strftime('%Y-%m-%d %X', time.localtime()) - wmsg = getInfo(msg, args) - data = (stype, wmsg, uid, mdate) - result = sql.table('logs').add('type,log,uid,addtime', data) + add_time = formatDate() + format_msg = getInfo(msg, args) + add_data = { + 'type':stype, + 'msg':format_msg, + 'uid':uid, + 'add_time':add_time, + } + result = M('logs').insert(add_data) return True except Exception as e: return False diff --git a/panel_task.py b/panel_task.py index 263e9366f..67344a002 100755 --- a/panel_task.py +++ b/panel_task.py @@ -27,16 +27,10 @@ sys.path.append(web_dir) from admin import app from admin import model +import thisdb import core.mw as mw import core.db as db - -global pre, timeoutCount, logPath, isTask, oldEdate, isCheck -pre = 0 -timeoutCount = 0 -isCheck = 0 -oldEdate = None - g_log_file = mw.getPanelTaskLog() isTask = mw.getMWLogs() + '/panelTask.pl' @@ -44,30 +38,8 @@ if not os.path.exists(g_log_file): os.system("touch " + g_log_file) def execShell(cmdstring, cwd=None, timeout=None, shell=True): - try: - global g_log_file - import shlex - import datetime - import subprocess - - if timeout: - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) - - cmd = cmdstring + ' > ' + g_log_file + ' 2>&1' - sub = subprocess.Popen(cmd, cwd=cwd, stdin=subprocess.PIPE, shell=shell, bufsize=4096) - while sub.poll() is None: - time.sleep(0.1) - - data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) - except Exception as e: - return (None, None) + cmd = cmdstring + ' > ' + g_log_file + ' 2>&1' + return mw.execShell(cmd) def service_cmd(method): cmd = '/etc/init.d/mw' @@ -76,37 +48,12 @@ def service_cmd(method): return cmd = mw.getPanelDir() + '/scripts/init.d/mw' - print(cmd) if os.path.exists(cmd): print(cmd + ' ' + method) data = execShell(cmd + ' ' + method) print(data) return - -def openresty_cmd(method = 'reload'): - # 检查是否安装 - odir = mw.getServerDir() + '/openresty' - if not os.path.exists(odir): - return False - - # systemd - systemd = mw.systemdCfgDir()+'/openresty.service' - if os.path.exists(systemd): - execShell('systemctl ' + method + ' openresty') - return True - - sys_initd = '/etc/init.d/openresty' - if os.path.exists(sys_initd): - os.system(sys_initd + ' ' + method) - return True - - install_initd = mw.getServerDir()+'/openresty/init.d/openresty' - if os.path.exists(install_initd): - os.system(install_initd + ' ' + method) - return True - return False - def mw_async(f): def wrapper(*args, **kwargs): thr = threading.Thread(target=f, args=args, kwargs=kwargs) @@ -225,8 +172,8 @@ def siteEdate(): def systemTask(): - from utils.system import monitor # 系统监控任务 + from utils.system import monitor try: while True: monitor.instance().run() @@ -240,10 +187,11 @@ def systemTask(): # 502错误检查线程 def check502Task(): try: + check_file = mw.getPanelDir() + '/data/502Task.pl' while True: - if os.path.exists(mw.getPanelDir() + '/data/502Task.pl'): + if os.path.exists(check_file): check502() - time.sleep(30) + time.sleep(10) except: time.sleep(30) check502Task() @@ -257,8 +205,8 @@ def check502(): '82', '83', '84' ] for ver in verlist: - sdir = mw.getServerDir() - php_path = sdir + '/php/' + ver + '/sbin/php-fpm' + server_dir = mw.getServerDir() + php_path = server_dir + '/php/' + ver + '/sbin/php-fpm' if not os.path.exists(php_path): continue if checkPHPVersion(ver): @@ -266,13 +214,14 @@ def check502(): if startPHPVersion(ver): print('检测到PHP-' + ver + '处理异常,已自动修复!') mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!') + except Exception as e: - print(str(e)) + mw.writeLog('PHP守护程序', '自动修复异常:'+str(e)) # 处理指定PHP版本 def startPHPVersion(version): - sdir = mw.getServerDir() + server_dir = mw.getServerDir() try: # system phpService = mw.systemdCfgDir() + '/php' + version + '.service' @@ -282,8 +231,8 @@ def startPHPVersion(version): return True # initd - fpm = sdir + '/php/init.d/php' + version - php_path = sdir + '/php/' + version + '/sbin/php-fpm' + fpm = server_dir + '/php/init.d/php' + version + php_path = server_dir + '/php/' + version + '/sbin/php-fpm' if not os.path.exists(php_path): if os.path.exists(fpm): os.remove(fpm) @@ -296,7 +245,6 @@ def startPHPVersion(version): os.system(fpm + ' reload') if checkPHPVersion(version): return True - # 尝试重启服务 cgi = '/tmp/php-cgi-' + version + '.sock' pid = sdir + '/php/' + version + '/var/run/php-fpm.pid' @@ -316,47 +264,18 @@ def startPHPVersion(version): if os.path.exists(cgi): return True except Exception as e: - print(str(e)) + mw.writeLog('PHP守护程序', '自动修复异常:'+str(e)) return True -def getFpmConfFile(version): - return mw.getServerDir() + '/php/' + version + '/etc/php-fpm.d/www.conf' - - -def getFpmAddress(version): - fpm_address = '/tmp/php-cgi-{}.sock'.format(version) - php_fpm_file = getFpmConfFile(version) - try: - content = readFile(php_fpm_file) - tmp = re.findall(r"listen\s*=\s*(.+)", content) - if not tmp: - return fpm_address - if tmp[0].find('sock') != -1: - return fpm_address - if tmp[0].find(':') != -1: - listen_tmp = tmp[0].split(':') - if bind: - fpm_address = (listen_tmp[0], int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(tmp[0])) - return fpm_address - except: - return fpm_address - - def checkPHPVersion(version): # 检查指定PHP版本 try: - sock = getFpmAddress(version) + sock = mw.getFpmAddress(version) data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json') result = str(data, encoding='utf-8') except Exception as e: result = 'Bad Gateway' - - # print(version,result) # 检查openresty if result.find('Bad Gateway') != -1: return False @@ -366,24 +285,9 @@ def checkPHPVersion(version): # 检查Web服务是否启动 if result.find('Connection refused') != -1: return False - # global isTask - # if os.path.exists(isTask): - # isStatus = mw.readFile(isTask) - # if isStatus == 'True': - # return True - - # # systemd - # systemd = mw.systemdCfgDir() + '/openresty.service' - # if os.path.exists(systemd): - # execShell('systemctl reload openresty') - # return True - # # initd - # initd = '/etc/init.d/openresty' - # if os.path.exists(initd): - # os.system(initd + ' reload') return True -# --------------------------------------PHP监控 end--------------------------------------------- # +# -------------------------------------- PHP监控 end --------------------------------------------- # # --------------------------------------OpenResty Auto Restart Start --------------------------------------------- # @@ -396,11 +300,10 @@ def openrestyAutoRestart(): if not os.path.exists(odir): time.sleep(86400) continue - - openresty_cmd('reload') + mw.opWeb('reload') time.sleep(86400) except Exception as e: - print(str(e)) + mw.writeLog('OpenResty检测', '自动修复异常:'+str(e)) time.sleep(86400) # --------------------------------------OpenResty Auto Restart End --------------------------------------------- # @@ -409,11 +312,11 @@ def openrestyAutoRestart(): def openrestyRestartAtOnce(): - restart_nginx_tip = 'data/restart_nginx.pl' + restart_nginx_tip = mw.getPanelDir()+'/data/restart_nginx.pl' while True: if os.path.exists(restart_nginx_tip): os.remove(restart_nginx_tip) - openresty_cmd('reload') + mw.opWeb('reload') time.sleep(1) # ----------------------------------- OpenResty Restart At Once End ------------------------------------------ # @@ -424,7 +327,7 @@ def restartPanelService(): while True: if os.path.exists(restartTip): os.remove(restartTip) - service_cmd('restart_panel') + mw.panelCmd('restart_panel') time.sleep(1) # --------------------------------------Panel Restart End --------------------------------------------- # @@ -443,20 +346,20 @@ def run(): sysTask.start() # # PHP 502错误检查线程 - # php502 = threading.Thread(target=check502Task) - # php502 = setDaemon(php502) - # php502.start() + php502 = threading.Thread(target=check502Task) + php502 = setDaemon(php502) + php502.start() - # # OpenResty Restart At Once Start - # oraos = threading.Thread(target=openrestyRestartAtOnce) - # oraos = setDaemon(oraos) - # oraos.start() + # OpenResty Restart At Once Start + oraos = threading.Thread(target=openrestyRestartAtOnce) + oraos = setDaemon(oraos) + oraos.start() - # # OpenResty Auto Restart Start - # oar = threading.Thread(target=openrestyAutoRestart) - # oar = setDaemon(oar) - # oar.start() + # OpenResty Auto Restart Start + oar = threading.Thread(target=openrestyAutoRestart) + oar = setDaemon(oar) + oar.start() # Panel Restart Start diff --git a/web/core/mw.py b/web/core/mw.py index 7f581af31..0138b1e87 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -679,6 +679,18 @@ def restartMw(): restart_file = getPanelDir()+'/data/restart.pl' writeFile(restart_file, 'True') return True + +def panelCmd(method): + cmd = '/etc/init.d/mw' + if os.path.exists(cmd): + execShell(cmd + ' ' + method) + return + + cmd = mw.getPanelDir() + '/scripts/init.d/mw' + if os.path.exists(cmd): + data = execShell(cmd + ' ' + method) + return + # ------------------------------ panel end ----------------------------- # ------------------------------ openresty start ----------------------------- @@ -696,14 +708,19 @@ def opWeb(method): return False # systemd - systemd = '/lib/systemd/system/openresty.service' + systemd = systemdCfgDir() + '/openresty.service' if os.path.exists(systemd): execShell('systemctl ' + method + ' openresty') return True + + sys_initd = '/etc/init.d/openresty' + if os.path.exists(sys_initd): + os.system(sys_initd + ' ' + method) + return True + # initd initd = getServerDir() + '/openresty/init.d/openresty' - if os.path.exists(initd): execShell(initd + ' ' + method) return True @@ -766,9 +783,59 @@ def opLuaMakeAll(): # ------------------------------ openresty end ----------------------------- +# --------------------------------------------------------------------------------- +# PHP START +# --------------------------------------------------------------------------------- + +def getFpmConfFile(version): + return getServerDir() + '/php/' + version + '/etc/php-fpm.d/www.conf' + +def getFpmAddress(version): + fpm_address = '/tmp/php-cgi-{}.sock'.format(version) + php_fpm_file = getFpmConfFile(version) + try: + content = readFile(php_fpm_file) + tmp = re.findall(r"listen\s*=\s*(.+)", content) + if not tmp: + return fpm_address + if tmp[0].find('sock') != -1: + return fpm_address + if tmp[0].find(':') != -1: + listen_tmp = tmp[0].split(':') + if bind: + fpm_address = (listen_tmp[0], int(listen_tmp[1])) + else: + fpm_address = ('127.0.0.1', int(listen_tmp[1])) + else: + fpm_address = ('127.0.0.1', int(tmp[0])) + return fpm_address + except: + return fpm_address + +def requestFcgiPHP(sock, uri, document_root='/tmp', method='GET', pdata=b''): + # 直接请求到PHP-FPM + # version php版本 + # uri 请求uri + # filename 要执行的php文件 + # args 请求参数 + # method 请求方式 + + import utils.php.fpm as fpm + p = fpm.fpm(sock, document_root) + + if type(pdata) == dict: + pdata = url_encode(pdata) + result = p.load_url_public(uri, pdata, method) + return result +# --------------------------------------------------------------------------------- +# PHP END +# --------------------------------------------------------------------------------- + + # --------------------------------------------------------------------------------- # 数据库 START # --------------------------------------------------------------------------------- + def getMyORM(): ''' 获取MySQL资源的ORM diff --git a/web/utils/crontab.py b/web/utils/crontab.py index b15af4bdb..57a654e17 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -7,3 +7,5 @@ # --------------------------------------------------------------------------------- # Author: midoks # --------------------------------------------------------------------------------- + + diff --git a/web/utils/php/fcgi_client.py b/web/utils/php/fcgi_client.py new file mode 100644 index 000000000..f1e0c67e7 --- /dev/null +++ b/web/utils/php/fcgi_client.py @@ -0,0 +1,408 @@ +# Copyright (c) 2006 Allan Saddi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id$ +# +# Copyright (c) 2011 Vladimir Rusinov + +__author__ = 'Allan Saddi ' +__version__ = '$Revision$' +import sys +import select +import struct +import socket +import errno +import types + + +__all__ = ['FCGIApp'] + +# Constants from the spec. +FCGI_LISTENSOCK_FILENO = 0 + +FCGI_HEADER_LEN = 8 + +FCGI_VERSION_1 = 1 + +FCGI_BEGIN_REQUEST = 1 +FCGI_ABORT_REQUEST = 2 +FCGI_END_REQUEST = 3 +FCGI_PARAMS = 4 +FCGI_STDIN = 5 +FCGI_STDOUT = 6 +FCGI_STDERR = 7 +FCGI_DATA = 8 +FCGI_GET_VALUES = 9 +FCGI_GET_VALUES_RESULT = 10 +FCGI_UNKNOWN_TYPE = 11 +FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE + +FCGI_NULL_REQUEST_ID = 0 + +FCGI_KEEP_CONN = 1 + +FCGI_RESPONDER = 1 +FCGI_AUTHORIZER = 2 +FCGI_FILTER = 3 + +FCGI_REQUEST_COMPLETE = 0 +FCGI_CANT_MPX_CONN = 1 +FCGI_OVERLOADED = 2 +FCGI_UNKNOWN_ROLE = 3 + +FCGI_MAX_CONNS = 'FCGI_MAX_CONNS' +FCGI_MAX_REQS = 'FCGI_MAX_REQS' +FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS' + +FCGI_Header = '!BBHHBx' +FCGI_BeginRequestBody = '!HB5x' +FCGI_EndRequestBody = '!LB3x' +FCGI_UnknownTypeBody = '!B7x' + +FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody) +FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody) +FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody) + +if __debug__: + import time + + # Set non-zero to write debug output to a file. + DEBUG = 0 + DEBUGLOG = '/www/server/mdserver-web/logs/fastcgi.log' + + def _debug(level, msg): + if DEBUG < level: + return + + try: + f = open(DEBUGLOG, 'a') + f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg)) + f.close() + except: + pass + + +def decode_pair(s, pos=0): + """ + Decodes a name/value pair. + + The number of bytes decoded as well as the name/value pair + are returned. + """ + nameLength = ord(s[pos]) + if nameLength & 128: + nameLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff + pos += 4 + else: + pos += 1 + + valueLength = ord(s[pos]) + if valueLength & 128: + valueLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff + pos += 4 + else: + pos += 1 + + name = s[pos:pos + nameLength] + pos += nameLength + value = s[pos:pos + valueLength] + pos += valueLength + + return (pos, (name, value)) + + +def encode_pair(name, value): + """ + Encodes a name/value pair. + + The encoded string is returned. + """ + nameLength = len(name) + if nameLength < 128: + s = chr(nameLength).encode() + else: + s = struct.pack('!L', nameLength | 0x80000000) + + valueLength = len(value) + if valueLength < 128: + s += chr(valueLength).encode() + else: + s += struct.pack('!L', valueLength | 0x80000000) + + return s + name + value + + +class Record(object): + """ + A FastCGI Record. + + Used for encoding/decoding records. + """ + + def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID): + self.version = FCGI_VERSION_1 + self.type = type + self.requestId = requestId + self.contentLength = 0 + self.paddingLength = 0 + self.contentData = '' + + def _recvall(sock, length): + """ + Attempts to receive length bytes from a socket, blocking if necessary. + (Socket may be blocking or non-blocking.) + """ + dataList = [] + recvLen = 0 + while length: + try: + data = sock.recv(length) + except socket.error as e: + if e[0] == errno.EAGAIN: + select.select([sock], [], []) + continue + else: + raise + if not data: # EOF + break + dataList.append(data) + dataLen = len(data) + recvLen += dataLen + length -= dataLen + return b''.join(dataList), recvLen + _recvall = staticmethod(_recvall) + + def read(self, sock): + """Read and decode a Record from a socket.""" + try: + header, length = self._recvall(sock, FCGI_HEADER_LEN) + except: + raise EOFError + + if length < FCGI_HEADER_LEN: + raise EOFError + + self.version, self.type, self.requestId, self.contentLength, \ + self.paddingLength = struct.unpack(FCGI_Header, header) + + if __debug__: + _debug(9, 'read: fd = %d, type = %d, requestId = %d, ' + 'contentLength = %d' % + (sock.fileno(), self.type, self.requestId, + self.contentLength)) + + if self.contentLength: + try: + self.contentData, length = self._recvall(sock, + self.contentLength) + except: + raise EOFError + + if length < self.contentLength: + raise EOFError + + if self.paddingLength: + try: + self._recvall(sock, self.paddingLength) + except: + raise EOFError + + def _sendall(sock, data): + """ + Writes data to a socket and does not return until all the data is sent. + """ + length = len(data) + while length: + try: + sent = sock.send(data) + except socket.error as e: + if e[0] == errno.EAGAIN: + select.select([], [sock], []) + continue + else: + raise + data = data[sent:] + length -= sent + _sendall = staticmethod(_sendall) + + def write(self, sock): + """Encode and write a Record to a socket.""" + self.paddingLength = -self.contentLength & 7 + + if __debug__: + _debug(9, 'write: fd = %d, type = %d, requestId = %d, ' + 'contentLength = %d' % + (sock.fileno(), self.type, self.requestId, + self.contentLength)) + + header = struct.pack(FCGI_Header, self.version, self.type, + self.requestId, self.contentLength, + self.paddingLength) + self._sendall(sock, header) + if self.contentLength: + self._sendall(sock, self.contentData) + if self.paddingLength: + self._sendall(sock, b'\x00' * self.paddingLength) + + +class FCGIApp(object): + + def __init__(self, connect=None, host=None, port=None, filterEnviron=True): + if host is not None: + assert port is not None + connect = (host, port) + + self._connect = connect + self._filterEnviron = filterEnviron + + def __call__(self, environ, io, start_response=None): + # For sanity's sake, we don't care about FCGI_MPXS_CONN + # (connection multiplexing). For every request, we obtain a new + # transport socket, perform the request, then discard the socket. + # This is, I believe, how mod_fastcgi does things... + + sock = self._getConnection() + + # Since this is going to be the only request on this connection, + # set the request ID to 1. + requestId = 1 + + # Begin the request + rec = Record(FCGI_BEGIN_REQUEST, requestId) + rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0) + rec.contentLength = FCGI_BeginRequestBody_LEN + rec.write(sock) + + # Filter WSGI environ and send it as FCGI_PARAMS + if self._filterEnviron: + params = self._defaultFilterEnviron(environ) + else: + params = self._lightFilterEnviron(environ) + # TODO: Anything not from environ that needs to be sent also? + # return '200 OK',[],str(params),'' + self._fcgiParams(sock, requestId, params) + self._fcgiParams(sock, requestId, {}) + + # Transfer wsgi.input to FCGI_STDIN + content_length = int(environ.get('CONTENT_LENGTH') or 0) + s = '' + #io = StringIO(stdin) + while True: + if not io: + break + chunk_size = min(content_length, 4096) + s = io.read(chunk_size) + content_length -= len(s) + rec = Record(FCGI_STDIN, requestId) + rec.contentData = s + rec.contentLength = len(s) + rec.write(sock) + if not s: + break + # Empty FCGI_DATA stream + rec = Record(FCGI_DATA, requestId) + rec.write(sock) + return sock + + def _getConnection(self): + if self._connect is not None: + # The simple case. Create a socket and connect to the + # application. + if isinstance(self._connect, str): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(self._connect) + elif hasattr(socket, 'create_connection'): + sock = socket.create_connection(self._connect) + else: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(self._connect) + return sock + + # To be done when I have more time... + # , 'Launching and managing FastCGI programs not yet implemented' + raise NotImplementedError + + def _fcgiGetValues(self, sock, vars): + # Construct FCGI_GET_VALUES record + outrec = Record(FCGI_GET_VALUES) + data = [] + for name in vars: + data.append(encode_pair(name, '')) + data = ''.join(data) + outrec.contentData = data + outrec.contentLength = len(data) + outrec.write(sock) + + # Await response + inrec = Record() + inrec.read(sock) + result = {} + if inrec.type == FCGI_GET_VALUES_RESULT: + pos = 0 + while pos < inrec.contentLength: + pos, (name, value) = decode_pair(inrec.contentData, pos) + result[name] = value + return result + + def _fcgiParams(self, sock, requestId, params): + rec = Record(FCGI_PARAMS, requestId) + data = [] + for name, value in params.items(): + data.append(encode_pair(name.encode( + 'latin-1'), value.encode('latin-1'))) + data = b''.join(data) + rec.contentData = data + rec.contentLength = len(data) + rec.write(sock) + + _environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_', + 'CONTENT_', 'DOCUMENT_', 'SCRIPT_'] + _environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE'] + _environRenames = [] + + def _defaultFilterEnviron(self, environ): + result = {} + for n in environ.keys(): + iv = False + for p in self._environPrefixes: + if n.startswith(p): + result[n] = environ[n] + iv = True + if n in self._environCopies: + result[n] = environ[n] + iv = True + if n in self._environRenames: + result[self._environRenames[n]] = environ[n] + iv = True + if not iv: + result[n] = environ[n] + + return result + + def _lightFilterEnviron(self, environ): + result = {} + for n in environ.keys(): + if n.upper() == n: + result[n] = environ[n] + return result diff --git a/web/utils/php/fpm.py b/web/utils/php/fpm.py new file mode 100644 index 000000000..66f72494e --- /dev/null +++ b/web/utils/php/fpm.py @@ -0,0 +1,209 @@ +# coding:utf-8 + + +import json +import os +import time +import re +import sys +import time +import struct + +from .fcgi_client import FCGIApp + +FCGI_Header = '!BBHHBx' + +if sys.version_info[0] == 2: + try: + from cStringIO import StringIO + except: + from StringIO import StringIO +else: + from io import BytesIO as StringIO + + +def get_header_data(sock): + ''' + @name 获取头部32KB数据 + @param sock socketobject(fastcgi套接字对象) + @return bytes + ''' + headers_data = b'' + total_len = 0 + header_len = 1024 * 128 + while True: + fastcgi_header = sock.recv(8) + if not fastcgi_header: + break + if len(fastcgi_header) != 8: + headers_data += fastcgi_header + break + fast_pack = struct.unpack(FCGI_Header, fastcgi_header) + if fast_pack[1] == 3: + break + + tlen = fast_pack[3] + while tlen > 0: + sd = sock.recv(tlen) + if not sd: + break + headers_data += sd + tlen -= len(sd) + + total_len += fast_pack[3] + if fast_pack[4]: + sock.recv(fast_pack[4]) + if total_len > header_len: + break + return headers_data + + +def format_header_data(headers_data): + ''' + @name 格式化响应头 + @param headers_data bytes(fastcgi头部32KB数据) + @return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据) + ''' + status = '200 OK' + headers = {} + pos = 0 + while True: + eolpos = headers_data.find(b'\n', pos) + if eolpos < 0: + break + line = headers_data[pos:eolpos - 1] + pos = eolpos + 1 + line = line.strip() + if len(line) < 2: + break + if line.find(b':') == -1: + continue + header, value = line.split(b':', 1) + header = header.strip() + value = value.strip() + if isinstance(header, bytes): + header = header.decode() + value = value.decode() + if header == 'Status': + status = value + if status.find(' ') < 0: + status += ' BTPanel' + else: + headers[header] = value + bdata = headers_data[pos:] + status = int(status.split(' ')[0]) + return status, headers, bdata + + +def resp_sock(sock, bdata): + ''' + @name 以流的方式发送剩余数据 + @param sock socketobject(fastcgi套接字对象) + @param bdata bytes(格式化响应头后的多余数据) + @return yield bytes + ''' + # 发送除响应头以外的多余头部数据 + yield bdata + while True: + fastcgi_header = sock.recv(8) + if not fastcgi_header: + break + if len(fastcgi_header) != 8: + yield fastcgi_header + break + fast_pack = struct.unpack(FCGI_Header, fastcgi_header) + if fast_pack[1] == 3: + break + tlen = fast_pack[3] + while tlen > 0: + sd = sock.recv(tlen) + if not sd: + break + tlen -= len(sd) + if sd: + yield sd + + if fast_pack[4]: + sock.recv(fast_pack[4]) + sock.close() + + +class fpm(object): + + def __init__(self, sock=None, document_root='', last_path=''): + ''' + @name 实例化FPM对象 + @param sock string(unixsocket路径) + @param document_root string(PHP文档根目录) + @return FPM + ''' + if sock: + self.fcgi_sock = sock + if document_root[-1:] != '/': + document_root += '/' + self.document_root = document_root + self.last_path = last_path + + def load_url_public(self, url, content=b'', method='GET', content_type='application/x-www-form-urlencoded'): + ''' + @name 转发URL到PHP-FPM 公共 + @param url string(URI地址) + @param content stream(POST数据io对象) + @return fastcgi-socket + ''' + fcgi = FCGIApp(connect=self.fcgi_sock) + try: + script_name, query_string = url.split('?') + except ValueError: + script_name = url + query_string = '' + + content_length = len(content) + if content: + content = StringIO(content) + + env = { + 'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name), + 'QUERY_STRING': query_string, + 'REQUEST_METHOD': method, + 'SCRIPT_NAME': self.last_path + script_name, + 'REQUEST_URI': url, + 'GATEWAY_INTERFACE': 'CGI/1.1', + 'SERVER_SOFTWARE': 'MW-PANEL', + 'REDIRECT_STATUS': '200', + 'CONTENT_TYPE': content_type, + 'CONTENT_LENGTH': str(content_length), + 'DOCUMENT_URI': script_name, + 'DOCUMENT_ROOT': self.document_root, + 'SERVER_PROTOCOL': 'HTTP/1.1', + 'REMOTE_ADDR': '127.0.0.1', + 'REMOTE_PORT': '7200', + 'SERVER_ADDR': '127.0.0.1', + 'SERVER_PORT': '80', + 'SERVER_NAME': 'MW-Panel' + } + + fpm_sock = fcgi(env, content) + + _data = b'' + while True: + fastcgi_header = fpm_sock.recv(8) + if not fastcgi_header: + break + if len(fastcgi_header) != 8: + _data += fastcgi_header + break + fast_pack = struct.unpack(FCGI_Header, fastcgi_header) + if fast_pack[1] == 3: + break + tlen = fast_pack[3] + while tlen > 0: + sd = fpm_sock.recv(tlen) + if not sd: + break + tlen -= len(sd) + _data += sd + if fast_pack[4]: + fpm_sock.recv(fast_pack[4]) + status, headers, data = format_header_data(_data) + return data From 83ae4562fad3e0b66328a7eb22b54861c3cfb73e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 23:56:25 +0800 Subject: [PATCH 085/965] update --- web/core/mw.py | 4 ++-- web/thisdb/logs.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 web/thisdb/logs.py diff --git a/web/core/mw.py b/web/core/mw.py index 0138b1e87..e5b83f08b 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -602,9 +602,9 @@ def writeLog(stype, msg, args=()): def writeDbLog(stype, msg, args=(), uid=1): try: - from admin import model + import thisdb format_msg = getInfo(msg, args) - model.addLog(stype, format_msg, uid) + thisdb.addLog(stype, format_msg, uid) return True except Exception as e: print("writeDbLog:"+str(e)) diff --git a/web/thisdb/logs.py b/web/thisdb/logs.py new file mode 100644 index 000000000..e7440f041 --- /dev/null +++ b/web/thisdb/logs.py @@ -0,0 +1,36 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + + +import core.mw as mw + +def clearLog(): + mw.M('logs').where('id>?', (0,)).delete() + return True + +def addLog(type, log, + uid: int | None = 1 +) -> str: + ''' + 添加日志 + :type -> str 类型 (必填) + :log -> str 日志内容 (必填) + :uid -> int 用户ID + ''' + add_time = mw.formatDate() + insert_data = { + 'name':name, + 'type':type, + 'log':log, + 'add_time':add_time, + } + mw.M('logs').insert(insert_data) + return True \ No newline at end of file From 4a7fea3b4ce18de65c5f205deea7687083f04f31 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 3 Nov 2024 23:59:22 +0800 Subject: [PATCH 086/965] Update __init__.py --- web/thisdb/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 9022ba603..a2517d0f7 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -13,3 +13,4 @@ from .option import * from .user import * from .sites import * from .tasks import * +from .logs import * From d795dd7d4724c0cf49ffa162e80ea9ba6479c9e9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 00:02:30 +0800 Subject: [PATCH 087/965] Update logs.py --- web/thisdb/logs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/thisdb/logs.py b/web/thisdb/logs.py index e7440f041..967373068 100644 --- a/web/thisdb/logs.py +++ b/web/thisdb/logs.py @@ -27,7 +27,6 @@ def addLog(type, log, ''' add_time = mw.formatDate() insert_data = { - 'name':name, 'type':type, 'log':log, 'add_time':add_time, From 477b8b1f73c4c83ac53178c3934145c830168ec7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 00:13:21 +0800 Subject: [PATCH 088/965] Update mw.py --- web/core/mw.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index e5b83f08b..787a50926 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -590,12 +590,12 @@ def debugLog(*data): def writeLog(stype, msg, args=()): # 写日志 uid = 0 - try: - from flask import session - if 'uid' in session: - uid = session['uid'] - except Exception as e: - print("writeLog:"+str(e)) + # try: + # from flask import session + # if 'uid' in session: + # uid = session['uid'] + # except Exception as e: + # print("writeLog:"+str(e)) # pass # writeFileLog(getTracebackInfo()) return writeDbLog(stype, msg, args, uid) From 795bee7de36549b0d586bb715580a46dadeeae72 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 00:27:32 +0800 Subject: [PATCH 089/965] update --- .gitignore | 4 +++- data/default_new.pl | 1 + web/admin/files/__init__.py | 7 +++++++ web/core/mw.py | 25 +++++++++++++++++++++++++ web/utils/file.py | 33 +++++++++++++++++++++++++++------ 5 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 data/default_new.pl diff --git a/.gitignore b/.gitignore index 63aba0e20..5de67f7af 100644 --- a/.gitignore +++ b/.gitignore @@ -161,6 +161,7 @@ data/api.json data/bind_domain.pl data/unauthorized_status.pl data/auth_secret.pl +data/panelSpeed.pl plugins/vip_* plugins/own_* @@ -185,4 +186,5 @@ plugins/goedge-node plugins/goedge-happy plugins/dztasks -data/default_new.pl + + diff --git a/data/default_new.pl b/data/default_new.pl new file mode 100644 index 000000000..d72607a41 --- /dev/null +++ b/data/default_new.pl @@ -0,0 +1 @@ +88rr2g7e \ No newline at end of file diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py index f7baeeb0a..42d0b3a20 100644 --- a/web/admin/files/__init__.py +++ b/web/admin/files/__init__.py @@ -156,6 +156,13 @@ def recycle_bin(): return file.toggleRecycleBin() +# 回收站文件 +@blueprint.route('/close_recycle_bin', endpoint='close_recycle_bin', methods=['POST']) +@panel_login_required +def close_recycle_bin(): + return file.closeRecycleBin() + + diff --git a/web/core/mw.py b/web/core/mw.py index 787a50926..4b6c875a4 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -610,6 +610,31 @@ def writeDbLog(stype, msg, args=(), uid=1): print("writeDbLog:"+str(e)) return False +def writeSpeed(title, used, total, speed=0): + path = getPanelDir() + speed_file= path + '/data/panelSpeed.pl' + # 写进度 + if not title: + data = {'title': None, 'progress': 0,'total': 0, 'used': 0, 'speed': 0} + else: + progress = int((100.0 * used / total)) + data = {'title': title, 'progress': progress,'total': total, 'used': used, 'speed': speed} + writeFile(speed_file, json.dumps(data)) + return True + + +def getSpeed(): + path = getPanelDir() + speed_file= path + '/data/panelSpeed.pl' + # 取进度 + path = getPanelDir() + data = readFile(speed_file) + if not data: + data = json.dumps({'title': None, 'progress': 0,'total': 0, 'used': 0, 'speed': 0}) + writeFile(speed_file, data) + return json.loads(data) + + def M(table=''): import core.db as db diff --git a/web/utils/file.py b/web/utils/file.py index 56aa0a13a..69203efeb 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -15,6 +15,7 @@ import time from admin import model import core.mw as mw +import thisdb def getFileBody(path): if not os.path.exists(path): @@ -284,7 +285,7 @@ def fileDelete(path): mw.execShell(cmd) try: - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') if recycle_bin == 'open': if mvRecycleBin(path): return mw.returnData(True, '已将文件移动到回收站!') @@ -303,7 +304,7 @@ def dirDelete(path): if path.find('.user.ini'): os.system("which chattr && chattr -i '" + path + "'") try: - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') if recycle_bin == 'open': if mvRecycleBin(path): return mw.returnData(True, '已将文件移动到回收站!') @@ -315,19 +316,19 @@ def dirDelete(path): # 关闭 def toggleRecycleBin(): - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') if recycle_bin == 'open': - model.setOption('recycle_bin','close') + thisdb.setOption('recycle_bin','close') mw.writeLog('文件管理', '已关闭回收站功能!') return mw.returnData(True, '已关闭回收站功能!') else: - model.setOption('recycle_bin','open') + thisdb.setOption('recycle_bin','open') mw.writeLog('文件管理', '已开启回收站功能!') return mw.returnData(True, '已开启回收站功能!') def getRecycleBin(): rb_dir = mw.getRecycleBinDir() - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') data = {} data['dirs'] = [] @@ -392,6 +393,26 @@ def reRecycleBin(path): mw.writeLog('文件管理', msg) return mw.returnData(False, '恢复失败!') + +def closeRecycleBin(): + rb_dir = mw.getRecycleBinDir() + mw.execShell('which chattr && chattr -R -i ' + rb_dir) + rlist = os.listdir(rb_dir) + i = 0 + l = len(rlist) + for name in rlist: + i += 1 + path = rb_dir + '/' + name + mw.writeSpeed(name, i, l) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) + mw.writeSpeed(None, 0, 0) + mw.writeLog('文件管理', '已清空回收站!') + return mw.returnJson(True, '已清空回收站!') + + # 设置文件和目录权限 def setMode(path): s_path = os.path.dirname(path) From 98a53b863910ad9a1d8a6f6e3cd3ca47cad3433c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 00:28:52 +0800 Subject: [PATCH 090/965] Update mw.py --- web/core/mw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/mw.py b/web/core/mw.py index 4b6c875a4..e935077c7 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -604,7 +604,7 @@ def writeDbLog(stype, msg, args=(), uid=1): try: import thisdb format_msg = getInfo(msg, args) - thisdb.addLog(stype, format_msg, uid) + thisdb.addLog(stype, format_msg, uid=uid) return True except Exception as e: print("writeDbLog:"+str(e)) From f37cd39a659e8d996f21f4a74a265f77d9ba4be1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 00:32:57 +0800 Subject: [PATCH 091/965] Update new_cli.sh --- new_cli.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/new_cli.sh b/new_cli.sh index 33bfe5bd5..ca5296567 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -63,12 +63,12 @@ mw_start_debug(){ cd ${DIR}/web && gunicorn -b :${port} -w 1 app:app } -mw_start_debug2(){ +mw_start_panel(){ python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & cd ${DIR}/web && gunicorn -b :7200 -w 1 app:app } -mw_start_debug3(){ +mw_start_bgtask(){ cd ${DIR}/web && gunicorn -c setting.py app:app cd ${DIR} && python3 panel_task.py } @@ -101,12 +101,12 @@ case "$1" in mw_stop mw_start_debug ;; - 'debug2') + 'panel') mw_stop - mw_start_debug2 + mw_start_panel ;; - 'debug3') + 'task') mw_stop - mw_start_debug3 + mw_start_bgtask ;; esac \ No newline at end of file From b58d4577422278124623e08b5c9e5580cbcc757f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 00:33:15 +0800 Subject: [PATCH 092/965] Update new_cli.sh --- new_cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new_cli.sh b/new_cli.sh index ca5296567..2505806f5 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -39,7 +39,7 @@ mw_start_task() } mw_start(){ - cd web && gunicorn -c setting.py app:app + cd ${DIR}/web && gunicorn -c setting.py app:app #安全启动 mw_start_task } From 1826adf50ea615a80bd752b5eec751faa03bf3cb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 01:21:17 +0800 Subject: [PATCH 093/965] Update install.sh --- plugins/php/versions/83/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index 415ebf780..218b88925 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -127,7 +127,7 @@ echo "$sourcePath/php/php${PHP_VER}" if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} - ./buildconf --force + # ./buildconf --force ./configure \ --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ From d4cfd90ccda62d0f72e5cb72532a19e008a116f7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 4 Nov 2024 19:08:39 +0800 Subject: [PATCH 094/965] update --- class/core/crontab_api.py | 3 +- data/control.conf | 1 - data/default_new.pl | 1 - data/recycle_bin.pl | 1 - data/sql/default.sql | 24 +- new_cli.sh | 1 - panel_task.py | 32 +-- panel_tools.py | 4 +- web/admin/__init__.py | 26 +- web/admin/common.py | 5 +- web/admin/crontab/__init__.py | 60 +++-- web/admin/dashboard/__init__.py | 234 +---------------- web/admin/dashboard/dashboard.py | 81 ++++++ web/admin/dashboard/login.py | 189 ++++++++++++++ web/admin/logs/__init__.py | 27 +- web/admin/setting/setting.py | 40 +-- web/admin/setup/option.py | 35 ++- web/admin/setup/user.py | 22 +- web/admin/site/__init__.py | 24 +- web/admin/system/system.py | 16 +- web/admin/task/__init__.py | 3 +- web/admin/user_login_check.py | 4 +- web/core/db.py | 52 +++- web/static/app/crontab.js | 5 +- web/thisdb/__init__.py | 1 + web/thisdb/crontab.py | 43 ++++ web/thisdb/init.py | 16 +- web/thisdb/logs.py | 29 ++- web/thisdb/option.py | 17 +- web/thisdb/sites.py | 35 ++- web/thisdb/tasks.py | 35 +++ web/thisdb/temp_login.py | 33 +++ web/thisdb/user.py | 68 +++-- web/utils/crontab.py | 419 +++++++++++++++++++++++++++++++ web/utils/plugin.py | 51 ++-- 35 files changed, 1158 insertions(+), 479 deletions(-) delete mode 100644 data/control.conf delete mode 100644 data/default_new.pl delete mode 100755 data/recycle_bin.pl create mode 100644 web/admin/dashboard/dashboard.py create mode 100644 web/admin/dashboard/login.py create mode 100644 web/thisdb/crontab.py create mode 100644 web/thisdb/temp_login.py diff --git a/class/core/crontab_api.py b/class/core/crontab_api.py index ba77196a2..d9556241a 100755 --- a/class/core/crontab_api.py +++ b/class/core/crontab_api.py @@ -40,8 +40,7 @@ class crontab_api: startPage = (int(p) - 1) * psize pageInfo = str(startPage) + ',' + str(psize) - _list = mw.M('crontab').where('', ()).field( - self.field).limit(pageInfo).order('id desc').select() + _list = mw.M('crontab').where('', ()).field(self.field).limit(pageInfo).order('id desc').select() data = [] for i in range(len(_list)): diff --git a/data/control.conf b/data/control.conf deleted file mode 100644 index 8580e7b68..000000000 --- a/data/control.conf +++ /dev/null @@ -1 +0,0 @@ -30 \ No newline at end of file diff --git a/data/default_new.pl b/data/default_new.pl deleted file mode 100644 index d72607a41..000000000 --- a/data/default_new.pl +++ /dev/null @@ -1 +0,0 @@ -88rr2g7e \ No newline at end of file diff --git a/data/recycle_bin.pl b/data/recycle_bin.pl deleted file mode 100755 index 4791ed555..000000000 --- a/data/recycle_bin.pl +++ /dev/null @@ -1 +0,0 @@ -True \ No newline at end of file diff --git a/data/sql/default.sql b/data/sql/default.sql index 1acdaa795..bc99e4163 100755 --- a/data/sql/default.sql +++ b/data/sql/default.sql @@ -33,8 +33,9 @@ CREATE TABLE IF NOT EXISTS `crontab` ( `sname` TEXT, `sbody` TEXT, 'stype' TEXT, - `urladdress` TEXT - `add_time` TEXT + `url_address` TEXT, + `add_time` TEXT, + `update_time` TEXT ); CREATE TABLE IF NOT EXISTS `firewall` ( @@ -60,7 +61,6 @@ CREATE TABLE IF NOT EXISTS `logs` ( `uid` INTEGER DEFAULT '1', `add_time` TEXT ); -ALTER TABLE `logs` ADD COLUMN `uid` INTEGER DEFAULT '1'; CREATE TABLE IF NOT EXISTS `sites` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -73,12 +73,10 @@ CREATE TABLE IF NOT EXISTS `sites` ( `edate` TEXT, `ssl_effective_date` TEXT, `ssl_expiration_date` TEXT, - `add_time` TEXT + `add_time` TEXT, + `update_time` TEXT ); -ALTER TABLE `sites` ADD COLUMN `ssl_effective_date` TEXT DEFAULT ''; -ALTER TABLE `sites` ADD COLUMN `ssl_expiration_date` TEXT DEFAULT ''; - CREATE TABLE IF NOT EXISTS `site_types` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT @@ -94,18 +92,16 @@ CREATE TABLE IF NOT EXISTS `domain` ( CREATE TABLE IF NOT EXISTS `users` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `username` TEXT, + `name` TEXT, `password` TEXT, `login_ip` TEXT, `login_time` TEXT, `phone` TEXT, - `email` TEXT + `email` TEXT, + `add_time` INTEGER, + `update_time` INTEGER ); -INSERT INTO `users` (`id`, `username`, `password`, `login_ip`, `login_time`, `phone`, `email`) VALUES -(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2022-02-02 00:00:00', 0, 'midoks@163.com'); - - CREATE TABLE IF NOT EXISTS `tasks` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, @@ -145,3 +141,5 @@ CREATE TABLE IF NOT EXISTS `option` ( `type` TEXT, `value` TEXT ); + +CREATE UNIQUE INDEX name_idx ON option(name); diff --git a/new_cli.sh b/new_cli.sh index 2505806f5..4d6a96388 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -64,7 +64,6 @@ mw_start_debug(){ } mw_start_panel(){ - python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & cd ${DIR}/web && gunicorn -b :7200 -w 1 app:app } diff --git a/panel_task.py b/panel_task.py index 67344a002..9432db180 100755 --- a/panel_task.py +++ b/panel_task.py @@ -41,26 +41,12 @@ def execShell(cmdstring, cwd=None, timeout=None, shell=True): cmd = cmdstring + ' > ' + g_log_file + ' 2>&1' return mw.execShell(cmd) -def service_cmd(method): - cmd = '/etc/init.d/mw' - if os.path.exists(cmd): - execShell(cmd + ' ' + method) - return - - cmd = mw.getPanelDir() + '/scripts/init.d/mw' - if os.path.exists(cmd): - print(cmd + ' ' + method) - data = execShell(cmd + ' ' + method) - print(data) - return - def mw_async(f): def wrapper(*args, **kwargs): thr = threading.Thread(target=f, args=args, kwargs=kwargs) thr.start() return wrapper - @mw_async def restartMw(): time.sleep(1) @@ -68,7 +54,6 @@ def restartMw(): mw.execShell(cmd) - def downloadFile(url, filename): # 下载文件 try: @@ -115,23 +100,23 @@ def writeLogs(data): def runPanelTask(): try: - bash_list = model.getTaskList(status=-1) + bash_list = thisdb.getTaskList(status=-1) for task in bash_list: - model.setTaskStatus(task['id'], 0) + thisdb.setTaskStatus(task['id'], 0) - run_list = model.getTaskList(status=0) + run_list = thisdb.getTaskList(status=0) for run_task in run_list: start = int(time.time()) - model.setTaskData(run_task['id'], start=start) - model.setTaskStatus(run_task['id'], -1) + thisdb.setTaskData(run_task['id'], start=start) + thisdb.setTaskStatus(run_task['id'], -1) if run_task['type'] == 'download': argv = run_task['cmd'].split('|mw|') downloadFile(argv[0], argv[1]) elif run_task['type'] == 'execshell': execShell(run_task['cmd']) end = int(time.time()) - model.setTaskData(run_task['id'], end=end) - model.setTaskStatus(run_task['id'], 1) + thisdb.setTaskData(run_task['id'], end=end) + thisdb.setTaskStatus(run_task['id'], 1) except Exception as e: print(str(e)) @@ -371,6 +356,5 @@ def run(): startPanelTask() if __name__ == "__main__": - with app.app_context(): - run() + run() diff --git a/panel_tools.py b/panel_tools.py index f54266e9a..ec54c05f4 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -190,7 +190,7 @@ def mwcli(mw_input=0): cont = re.sub("\"53\"", "\"52\",\"53\"", cont) cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont) mw.writeFile(php_conf, cont) - print("|-执行PHP52显示成功!") + mw.echoInfo("执行PHP52显示成功!") elif mw_input == 101: php_conf = 'plugins/php/info.json' if os.path.exists(php_conf): @@ -198,7 +198,7 @@ def mwcli(mw_input=0): cont = re.sub("\"52\",", "", cont) cont = re.sub("\"5.2.17\",", cont) mw.writeFile(php_conf, cont) - print("|-执行PHP52隐藏成功!") + mw.echoInfo("执行PHP52隐藏成功!") elif mw_input == 200: os.system(INIT_CMD + " mirror") elif mw_input == 201: diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 949734855..2c3d468c9 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -31,8 +31,9 @@ from flask_migrate import Migrate from flask_caching import Cache from werkzeug.local import LocalProxy -from admin import model +# from admin import model from admin import setup +import thisdb from admin.model import db as sys_db @@ -68,12 +69,13 @@ app.config['SESSION_COOKIE_NAME'] = "MW_VER_1" app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) # db的配置 -app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 SQLite 数据库 +# app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 SQLite 数据库 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True + # 初始化db -sys_db.init_app(app) -Migrate(app, sys_db) +# sys_db.init_app(app) +# Migrate(app, sys_db) # 检查数据库是否存在。如果没有就创建它。 @@ -84,15 +86,15 @@ if not os.path.isfile(config.SQLITE_PATH): # with app.app_context(): # sys_db.create_all() -with app.app_context(): - if setup_db_required: - sys_db.create_all() +# with app.app_context(): +# if setup_db_required: +# sys_db.create_all() -# 初始化用户信息 -with app.app_context(): - if setup_db_required: - setup.init_admin_user() - setup.init_option() +if setup_db_required: + # 初始化用户信息 + thisdb.initPanelData() + setup.init_admin_user() + setup.init_option() setup.init_cmd() setup.init_db_system() diff --git a/web/admin/common.py b/web/admin/common.py index c1692c081..cc1be06db 100644 --- a/web/admin/common.py +++ b/web/admin/common.py @@ -10,13 +10,14 @@ import time -from admin import model +# from admin import model from admin import session +import thisdb def isLogined(): if 'login' in session and session['login'] == True and 'username' in session: username = session['username'] - info = model.getUserByName(username) + info = thisdb.getUserByName(username) if info is None: return False diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index 782623ca2..3e8232e40 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -13,7 +13,10 @@ from flask import Blueprint, render_template from flask import request from admin.user_login_check import panel_login_required -from admin.model import Crontab + +from utils.crontab import crontab as MwCrontab +import core.mw as mw +import thisdb blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folder='../../templates/default') @blueprint.route('/index', endpoint='index') @@ -24,26 +27,51 @@ def index(): @blueprint.route('/list', endpoint='list', methods=['GET','POST']) @panel_login_required def list(): - page = request.args.get('p', 1) - size = 10 - count = Crontab.query.count() - # print(count) - clist = Crontab.query.paginate(page=int(page), per_page=size) - # print(clist) + page = request.args.get('p', '1').strip() + limit = request.args.get('limit', '10').strip() + return MwCrontab.instance().getCrontabList(page=int(page),size=int(limit)) - return [] +# 插件列表 +@blueprint.route('/logs', endpoint='logs', methods=['GET','POST']) +def logs(self): + sid = request.form.get('id', '') + echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find() + logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log' + if not os.path.exists(logFile): + return mw.returnData(False, '当前日志为空!') + log = mw.getLastLine(logFile, 500) + return mw.returnData(True, log) +# 获取计划任务 +@blueprint.route('/del', endpoint='del', methods=['GET','POST']) +def crontab_del(): + task_id = request.form.get('id', '') + return MwCrontab.instance().delete(task_id) +# 获取计划任务 +@blueprint.route('/get_crond_find', endpoint='get_crond_find', methods=['GET','POST']) +def get_crond_find(): + sid = request.form.get('id', '') + data = MwCrontab.instance().getCrondFind(sid) + return data + +# 添加计划任务 @blueprint.route('/add', endpoint='add', methods=['GET','POST']) @panel_login_required def add(): - page = request.args.get('p', 1) - size = 10 - count = Crontab.query.count() - # print(count) - clist = Crontab.query.paginate(page=int(page), per_page=size) - # print(clist) - - return [] + request_data = {} + request_data['name'] = request.form.get('name', '') + request_data['type'] = request.form.get('type', '') + request_data['week'] = request.form.get('week', '') + request_data['where1'] = request.form.get('where1', '') + request_data['hour'] = request.form.get('hour', '') + request_data['minute'] = request.form.get('minute', '') + request_data['save'] = request.form.get('save', '') + request_data['backup_to'] = request.form.get('backupTo', '') + request_data['stype'] = request.form.get('sType', '') + request_data['sname'] = request.form.get('sName', '') + request_data['sbody'] = request.form.get('sBody', '') + request_data['url_address'] = request.form.get('urladdress', '') + return MwCrontab.instance().add(request_data) diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py index c77e84623..77a962a50 100644 --- a/web/admin/dashboard/__init__.py +++ b/web/admin/dashboard/__init__.py @@ -8,236 +8,6 @@ # Author: midoks # --------------------------------------------------------------------------------- -import io -import time -from flask import Blueprint, render_template -from flask import make_response -from flask import redirect -from flask import Response -from flask import request,g - -from admin.common import isLogined -from admin.user_login_check import panel_login_required -from admin import cache,session -from admin import model -from admin.model import db,TempLogin,Users - -import core.mw as mw - - -blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates') -@blueprint.route('/') -@panel_login_required -def index(): - return render_template('default/index.html') - -# 安全路径 -@blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) -def admin_safe_path(path): - db_path = model.getOption('admin_path') - - if isLogined(): - return redirect('/') - - if db_path == path: - return render_template('default/login.html') - - unauthorized_status = model.getOption('unauthorized_status') - if unauthorized_status == '0': - return render_template('default/path.html') - return Response(status=int(unauthorized_status)) - -# 仅针对webhook插件 -@blueprint.route("/hook", methods=['POST', 'GET']) -def webhook(): - # 兼容获取关键数据 - access_key = request.args.get('access_key', '').strip() - if access_key == '': - access_key = request.form.get('access_key', '').strip() - - params = request.args.get('params', '').strip() - if params == '': - params = request.form.get('params', '').strip() - - input_args = { - 'access_key': access_key, - 'params': params, - } - - wh_install_path = mw.getServerDir() + '/webhook' - if not os.path.exists(wh_install_path): - return mw.returnData(False, '请先安装WebHook插件!') - - package = mw.getPanelDir() + "/plugins/webhook" - if not package in sys.path: - sys.path.append(package) - - try: - import webhook_index - return webhook_index.runShellArgs(input_args) - except Exception as e: - return str(e) - -# --------------------------------------------------------------------------------- -# 定义登录入口相关方法 -# --------------------------------------------------------------------------------- - -def getErrorNum(key, limit=None): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - if not limit: - return num - if limit > num: - return True - return False - - -def setErrorNum(key, empty=False, expire=3600): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - else: - if empty: - cache.delete(key) - return True - cache.set(key, num + 1, expire) - return True - -def login_temp_user(token): - if len(token) != 32: - return '错误的参数!' - - skey = mw.getClientIp() + '_temp_login' - if not getErrorNum(skey, 10): - return '连续10次验证失败,禁止1小时' - - stime = int(time.time()) - - tmp_data = model.getTempLoginByToken(token) - if not tmp_data: - setErrorNum(skey) - return '验证失败!' - - if stime > int(tmp_data['expire']): - setErrorNum(skey) - return "过期" - - user_data = model.getUserById(1) - - login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT')) - mw.writeLog('用户临时登录', "登录成功,帐号:{1},登录IP:{2}",(user_data['name'], login_addr)) - - TempLogin.query.filter(TempLogin.id==tmp_data['id']).update({"login_time": stime, 'state': 1, 'login_addr': login_addr}) - db.session.commit() - - session['login'] = True - session['username'] = user_data['name'] - session['tmp_login'] = True - session['tmp_login_id'] = str(tmp_data['id']) - session['tmp_login_expire'] = int(tmp_data['expire']) - session['uid'] = user_data['id'] - - return redirect('/') - -# 登录页: 当设置了安全路径,本页失效。 -@blueprint.route('/login') -def login(): - - # 临时登录功能 - token = request.args.get('tmp_token', '').strip() - if token != '': - return login_temp_user(token) - - # 注销登录 - signout = request.args.get('signout', '') - if signout == 'True': - session.clear() - session['login'] = False - session['overdue'] = 0 - - db_path = model.getOption('admin_path') - if db_path == '': - return render_template('default/login.html') - else: - unauthorized_status = model.getOption('unauthorized_status') - if unauthorized_status == '0': - return render_template('default/path.html') - return Response(status=int(unauthorized_status)) - -# 验证码 -@blueprint.route('/code') -def code(): - import utils.vilidate as vilidate - vie = vilidate.vieCode() - codeImage = vie.GetCodeImage(80, 4) - out = io.BytesIO() - codeImage[0].save(out, "png") - session['code'] = mw.md5(''.join(codeImage[1]).lower()) - - img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) - return make_response(img) - -# 检查是否登录 -@blueprint.route('/check_login',methods=['GET','POST']) -def check_login(): - if isLogined(): - return mw.returnData(True,'已登录') - return mw.returnData(False,'未登录') - -# 执行登录操作 -@blueprint.route('/do_login', endpoint='do_login', methods=['POST']) -def do_login(): - username = request.form.get('username', '').strip() - password = request.form.get('password', '').strip() - code = request.form.get('code', '').strip() - - login_cache_count = 5 - login_cache_limit = cache.get('login_cache_limit') - - if 'code' in session: - if session['code'] != mw.md5(code): - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - model.setOption('admin_close', 'yes') - return mw.returnJson(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - login_err_msg = mw.getInfo("验证码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit))) - mw.writeLog('用户登录', login_err_msg) - return mw.returnData(False, login_err_msg) - - info = model.getUserByName(username) - password = mw.md5(password) - - if info['name'] != username or info['password'] != password: - msg = "密码错误,帐号:{1},密码:{2},登录IP:{3}", (('****', '******', request.remote_addr)) - - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - model.setOption('admin_close', 'yes') - return mw.returnData(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - mw.writeLog('用户登录', mw.getInfo(msg)) - return mw.returnData(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))) - - - session['login'] = True - session['username'] = info['name'] - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - - return mw.returnJson(1, '登录成功,正在跳转...') +from .dashboard import * +from .login import * diff --git a/web/admin/dashboard/dashboard.py b/web/admin/dashboard/dashboard.py new file mode 100644 index 000000000..cf1c82f0f --- /dev/null +++ b/web/admin/dashboard/dashboard.py @@ -0,0 +1,81 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import io +import time + +from flask import Blueprint, render_template +from flask import make_response +from flask import redirect +from flask import Response +from flask import request,g + +from admin.common import isLogined +from admin.user_login_check import panel_login_required +from admin import cache,session +from admin import model +from admin.model import db,TempLogin,Users + +import core.mw as mw +import thisdb + + +blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates') +@blueprint.route('/') +@panel_login_required +def index(): + return render_template('default/index.html') + +# 安全路径 +@blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) +def admin_safe_path(path): + db_path = thisdb.getOption('admin_path') + if isLogined(): + return redirect('/') + + print(db_path,path) + if db_path == path: + return render_template('default/login.html') + + unauthorized_status = thisdb.getOption('unauthorized_status') + if unauthorized_status == '0': + return render_template('default/path.html') + return Response(status=int(unauthorized_status)) + +# 仅针对webhook插件 +@blueprint.route("/hook", methods=['POST', 'GET']) +def webhook(): + # 兼容获取关键数据 + access_key = request.args.get('access_key', '').strip() + if access_key == '': + access_key = request.form.get('access_key', '').strip() + + params = request.args.get('params', '').strip() + if params == '': + params = request.form.get('params', '').strip() + + input_args = { + 'access_key': access_key, + 'params': params, + } + + wh_install_path = mw.getServerDir() + '/webhook' + if not os.path.exists(wh_install_path): + return mw.returnData(False, '请先安装WebHook插件!') + + package = mw.getPanelDir() + "/plugins/webhook" + if not package in sys.path: + sys.path.append(package) + + try: + import webhook_index + return webhook_index.runShellArgs(input_args) + except Exception as e: + return str(e) diff --git a/web/admin/dashboard/login.py b/web/admin/dashboard/login.py new file mode 100644 index 000000000..7f1abe8eb --- /dev/null +++ b/web/admin/dashboard/login.py @@ -0,0 +1,189 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import io +import time + +from flask import Blueprint, render_template +from flask import make_response +from flask import redirect +from flask import Response +from flask import request,g + +from admin.common import isLogined +from admin.user_login_check import panel_login_required +from admin import cache,session +from admin import model +from admin.model import db,TempLogin,Users + +import core.mw as mw +import thisdb + +from .dashboard import blueprint + + +def getErrorNum(key, limit=None): + key = mw.md5(key) + num = cache.get(key) + if not num: + num = 0 + if not limit: + return num + if limit > num: + return True + return False + + +def setErrorNum(key, empty=False, expire=3600): + key = mw.md5(key) + num = cache.get(key) + if not num: + num = 0 + else: + if empty: + cache.delete(key) + return True + cache.set(key, num + 1, expire) + return True + +def login_temp_user(token): + if len(token) != 32: + return '错误的参数!' + + skey = mw.getClientIp() + '_temp_login' + if not getErrorNum(skey, 10): + return '连续10次验证失败,禁止1小时' + + stime = int(time.time()) + + tmp_data = model.getTempLoginByToken(token) + if not tmp_data: + setErrorNum(skey) + return '验证失败!' + + if stime > int(tmp_data['expire']): + setErrorNum(skey) + return "过期" + + user_data = thisdb.getUserById(1) + + login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT')) + mw.writeLog('用户临时登录', "登录成功,帐号:{1},登录IP:{2}",(user_data['name'], login_addr)) + + TempLogin.query.filter(TempLogin.id==tmp_data['id']).update({"login_time": stime, 'state': 1, 'login_addr': login_addr}) + db.session.commit() + + session['login'] = True + session['username'] = user_data['name'] + session['tmp_login'] = True + session['tmp_login_id'] = str(tmp_data['id']) + session['tmp_login_expire'] = int(tmp_data['expire']) + session['uid'] = user_data['id'] + + return redirect('/') + +# 登录页: 当设置了安全路径,本页失效。 +@blueprint.route('/login') +def login(): + + # 临时登录功能 + token = request.args.get('tmp_token', '').strip() + if token != '': + return login_temp_user(token) + + # 注销登录 + signout = request.args.get('signout', '') + if signout == 'True': + session.clear() + session['login'] = False + session['overdue'] = 0 + + db_path = model.getOption('admin_path') + if db_path == '': + return render_template('default/login.html') + else: + unauthorized_status = model.getOption('unauthorized_status') + if unauthorized_status == '0': + return render_template('default/path.html') + return Response(status=int(unauthorized_status)) + +# 验证码 +@blueprint.route('/code') +def code(): + import utils.vilidate as vilidate + vie = vilidate.vieCode() + codeImage = vie.GetCodeImage(80, 4) + out = io.BytesIO() + codeImage[0].save(out, "png") + session['code'] = mw.md5(''.join(codeImage[1]).lower()) + + img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) + return make_response(img) + +# 检查是否登录 +@blueprint.route('/check_login',methods=['GET','POST']) +def check_login(): + if isLogined(): + return mw.returnData(True,'已登录') + return mw.returnData(False,'未登录') + +# 执行登录操作 +@blueprint.route('/do_login', endpoint='do_login', methods=['POST']) +def do_login(): + username = request.form.get('username', '').strip() + password = request.form.get('password', '').strip() + code = request.form.get('code', '').strip() + + login_cache_count = 5 + login_cache_limit = cache.get('login_cache_limit') + + if 'code' in session: + if session['code'] != mw.md5(code): + if login_cache_limit == None: + login_cache_limit = 1 + else: + login_cache_limit = int(login_cache_limit) + 1 + + if login_cache_limit >= login_cache_count: + model.setOption('admin_close', 'yes') + return mw.returnJson(False, '面板已经关闭!') + + cache.set('login_cache_limit', login_cache_limit, timeout=10000) + login_cache_limit = cache.get('login_cache_limit') + login_err_msg = mw.getInfo("验证码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit))) + mw.writeLog('用户登录', login_err_msg) + return mw.returnData(False, login_err_msg) + + info = thisdb.getUserByName(username) + password = mw.md5(password) + + if info['name'] != username or info['password'] != password: + msg = "密码错误,帐号:{1},密码:{2},登录IP:{3}", (('****', '******', request.remote_addr)) + + if login_cache_limit == None: + login_cache_limit = 1 + else: + login_cache_limit = int(login_cache_limit) + 1 + + if login_cache_limit >= login_cache_count: + model.setOption('admin_close', 'yes') + return mw.returnData(False, '面板已经关闭!') + + cache.set('login_cache_limit', login_cache_limit, timeout=10000) + login_cache_limit = cache.get('login_cache_limit') + mw.writeLog('用户登录', mw.getInfo(msg)) + return mw.returnData(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))) + + + session['login'] = True + session['username'] = info['name'] + session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 + + return mw.returnJson(1, '登录成功,正在跳转...') diff --git a/web/admin/logs/__init__.py b/web/admin/logs/__init__.py index eb988b6e4..67c75234b 100644 --- a/web/admin/logs/__init__.py +++ b/web/admin/logs/__init__.py @@ -14,11 +14,9 @@ from flask import request from admin.user_login_check import panel_login_required -from admin import model -from admin.model import db, Logs - import core.mw as mw import utils.adult_log as adult_log +import thisdb # 日志页面 blueprint = Blueprint('logs', __name__, url_prefix='/logs', template_folder='../../templates') @@ -36,33 +34,18 @@ def get_log_list(): size = request.form.get('limit', '10').strip() search = request.form.get('search', '').strip() - count = Logs.query.filter_by().count() - if search != '': - pagination = Logs.query.filter_by(Logs.type.like(search) or Logs.log.like(search)).paginate(page=int(p), per_page=int(size)) - else: - pagination = Logs.query.filter_by().order_by(Logs.id.desc()).paginate(page=int(p), per_page=int(size)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['type'] = item.type - t['log'] = item.log - t['uid'] = item.uid - t['add_time'] = item.add_time - rows.append(t) + info = thisdb.getLogsList(page=int(p),size=int(size), search=search) data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'getLogs','p':p}) + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':'getLogs','p':p,'row':size}) return data # 日志清空 @blueprint.route('/del_panel_logs', endpoint='del_panel_logs', methods=['POST']) @panel_login_required def del_panel_logs(): - model.clearLog() - # mw.M('logs').dbPos(mw.getPanelDataDir(),'panel').where('id>?', (0,)).delete() + thisdb.clearLog() mw.writeLog('面板设置', '面板操作日志已清空!') return mw.returnData(True, '面板操作日志已清空!') diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index f6e663e27..3b2d81a0f 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -16,10 +16,12 @@ from flask import Blueprint, render_template from flask import request from admin import model + from admin.user_login_check import panel_login_required import core.mw as mw +import thisdb import utils.config as utils_config @@ -41,9 +43,9 @@ def get_panel_list(): @panel_login_required def set_webname(): webname = request.form.get('webname', '') - src_webname = model.getOption('title') + src_webname = thisdb.getOption('title') if webname != src_webname: - model.setOption('title', webname) + thisdb.setOption('title', webname) return mw.returnData(True, '面板别名保存成功!') # 设置服务器IP @@ -51,9 +53,9 @@ def set_webname(): @panel_login_required def set_ip(): host_ip = request.form.get('host_ip', '') - src_host_ip = model.getOption('server_ip') + src_host_ip = thisdb.getOption('server_ip') if host_ip != src_host_ip: - model.setOption('server_ip', host_ip) + thisdb.setOption('server_ip', host_ip) return mw.returnData(True, 'IP保存成功!') # 默认备份目录 @@ -61,9 +63,9 @@ def set_ip(): @panel_login_required def set_backup_dir(): backup_path = request.form.get('backup_path', '') - src_backup_path = model.getOption('backup_path') + src_backup_path = thisdb.getOption('backup_path') if backup_path != src_backup_path: - model.setOption('backup_path', backup_path) + thisdb.setOption('backup_path', backup_path) return mw.returnData(True, '修改默认备份目录成功!') # 默认站点目录 @@ -71,9 +73,9 @@ def set_backup_dir(): @panel_login_required def set_www_dir(): sites_path = request.form.get('sites_path', '') - src_sites_path = model.getOption('sites_path') + src_sites_path = thisdb.getOption('sites_path') if sites_path != src_sites_path: - model.setOption('sites_path', sites_path) + thisdb.setOption('sites_path', sites_path) return mw.returnData(True, '修改默认建站目录成功!') @@ -102,9 +104,9 @@ def set_admin_path(): if not re.match(r"^/[\w]+$", admin_path): return mw.returnData(False, '入口地址格式不正确,示例: /mw_rand') - src_admin_path = model.getOption('admin_path') + src_admin_path = thisdb.getOption('admin_path') if admin_path != src_admin_path: - model.setOption('admin_path', admin_path[1:]) + thisdb.setOption('admin_path', admin_path[1:]) return mw.returnData(True, '修改成功!') @@ -125,7 +127,7 @@ def set_basic_auth(): is_open = False if basic_open == 'false': - model.setOption('basic_auth', json.dumps({'open':False})) + thisdb.setOption('basic_auth', json.dumps({'open':False})) mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open) return mw.returnData(True, '删除BasicAuth成功!') @@ -139,7 +141,7 @@ def set_basic_auth(): data['basic_pwd'] = mw.md5(basic_pwd + salt) data['open'] = is_open - model.setOption('basic_auth', json.dumps(data)) + thisdb.setOption('basic_auth', json.dumps(data)) mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open) return mw.returnData(True, '设置成功!') @@ -158,7 +160,7 @@ def set_status_code(): return mw.returnData(False, '状态码范围错误!') info = utils_config.getUnauthStatus(code=str(status_code)) - model.setOption('unauthorized_status', str(status_code)) + thisdb.setOption('unauthorized_status', str(status_code)) mw.writeLog('面板设置', '将未授权响应状态码设置为:{0}:{1}'.format(status_code,info['text'])) return mw.returnData(True, '设置成功!') @@ -166,11 +168,11 @@ def set_status_code(): @blueprint.route('/open_debug', endpoint='open_debug', methods=['POST']) @panel_login_required def open_debug(): - debug = model.getOption('debug',default='close') + debug = thisdb.getOption('debug',default='close') if debug == 'open': - model.setOption('debug','close') + thisdb.setOption('debug','close') return mw.returnData(True, '开发模式关闭!') - model.setOption('debug','open') + thisdb.setOption('debug','open') return mw.returnData(True, '开发模式开启!') @@ -178,11 +180,11 @@ def open_debug(): @blueprint.route('/close_panel', endpoint='close_panel', methods=['POST']) @panel_login_required def close_panel(): - admin_close = model.getOption('admin_close',default='no') + admin_close = thisdb.getOption('admin_close',default='no') if admin_close == 'no': - model.setOption('admin_close','yes') + thisdb.setOption('admin_close','yes') return mw.returnData(True, '开启面板成功!') - model.setOption('admin_close','no') + thisdb.setOption('admin_close','no') return mw.returnData(True, '关闭面板成功!') # 设置IPV6状态 diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 87f691471..da08bedd0 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -12,50 +12,47 @@ import json from flask import request -from admin import model -from admin.model import db, Users - import core.mw as mw +import thisdb def init_option(): - model.setOption('title', '后羿面板') - model.setOption('recycle_bin', 'open') - model.setOption('template', 'default') + thisdb.setOption('title', '后羿面板') + thisdb.setOption('recycle_bin', 'open') + thisdb.setOption('template', 'default') # 后台面板是否关闭 - model.setOption('admin_close', 'no') + thisdb.setOption('admin_close', 'no') # 未认证状态码 - model.setOption('unauthorized_status', '0') + thisdb.setOption('unauthorized_status', '0') # 调式模式,默认关闭 - model.setOption('debug', 'close') + thisdb.setOption('debug', 'close') # basic auth 配置 - model.setOption('basic_auth', json.dumps({'open':False})) + thisdb.setOption('basic_auth', json.dumps({'open':False})) # 开启后台任务 # model.setOption('run_bg_task', 'close') # 首页展示初始化 - model.setOption('display_index', '[]') + thisdb.setOption('display_index', '[]') # 监控默认配置 - model.setOption('monitor_status', 'open', type='monitor') - model.setOption('monitor_day', '30', type='monitor') - model.setOption('monitor_only_netio', 'open', type='monitor') + thisdb.setOption('monitor_status', 'open', type='monitor') + thisdb.setOption('monitor_day', '30', type='monitor') + thisdb.setOption('monitor_only_netio', 'open', type='monitor') # 初始化安全路径 - model.setOption('admin_path', mw.getRandomString(8)) + thisdb.setOption('admin_path', mw.getRandomString(8)) ip = mw.getLocalIp() - model.setOption('server_ip', ip) + thisdb.setOption('server_ip', ip) # 默认备份目录 - model.setOption('backup_path', mw.getFatherDir()+'/backup') + thisdb.setOption('backup_path', mw.getFatherDir()+'/backup') # 默认站点目录 - model.setOption('site_path', mw.getFatherDir()+'/wwwroot') + thisdb.setOption('site_path', mw.getFatherDir()+'/wwwroot') - return True \ No newline at end of file diff --git a/web/admin/setup/user.py b/web/admin/setup/user.py index e18008d30..7c2092ace 100644 --- a/web/admin/setup/user.py +++ b/web/admin/setup/user.py @@ -14,28 +14,10 @@ from admin import model from admin.model import db, Users import core.mw as mw +import thisdb # 初始化用户信息 def init_admin_user(): - data = Users.query.filter_by(id=1).first() - if not data: - name = mw.getRandomString(8).lower() - password = mw.getRandomString(8).lower() - file_pass_pl = mw.getPanelDataDir() + '/default.pl' - mw.writeFile(file_pass_pl, password) - insert_time = mw.formatDate() - login_ip = '127.0.0.1' - add_user = Users( - name=name, - password=mw.md5(password), - login_ip=login_ip, - login_time=insert_time, - phone='', - email='', - add_time=insert_time, - update_time=insert_time) - db.session.add(add_user) - db.session.commit() - db.session.close() + thisdb.initAdminUser() return True diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index d51580a0c..6036672f2 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -19,6 +19,7 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin import utils.site as site import core.mw as mw +import thisdb blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') @blueprint.route('/index', endpoint='index') @@ -34,28 +35,11 @@ def list(): type_id = request.form.get('type_id', '0').strip() search = request.form.get('search', '').strip() - count = Sites.query.count() - pagination = Sites.query.paginate(page=int(p), per_page=int(limit)) - - site_list = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['name'] = item.name - t['path'] = item.path - t['index'] = item.index - t['ps'] = item.ps - t['edate'] = item.edate - t['type_id'] = item.type_id - t['status'] = item.status - t['add_time'] = item.add_time - t['update_time'] = item.update_time - site_list.append(t) + info = thisdb.getSitesList(page=int(p),size=int(limit),type_id=int(type_id), search=search) data = {} - data['data'] = site_list - data['page'] = mw.getPage({'count':count,'tojs':'getWeb','p':p, 'row':limit}) - + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':'getWeb','p':p, 'row':limit}) return data @blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) diff --git a/web/admin/system/system.py b/web/admin/system/system.py index 125563e2b..d224e408d 100644 --- a/web/admin/system/system.py +++ b/web/admin/system/system.py @@ -16,9 +16,9 @@ from flask import request from admin.user_login_check import panel_login_required from utils.system import monitor -import admin.model.option as option import core.mw as mw import utils.system as sys +import thisdb @@ -112,19 +112,19 @@ def set_control(): day = request.form.get('day', '') if stype == '0': - option.setOption('monitor_status', 'close', type='monitor') + thisdb.setOption('monitor_status', 'close', type='monitor') return mw.returnData(True, "设置成功!") elif stype == '1': _day = int(day) if _day < 1: return mw.returnData(False, "设置失败!") - option.setOption('monitor_day', day, type='monitor') + thisdb.setOption('monitor_day', day, type='monitor') return mw.returnData(True, "设置成功!") elif stype == '2': - option.setOption('monitor_only_netio', 'close', type='monitor') + thisdb.setOption('monitor_only_netio', 'close', type='monitor') return mw.returnData(True, "设置成功!") elif stype == '3': - option.setOption('monitor_only_netio', 'open', type='monitor') + thisdb.setOption('monitor_only_netio', 'open', type='monitor') return mw.returnData(True, "设置成功!") elif stype == 'del': if not mw.isRestart(): @@ -132,9 +132,9 @@ def set_control(): monitor.instance().clearDbFile() return mw.returnData(True, "清空监控记录成功!") else: - monitor_status = option.getOption('monitor_status', default='open', type='monitor') - monitor_day = option.getOption('monitor_day', default='30', type='monitor') - monitor_only_netio = option.getOption('monitor_only_netio', default='open', type='monitor') + monitor_status = thisdb.getOption('monitor_status', default='open', type='monitor') + monitor_day = thisdb.getOption('monitor_day', default='30', type='monitor') + monitor_only_netio = thisdb.getOption('monitor_only_netio', default='open', type='monitor') data = {} data['day'] = monitor_day if monitor_status == 'open': diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index e90f33a89..0567a7531 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -18,6 +18,7 @@ from admin.user_login_check import panel_login_required import core.mw as mw import utils.task as MwTasks +import thisdb blueprint = Blueprint('task', __name__, url_prefix='/task', template_folder='../../templates/default') @@ -25,7 +26,7 @@ blueprint = Blueprint('task', __name__, url_prefix='/task', template_folder='../ @blueprint.route('/count', endpoint='task_count') @panel_login_required def task_count(): - return str(model.getTaskUnexecutedCount()) + return str(thisdb.getTaskUnexecutedCount()) @blueprint.route('/list', endpoint='list', methods=['POST']) diff --git a/web/admin/user_login_check.py b/web/admin/user_login_check.py index 63fbb096d..c2ba5b1dd 100644 --- a/web/admin/user_login_check.py +++ b/web/admin/user_login_check.py @@ -14,17 +14,17 @@ from flask import Response from functools import wraps -from admin import model from admin import session from admin.common import isLogined +import thisdb def panel_login_required(func): @wraps(func) def wrapper(*args, **kwargs): if not isLogined(): - unauthorized_status = model.getOption('unauthorized_status') + unauthorized_status = thisdb.getOption('unauthorized_status') if unauthorized_status == '0': return render_template('default/path.html') return Response(status=int(unauthorized_status)) diff --git a/web/core/db.py b/web/core/db.py index e5c1107a8..5962ac56a 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -120,6 +120,21 @@ class Sql(): self.__OPT_FIELD = field return self + def getDbField(self,name): + sql = "PRAGMA table_info(%s)" % name + result = self.__DB_CONN.execute(sql) + data = result.fetchall() + + fields = [] + for i in data: + fields.append(i[1]) + return fields + + def getDbFieldString(self,name): + fields = self.getDbField(name) + return ','.join(fields) + + def select(self): # 查询数据集 self.__getConn() @@ -130,25 +145,40 @@ class Sql(): # print(self.__OPT_PARAM) result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) data = result.fetchall() + if len(data) == 0: + return data + # 构造字曲系列 if self.__OPT_FIELD != "*": field = self.__OPT_FIELD.split(',') tmp = [] for row in data: i = 0 - tmp1 = {} + t = {} for key in field: - tmp1[key] = row[i] + t[key] = row[i] i += 1 - tmp.append(tmp1) - del(tmp1) + tmp.append(t) + del(t) data = tmp del(tmp) else: - # 将元组转换成列表 - tmp = map(list, data) + field = self.getDbField(self.__DB_TABLE) + tmp = [] + for row in data: + i = 0 + t = {} + for key in field: + t[key] = row[i] + i += 1 + tmp.append(t) + del(t) data = tmp del(tmp) + # 将元组转换成列表 + # tmp = map(list, data) + # data = tmp + # del(tmp) self.__close() return data except Exception as ex: @@ -162,7 +192,8 @@ class Sql(): sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - if os.path.exists('data/debug.pl'): + debug = getPanelDir()+'/data/debug.pl' + if os.path.exists(debug): print(sql, self.__OPT_PARAM) result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) data = result.fetchall() @@ -199,7 +230,7 @@ class Sql(): result = self.field(keyName).select() if len(result) == 1: return result[0][keyName] - return result + return None def setField(self, keyName, keyValue): # 更新指定字段 @@ -210,7 +241,7 @@ class Sql(): result = self.limit("1").select() if len(result) == 1: return result[0] - return result + return None def count(self): # 取行数 @@ -237,7 +268,8 @@ class Sql(): self.__DB_CONN.commit() return last_id except Exception as ex: - return "error: " + str(ex) + print(str(ex)) + return 0 # 插入数据 def insert(self, pdata): diff --git a/web/static/app/crontab.js b/web/static/app/crontab.js index 1d6302e1f..b8aea37d1 100755 --- a/web/static/app/crontab.js +++ b/web/static/app/crontab.js @@ -83,6 +83,7 @@ function getBackupName(hook_data, name){ function getCronData(page){ var load = layer.msg(lan.public.the,{icon:16,time:0,shade: [0.3, '#000']}); $.post("/crontab/list?p="+page,'', function(rdata){ + console.log(rdata); layer.close(load); var cbody = ""; if(rdata == ""){ @@ -115,7 +116,7 @@ function getCronData(page){ "+rdata.data[i].cycle+"\ "+cron_save +"\ "+cron_backupto+"\ - "+rdata.data[i].addtime+"\ + "+rdata.data[i].add_time+"\ \ 执行 | \ 编辑 | \ @@ -341,7 +342,7 @@ function planAdd(){ $("#cronConfig input[name='sName']").val(sName); layer.msg('正在添加,请稍候...!',{icon:16,time:0,shade: [0.3, '#000']}); - var data = $("#cronConfig").serialize() + '&sBody='+sBody + '&urladdress=' + urladdress; + var data = $("#cronConfig").serialize() + '&sBody='+sBody; // console.log(data); $.post('/crontab/add',data,function(rdata){ if(!rdata.status) { diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index a2517d0f7..85125f384 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -14,3 +14,4 @@ from .user import * from .sites import * from .tasks import * from .logs import * +from .crontab import * diff --git a/web/thisdb/crontab.py b/web/thisdb/crontab.py new file mode 100644 index 000000000..27dad974f --- /dev/null +++ b/web/thisdb/crontab.py @@ -0,0 +1,43 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +import core.mw as mw + +__field = 'id,name,type,where1,where_hour,where_minute,echo,status,save,backup_to,stype,sname,sbody,url_address,add_time,update_time' + +def addCrontab(data): + now_time = mw.formatDate() + data['add_time'] = now_time + data['update_time'] = now_time + return mw.M('crontab').insert(data) + +def getCrond(id): + return mw.M('crontab').where('id=?', (id,)).field(__field).find() + +def deleteCronById(id): + mw.M('crontab').where("id=?", (id,)).delete() + return True + +def getCrontabList( + page:int | None = 1, + size:int | None = 10, +): + start = (int(page) - 1) * size + limit = str(start) + ',' + str(size) + + cron_list = mw.M('crontab').field(__field).limit(limit).order('id desc').select() + count = mw.M('crontab').count() + + data = {} + data['count'] = count + data['list'] = cron_list + return data \ No newline at end of file diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 6fa7431ff..a007d27e1 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -8,6 +8,20 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os + +import core.mw as mw + def initPanelData(): - pass \ No newline at end of file + _dbfile = mw.getPanelDataDir() + '/panel.db' + if os.path.exists(_dbfile): + return True + sql_file = mw.getPanelDataDir() + '/sql/default.sql' + sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') + csql = mw.readFile(sql_file) + csql_list = csql.split(';') + for index in range(len(csql_list)): + print(index) + sql.execute(csql_list[index], ()) + return True \ No newline at end of file diff --git a/web/thisdb/logs.py b/web/thisdb/logs.py index 967373068..d23d59719 100644 --- a/web/thisdb/logs.py +++ b/web/thisdb/logs.py @@ -8,6 +8,7 @@ # Author: midoks # --------------------------------------------------------------------------------- +import json import core.mw as mw @@ -32,4 +33,30 @@ def addLog(type, log, 'add_time':add_time, } mw.M('logs').insert(insert_data) - return True \ No newline at end of file + return True + +def getLogsList( + page:int | None = 1, + size:int | None = 10, + search: str | None = '' +): + sql_where = '' + if search != '' : + sql_where = " type like '%" + search + "%' or log like '%" + search + "%' " + + field = 'id,type,log,uid,add_time' + dbM = dbC = mw.M('logs').field(field) + + if sql_where != '': + count = mw.M('logs').field(field).where(sql_where).count() + else: + count = mw.M('logs').field(field).count() + + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + logs_list = mw.M('logs').field(field).limit(limit).order('id desc').select() + + data = {} + data['list'] = logs_list + data['count'] = count + return data \ No newline at end of file diff --git a/web/thisdb/option.py b/web/thisdb/option.py index 033a13925..767078425 100644 --- a/web/thisdb/option.py +++ b/web/thisdb/option.py @@ -23,8 +23,7 @@ def getOption(name, :default -> str 默认值 (可选) ''' data = mw.M('option').field('name').where('name=? and type=?',(name, type,)).getField('value') - - if len(data) == 0: + if data is None: return default return data @@ -40,7 +39,7 @@ def getOptionByJson(name, :default -> str 默认值 (可选) ''' data = mw.M('option').field('name').where('name=? and type=?',(name, type,)).getField('value') - if len(data) == 0: + if data is None: return default if data is not None: return json.loads(data) @@ -54,5 +53,15 @@ def setOption(name, value, :value -> object值 (必填) :type -> str 类型 (可选|默认common) ''' - mw.M('option').field('name').where('name=? and type=?',(name, type,)).setField('value', value) + + data = mw.M('option').field('name,type,value').where('name=? and type=?',(name, type,)).find() + if data is not None: + mw.M('option').field('name').where('name=? and type=?',(name, type,)).setField('value', value) + return True + add_option = { + 'name':name, + 'type':type, + 'value':value + } + mw.M('option').insert(add_option) return True \ No newline at end of file diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index b1f5a7082..e20f30071 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -11,5 +11,38 @@ import core.mw as mw def getSitesCount(): - return mw.M('sites').count() + return mw.M('sites').count() + + +def getSitesList( + page:int | None = 1, + size:int | None = 10, + type_id:int | None = 0, + search: str | None = '' +): + sql_where = '' + if search != '' : + sql_where = " name like '%" + search + "%' or ps like '%" + search + "%' " + if type_id != '' and int(type_id) >= 0 and search != '' : + sql_where = sql_where + " and type_id=" + str(type_id) + "" + if type_id != '' and int(type_id) >= 0: + sql_where = " type_id=" + str(type_id) + + + dbM = dbC = mw.M('sites').field('id,name,path,status,ps,edate,type_id,add_time,update_time') + + if sql_where != '': + count = dbC.where(sql_where).count() + else: + count = dbC.count() + + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + site_list = dbM.limit(limit).order('id desc').select() + + + data = {} + data['list'] = site_list + data['count'] = count + return data diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 7dcab2a21..c209794d7 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -10,6 +10,10 @@ import core.mw as mw +# 未执行任务总数 +def getTaskUnexecutedCount() -> int: + return mw.M('tasks').where('status!=?',(1,)).count() + def addTask( name: str | None = '常用任务', cmd: str | None = None, @@ -36,4 +40,35 @@ def addTask( 'add_time':add_time, } mw.M('tasks').insert(insert_data) + return True + + +def getTaskList( + status: int | None = 1, + page: int | None = 1, + size: int | None = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + + field = 'id,name,type,start,end,status,add_time' + data = mw.M('tasks').where('', ()).field(field).limit(limit).order('id asc').select() + return data + + + +def setTaskStatus(id, + status: int | None = 0 +): + mw.M('tasks').where('id=?',(id,)).update({'status':status}) + return True + +def setTaskData(id, + start: int | None = None, + end: int | None = None, +): + if start is not None: + mw.M('tasks').where('id=?',(id,)).update({'start':start}) + if end is not None: + mw.M('tasks').where('id=?',(id,)).update({'end':end}) return True \ No newline at end of file diff --git a/web/thisdb/temp_login.py b/web/thisdb/temp_login.py new file mode 100644 index 000000000..ab75c3923 --- /dev/null +++ b/web/thisdb/temp_login.py @@ -0,0 +1,33 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import time + +from admin.model import db, TempLogin + +import core.mw as mw + +def getTempLoginByToken(token, +) -> None: + ''' + 获取用户信息通过用户名 + ''' + field = 'id,addtime,expire,login_time,login_addr,state,add_time' + data = mw.M('temp_login').where('token=?', (token,)).field(field).order('id asc').select() + return data + +def clearTempLogin()->bool: + ''' + 清空过期数据 + ''' + + now_time = int(time.time()) + mw.M('temp_login').where('expire None: ''' 获取用户信息通过用户名 ''' users_field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' - item = mw.M('users').field(users_field).where('name=?', (name,)).select() - if len(item) == 0: + data = mw.M('users').field(users_field).where('name=?', (name,)).find() + if data is None: return None row = {} - row['id'] = item[0]['id'] - row['name'] = item[0]['name'] - row['password'] = item[0]['password'] - row['login_ip'] = item[0]['login_ip'] - row['login_time'] = item[0]['login_time'] - row['phone'] = item[0]['phone'] - row['email'] = item[0]['email'] - row['add_time'] = item[0]['add_time'] - row['update_time'] = item[0]['update_time'] + row['id'] = data['id'] + row['name'] = data['name'] + row['password'] = data['password'] + row['login_ip'] = data['login_ip'] + row['login_time'] = data['login_time'] + row['phone'] = data['phone'] + row['email'] = data['email'] + row['add_time'] = data['add_time'] + row['update_time'] = data['update_time'] return row def getUserById(id, @@ -37,19 +61,19 @@ def getUserById(id, 获取用户信息通过用户名 ''' users_field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' - item = mw.M('users').field(users_field).where('id=?', (1,)).select() - if len(item) == 0: + data = mw.M('users').field(users_field).where('id=?', (1,)).find() + if data is None: return None row = {} - row['id'] = item[0]['id'] - row['name'] = item[0]['name'] - row['password'] = item[0]['password'] - row['login_ip'] = item[0]['login_ip'] - row['login_time'] = item[0]['login_time'] - row['phone'] = item[0]['phone'] - row['email'] = item[0]['email'] - row['add_time'] = item[0]['add_time'] - row['update_time'] = item[0]['update_time'] + row['id'] = data['id'] + row['name'] = data['name'] + row['password'] = data['password'] + row['login_ip'] = data['login_ip'] + row['login_time'] = data['login_time'] + row['phone'] = data['phone'] + row['email'] = data['email'] + row['add_time'] = data['add_time'] + row['update_time'] = data['update_time'] return row diff --git a/web/utils/crontab.py b/web/utils/crontab.py index 57a654e17..f9b2d14d5 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -8,4 +8,423 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os +import sys +import time +import json +import threading +import multiprocessing + +from admin import model + +import core.mw as mw +import thisdb + +class crontab(object): + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(crontab, "_instance"): + with crontab._instance_lock: + if not hasattr(crontab, "_instance"): + crontab._instance = crontab(*args, **kwargs) + return crontab._instance + + # 获取指定任务数据 + def getCrondFind(self, cron_id): + return thisdb.getCrond(cron_id) + + def add(self, data): + if len(data['name']) < 1: + return mw.returnData(False, '任务名称不能为空!') + + is_check_pass, msg = self.cronCheck(data) + if not is_check_pass: + return mw.returnData(is_check_pass, msg) + + cmd, title = self.getCrondCycle(data) + cron_path = mw.getServerDir() + '/cron' + cron_name = self.getShell(data) + + cmd += ' ' + cron_path + '/' + cron_name + ' >> ' + cron_path + '/' + cron_name + '.log 2>&1' + + if not mw.isAppleSystem(): + data = self.writeShell(cmd) + if not data['status']: + return data + self.crondReload() + + add_dbdata = {} + add_dbdata['name'] = data['name'] + add_dbdata['type'] = data['type'] + add_dbdata['where1'] = data['where1'] + add_dbdata['where_hour'] = data['hour'] + add_dbdata['where_minute'] = data['minute'] + add_dbdata['save'] = data['save'] + add_dbdata['backup_to'] = data['backup_to'] + add_dbdata['sname'] = data['sname'] + add_dbdata['sbody'] = data['sbody'] + add_dbdata['stype'] = data['stype'] + add_dbdata['echo'] = cron_name + add_dbdata['url_address'] = data['url_address'] + + tid = thisdb.addCrontab(add_dbdata) + if tid > 0: + return mw.returnData(True, '添加成功') + return mw.returnData(False, '添加失败') + + def delete(self, tid): + data = thisdb.getCrond(tid) + if not self.removeForCrond(data['echo']): + return mw.returnData(False, '无法写入文件,请检查是否开启了系统加固功能!') + + cron_path = mw.getServerDir() + '/cron' + cron_file = cron_path + '/' + data['echo'] + + if os.path.exists(cron_file): + os.remove(cron_file) + cron_file = cron_path + '/' + data['echo'] + '.log' + if os.path.exists(cron_file): + os.remove(cron_file) + + thisdb.deleteCronById(tid) + msg = mw.getInfo('删除计划任务[{1}]成功!', (data['name'],)) + mw.writeLog('计划任务', msg) + return mw.returnData(True, msg) + + def getCrontabHuman(self, data): + rdata = [] + for i in range(len(data)): + t = data[i] + if t['type'] == "day": + t['type'] = '每天' + t['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str(t['where_hour']), str(t['where_minute']))) + elif t['type'] == "day-n": + t['type'] = mw.getInfo('每{1}天', (str(t['where1']),)) + t['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str(t['where1']), str(t['where_hour']), str(t['where_minute']))) + elif t['type'] == "hour": + t['type'] = '每小时' + t['cycle'] = mw.getInfo('每小时, 第{1}分钟 执行', (str(t['where_minute']),)) + elif t['type'] == "hour-n": + t['type'] = mw.getInfo('每{1}小时', (str(t['where1']),)) + t['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str(t['where1']), str(t['where_minute']))) + elif t['type'] == "minute-n": + t['type'] = mw.getInfo('每{1}分钟', (str(t['where1']),)) + t['cycle'] = mw.getInfo('每隔{1}分钟执行', (str(t['where1']),)) + elif t['type'] == "week": + t['type'] = '每周' + if not t['where1']: + t['where1'] = '0' + t['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int(t['where1'])), str(t['where_hour']), str(t['where_minute']))) + elif t['type'] == "month": + t['type'] = '每月' + t['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(t['where1']), str(t['where_hour']), str(t['where_minute']))) + rdata.append(t) + return rdata + + # 从crond删除 + def removeForCrond(self, echo): + if mw.isAppleSystem(): + return True + + u_file = '/var/spool/cron/crontabs/root' + if not os.path.exists(u_file): + file = '/var/spool/cron/root' + if not os.path.exists(file): + return False + else: + file = u_file + + conf = mw.readFile(file) + rep = ".+" + str(echo) + ".+\n" + conf = re.sub(rep, "", conf) + if not mw.writeFile(file, conf): + return False + self.crondReload() + return True + + def getCrontabList(self, + page:int | None = 1, + size:int | None = 10 + ): + info = thisdb.getCrontabList(page=int(page),size=int(size)) + + rdata = {} + rdata['data'] = self.getCrontabHuman(info['list']) + rdata['page'] = mw.getPage({'count':info['count'],'tojs':'getCronData','p':page,'row':size}) + return rdata + + def getCrondCycle(self, params): + cron_cmd = '' + title = '' + if params['type'] == "day": + cron_cmd = self.getDay(params) + title = '每天' + elif params['type'] == "day-n": + cron_cmd = self.getDay_N(params) + title = mw.getInfo('每{1}天', (params['where1'],)) + elif params['type'] == "hour": + cron_cmd = self.getHour(params) + title = '每小时' + elif params['type'] == "hour-n": + cron_cmd = self.getHour_N(params) + title = '每小时' + elif params['type'] == "minute-n": + cron_cmd = self.minute_N(params) + elif params['type'] == "week": + params['where1'] = params['week'] + cron_cmd = self.week(params) + elif params['type'] == "month": + cron_cmd = self.month(params) + return cron_cmd, title + + # 转换大写星期 + def toWeek(self, num): + wheres = { + 0: '日', + 1: '一', + 2: '二', + 3: '三', + 4: '四', + 5: '五', + 6: '六' + } + try: + return wheres[num] + except: + return '' + + # 取任务构造Day + def getDay(self, param): + cmd = "{0} {1} * * * ".format(param['minute'], param['hour']) + return cmd + + # 取任务构造Day_n + def getDay_N(self, param): + cmd = "{0} {1} */{2} * * ".format(param['minute'], param['hour'], param['where1']) + return cmd + + # 取任务构造Hour + def getHour(self, param): + cmd = "{0} * * * * ".format(param['minute']) + return cmd + + # 取任务构造Hour-N + def getHour_N(self, param): + cmd = "{0} */{1} * * * ".format(param['minute'], param['where1']) + return cmd + + # 取任务构造Minute-N + def minute_N(self, param): + cmd = "*/{0} * * * * ".format(param['where1']) + return cmd + + # 取任务构造week + def week(self, param): + cmd = "{0} {1} * * {2}".format(param['minute'], param['hour'], param['week']) + return cmd + + # 取任务构造Month + def month(self, param): + cmd = "{0} {1} {2} * * ".format(param['minute'], param['hour'], param['where1']) + return cmd + + # 参数校验 + def cronCheck(self, params): + if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path': + if params['save'] == '': + return False, '保留份数不能为空!' + + if params['type'] == 'day': + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'day-n': + if params['where1'] == '': + return False, '天不能为空!' + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + if params['type'] == 'hour': + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'hour-n': + if params['where1'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'minute-n': + if params['where1'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'week': + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'month': + if params['where1'] == '': + return False, '日不能为空!' + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + return True, 'OK' + + + # 取执行脚本 + def getShell(self, param): + # try: + stype = param['stype'] + if stype == 'toFile': + shell = param.sFile + else: + head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n" + + start_head = ''' +SCRIPT_RUN_TIME="0s" +MW_ToSeconds() +{ + SEC=$1 + if [ $SEC -lt 60 ]; then + SCRIPT_RUN_TIME="${SEC}s" + elif [ $SEC -ge 60 ] && [ $SEC -lt 3600 ];then + SCRIPT_RUN_TIME="$(( SEC / 60 ))m$(( SEC % 60 ))s" + elif [ $SEC -ge 3600 ]; then + SCRIPT_RUN_TIME="$(( SEC / 3600 ))h$(( (SEC % 3600) / 60 ))m$(( (SEC % 3600) % 60 ))s" + fi +} +START_MW_SHELL_TIME=`date +%s` +''' + + source_bin_activate = ''' +export LANG=en_US.UTF-8 +MW_PATH=%s/bin/activate +if [ -f $MW_PATH ];then + source $MW_PATH +fi''' % (mw.getPanelDir(),) + + head = head + start_head + source_bin_activate + "\n" + log = '.log' + + #所有 + if param['sname'] == 'ALL': + log = '' + + script_dir = mw.getPanelDir() + "/scripts" + source_stype = 'database' + if stype.find('database_') > -1: + plugin_name = stype.replace('database_', '') + script_dir = mw.getPanelDir() + "/plugins/" + plugin_name + "/scripts" + + source_stype = stype + stype = 'database' + + wheres = { + 'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']), + 'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']), + 'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']), + 'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']), + 'rememory': head + "/bin/bash " + script_dir + '/rememory.sh' + } + if param['backup_to'] != 'localhost': + cfile = mw.getPluginDir() + "/" + \ + param['backup_to'] + "/index.py" + + wheres['path'] = head + "python3 " + cfile + \ + " path " + param['sname'] + " " + str(param['save']) + wheres['site'] = head + "python3 " + cfile + \ + " site " + param['sname'] + " " + str(param['save']) + wheres['database'] = head + "python3 " + cfile + " " + \ + source_stype + " " + \ + param['sname'] + " " + str(param['save']) + try: + shell = wheres[stype] + except: + if stype == 'toUrl': + shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \ + param['urladdress'] + "'" + else: + shell = head + param['sbody'].replace("\r\n", "\n") + + shell += ''' +echo "----------------------------------------------------------------------------" +endDate=`date +"%Y-%m-%d %H:%M:%S"` +END_MW_SHELL_TIME=`date +"%s"` +((SHELL_COS_TIME=($END_MW_SHELL_TIME-$START_MW_SHELL_TIME))) +MW_ToSeconds $SHELL_COS_TIME +echo "★[$endDate] Successful | Script Run [$SCRIPT_RUN_TIME] " +echo "----------------------------------------------------------------------------" +''' + cron_path = mw.getServerDir() + '/cron' + if not os.path.exists(cron_path): + mw.execShell('mkdir -p ' + cron_path) + + if not 'echo' in param: + cron_name = mw.md5(mw.md5(str(time.time()) + '_mw')) + else: + cron_name = param['echo'] + file = cron_path + '/' + cron_name + mw.writeFile(file, self.checkScript(shell)) + mw.execShell('chmod 750 ' + file) + return cron_name + + # 检查脚本 + def checkScript(self, shell): + keys = ['shutdown', 'init 0', 'mkfs', 'passwd', + 'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs'] + for key in keys: + shell = shell.replace(key, '[***]') + return shell + + + + # 将Shell脚本写到文件 + def writeShell(self, config): + file = '/var/spool/cron/crontabs/root' + current_os = mw.getOs() + if current_os == 'darwin': + file = '/etc/crontab' + elif current_os.startswith("freebsd"): + file = '/var/cron/tabs/root' + + if not os.path.exists(file): + file = '/var/spool/cron/root' + + if not os.path.exists(file): + mw.writeFile(file, '') + conf = mw.readFile(file) + conf += str(config) + "\n" + if mw.writeFile(file, conf): + if not os.path.exists(file): + mw.execShell("chmod 600 '" + file +"' && chown root.root " + file) + else: + mw.execShell("chmod 600 '" + file +"' && chown root.crontab " + file) + return mw.returnData(True, 'ok') + return mw.returnData(False, '文件写入失败,请检查是否开启系统加固功能!') + + # 重载配置 + def crondReload(self): + if mw.isAppleSystem(): + if os.path.exists('/etc/crontab'): + pass + else: + if os.path.exists('/etc/init.d/crond'): + mw.execShell('/etc/init.d/crond reload') + elif os.path.exists('/etc/init.d/cron'): + mw.execShell('service cron restart') + else: + mw.execShell("systemctl reload crond") + + + + diff --git a/web/utils/plugin.py b/web/utils/plugin.py index 81eda5a51..3c0bf683e 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -17,7 +17,6 @@ import multiprocessing from admin import model import core.mw as mw -import admin.model.option as option import thisdb class pg_thread(threading.Thread): @@ -108,7 +107,7 @@ class plugin(object): self.__index_data = json.loads(mw.readFile(self.__index)) def getIndexList(self): - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index') plist = [] for i in indexList: tmp = i.split('-') @@ -152,30 +151,30 @@ class plugin(object): def addIndex(self, name, version): vname = name + '-' + version - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index',default=[]) + if vname in indexList: return mw.returnData(False, '请不要重复添加!') if len(indexList) > 12: return mw.returnData(False, '首页最多只能显示12个软件!') indexList.append(vname) - option.setOption('display_index', json.dumps(indexList)) + + thisdb.setOption('display_index', json.dumps(indexList)) return mw.returnData(True, '添加成功!') def removeIndex(self, name, version): vname = name + '-' + version - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index') if not vname in indexList: return mw.returnData(True, '删除成功!!') indexList.remove(vname) - - print(indexList) - option.setOption('display_index', json.dumps(indexList)) + thisdb.setOption('display_index', json.dumps(indexList)) return mw.returnData(True, '删除成功!') def hookInstallOption(self, hook_name, info): hn_name = 'hook_'+hook_name - src_data = option.getOptionByJson(hn_name,type='hook',default=[]) + src_data = thisdb.getOptionByJson(hn_name,type='hook',default=[]) isNeedAdd = True for x in range(len(src_data)): if src_data[x]['title'] == info['title'] and src_data[x]['name'] == info['name']: @@ -184,17 +183,17 @@ class plugin(object): if isNeedAdd: src_data.append(info) - option.setOption(hn_name, json.dumps(src_data), type='hook') + thisdb.setOption(hn_name, json.dumps(src_data), type='hook') return True def hookUninstallOption(self, hook_name, info): hn_name = 'hook_'+hook_name - src_data = option.getOptionByJson(hn_name,type='hook',default=[]) + src_data = thisdb.getOptionByJson(hn_name,type='hook',default=[]) for idx in range(len(src_data)): if src_data[idx]['name'] == info['name']: src_data.remove(src_data[idx]) break - option.setOption(hn_name, json.dumps(src_data), type='hook') + thisdb.setOption(hn_name, json.dumps(src_data), type='hook') return True def hookInstall(self, info): @@ -316,7 +315,7 @@ class plugin(object): return '' def checkIndexList(self, name, version): - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index',default=[]) for i in indexList: t = i.split('-') if t[0] == name: @@ -426,15 +425,28 @@ class plugin(object): return pInfo def makeCoexistData(self, data): - plugins_t = [] + plugins = [] if type(data['versions']) == list and 'coexist' in data and data['coexist']: data_t = self.makeCoexist(data) for index in range(len(data_t)): - plugins_t.append(data_t[index]) + plugins.append(data_t[index]) else: pg = self.getPluginInfo(data) - plugins_t.append(pg) - return plugins_t + plugins.append(pg) + return plugins + + def makeCoexistDataInstalled(self, data): + plugins = [] + if type(data['versions']) == list and 'coexist' in data and data['coexist']: + data_t = self.makeCoexist(data) + for index in range(len(data_t)): + if data_t[index]['setup']: + plugins.append(data_t[index]) + else: + pg = self.getPluginInfo(data) + if pg['setup']: + plugins.append(pg) + return plugins # 对多版本共存进行处理 def makeCoexistList(self, data, @@ -449,12 +461,11 @@ class plugin(object): if plugin_type == None or plugin_type == '0': return self.makeCoexistData(data) # 已经安装 - if plugin_type == '-1': - return self.makeCoexistData(data) + if str(plugin_type) == '-1': + return self.makeCoexistDataInstalled(data) return plugins_t - def getPluginList(self, name, keyword: str | None = None, type: str | None = None, From b1dd7ea60956bc8e94a66d576791faf2f279c0d7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 00:16:19 +0800 Subject: [PATCH 095/965] update --- data/sql/default.sql | 7 +- web/admin/__init__.py | 26 ++-- web/admin/crontab/__init__.py | 82 +++++++++---- web/admin/firewall/__init__.py | 26 +--- web/config.py | 3 + web/core/mw.py | 7 ++ web/static/app/crontab.js | 1 - web/static/app/public.js | 52 ++++---- web/thisdb/__init__.py | 1 + web/thisdb/crontab.py | 11 +- web/thisdb/firewall.py | 55 +++++++++ web/utils/crontab.py | 212 +++++++++++++++++++++++++++------ web/utils/firewall.py | 13 +- 13 files changed, 366 insertions(+), 130 deletions(-) create mode 100644 web/thisdb/firewall.py diff --git a/data/sql/default.sql b/data/sql/default.sql index bc99e4163..ec3bc47d0 100755 --- a/data/sql/default.sql +++ b/data/sql/default.sql @@ -43,14 +43,15 @@ CREATE TABLE IF NOT EXISTS `firewall` ( `port` TEXT, `protocol` TEXT DEFAULT 'tcp', `ps` TEXT, - `add_time` TEXT + `add_time` TEXT, + `update_time` TEXT ); ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp'; INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`) VALUES -(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00'), -(2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00'); +(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), +(2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00','0000-00-00 00:00:00'); diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 2c3d468c9..359557fea 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -100,13 +100,12 @@ setup.init_db_system() app.config['BASIC_AUTH_OPEN'] = False -with app.app_context(): - try: - basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) - if basic_auth['open']: - app.config['BASIC_AUTH_OPEN'] = True - except Exception as e: - pass +try: + basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) + if basic_auth['open']: + app.config['BASIC_AUTH_OPEN'] = True +except Exception as e: + pass @@ -127,6 +126,7 @@ def sendAuthenticated(): @app.before_request def requestCheck(): + config.APP_START_TIME=time.time() # 自定义basic auth认证 if app.config['BASIC_AUTH_OPEN']: basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) @@ -152,6 +152,8 @@ def requestCheck(): def requestAfter(response): response.headers['soft'] = config.APP_NAME response.headers['mw-version'] = config.APP_VERSION + if mw.isDebugMode(): + response.headers['mw-debug-cos'] = time.time() - config.APP_START_TIME return response @@ -163,20 +165,16 @@ def page_unauthorized(error): # 设置模板全局变量 @app.context_processor def inject_global_variables(): - start_t = time.time() - ver = config.APP_VERSION; + app_ver = config.APP_VERSION if mw.isDebugMode(): - ver = ver + str(time.time()) + app_ver = app_ver + str(time.time()) data = utils_config.getGlobalVar() g_config = { - 'version': ver, + 'version': app_ver, 'title' : 'MW面板', 'ip' : '127.0.0.1' } - - end_t = time.time() - print("cos:"+str(end_t-start_t)) return dict(config=g_config, data=data) diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index 3e8232e40..b1b76ba98 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -23,40 +23,82 @@ blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folde def index(): return render_template('crontab.html') -# 插件列表 -@blueprint.route('/list', endpoint='list', methods=['GET','POST']) +# 计划任务列表 +@blueprint.route('/list', endpoint='list', methods=['POST']) @panel_login_required def list(): page = request.args.get('p', '1').strip() limit = request.args.get('limit', '10').strip() return MwCrontab.instance().getCrontabList(page=int(page),size=int(limit)) -# 插件列表 -@blueprint.route('/logs', endpoint='logs', methods=['GET','POST']) -def logs(self): - sid = request.form.get('id', '') - echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find() - logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log' - if not os.path.exists(logFile): - return mw.returnData(False, '当前日志为空!') - log = mw.getLastLine(logFile, 500) - return mw.returnData(True, log) +# 计划任务日志 +@blueprint.route('/logs', endpoint='logs', methods=['POST']) +def logs(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().cronLog(cron_id) -# 获取计划任务 -@blueprint.route('/del', endpoint='del', methods=['GET','POST']) +# 删除计划任务 +@blueprint.route('/del', endpoint='del', methods=['POST']) def crontab_del(): - task_id = request.form.get('id', '') - return MwCrontab.instance().delete(task_id) + cron_id = request.form.get('id', '') + return MwCrontab.instance().delete(cron_id) + +# 删除计划任务日志 +@blueprint.route('/del_logs', endpoint='del_logs', methods=['POST']) +def del_logs(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().delLogs(cron_id) + + +# 设置计划任务状态 +@blueprint.route('/set_cron_status', endpoint='set_cron_status', methods=['POST']) +def set_cron_status(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().setCronStatus(cron_id) + +# 设置计划任务状态 +@blueprint.route('/get_data_list', endpoint='get_data_list', methods=['POST']) +def get_data_list(): + stype = request.form.get('type', '') + return MwCrontab.instance().getDataList(stype) + # 获取计划任务 -@blueprint.route('/get_crond_find', endpoint='get_crond_find', methods=['GET','POST']) +@blueprint.route('/get_crond_find', endpoint='get_crond_find', methods=['POST']) def get_crond_find(): - sid = request.form.get('id', '') - data = MwCrontab.instance().getCrondFind(sid) + cron_id = request.form.get('id', '') + data = MwCrontab.instance().getCrondFind(cron_id) return data +# 修改计划任务 +@blueprint.route('/modify_crond', endpoint='modify_crond', methods=['POST']) +def modify_crond(): + request_data = {} + + request_data['name'] = request.form.get('name', '') + request_data['type'] = request.form.get('type', '') + request_data['week'] = request.form.get('week', '') + request_data['where1'] = request.form.get('where1', '') + request_data['hour'] = request.form.get('hour', '') + request_data['minute'] = request.form.get('minute', '') + request_data['save'] = request.form.get('save', '') + request_data['backup_to'] = request.form.get('backupTo', '') + request_data['stype'] = request.form.get('stype', '') + request_data['sname'] = request.form.get('sname', '') + request_data['sbody'] = request.form.get('sbody', '') + request_data['url_address'] = request.form.get('urladdress', '') + cron_id = request.form.get('id', '') + data = MwCrontab.instance().modifyCrond(cron_id,request_data) + return data + +# 执行计划任务 +@blueprint.route('/start_task', endpoint='start_task', methods=['POST']) +def start_task(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().startTask(cron_id) + # 添加计划任务 -@blueprint.route('/add', endpoint='add', methods=['GET','POST']) +@blueprint.route('/add', endpoint='add', methods=['POST']) @panel_login_required def add(): request_data = {} diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index a14c0dff7..8cd65a986 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -33,25 +33,7 @@ def index(): def get_list(): p = request.form.get('p', '1').strip() limit = request.form.get('limit', '10').strip() - - count = Firewall.query.filter_by().count() - pagination = Firewall.query.filter_by().paginate(page=int(p), per_page=int(limit)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['port'] = item.port - t['protocol'] = item.protocol - t['ps'] = item.ps - t['add_time'] = item.add_time - t['update_time'] = item.update_time - rows.append(t) - - data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'getLogs','p':p,'row':limit}) - return data + return MwFirewall.instance().getList(p,limit) # 获取站点日志目录 @blueprint.route('/get_www_path', endpoint='get_www_path', methods=['POST']) @@ -64,8 +46,7 @@ def get_www_path(): @blueprint.route('/get_ssh_info', endpoint='get_ssh_info', methods=['POST']) @panel_login_required def get_ssh_info(): - mf = MwFirewall.instance() - return mf.getSshInfo() + return MwFirewall.instance().getSshInfo() # 切换ping开关 @@ -88,8 +69,7 @@ def add_accept_port(): protocol = request.form.get('protocol', '').strip() stype = request.form.get('type', '').strip() - data = mf.addAcceptPort(port, ps, stype, protocol=protocol) - return mw.getJson(data) + return mf.addAcceptPort(port, ps, stype, protocol=protocol) diff --git a/web/config.py b/web/config.py index 1b84db8df..82aa42513 100644 --- a/web/config.py +++ b/web/config.py @@ -60,3 +60,6 @@ SQLITE_PATH = os.path.join(DATA_DIR, APP_SQLITE_NAME + '.db') DEFAULT_SERVER = '127.0.0.1' DEFAULT_SERVER_PORT = 7201 +# APP启动时间 +APP_START_TIME=0 + diff --git a/web/core/mw.py b/web/core/mw.py index e935077c7..8b970cab6 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -97,6 +97,13 @@ def getServerDir(): def getLogsDir(): return getFatherDir() + '/wwwlogs' +def getWwwDir(): + file = getPanelDir() + '/data/site.pl' + if os.path.exists(file): + return readFile(file).strip() + return getFatherDir() + '/wwwroot' + + def getRecycleBinDir(): rb_dir = getFatherDir() + '/recycle_bin' if not os.path.exists(rb_dir): diff --git a/web/static/app/crontab.js b/web/static/app/crontab.js index b8aea37d1..fe21632c4 100755 --- a/web/static/app/crontab.js +++ b/web/static/app/crontab.js @@ -83,7 +83,6 @@ function getBackupName(hook_data, name){ function getCronData(page){ var load = layer.msg(lan.public.the,{icon:16,time:0,shade: [0.3, '#000']}); $.post("/crontab/list?p="+page,'', function(rdata){ - console.log(rdata); layer.close(load); var cbody = ""; if(rdata == ""){ diff --git a/web/static/app/public.js b/web/static/app/public.js index 8516508d4..6188975cf 100755 --- a/web/static/app/public.js +++ b/web/static/app/public.js @@ -259,7 +259,6 @@ function getFormatTime(tm, format) { function changePathCallback(default_dir, callback) { - var c = layer.open({ type: 1, area: "650px", @@ -276,7 +275,7 @@ function changePathCallback(default_dir, callback) { \
\
\ -
计算机
\ +
计算机
\
\
\
    \ @@ -387,14 +386,15 @@ function getDiskList(b) { var a = ""; var c = "path=" + b + "&disk=True"; $.post("/files/get_dir", c, function(h) { - if(h.DISK != undefined) { - for(var f = 0; f < h.DISK.length; f++) { - a += "
     " + h.DISK[f].path + "
    " - } - $("#changecomlist").html(a) - } - for(var f = 0; f < h.DIR.length; f++) { - var g = h.DIR[f].split(";"); + // console.log(h); + // if(h.dir != undefined) { + // for(var f = 0; f < h.dir.length; f++) { + // a += "
     " + h.dir[f].path + "
    "; + // } + // $("#changecomlist").html(a); + // } + for(var f = 0; f < h.dir.length; f++) { + var g = h.dir[f].split(";"); var e = g[0]; if(isChineseChar(e)) { @@ -408,16 +408,16 @@ function getDiskList(b) { } d += "\ - \ + \ " + e + "" + getLocalTime(g[2]) + "\ " + g[3] + "\ " + g[4] + "\ - X\ + X\ "; } - if(h.FILES != null && h.FILES != "") { - for(var f = 0; f < h.FILES.length; f++) { - var g = h.FILES[f].split(";"); + 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) { @@ -441,10 +441,10 @@ function getDiskList(b) { $(".default").hide(); $(".file-list").show(); $("#tbody").html(d); - if(h.PATH.substr(h.PATH.length - 1, 1) != "/") { - h.PATH += "/"; + if(h.path.substr(h.path.length - 1, 1) != "/") { + h.path += "/"; } - $("#PathPlace").find("span").html(h.PATH); + $("#PathPlace").find("span").html(h.path); activeDisk(); return; },'json'); @@ -530,10 +530,11 @@ function activeDisk() { } function backMyComputer() { - $(".default").show(); - $(".file-list").hide(); - $("#PathPlace").find("span").html(""); - activeDisk(); + // $(".default").show(); + // $(".file-list").hide(); + // $("#PathPlace").find("span").html(""); + // activeDisk(); + return; } function backFile() { @@ -1601,13 +1602,6 @@ function activeDisk() { } -function backMyComputer() { - $(".default").show(); - $(".file-list").hide(); - $("#PathPlace").find("span").html(""); - activeDisk(); -} - //检查登陆状态 function check_login(){ $.post('/check_login',{},function(rdata){ diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 85125f384..8958fa60f 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -15,3 +15,4 @@ from .sites import * from .tasks import * from .logs import * from .crontab import * +from .firewall import * diff --git a/web/thisdb/crontab.py b/web/thisdb/crontab.py index 27dad974f..32f755598 100644 --- a/web/thisdb/crontab.py +++ b/web/thisdb/crontab.py @@ -20,6 +20,14 @@ def addCrontab(data): data['update_time'] = now_time return mw.M('crontab').insert(data) +def setCrontabData(cron_id, data): + mw.M('crontab').where('id=?', (cron_id,)).update(data) + return True + +def setCrontabStatus(cron_id, status): + mw.M('crontab').where('id=?', (cron_id,)).update({'status':status}) + return True + def getCrond(id): return mw.M('crontab').where('id=?', (id,)).field(__field).find() @@ -40,4 +48,5 @@ def getCrontabList( data = {} data['count'] = count data['list'] = cron_list - return data \ No newline at end of file + return data + diff --git a/web/thisdb/firewall.py b/web/thisdb/firewall.py new file mode 100644 index 000000000..88c7b0c39 --- /dev/null +++ b/web/thisdb/firewall.py @@ -0,0 +1,55 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,port,protocol,ps,add_time,update_time' + +def getFirewallList( + page:int | None = 1, + size:int | None = 10, +): + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + + firewall_list = mw.M('firewall').field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('firewall').count() + + data = {} + data['count'] = count + data['list'] = firewall_list + return data + +def addFirewall(port, + protocol: str | None = 'tcp', + ps: str | None = '备注' +) -> bool: + ''' + 设置配置的值 + :port -> str 端口 (必填) + :protocol -> str 协议 (可选|tcp,udp,tcp/udp) + :ps -> str 备注 (可选) + ''' + now_time = mw.formatDate() + insert_data = { + 'port':port, + 'protocol':protocol, + 'ps':ps, + 'add_time':now_time, + 'update_time':now_time, + } + mw.M('firewall').insert(insert_data) + return True + + +def getFirewallCountByPort(port): + return mw.M('firewall').where('port=?',(port,)).count() + + diff --git a/web/utils/crontab.py b/web/utils/crontab.py index f9b2d14d5..4a8b6035f 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -10,6 +10,7 @@ import os import sys +import re import time import json import threading @@ -20,6 +21,7 @@ from admin import model import core.mw as mw import thisdb + class crontab(object): # lock _instance_lock = threading.Lock() @@ -32,6 +34,119 @@ class crontab(object): crontab._instance = crontab(*args, **kwargs) return crontab._instance + def modifyCrond(self,cron_id,data): + if len(data['name']) < 1: + return mw.returnData(False, '任务名称不能为空!') + + is_check_pass, msg = self.cronCheck(data) + if not is_check_pass: + return mw.returnData(is_check_pass, msg) + + info = thisdb.getCrond(cron_id) + + dbdata = {} + dbdata['name'] = data['name'] + dbdata['type'] = data['type'] + dbdata['where1'] = data['where1'] + dbdata['where_hour'] = data['hour'] + dbdata['where_minute'] = data['minute'] + dbdata['save'] = data['save'] + dbdata['backup_to'] = data['backup_to'] + dbdata['sname'] = data['sname'] + dbdata['sbody'] = data['sbody'] + dbdata['stype'] = data['stype'] + dbdata['url_address'] = data['url_address'] + + if not self.removeForCrond(info['echo']): + return mw.returnData(False, '无法写入文件,是否开启了系统加固功能!') + + thisdb.setCrontabData(cron_id, dbdata) + self.syncToCrond(cron_id) + msg = '修改计划任务[' + data['name'] + ']成功' + mw.writeLog('计划任务', msg) + return mw.returnData(True, msg) + + # 取数据列表 + def getDataList(self,stype=''): + + bak_data = [] + if stype == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path': + hookPath = mw.getPanelDataDir() + "/hook_backup.json" + if os.path.exists(hookPath): + t = mw.readFile(hookPath) + bak_data = json.loads(t) + + if stype == 'database' or stype.find('database_') > -1: + sqlite3_name = 'mysql' + path = mw.getServerDir() + '/mysql' + if stype != 'database': + soft_name = stype.replace('database_', '') + path = mw.getServerDir() + '/' + soft_name + + if soft_name == 'postgresql': + sqlite3_name = 'pgsql' + + if soft_name == 'mongodb': + sqlite3_name = 'mongodb' + + db_list = {} + db_list['orderOpt'] = bak_data + + if not os.path.exists(path + '/' + sqlite3_name + '.db'): + db_list['data'] = [] + else: + db_list['data'] = mw.M('databases').dbPos(path, sqlite3_name).field('name,ps').select() + return mw.getJson(db_list) + + if stype == 'path': + db_list = {} + db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}] + db_list['orderOpt'] = bak_data + return mw.getJson(db_list) + + data = {} + data['orderOpt'] = bak_data + + default_db = 'sites' + data['data'] = mw.M(default_db).field('name,ps').select() + return mw.getJson(data) + + def setCronStatus(self,cron_id): + data = thisdb.getCrond(cron_id) + + status = 1 + status_msg = '开启' + if data['status'] == status: + status = 0 + status_msg = '关闭' + self.removeForCrond(data['echo']) + else: + data['status'] = 1 + self.syncToCrond(cron_id) + + thisdb.setCrontabStatus(cron_id, status) + + msg = '修改计划任务[' + data['name'] + ']状态为[' + str(status_msg) + ']' + mw.writeLog('计划任务', msg) + return mw.returnJson(True, msg) + + + def cronLog(self, cron_id): + data = thisdb.getCrond(cron_id) + log_file = mw.getServerDir() + '/cron/' + data['echo'] + '.log' + if not os.path.exists(log_file): + return mw.returnData(False, '当前日志为空!') + content = mw.getLastLine(log_file, 500) + return mw.returnData(True, content) + + def startTask(self, cron_id): + data = thisdb.getCrond(cron_id) + cmd_file = mw.getServerDir() + '/cron/' + data['echo'] + os.system('chmod +x ' + cmd_file) + os.system('nohup ' + cmd_file + ' >> ' + cmd_file + '.log 2>&1 &') + return mw.returnData(True, '计划任务【%s】已执行!' % data['name']) + + # 获取指定任务数据 def getCrondFind(self, cron_id): return thisdb.getCrond(cron_id) @@ -78,7 +193,7 @@ class crontab(object): def delete(self, tid): data = thisdb.getCrond(tid) if not self.removeForCrond(data['echo']): - return mw.returnData(False, '无法写入文件,请检查是否开启了系统加固功能!') + return mw.returnData(False, '无法写入文件,是否开启了系统加固功能!') cron_path = mw.getServerDir() + '/cron' cron_file = cron_path + '/' + data['echo'] @@ -94,6 +209,17 @@ class crontab(object): mw.writeLog('计划任务', msg) return mw.returnData(True, msg) + + def delLogs(self,cron_id): + try: + data = thisdb.getCrond(cron_id) + log_file = mw.getServerDir() + '/cron/' + data['echo'] + '.log' + if os.path.exists(log_file): + os.remove(log_file) + return mw.returnData(True, '任务日志已清空!') + except: + return mw.returnData(False, '任务日志清空失败!') + def getCrontabHuman(self, data): rdata = [] for i in range(len(data)): @@ -129,18 +255,23 @@ class crontab(object): if mw.isAppleSystem(): return True - u_file = '/var/spool/cron/crontabs/root' - if not os.path.exists(u_file): - file = '/var/spool/cron/root' - if not os.path.exists(file): - return False - else: - file = u_file + cron_file = [ + '/var/spool/cron/crontabs/root', + '/var/spool/cron/root', + ] - conf = mw.readFile(file) + file = '' + for i in cron_file: + if os.path.exists(file): + file = i + + if file == '': + return False + + content = mw.readFile(file) rep = ".+" + str(echo) + ".+\n" - conf = re.sub(rep, "", conf) - if not mw.writeFile(file, conf): + content = re.sub(rep, "", content) + if not mw.writeFile(file, content): return False self.crondReload() return True @@ -288,7 +419,6 @@ class crontab(object): shell = param.sFile else: head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n" - start_head = ''' SCRIPT_RUN_TIME="0s" MW_ToSeconds() @@ -336,22 +466,16 @@ fi''' % (mw.getPanelDir(),) 'rememory': head + "/bin/bash " + script_dir + '/rememory.sh' } if param['backup_to'] != 'localhost': - cfile = mw.getPluginDir() + "/" + \ - param['backup_to'] + "/index.py" + cfile = mw.getPluginDir() + "/" + param['backup_to'] + "/index.py" - wheres['path'] = head + "python3 " + cfile + \ - " path " + param['sname'] + " " + str(param['save']) - wheres['site'] = head + "python3 " + cfile + \ - " site " + param['sname'] + " " + str(param['save']) - wheres['database'] = head + "python3 " + cfile + " " + \ - source_stype + " " + \ - param['sname'] + " " + str(param['save']) + wheres['path'] = head + "python3 " + cfile + " path " + param['sname'] + " " + str(param['save']) + wheres['site'] = head + "python3 " + cfile + " site " + param['sname'] + " " + str(param['save']) + wheres['database'] = head + "python3 " + cfile + " " + source_stype + " " + param['sname'] + " " + str(param['save']) try: shell = wheres[stype] except: if stype == 'toUrl': - shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \ - param['urladdress'] + "'" + shell = head + "curl -sS --connect-timeout 10 -m 60 '" + param['urladdress'] + "'" else: shell = head + param['sbody'].replace("\r\n", "\n") @@ -381,19 +505,19 @@ echo "-------------------------------------------------------------------------- def checkScript(self, shell): keys = ['shutdown', 'init 0', 'mkfs', 'passwd', 'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs'] - for key in keys: - shell = shell.replace(key, '[***]') + for k in keys: + shell = shell.replace(k, '[***]') return shell - - # 将Shell脚本写到文件 - def writeShell(self, config): + def writeShell(self, bash_script): + if mw.isAppleSystem(): + return mw.returnData(True, 'ok') file = '/var/spool/cron/crontabs/root' - current_os = mw.getOs() - if current_os == 'darwin': + sys_os = mw.getOs() + if sys_os == 'darwin': file = '/etc/crontab' - elif current_os.startswith("freebsd"): + elif sys_os.startswith("freebsd"): file = '/var/cron/tabs/root' if not os.path.exists(file): @@ -401,15 +525,16 @@ echo "-------------------------------------------------------------------------- if not os.path.exists(file): mw.writeFile(file, '') - conf = mw.readFile(file) - conf += str(config) + "\n" - if mw.writeFile(file, conf): + + content = mw.readFile(file) + content += str(bash_script) + "\n" + if mw.writeFile(file, content): if not os.path.exists(file): mw.execShell("chmod 600 '" + file +"' && chown root.root " + file) else: mw.execShell("chmod 600 '" + file +"' && chown root.crontab " + file) return mw.returnData(True, 'ok') - return mw.returnData(False, '文件写入失败,请检查是否开启系统加固功能!') + return mw.returnData(False, '文件写入失败,是否开启系统加固功能!') # 重载配置 def crondReload(self): @@ -424,7 +549,22 @@ echo "-------------------------------------------------------------------------- else: mw.execShell("systemctl reload crond") + def syncToCrond(self, cron_id): + info = thisdb.getCrond(cron_id) + if 'status' in info: + if info['status'] == 0: + return False + if 'where_hour' in info: + info['hour'] = info['where_hour'] + info['minute'] = info['where_minute'] + info['week'] = info['where1'] - + cmd, _ = self.getCrondCycle(info) + cron_path = mw.getServerDir() + '/cron' + cron_name = self.getShell(info) + cmd += ' ' + cron_path + '/' + cron_name + ' >> ' + cron_path + '/' + cron_name + '.log 2>&1' + self.writeShell(cmd) + self.crondReload() + return True diff --git a/web/utils/firewall.py b/web/utils/firewall.py index e100925ad..715810947 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -14,9 +14,9 @@ import threading import re import time -from admin import model import core.mw as mw +import thisdb class Firewall(object): @@ -47,6 +47,14 @@ class Firewall(object): elif mw.isAppleSystem(): self.__isMac = True + def getList(self, page=1,size=10): + info = thisdb.getFirewallList(page=page, size=size) + + rdata = {} + rdata['data'] = info['list'] + rdata['page'] = mw.getPage({'count':info['count'],'tojs':'showAccept','p':page,'row':size}) + return rdata + def reload(self): if self.__isUfw: mw.execShell('/usr/sbin/ufw reload') @@ -236,8 +244,7 @@ class Firewall(object): msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,)) mw.writeLog("防火墙管理", msg) - - return mw.returnData(True, '添加放行(' + port + ')端口成功!') + return mw.returnData(True, msg) From fbe2da1cd832e9db08f9c142b9b549c91c3a70f8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 00:38:22 +0800 Subject: [PATCH 096/965] update --- web/admin/firewall/__init__.py | 9 ++------- web/admin/task/__init__.py | 22 +--------------------- web/thisdb/tasks.py | 9 +++++++-- web/utils/firewall.py | 6 +++--- web/utils/task.py | 9 +++++++++ 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 8cd65a986..5002551a4 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -53,23 +53,18 @@ def get_ssh_info(): @blueprint.route('/set_ping', endpoint='set_ping', methods=['POST']) @panel_login_required def set_ping(): - mf = MwFirewall.instance() - return mf.setPing() + return MwFirewall.instance().setPing() # 添加放行端口 @blueprint.route('/add_accept_port', endpoint='add_accept_port', methods=['POST']) @panel_login_required def add_accept_port(): - mf = MwFirewall.instance() - if not mf.getFwStatus(): - return mw.returnData(False, '防火墙启动时,才能添加规则!') - port = request.form.get('port', '').strip() ps = request.form.get('ps', '').strip() protocol = request.form.get('protocol', '').strip() stype = request.form.get('type', '').strip() - return mf.addAcceptPort(port, ps, stype, protocol=protocol) + return MwFirewall.instance().addAcceptPort(port, ps, stype, protocol=protocol) diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index 0567a7531..d0cff30e3 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -35,27 +35,7 @@ def list(): p = request.form.get('p', '1') limit = request.form.get('limit', '10').strip() search = request.form.get('search', '').strip() - - count = Tasks.query.filter_by().count() - pagination = Tasks.query.filter_by().order_by(Tasks.id.desc()).paginate(page=int(p), per_page=int(limit)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['name'] = item.name - t['type'] = item.type - t['cmd'] = item.cmd - t['start'] = item.start - t['end'] = item.end - t['status'] = item.status - t['add_time'] = item.add_time - rows.append(t) - - data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'remind','p':p}) - return data + return MwTasks.getTaskList(int(p), int(limit)) @blueprint.route('/get_exec_log', endpoint='get_exec_log', methods=['POST']) @panel_login_required diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index c209794d7..59168762d 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -52,8 +52,13 @@ def getTaskList( limit = str(start) + ',' + str(size) field = 'id,name,type,start,end,status,add_time' - data = mw.M('tasks').where('', ()).field(field).limit(limit).order('id asc').select() - return data + task_list = mw.M('tasks').where('', ()).field(field).limit(limit).order('id asc').select() + count = mw.M('tasks').where('', ()).count() + + rdata = {} + rdata['list'] = task_list + rdata['count'] = count + return rdata diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 715810947..fcb48cca3 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -229,16 +229,16 @@ class Firewall(object): ): if not self.getFwStatus(): self.setFw(0) - + return mw.returnData(False, '防火墙启动时,才能添加规则!') rep = r"^\d{1,5}(:\d{1,5})?$" if not re.search(rep, port): return mw.returnData(False, '端口范围不正确!') - if model.getFirewallCountByPort(port) > 0: + if thisdb.getFirewallCountByPort(port) > 0: return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') - model.addFirewall(port, ps=ps,protocol=protocol) + thisdb.addFirewall(port, ps=ps,protocol=protocol) self.addAcceptPortCmd(port, protocol=protocol) self.reload() diff --git a/web/utils/task.py b/web/utils/task.py index 49e269647..a069aa963 100644 --- a/web/utils/task.py +++ b/web/utils/task.py @@ -13,6 +13,15 @@ import pwd import time import core.mw as mw +import thisdb + +def getTaskList(page=1,size=10): + info = thisdb.getTaskList(page=page, size=size) + + rdata = {} + rdata['data'] = info['list'] + rdata['page'] = mw.getPage({'count':info['count'],'tojs':'remind','p':page,'row':size}) + return rdata # 删除进程下的所有进程 def removeTaskRecursion(pid): From 9cf9b5367181535768c093bf0f48c1b5d3bd13ff Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 00:46:25 +0800 Subject: [PATCH 097/965] update --- web/admin/task/__init__.py | 10 +++++----- web/thisdb/tasks.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index d0cff30e3..e06404184 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -47,15 +47,15 @@ def get_exec_log(): @blueprint.route('/get_task_speed', endpoint='get_task_speed', methods=['POST']) @panel_login_required def get_task_speed(): - count = model.getTaskUnexecutedCount() + count = thisdb.getTaskUnexecutedCount() if count == 0: return mw.returnData(False, '当前没有任务队列在执行-2!') - row = model.getTaskFirstByRun() + row = thisdb.getTaskFirstByRun() if row is None: return mw.returnData(False, '当前没有任务队列在执行-3!') - task_logfile = mw.getPanelTaskLog() + task_logfile = thisdb.getPanelTaskLog() data = {} data['name'] = row['name'] @@ -72,7 +72,7 @@ def get_task_speed(): break except Exception as e: if i == 2: - model.setTaskStatus(row['id'],0) + thisdb.setTaskStatus(row['id'],0) return mw.returnData(False, '当前没有任务队列在执行-4:' + str(e)) time.sleep(0.5) else: @@ -80,7 +80,7 @@ def get_task_speed(): data['isDownload'] = False - data['task'] = model.getTaskRunList() + data['task'] = thisdb.getTaskRunList() return data @blueprint.route('/remove_task', endpoint='remove_task', methods=['POST']) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 59168762d..2f551204a 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -10,6 +10,12 @@ import core.mw as mw +def getTaskCount( + status: int | None = -1 + ) -> int: + return mw.M('tasks').where('status=?',(1,)).count() + + # 未执行任务总数 def getTaskUnexecutedCount() -> int: return mw.M('tasks').where('status!=?',(1,)).count() @@ -60,6 +66,28 @@ def getTaskList( rdata['count'] = count return rdata +def getTaskFirstByRun() -> None: + field = 'id,name,type,start,end,status,add_time' + data = mw.M('tasks').where('status=?', (-1,)).field(field).order('id asc').find() + if item is None: + return None + return data + +def getTaskRunList( + page: int | None = 1, + size: int | None = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + + field = 'id,name,type,start,end,status,add_time' + task_list = mw.M('tasks').where('', ()).field(field).limit(limit).order('id desc').select() + count = mw.M('tasks').where('', ()).count() + + rdata = {} + rdata['list'] = task_list + rdata['count'] = count + return rdata def setTaskStatus(id, From 39c634ab14ac56e4d7be974d53c5aeea74515069 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 00:51:55 +0800 Subject: [PATCH 098/965] Update panel_tools.py --- panel_tools.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index ec54c05f4..45af39793 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -23,12 +23,8 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) - -from admin import app -from admin import model -import thisdb import core.mw as mw -import core.db as db +import thisdb INIT_DIR = "/etc/rc.d/init.d" if mw.isAppleSystem(): From 8010ead35af5d4a7f523500681c774473063fecc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 00:58:40 +0800 Subject: [PATCH 099/965] update --- panel_task.py | 48 ++++++++++++++++++++++----------------------- web/thisdb/tasks.py | 12 ++++++++++++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/panel_task.py b/panel_task.py index 9432db180..8022b403a 100755 --- a/panel_task.py +++ b/panel_task.py @@ -24,12 +24,8 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) -from admin import app -from admin import model - -import thisdb import core.mw as mw -import core.db as db +import thisdb g_log_file = mw.getPanelTaskLog() isTask = mw.getMWLogs() + '/panelTask.pl' @@ -118,7 +114,7 @@ def runPanelTask(): thisdb.setTaskData(run_task['id'], end=end) thisdb.setTaskStatus(run_task['id'], 1) except Exception as e: - print(str(e)) + print(mw.getTracebackInfo()) # 站点过期检查 # siteEdate() @@ -130,7 +126,7 @@ def startPanelTask(): runPanelTask() time.sleep(1) except Exception as e: - print(str(e)) + print(mw.getTracebackInfo()) time.sleep(10) startPanelTask() @@ -164,6 +160,7 @@ def systemTask(): monitor.instance().run() time.sleep(5) except Exception as ex: + print(mw.getTracebackInfo()) time.sleep(30) systemTask() @@ -249,6 +246,7 @@ def startPHPVersion(version): if os.path.exists(cgi): return True except Exception as e: + print(mw.getTracebackInfo()) mw.writeLog('PHP守护程序', '自动修复异常:'+str(e)) return True @@ -326,31 +324,31 @@ def setDaemon(t): def run(): # # 系统监控 - sysTask = threading.Thread(target=systemTask) - sysTask = setDaemon(sysTask) - sysTask.start() + # sysTask = threading.Thread(target=systemTask) + # sysTask = setDaemon(sysTask) + # sysTask.start() # # PHP 502错误检查线程 - php502 = threading.Thread(target=check502Task) - php502 = setDaemon(php502) - php502.start() + # php502 = threading.Thread(target=check502Task) + # php502 = setDaemon(php502) + # php502.start() - # OpenResty Restart At Once Start - oraos = threading.Thread(target=openrestyRestartAtOnce) - oraos = setDaemon(oraos) - oraos.start() + # # OpenResty Restart At Once Start + # oraos = threading.Thread(target=openrestyRestartAtOnce) + # oraos = setDaemon(oraos) + # oraos.start() - # OpenResty Auto Restart Start - oar = threading.Thread(target=openrestyAutoRestart) - oar = setDaemon(oar) - oar.start() + # # OpenResty Auto Restart Start + # oar = threading.Thread(target=openrestyAutoRestart) + # oar = setDaemon(oar) + # oar.start() - # Panel Restart Start - rps = threading.Thread(target=restartPanelService) - rps = setDaemon(rps) - rps.start() + # # Panel Restart Start + # rps = threading.Thread(target=restartPanelService) + # rps = setDaemon(rps) + # rps.start() # 面板后台任务 startPanelTask() diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 2f551204a..fb01d65fe 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -57,6 +57,18 @@ def getTaskList( start = (page - 1) * size limit = str(start) + ',' + str(size) + field = 'id,name,type,start,end,status,add_time' + task_list = mw.M('tasks').where('', ()).field(field).limit(limit).order('id asc').select() + return task_list + +def getTaskPage( + status: int | None = 1, + page: int | None = 1, + size: int | None = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + field = 'id,name,type,start,end,status,add_time' task_list = mw.M('tasks').where('', ()).field(field).limit(limit).order('id asc').select() count = mw.M('tasks').where('', ()).count() From 6f232aa46e1ef511372006279a87c875fd5934f2 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 00:59:38 +0800 Subject: [PATCH 100/965] Update panel_task.py --- panel_task.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/panel_task.py b/panel_task.py index 8022b403a..da7f96d7b 100755 --- a/panel_task.py +++ b/panel_task.py @@ -37,6 +37,15 @@ def execShell(cmdstring, cwd=None, timeout=None, shell=True): cmd = cmdstring + ' > ' + g_log_file + ' 2>&1' return mw.execShell(cmd) +def writeLogs(data): + # 写输出日志 + try: + fp = open(g_log_file, 'w+') + fp.write(data) + fp.close() + except: + pass + def mw_async(f): def wrapper(*args, **kwargs): thr = threading.Thread(target=f, args=args, kwargs=kwargs) @@ -72,7 +81,6 @@ def downloadFile(url, filename): writeLogs(str(e)) return True - def downloadHook(count, blockSize, totalSize): # 下载文件进度回调 global pre @@ -83,17 +91,6 @@ def downloadHook(count, blockSize, totalSize): speed = {'total': totalSize, 'used': used, 'pre': pre1} writeLogs(json.dumps(speed)) - -def writeLogs(data): - # 写输出日志 - try: - fp = open(g_log_file, 'w+') - fp.write(data) - fp.close() - except: - pass - - def runPanelTask(): try: bash_list = thisdb.getTaskList(status=-1) From 910defe18b21bcdf5a7ee4be56f932df28d07852 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:03:37 +0800 Subject: [PATCH 101/965] update --- panel_task.py | 40 +++++++++++++++++++------------------- web/admin/task/__init__.py | 2 +- web/utils/task.py | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/panel_task.py b/panel_task.py index da7f96d7b..2ee2a3f6a 100755 --- a/panel_task.py +++ b/panel_task.py @@ -320,32 +320,32 @@ def setDaemon(t): return t def run(): - # # 系统监控 - # sysTask = threading.Thread(target=systemTask) - # sysTask = setDaemon(sysTask) - # sysTask.start() + # 系统监控 + sysTask = threading.Thread(target=systemTask) + sysTask = setDaemon(sysTask) + sysTask.start() - # # PHP 502错误检查线程 - # php502 = threading.Thread(target=check502Task) - # php502 = setDaemon(php502) - # php502.start() + # PHP 502错误检查线程 + php502 = threading.Thread(target=check502Task) + php502 = setDaemon(php502) + php502.start() - # # OpenResty Restart At Once Start - # oraos = threading.Thread(target=openrestyRestartAtOnce) - # oraos = setDaemon(oraos) - # oraos.start() + # OpenResty Restart At Once Start + oraos = threading.Thread(target=openrestyRestartAtOnce) + oraos = setDaemon(oraos) + oraos.start() - # # OpenResty Auto Restart Start - # oar = threading.Thread(target=openrestyAutoRestart) - # oar = setDaemon(oar) - # oar.start() + # OpenResty Auto Restart Start + oar = threading.Thread(target=openrestyAutoRestart) + oar = setDaemon(oar) + oar.start() - # # Panel Restart Start - # rps = threading.Thread(target=restartPanelService) - # rps = setDaemon(rps) - # rps.start() + # Panel Restart Start + rps = threading.Thread(target=restartPanelService) + rps = setDaemon(rps) + rps.start() # 面板后台任务 startPanelTask() diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index e06404184..64cd42ad1 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -35,7 +35,7 @@ def list(): p = request.form.get('p', '1') limit = request.form.get('limit', '10').strip() search = request.form.get('search', '').strip() - return MwTasks.getTaskList(int(p), int(limit)) + return MwTasks.getTaskPage(int(p), int(limit)) @blueprint.route('/get_exec_log', endpoint='get_exec_log', methods=['POST']) @panel_login_required diff --git a/web/utils/task.py b/web/utils/task.py index a069aa963..a6ddbd990 100644 --- a/web/utils/task.py +++ b/web/utils/task.py @@ -15,8 +15,8 @@ import time import core.mw as mw import thisdb -def getTaskList(page=1,size=10): - info = thisdb.getTaskList(page=page, size=size) +def getTaskPage(page=1,size=10): + info = thisdb.getTaskPage(page=page, size=size) rdata = {} rdata['data'] = info['list'] From ef7b680e37e7f389212069e4c1bd65ffd6894ec7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:12:04 +0800 Subject: [PATCH 102/965] update --- panel_task.py | 2 +- web/thisdb/tasks.py | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/panel_task.py b/panel_task.py index 2ee2a3f6a..bea5152da 100755 --- a/panel_task.py +++ b/panel_task.py @@ -99,6 +99,7 @@ def runPanelTask(): run_list = thisdb.getTaskList(status=0) for run_task in run_list: + print(run_task) start = int(time.time()) thisdb.setTaskData(run_task['id'], start=start) thisdb.setTaskStatus(run_task['id'], -1) @@ -157,7 +158,6 @@ def systemTask(): monitor.instance().run() time.sleep(5) except Exception as ex: - print(mw.getTracebackInfo()) time.sleep(30) systemTask() diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index fb01d65fe..179a525e0 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -10,6 +10,8 @@ import core.mw as mw +__FIELD = 'id,name,type,start,end,cmd,status,add_time' + def getTaskCount( status: int | None = -1 ) -> int: @@ -56,9 +58,7 @@ def getTaskList( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - - field = 'id,name,type,start,end,status,add_time' - task_list = mw.M('tasks').where('', ()).field(field).limit(limit).order('id asc').select() + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id asc').select() return task_list def getTaskPage( @@ -68,9 +68,7 @@ def getTaskPage( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - - field = 'id,name,type,start,end,status,add_time' - task_list = mw.M('tasks').where('', ()).field(field).limit(limit).order('id asc').select() + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id asc').select() count = mw.M('tasks').where('', ()).count() rdata = {} @@ -79,8 +77,7 @@ def getTaskPage( return rdata def getTaskFirstByRun() -> None: - field = 'id,name,type,start,end,status,add_time' - data = mw.M('tasks').where('status=?', (-1,)).field(field).order('id asc').find() + data = mw.M('tasks').where('status=?', (-1,)).field(__FIELD).order('id asc').find() if item is None: return None return data @@ -92,8 +89,7 @@ def getTaskRunList( start = (page - 1) * size limit = str(start) + ',' + str(size) - field = 'id,name,type,start,end,status,add_time' - task_list = mw.M('tasks').where('', ()).field(field).limit(limit).order('id desc').select() + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() count = mw.M('tasks').where('', ()).count() rdata = {} From 72c6dbabab36883830bdd1857770794721f92467 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:15:17 +0800 Subject: [PATCH 103/965] update --- web/admin/task/__init__.py | 2 +- web/thisdb/tasks.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index 64cd42ad1..68598414c 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -55,7 +55,7 @@ def get_task_speed(): if row is None: return mw.returnData(False, '当前没有任务队列在执行-3!') - task_logfile = thisdb.getPanelTaskLog() + task_logfile = mw.getPanelTaskLog() data = {} data['name'] = row['name'] diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 179a525e0..f7f1b64a9 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -78,7 +78,7 @@ def getTaskPage( def getTaskFirstByRun() -> None: data = mw.M('tasks').where('status=?', (-1,)).field(__FIELD).order('id asc').find() - if item is None: + if data is None: return None return data @@ -88,6 +88,15 @@ def getTaskRunList( ): start = (page - 1) * size limit = str(start) + ',' + str(size) + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() + return task_list + +def getTaskRunPage( + page: int | None = 1, + size: int | None = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() count = mw.M('tasks').where('', ()).count() From 6bf4371a2b3726b97ba959a4d7ec5a943d041801 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:15:40 +0800 Subject: [PATCH 104/965] Update panel_task.py --- panel_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index bea5152da..19274a646 100755 --- a/panel_task.py +++ b/panel_task.py @@ -99,7 +99,6 @@ def runPanelTask(): run_list = thisdb.getTaskList(status=0) for run_task in run_list: - print(run_task) start = int(time.time()) thisdb.setTaskData(run_task['id'], start=start) thisdb.setTaskStatus(run_task['id'], -1) @@ -113,6 +112,7 @@ def runPanelTask(): thisdb.setTaskStatus(run_task['id'], 1) except Exception as e: print(mw.getTracebackInfo()) + time.sleep(10) # 站点过期检查 # siteEdate() From 36bd8d8a1fef9975b366c78dbffdfd78d62100cc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:16:08 +0800 Subject: [PATCH 105/965] Update panel_task.py --- panel_task.py | 1 - 1 file changed, 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index 19274a646..e299fe77c 100755 --- a/panel_task.py +++ b/panel_task.py @@ -112,7 +112,6 @@ def runPanelTask(): thisdb.setTaskStatus(run_task['id'], 1) except Exception as e: print(mw.getTracebackInfo()) - time.sleep(10) # 站点过期检查 # siteEdate() From 8d2e70c31ed7efbe62685c43ef0cb1ae15ea97e6 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:16:17 +0800 Subject: [PATCH 106/965] Update panel_task.py --- panel_task.py | 1 - 1 file changed, 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index e299fe77c..933180432 100755 --- a/panel_task.py +++ b/panel_task.py @@ -123,7 +123,6 @@ def startPanelTask(): runPanelTask() time.sleep(1) except Exception as e: - print(mw.getTracebackInfo()) time.sleep(10) startPanelTask() From f217c07d129c55064e9492803c3595f4b66496c0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:19:09 +0800 Subject: [PATCH 107/965] Update tasks.py --- web/thisdb/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index f7f1b64a9..10a8f5150 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -68,7 +68,7 @@ def getTaskPage( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id asc').select() + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() count = mw.M('tasks').where('', ()).count() rdata = {} From fed4a4ae48c2b96c6599e97d3e9a63bf6e93d042 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:20:54 +0800 Subject: [PATCH 108/965] Update tasks.py --- web/thisdb/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 10a8f5150..f7f1b64a9 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -68,7 +68,7 @@ def getTaskPage( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id asc').select() count = mw.M('tasks').where('', ()).count() rdata = {} From 024dedabaff25ad49d4e85dc187bb05fc0316906 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:26:45 +0800 Subject: [PATCH 109/965] update --- web/admin/setup/sql/default.sql | 146 ++++++++++++++++++++++++++++++++ web/admin/setup/sql/system.sql | 41 +++++++++ web/thisdb/init.py | 3 +- web/utils/system/monitor.py | 2 +- 4 files changed, 189 insertions(+), 3 deletions(-) create mode 100755 web/admin/setup/sql/default.sql create mode 100755 web/admin/setup/sql/system.sql diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql new file mode 100755 index 000000000..ec3bc47d0 --- /dev/null +++ b/web/admin/setup/sql/default.sql @@ -0,0 +1,146 @@ + +CREATE TABLE IF NOT EXISTS `backup` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `type` INTEGER, + `name` TEXT, + `pid` INTEGER, + `filename` TEXT, + `size` INTEGER, + `add_time` TEXT +); + +CREATE TABLE IF NOT EXISTS `binding` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `pid` INTEGER, + `domain` TEXT, + `path` TEXT, + `port` INTEGER, + `add_time` TEXT +); + + +CREATE TABLE IF NOT EXISTS `crontab` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `type` TEXT, + `where1` TEXT, + `where_hour` INTEGER, + `where_minute` INTEGER, + `echo` TEXT, + `status` INTEGER DEFAULT '1', + `save` INTEGER DEFAULT '3', + `backup_to` TEXT DEFAULT 'off', + `sname` TEXT, + `sbody` TEXT, + 'stype' TEXT, + `url_address` TEXT, + `add_time` TEXT, + `update_time` TEXT +); + +CREATE TABLE IF NOT EXISTS `firewall` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `port` TEXT, + `protocol` TEXT DEFAULT 'tcp', + `ps` TEXT, + `add_time` TEXT, + `update_time` TEXT +); + +ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp'; + +INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`) VALUES +(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), +(2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00','0000-00-00 00:00:00'); + + + +CREATE TABLE IF NOT EXISTS `logs` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `type` TEXT, + `log` TEXT, + `uid` INTEGER DEFAULT '1', + `add_time` TEXT +); + +CREATE TABLE IF NOT EXISTS `sites` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `path` TEXT, + `status` TEXT, + `index` TEXT, + `type_id` INTEGER, + `ps` TEXT, + `edate` TEXT, + `ssl_effective_date` TEXT, + `ssl_expiration_date` TEXT, + `add_time` TEXT, + `update_time` TEXT +); + +CREATE TABLE IF NOT EXISTS `site_types` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT +); + +CREATE TABLE IF NOT EXISTS `domain` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `pid` INTEGER, + `name` TEXT, + `port` INTEGER, + `add_time` TEXT +); + +CREATE TABLE IF NOT EXISTS `users` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `password` TEXT, + `login_ip` TEXT, + `login_time` TEXT, + `phone` TEXT, + `email` TEXT, + `add_time` INTEGER, + `update_time` INTEGER +); + +CREATE TABLE IF NOT EXISTS `tasks` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `type` TEXT, + `start` INTEGER, + `end` INTEGER, + `cmd` TEXT, + `status` INTEGER, + `add_time` INTEGER +); + +CREATE TABLE IF NOT EXISTS `temp_login` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `token` REAL, + `salt` REAL, + `state` INTEGER, + `login_time` INTEGER, + `login_addr` REAL, + `logout_time` INTEGER, + `expire` INTEGER, + `add_time` INTEGER +); + +CREATE TABLE IF NOT EXISTS `panel` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `title` TEXT, + `url` TEXT, + `username` TEXT, + `password` TEXT, + `click` INTEGER, + `add_time` INTEGER +); + +CREATE TABLE IF NOT EXISTS `option` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `type` TEXT, + `value` TEXT +); + +CREATE UNIQUE INDEX name_idx ON option(name); diff --git a/web/admin/setup/sql/system.sql b/web/admin/setup/sql/system.sql new file mode 100755 index 000000000..75fe9557f --- /dev/null +++ b/web/admin/setup/sql/system.sql @@ -0,0 +1,41 @@ +CREATE TABLE IF NOT EXISTS `network` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `up` TEXT, + `down` TEXT, + `total_up` INTEGER, + `total_down` INTEGER, + `down_packets` INTEGER, + `up_packets` INTEGER, + `addtime` INTEGER +); +CREATE INDEX addtime_idx ON network(`addtime`); + +CREATE TABLE IF NOT EXISTS `cpuio` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `pro` TEXT, + `mem` TEXT, + `addtime` INTEGER +); +CREATE INDEX addtime_idx ON cpuio(`addtime`); + +CREATE TABLE IF NOT EXISTS `diskio` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `read_count` INTEGER, + `write_count` INTEGER, + `read_bytes` INTEGER, + `write_bytes` INTEGER, + `read_time` INTEGER, + `write_time` INTEGER, + `addtime` INTEGER +); +CREATE INDEX addtime_idx ON diskio(`addtime`); + +CREATE TABLE IF NOT EXISTS `load_average` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `pro` REAL, + `one` REAL, + `five` REAL, + `fifteen` REAL, + `addtime` INTEGER +); +CREATE INDEX addtime_idx ON load_average(`addtime`); \ No newline at end of file diff --git a/web/thisdb/init.py b/web/thisdb/init.py index a007d27e1..fac16d6bf 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -17,11 +17,10 @@ def initPanelData(): _dbfile = mw.getPanelDataDir() + '/panel.db' if os.path.exists(_dbfile): return True - sql_file = mw.getPanelDataDir() + '/sql/default.sql' + sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') csql = mw.readFile(sql_file) csql_list = csql.split(';') for index in range(len(csql_list)): - print(index) sql.execute(csql_list[index], ()) return True \ No newline at end of file diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py index 95a8d6e1c..801aaabfe 100644 --- a/web/utils/system/monitor.py +++ b/web/utils/system/monitor.py @@ -53,7 +53,7 @@ class monitor: def initDBFile(self): if os.path.exists(self._dbfile): return True - sql_file = mw.getPanelDataDir() + '/sql/system.sql' + sql_file = mw.getPanelDir() + '/web/admin/setup/sql/system.sql' sql = db.Sql().dbPos(mw.getPanelDataDir(),'system') csql = mw.readFile(sql_file) csql_list = csql.split(';') From 91327c50f0ad4cb6821a011c7c71c1eada63defe Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:34:27 +0800 Subject: [PATCH 110/965] Update install.sh --- plugins/mosquitto/install.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/mosquitto/install.sh b/plugins/mosquitto/install.sh index 6510720c6..37a795254 100755 --- a/plugins/mosquitto/install.sh +++ b/plugins/mosquitto/install.sh @@ -13,8 +13,6 @@ echo "use system: ${sysName}" # cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/mosquitto && bash install.sh install 2.0.18 # cd /www/mdserver-web/plugins/mosquitto && bash install.sh install 2.0.18 -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 Install_App() @@ -27,7 +25,7 @@ Install_App() useradd -g mosquitto mosquitto fi - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source if [ ! -f $serverPath/source/mosquitto-${VERSION}.tar.gz ];then @@ -55,7 +53,7 @@ Install_App() if [ -d $serverPath/mosquitto ];then echo "${VERSION}" > $serverPath/mosquitto/version.pl - echo '安装完成' > $install_tmp + echo '安装mosquitto完成' cd ${rootPath} && python3 ${rootPath}/plugins/mosquitto/index.py start @@ -77,7 +75,7 @@ Uninstall_App() fi rm -rf $serverPath/mosquitto - echo "uninstall mosquitto" > $install_tmp + echo "卸载mosquitto成功" } action=$1 From 4d4465060997e3b75021ae57c0d21ee5683a8478 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:35:39 +0800 Subject: [PATCH 111/965] Update install.sh --- plugins/docker/install.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/docker/install.sh b/plugins/docker/install.sh index 7b9bc7499..c0679a66e 100755 --- a/plugins/docker/install.sh +++ b/plugins/docker/install.sh @@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh uninstall 1.0 # cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh install 1.0 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 if [ -f ${rootPath}/bin/activate ];then @@ -46,7 +45,7 @@ Install_Docker() # exit 0 # fi - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source if [ ! -d $serverPath/docker ];then @@ -59,7 +58,7 @@ Install_Docker() if [ -d $serverPath/docker ];then echo "${VERSION}" > $serverPath/docker/version.pl - echo '安装完成' > $install_tmp + echo '安装Docker完成' cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py initd_install @@ -94,7 +93,7 @@ Uninstall_Docker() # docker-ce rm -rf $serverPath/docker - echo "Uninstall_Docker" > $install_tmp + echo "卸载Docker" } action=$1 From 804f7646d14b0c1feec8e174a847661c9073f95e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:39:07 +0800 Subject: [PATCH 112/965] update --- plugins/php-apt/index_php_apt.py | 7 +++++-- plugins/php-yum/index_php_yum.py | 7 +++++-- plugins/php/index_php.py | 8 +++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/php-apt/index_php_apt.py b/plugins/php-apt/index_php_apt.py index 97ade4c7d..0be394d49 100755 --- a/plugins/php-apt/index_php_apt.py +++ b/plugins/php-apt/index_php_apt.py @@ -11,8 +11,11 @@ import shutil # reload(sys) # sys.setdefaultencoding('utf8') -sys.path.append(os.getcwd() + "/class/core") -import mw +try: + sys.path.append(os.getcwd() + "/class/core") + import mw +except Exception as e: + import core.mw as mw if mw.isAppleSystem(): cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' diff --git a/plugins/php-yum/index_php_yum.py b/plugins/php-yum/index_php_yum.py index 0600580d6..5a421e423 100755 --- a/plugins/php-yum/index_php_yum.py +++ b/plugins/php-yum/index_php_yum.py @@ -11,8 +11,11 @@ import shutil # reload(sys) # sys.setdefaultencoding('utf8') -sys.path.append(os.getcwd() + "/class/core") -import mw +try: + sys.path.append(os.getcwd() + "/class/core") + import mw +except Exception as e: + import core.mw as mw if mw.isAppleSystem(): cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' diff --git a/plugins/php/index_php.py b/plugins/php/index_php.py index 5ffba3af6..bfd9bca31 100755 --- a/plugins/php/index_php.py +++ b/plugins/php/index_php.py @@ -10,9 +10,11 @@ import shutil # reload(sys) # sys.setdefaultencoding('utf8') - -sys.path.append(os.getcwd() + "/class/core") -import mw +try: + sys.path.append(os.getcwd() + "/class/core") + import mw +except Exception as e: + import core.mw as mw if mw.isAppleSystem(): cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' From 534af4f017b37711bbe2c081338c1ecbb429f8cc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:39:21 +0800 Subject: [PATCH 113/965] Update index_php_yum.py --- plugins/php-yum/index_php_yum.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/php-yum/index_php_yum.py b/plugins/php-yum/index_php_yum.py index 5a421e423..1a77fb329 100755 --- a/plugins/php-yum/index_php_yum.py +++ b/plugins/php-yum/index_php_yum.py @@ -17,12 +17,6 @@ try: except Exception as e: 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) - app_debug = False if mw.isAppleSystem(): app_debug = True From ec70d3e9555d750822a93c241f2e751bee071f09 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:42:09 +0800 Subject: [PATCH 114/965] Update firewall.js --- web/static/app/firewall.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/static/app/firewall.js b/web/static/app/firewall.js index 07f7230f1..cdd78f0d9 100755 --- a/web/static/app/firewall.js +++ b/web/static/app/firewall.js @@ -301,8 +301,8 @@ function showAccept(page,search) { " + 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 + "\ + " + data.data[i].add_time + "\ 删除\ "; } From f6c4f3b2ee149d9483100d68e5100b5bb7102579 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:44:04 +0800 Subject: [PATCH 115/965] Update mw.py --- web/core/mw.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/web/core/mw.py b/web/core/mw.py index 8b970cab6..7fcca6dd0 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -375,6 +375,14 @@ def getHostAddr(): return readFile(ip_text).strip() return '127.0.0.1' +def setMode(filename, mode): + # 设置文件权限 + if not os.path.exists(filename): + return False + mode = int(str(mode), 8) + os.chmod(filename, mode) + return True + def getSqitePrefix(): WIN = sys.platform.startswith('win') if WIN: # 如果是 Windows 系统,使用三个斜线 @@ -383,6 +391,26 @@ def getSqitePrefix(): prefix = 'sqlite:////' return prefix +def checkPort(port): + # 检查端口是否合法 + ports = ['21', '443', '888'] + if port in ports: + return False + intport = int(port) + if intport < 1 or intport > 65535: + return False + return True + +def getStrBetween(startStr, endStr, srcStr): + # 字符串取中间 + start = srcStr.find(startStr) + if start == -1: + return None + end = srcStr.find(endStr) + if end == -1: + return None + return srcStr[start + 1:end] + def getCpuType(): cpuType = '' if isAppleSystem(): From 0c9eae860f09845f162d2b836b74f3fe23b8c920 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 01:45:22 +0800 Subject: [PATCH 116/965] Update mw.py --- web/core/mw.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/web/core/mw.py b/web/core/mw.py index 7fcca6dd0..5dffe2e97 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -375,6 +375,49 @@ def getHostAddr(): return readFile(ip_text).strip() return '127.0.0.1' +def checkIp(ip): + # 检查是否为IPv4地址 + import re + p = re.compile(r'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$') + if p.match(ip): + return True + else: + return False + +def createLinuxUser(user, group): + execShell("groupadd {}".format(group)) + execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group)) + return True + + +def setOwn(filename, user, group=None): + if isAppleSystem(): + return True + + # 设置用户组 + if not os.path.exists(filename): + return False + from pwd import getpwnam + try: + user_info = getpwnam(user) + user = user_info.pw_uid + if group: + user_info = getpwnam(group) + group = user_info.pw_gid + except: + if user == 'www': + createLinuxUser(user) + # 如果指定用户或组不存在,则使用www + try: + user_info = getpwnam('www') + except: + createLinuxUser(user) + user_info = getpwnam('www') + user = user_info.pw_uid + group = user_info.pw_gid + os.chown(filename, user, group) + return True + def setMode(filename, mode): # 设置文件权限 if not os.path.exists(filename): From 2fe3ed54079a98969342b930721c5782276103da Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:28:06 +0800 Subject: [PATCH 117/965] update --- panel_task.py | 5 +++-- scripts/init.d/mw.tpl | 2 +- web/core/db.py | 8 ++++++-- web/thisdb/tasks.py | 6 +++--- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/panel_task.py b/panel_task.py index 933180432..ff94e5fcc 100755 --- a/panel_task.py +++ b/panel_task.py @@ -107,6 +107,7 @@ def runPanelTask(): downloadFile(argv[0], argv[1]) elif run_task['type'] == 'execshell': execShell(run_task['cmd']) + print(run_task) end = int(time.time()) thisdb.setTaskData(run_task['id'], end=end) thisdb.setTaskStatus(run_task['id'], 1) @@ -121,9 +122,9 @@ def startPanelTask(): try: while True: runPanelTask() - time.sleep(1) + time.sleep(5) except Exception as e: - time.sleep(10) + time.sleep(30) startPanelTask() # 网站到期处理 diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 79a566f59..722179492 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -345,7 +345,7 @@ mw_debug(){ if [ -d ${PANEL_DIR}/web ];then cd ${PANEL_DIR}/web fi - gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + gunicorn -b :$port -w 1 app:app } diff --git a/web/core/db.py b/web/core/db.py index 5962ac56a..27b97ae9e 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -56,6 +56,10 @@ class Sql(): self.__DB_CONN.text_factory = bytes return True + def debug(self, debug=False): + self.__debug = debug + return self + def autoTextFactory(self): if sys.version_info[0] == 3: self.__DB_CONN.text_factory = lambda x: str(x, encoding="utf-8", errors='ignore') @@ -341,8 +345,8 @@ class Sql(): opt = opt[0:len(opt) - 1] sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE - # import mw - # mw.writeFile('/tmp/test.pl', sql) + if self.__debug: + print(sql, param) # 处理拼接WHERE与UPDATE参数 tmp = list(param) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index f7f1b64a9..5eb895542 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -58,7 +58,7 @@ def getTaskList( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id asc').select() + task_list = mw.M('tasks').where('status=?', (status,)).field(__FIELD).limit(limit).order('id asc').select() return task_list def getTaskPage( @@ -107,10 +107,10 @@ def getTaskRunPage( return rdata -def setTaskStatus(id, +def setTaskStatus(task_id, status: int | None = 0 ): - mw.M('tasks').where('id=?',(id,)).update({'status':status}) + mw.M('tasks').where('id=?',(task_id,)).update({'status':status}) return True def setTaskData(id, From a0a4292cb873e9843b696639a8659e238c738f02 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:28:22 +0800 Subject: [PATCH 118/965] Update panel_task.py --- panel_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index ff94e5fcc..6b410dc84 100755 --- a/panel_task.py +++ b/panel_task.py @@ -107,7 +107,7 @@ def runPanelTask(): downloadFile(argv[0], argv[1]) elif run_task['type'] == 'execshell': execShell(run_task['cmd']) - print(run_task) + # print(run_task) end = int(time.time()) thisdb.setTaskData(run_task['id'], end=end) thisdb.setTaskStatus(run_task['id'], 1) From 1ec2c333841f1d93a112daf2a40ab8d3f5b55a4a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:30:46 +0800 Subject: [PATCH 119/965] Update tasks.py --- web/thisdb/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 5eb895542..821c6121d 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -68,7 +68,7 @@ def getTaskPage( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id asc').select() + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() count = mw.M('tasks').where('', ()).count() rdata = {} From e477a69b983f9359be345d69caa2163ba4743666 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:33:23 +0800 Subject: [PATCH 120/965] Update tasks.py --- web/thisdb/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 821c6121d..3f8a9fa54 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -88,7 +88,7 @@ def getTaskRunList( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() + task_list = mw.M('tasks').where('status!=?', (1)).field(__FIELD).limit(limit).order('id asc').select() return task_list def getTaskRunPage( From 1a3972fa21126537a0374671809733ef72432505 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:35:16 +0800 Subject: [PATCH 121/965] Update tasks.py --- web/thisdb/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 3f8a9fa54..c27466cb5 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -88,7 +88,7 @@ def getTaskRunList( ): start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('status!=?', (1)).field(__FIELD).limit(limit).order('id asc').select() + task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id asc').select() return task_list def getTaskRunPage( From a09a9d5889e5cea0edb1e2acbb98e99d3a9240d9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:35:36 +0800 Subject: [PATCH 122/965] Update tasks.py --- web/thisdb/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index c27466cb5..0533ae66e 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -98,8 +98,8 @@ def getTaskRunPage( start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() - count = mw.M('tasks').where('', ()).count() + task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('tasks').where('status!=?', (1,)).count() rdata = {} rdata['list'] = task_list From a4079c09ef3ff8650f3791178a5075578cfd4ec7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:37:39 +0800 Subject: [PATCH 123/965] Update tasks.py --- web/thisdb/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 0533ae66e..44c4c1722 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -98,7 +98,7 @@ def getTaskRunPage( start = (page - 1) * size limit = str(start) + ',' + str(size) - task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id desc').select() + task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id asc').select() count = mw.M('tasks').where('status!=?', (1,)).count() rdata = {} From 9ce92eb69e8b634fbfce551a48f659082257f81a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:56:12 +0800 Subject: [PATCH 124/965] update --- panel_task.py | 1 - web/admin/firewall/__init__.py | 10 ++++++ web/core/mw.py | 28 +++++++++------- web/utils/firewall.py | 59 ++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 12 deletions(-) diff --git a/panel_task.py b/panel_task.py index 6b410dc84..d60b4bc97 100755 --- a/panel_task.py +++ b/panel_task.py @@ -107,7 +107,6 @@ def runPanelTask(): downloadFile(argv[0], argv[1]) elif run_task['type'] == 'execshell': execShell(run_task['cmd']) - # print(run_task) end = int(time.time()) thisdb.setTaskData(run_task['id'], end=end) thisdb.setTaskStatus(run_task['id'], 1) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 5002551a4..435e706ae 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -66,6 +66,16 @@ def add_accept_port(): return MwFirewall.instance().addAcceptPort(port, ps, stype, protocol=protocol) +# 添加放行端口 +@blueprint.route('/del_accept_port', endpoint='del_accept_port', methods=['POST']) +@panel_login_required +def del_accept_port(): + port = request.form.get('port', '').strip() + firewall_id = request.form.get('id', '').strip() + protocol = request.form.get('protocol', '').strip() + return MwFirewall.instance().delAcceptPort(firewall_id, port, protocol=protocol) + + diff --git a/web/core/mw.py b/web/core/mw.py index 5dffe2e97..87574b7b0 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -87,28 +87,34 @@ def getMWLogs(): def getPanelTmp(): return getPanelDir() + '/tmp' -def getPanelTaskLog(): - return getMWLogs() + '/panel_task.log' - - def getServerDir(): return getFatherDir() + '/server' def getLogsDir(): return getFatherDir() + '/wwwlogs' +def getRecycleBinDir(): + rb_dir = getFatherDir() + '/recycle_bin' + if not os.path.exists(rb_dir): + os.system('mkdir -p ' + rb_dir) + return rb_dir + +def getPanelTaskLog(): + return getMWLogs() + '/panel_task.log' + def getWwwDir(): file = getPanelDir() + '/data/site.pl' if os.path.exists(file): return readFile(file).strip() return getFatherDir() + '/wwwroot' - -def getRecycleBinDir(): - rb_dir = getFatherDir() + '/recycle_bin' - if not os.path.exists(rb_dir): - os.system('mkdir -p ' + rb_dir) - return rb_dir + +def getPanelPort(): + port_file = mw.getPanelDir()+'/data/port.pl' + port = mw.readFile(port_file).strip() + if not port: + return 7200 + return int(port) def getRandomString(length): # 取随机字符串 @@ -417,7 +423,7 @@ def setOwn(filename, user, group=None): group = user_info.pw_gid os.chown(filename, user, group) return True - + def setMode(filename, mode): # 设置文件权限 if not os.path.exists(filename): diff --git a/web/utils/firewall.py b/web/utils/firewall.py index fcb48cca3..0d32abdeb 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -246,5 +246,64 @@ class Firewall(object): mw.writeLog("防火墙管理", msg) return mw.returnData(True, msg) + def delAcceptPort(self, firewall_id, port, + protocol: str | None ='tcp' + ): + panel_port = mw.getPanelPort() + + if(port == panel_port): + return mw.returnData(False, '失败,不能删除当前面板端口!') + try: + self.delAcceptPortCmd(port, protocol) + mw.M('firewall').where("id=?", (firewall_id,)).delete() + return mw.returnData(True, '删除成功!') + except Exception as e: + return mw.returnData(False, '删除失败!:' + str(e)) + + def delAcceptPortCmd(self, port, + protocol: str | None ='tcp' + ): + if self.__isUfw: + if protocol == 'tcp': + mw.execShell('ufw delete allow ' + port + '/tcp') + if protocol == 'udp': + mw.execShell('ufw delete allow ' + port + '/udp') + if protocol == 'tcp/udp': + mw.execShell('ufw delete allow ' + port + '/tcp') + mw.execShell('ufw delete allow ' + port + '/udp') + elif self.__isFirewalld: + port = port.replace(':', '-') + if protocol == 'tcp': + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') + if protocol == 'udp': + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') + if protocol == 'tcp/udp': + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') + elif self.__isIptables: + if protocol == 'tcp': + mw.execShell( + 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') + if protocol == 'udp': + mw.execShell( + 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') + if protocol == 'tcp/udp': + mw.execShell( + 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') + mw.execShell( + 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') + else: + pass + + mw.M('firewall').where("port=?", (port,)).delete() + msg = mw.getInfo('删除防火墙放行端口[{1}][{2}]成功!', (port, protocol,)) + mw.writeLog("防火墙管理", msg) + self.reload() + return True + From f9b7526007e511482e52022184fa355a795dd204 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 02:59:41 +0800 Subject: [PATCH 125/965] Update mw.py --- web/core/mw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 87574b7b0..d8773b439 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -110,8 +110,8 @@ def getWwwDir(): def getPanelPort(): - port_file = mw.getPanelDir()+'/data/port.pl' - port = mw.readFile(port_file).strip() + port_file = getPanelDir()+'/data/port.pl' + port = readFile(port_file).strip() if not port: return 7200 return int(port) From d83e7b69058ccb9111019bb33629a464cd64f11b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 03:00:02 +0800 Subject: [PATCH 126/965] Update firewall.py --- web/utils/firewall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 0d32abdeb..9b39ac475 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -251,7 +251,7 @@ class Firewall(object): ): panel_port = mw.getPanelPort() - if(port == panel_port): + if(int(port) == int(panel_port)): return mw.returnData(False, '失败,不能删除当前面板端口!') try: self.delAcceptPortCmd(port, protocol) From 82ee20b1a9ba6d7e5774418b59784bd1039d5f0d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 5 Nov 2024 03:29:36 +0800 Subject: [PATCH 127/965] update --- plugins/openresty/index.html | 2 +- plugins/openresty/index.py | 38 +++++++++--- web/core/mw.py | 108 ++++++++++++++++++++++++++++++++++- 3 files changed, 137 insertions(+), 11 deletions(-) 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 @@
    -
    +

    服务

    diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py index 217634a6f..778324890 100755 --- a/plugins/openresty/index.py +++ b/plugins/openresty/index.py @@ -8,8 +8,12 @@ import threading import subprocess import re -sys.path.append(os.getcwd() + "/class/core") -import mw + +web_dir = os.getcwd() + "/web" +sys.path.append(web_dir) +os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -41,18 +45,19 @@ def getInitDFile(): def getArgs(): args = sys.argv[2:] + # print(args) tmp = {} args_len = len(args) if args_len == 1: t = args[0].strip('{').strip('}') - t = t.split(':') + t = t.split(':',2) tmp[t[0]] = t[1] elif args_len > 1: for i in range(len(args)): - t = args[i].split(':') + t = args[i].split(':',2) tmp[t[0]] = t[1] - + # print(tmp) return tmp @@ -398,16 +403,25 @@ def initdUinstall(): mw.execShell('systemctl disable openresty') return 'ok' +def getNgxStatusPort(): + ngx_status_file = mw.getServerDir() + '/web_conf/nginx/vhost/0.nginx_status.conf' + content = mw.readFile(ngx_status_file) + rep = r'listen\s*(.*);' + tmp = re.search(rep, content) + port = tmp.groups()[0].strip() + return port + def runInfo(): op_status = status() if op_status == 'stop': return mw.returnJson(False, "未启动!") + port = getNgxStatusPort() # 取Openresty负载状态 try: - url = 'http://127.0.0.1/nginx_status' - result = mw.httpGet(url, timeout=1) + url = 'http://127.0.0.1:%s/nginx_status' % port + result = mw.httpGet(url, timeout=3) tmp = result.split() data = {} data['active'] = tmp[2] @@ -419,8 +433,7 @@ def runInfo(): data['Waiting'] = tmp[15] return mw.getJson(data) except Exception as e: - - url = 'http://' + mw.getHostAddr() + '/nginx_status' + url = 'http://' + mw.getHostAddr() + ':%s/nginx_status' % port result = mw.httpGet(url) tmp = result.split() data = {} @@ -550,6 +563,13 @@ def installPreInspection(): if __name__ == "__main__": + version_pl = getServerDir() + "/version.pl" + if not os.path.exists(version_pl): + print('error') + exit(-1) + version = mw.readFile(version_pl) + + func = sys.argv[1] if func == 'status': print(status()) diff --git a/web/core/mw.py b/web/core/mw.py index d8773b439..8ec077999 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -199,7 +199,7 @@ def readFile(filename): fp.close() return fBody except Exception as e: - # print(e) + print(e) return False def writeFile(filename, content, mode='w+'): @@ -764,6 +764,112 @@ def deDoubleCrypt(key, strings): writeFileLog(getTracebackInfo()) return strings +# ------------------------------ network start ----------------------------- + +def HttpGet(url, timeout=10): + """ + 发送GET请求 + @url 被请求的URL地址(必需) + @timeout 超时时间默认60秒 + return string + """ + if sys.version_info[0] == 2: + try: + import urllib2 + import ssl + if sys.version_info[0] == 2: + reload(urllib2) + reload(ssl) + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + response = urllib2.urlopen(url, timeout=timeout) + return response.read() + except Exception as ex: + return str(ex) + else: + try: + import urllib.request + import ssl + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + response = urllib.request.urlopen(url, timeout=timeout) + result = response.read() + if type(result) == bytes: + result = result.decode('utf-8') + return result + except Exception as ex: + return str(ex) + + +def HttpGet2(url, timeout): + import urllib.request + + try: + import ssl + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + req = urllib.request.urlopen(url, timeout=timeout) + result = req.read().decode('utf-8') + return result + + except Exception as e: + return str(e) + + +def httpGet(url, timeout=10): + return HttpGet2(url, timeout) + + +def HttpPost(url, data, timeout=10): + """ + 发送POST请求 + @url 被请求的URL地址(必需) + @data POST参数,可以是字符串或字典(必需) + @timeout 超时时间默认60秒 + return string + """ + if sys.version_info[0] == 2: + try: + import urllib + import urllib2 + import ssl + ssl._create_default_https_context = ssl._create_unverified_context + data = urllib.urlencode(data) + req = urllib2.Request(url, data) + response = urllib2.urlopen(req, timeout=timeout) + return response.read() + except Exception as ex: + return str(ex) + else: + try: + import urllib.request + import ssl + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + data = urllib.parse.urlencode(data).encode('utf-8') + req = urllib.request.Request(url, data) + response = urllib.request.urlopen(req, timeout=timeout) + result = response.read() + if type(result) == bytes: + result = result.decode('utf-8') + return result + except Exception as ex: + return str(ex) + + +def httpPost(url, data, timeout=10): + return HttpPost(url, data, timeout) + +# ------------------------------ network end ----------------------------- + # ------------------------------ panel start ----------------------------- def isRestart(): From bcfdd2216138268a7cd54a86bebc28ed9a1e5f15 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 6 Nov 2024 02:08:35 +0800 Subject: [PATCH 128/965] update --- new_cli.sh | 8 ++- panel_tools.py | 7 +- web/admin/dashboard/login.py | 46 ++++++++++--- web/admin/setting/secondary_verifiy.py | 44 +++++++++---- web/admin/setup/option.py | 3 + web/admin/site/__init__.py | 45 ++++++++++++- web/core/db.py | 2 + web/core/mw.py | 13 ++++ web/static/app/config.js | 4 +- web/templates/default/login.html | 2 +- web/templates/default/setting.html | 6 +- web/thisdb/user.py | 15 +++-- web/utils/config.py | 4 ++ web/utils/site.py | 91 +++++++++++++++----------- web/utils/system/main.py | 9 +-- 15 files changed, 217 insertions(+), 82 deletions(-) diff --git a/new_cli.sh b/new_cli.sh index 4d6a96388..77c21ddf6 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -51,7 +51,7 @@ mw_start_debug(){ fi python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & - port=7200 + port=7200 if [ -f /www/server/mdserver-web/data/port.pl ];then port=$(cat /www/server/mdserver-web/data/port.pl) fi @@ -64,7 +64,11 @@ mw_start_debug(){ } mw_start_panel(){ - cd ${DIR}/web && gunicorn -b :7200 -w 1 app:app + port=7200 + if [ -f ${DIR}/data/port.pl ];then + port=$(cat ${DIR}/data/port.pl) + fi + cd ${DIR}/web && gunicorn -b :${port} -w 1 app:app } mw_start_bgtask(){ diff --git a/panel_tools.py b/panel_tools.py index 45af39793..cd2e3734f 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -158,9 +158,10 @@ def mwcli(mw_input=0): open_ssh_port() print("|-已开启!") elif mw_input == 26: - auth_secret = 'data/auth_secret.pl' - if os.path.exists(auth_secret): - os.remove(auth_secret) + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + two_step_verification['open'] = False + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) print("|-关闭二次验证成功!") else: print("|-二次验证已关闭!") diff --git a/web/admin/dashboard/login.py b/web/admin/dashboard/login.py index 7f1abe8eb..746560ce2 100644 --- a/web/admin/dashboard/login.py +++ b/web/admin/dashboard/login.py @@ -20,8 +20,6 @@ from flask import request,g from admin.common import isLogined from admin.user_login_check import panel_login_required from admin import cache,session -from admin import model -from admin.model import db,TempLogin,Users import core.mw as mw import thisdb @@ -63,7 +61,7 @@ def login_temp_user(token): stime = int(time.time()) - tmp_data = model.getTempLoginByToken(token) + tmp_data = thisdb.getTempLoginByToken(token) if not tmp_data: setErrorNum(skey) return '验证失败!' @@ -73,12 +71,10 @@ def login_temp_user(token): return "过期" user_data = thisdb.getUserById(1) - login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT')) mw.writeLog('用户临时登录', "登录成功,帐号:{1},登录IP:{2}",(user_data['name'], login_addr)) - TempLogin.query.filter(TempLogin.id==tmp_data['id']).update({"login_time": stime, 'state': 1, 'login_addr': login_addr}) - db.session.commit() + mw.M('temp_login').where('id=?',(tmp_data['id'],)).update({"login_time": stime, 'state': 1, 'login_addr': login_addr}) session['login'] = True session['username'] = user_data['name'] @@ -105,11 +101,11 @@ def login(): session['login'] = False session['overdue'] = 0 - db_path = model.getOption('admin_path') + db_path = thisdb.getOption('admin_path') if db_path == '': return render_template('default/login.html') else: - unauthorized_status = model.getOption('unauthorized_status') + unauthorized_status = thisdb.getOption('unauthorized_status') if unauthorized_status == '0': return render_template('default/path.html') return Response(status=int(unauthorized_status)) @@ -134,6 +130,32 @@ def check_login(): return mw.returnData(True,'已登录') return mw.returnData(False,'未登录') +@blueprint.route("/verify_login", methods=['POST']) +def verifyLogin(): + import pyotp + + username = request.form.get('username', '').strip() + password = request.form.get('password', '').strip() + password = mw.md5(password) + + info = thisdb.getUserByRoot() + if info['name'] != username or info['password'] != password: + return mw.returnJson(-1, "密码错误?") + + auth = request.form.get('auth', '').strip() + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + sec = mw.deDoubleCrypt('mdserver-web', two_step_verification['secret']) + totp = pyotp.TOTP(sec) + if totp.verify(auth): + session['login'] = True + session['username'] = info['name'] + session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 + + thisdb.updateUserLoginTime() + return mw.returnData(1, '二次验证成功!') + return mw.returnData(-1, '二次验证失败!') + # 执行登录操作 @blueprint.route('/do_login', endpoint='do_login', methods=['POST']) def do_login(): @@ -181,9 +203,15 @@ def do_login(): mw.writeLog('用户登录', mw.getInfo(msg)) return mw.returnData(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))) + cache.delete('login_cache_limit') + # 二步验证密钥 + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + return mw.returnData(2, '需要两步验证!') session['login'] = True session['username'] = info['name'] session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - + + thisdb.updateUserLoginTime() return mw.returnJson(1, '登录成功,正在跳转...') diff --git a/web/admin/setting/secondary_verifiy.py b/web/admin/setting/secondary_verifiy.py index 562383706..2e170bf56 100644 --- a/web/admin/setting/secondary_verifiy.py +++ b/web/admin/setting/secondary_verifiy.py @@ -25,30 +25,48 @@ import core.mw as mw import utils.config as utils_config from .setting import blueprint +import thisdb - +# 获取二次验证信息 @blueprint.route('/get_auth_secret', endpoint='get_auth_secret', methods=['POST']) @panel_login_required def get_auth_secret(): - reset = request.form.get('reset', '') - __file = mw.getCommonFile() - import pyotp - auth = __file['auth_secret'] + + reset = request.form.get('reset', '') tag = 'mdserver-web' - if os.path.exists(auth) and reset != '1': - content = mw.readFile(auth) - sec = mw.deDoubleCrypt(tag,content) + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + + if 'secret' in two_step_verification and reset != '1': + secret = mw.deDoubleCrypt(tag, two_step_verification['secret']) else: - sec = pyotp.random_base32() - crypt_data = mw.enDoubleCrypt(tag, sec) - mw.writeFile(auth, crypt_data) + secret = pyotp.random_base32() + crypt_data = mw.enDoubleCrypt(tag, secret) + two_step_verification['secret'] = crypt_data + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) ip = mw.getHostAddr() - url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name=tag) + url = pyotp.totp.TOTP(secret).provisioning_uri(name=ip, issuer_name=tag) rdata = {} - rdata['secret'] = sec + rdata['secret'] = secret rdata['url'] = url return mw.returnData(True, '设置成功!', rdata) + + +# 设置二次验证,加强安全登录 +@blueprint.route('/set_auth_secret', endpoint='set_auth_secret', methods=['POST']) +@panel_login_required +def set_auth_secret(): + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + two_step_verification['open'] = False + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) + return mw.returnData(True, '关闭成功!', 0) + else: + two_step_verification['open'] = True + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) + return mw.returnData(True, '开启成功!', 1) + + \ No newline at end of file diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index da08bedd0..9a433961e 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -32,6 +32,9 @@ def init_option(): # basic auth 配置 thisdb.setOption('basic_auth', json.dumps({'open':False})) + # 二步验证|默认关闭 + thisdb.setOption('two_step_verification', json.dumps({'open':False})) + # 开启后台任务 # model.setOption('run_bg_task', 'close') diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 6036672f2..3bdf5979d 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -26,7 +26,7 @@ blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../ def index(): return render_template('site.html') -# 插件列表 +# 站点列表 @blueprint.route('/list', endpoint='list', methods=['GET','POST']) @panel_login_required def list(): @@ -42,11 +42,54 @@ def list(): data['page'] = mw.getPage({'count':info['count'],'tojs':'getWeb','p':p, 'row':limit}) return data +#添加站点 +@blueprint.route('/add', endpoint='add',methods=['POST']) +@panel_login_required +def add(): + 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 [] + +# 获取站点类型 @blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) @panel_login_required def get_site_types(): return [] +# 获取站点根目录 +@blueprint.route('/get_root_dir', endpoint='get_root_dir',methods=['POST']) +@panel_login_required +def get_root_dir(): + data = {} + data['dir'] = mw.getWwwDir() + return data + +# 检查OpenResty安装/启动状态 +@blueprint.route('/check_web_status', endpoint='check_web_status',methods=['POST']) +@panel_login_required +def check_web_status(): + ''' + 创建站点检查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.returnData(False, '请启动OpenResty服务!') + return mw.returnData(True, 'OK') + +# 获取PHP版本 +@blueprint.route('/get_php_version', endpoint='get_php_version',methods=['POST']) +@panel_login_required +def get_php_version(): + return site.getPhpVersion() + + @blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) @panel_login_required def get_cli_php_version(): diff --git a/web/core/db.py b/web/core/db.py index 27b97ae9e..6550f77c8 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -39,6 +39,8 @@ class Sql(): __OPT_FIELD = "*" # field条件 __OPT_PARAM = () # where值 + __debug = False + def __init__(self): self.__DB_FILE = getPanelDir()+'/data/panel.db' diff --git a/web/core/mw.py b/web/core/mw.py index 8ec077999..603456371 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -909,6 +909,19 @@ def panelCmd(method): # ------------------------------ panel end ----------------------------- # ------------------------------ openresty start ----------------------------- + +def checkWebConfig(): + op_dir = getServerDir() + '/openresty/nginx' + # "ulimit -n 10240 && " + + cmd = op_dir + "/sbin/nginx -t -c " + op_dir + "/conf/nginx.conf" + result = execShell(cmd) + searchStr = 'test is successful' + if result[1].find(searchStr) == -1: + msg = getInfo('配置文件错误: {1}', (result[1],)) + writeLog("软件管理", msg) + return result[1] + return True + def restartWeb(): return opWeb("reload") diff --git a/web/static/app/config.js b/web/static/app/config.js index 066a16dbe..542437619 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -1236,13 +1236,13 @@ function setTempAccess(){ }); } -//二次验证 +//二步验证 function setAuthBind(){ $.post('/setting/get_auth_secret', {}, function(rdata){ console.log(rdata); var tip = layer.open({ area: ['500px', '355px'], - title: '二次验证设置', + title: '二步验证设置', closeBtn:1, shift: 0, type: 1, diff --git a/web/templates/default/login.html b/web/templates/default/login.html index fb471f309..c361e6c3c 100755 --- a/web/templates/default/login.html +++ b/web/templates/default/login.html @@ -231,7 +231,7 @@ $('#login-button').click(function(){ layer.prompt({ formType: 3, value: '', - title: '二次验证', + title: '二步验证', }, function(value, index, elem){ $.post('/verify_login',{'auth':value,'username':username,'password':password},function(vdata){ if (vdata.status < 0){ diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index c001f7375..588d1eeb0 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -149,11 +149,11 @@

    - 二次验证 - + 二步验证 + - 二次验证,加强安全登录 + 二步验证,加强安全登录

    diff --git a/web/thisdb/user.py b/web/thisdb/user.py index 519a3476c..ac94b222b 100644 --- a/web/thisdb/user.py +++ b/web/thisdb/user.py @@ -10,9 +10,11 @@ import core.mw as mw +__field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' + # 初始化用户信息 def initAdminUser(): - data = mw.M('users').field('id').where('id=?', (1,)).find() + data = mw.M('users').field(__field).where('id=?', (1,)).find() if data is None: name = mw.getRandomString(8).lower() password = mw.getRandomString(8).lower() @@ -39,8 +41,7 @@ def getUserByName(name, ''' 获取用户信息通过用户名 ''' - users_field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' - data = mw.M('users').field(users_field).where('name=?', (name,)).find() + data = mw.M('users').field(__field).where('name=?', (name,)).find() if data is None: return None row = {} @@ -60,8 +61,7 @@ def getUserById(id, ''' 获取用户信息通过用户名 ''' - users_field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' - data = mw.M('users').field(users_field).where('id=?', (1,)).find() + data = mw.M('users').field(__field).where('id=?', (1,)).find() if data is None: return None row = {} @@ -83,6 +83,11 @@ def getUserByRoot() -> None: ''' return getUserById(1) +def updateUserLoginTime(): + now_time = mw.formatDate() + mw.M('users').field(__field).where('id=?', (1,)).update({'login_time',now_time}) + return True + def setUserByRoot( name: str | None = None, password: str | None = None, diff --git a/web/utils/config.py b/web/utils/config.py index 878dc74d3..89ffabb85 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -62,10 +62,14 @@ def getGlobalVar(): else: data['ipv6'] = '' + # 获取ROOT用户名 + data['username'] = mw.M('users').where("id=?", (1,)).getField('name') + # 获取未认证状态信息 unauthorized_status = thisdb.getOption('unauthorized_status', default='0') data['unauthorized_status'] = getUnauthStatus(code=unauthorized_status) data['basic_auth'] = thisdb.getOptionByJson('basic_auth', default={'open':False}) + data['two_step_verification'] = thisdb.getOptionByJson('two_step_verification', default={'open':False}) # 服务器时间 sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"' diff --git a/web/utils/site.py b/web/utils/site.py index a9a5b0c24..0d76ec0ff 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -18,43 +18,56 @@ from admin import model import core.mw as mw -def getPhpVersion(): - 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'] = '纯静态' + +class sites(object): + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(sites, "_instance"): + with sites._instance_lock: + if not hasattr(sites, "_instance"): + sites._instance = sites(*args, **kwargs) + return sites._instance + + + 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) - - # 标准判断 - 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 \ No newline at end of file + return data \ No newline at end of file diff --git a/web/utils/system/main.py b/web/utils/system/main.py index f2a29203b..8ace24d77 100644 --- a/web/utils/system/main.py +++ b/web/utils/system/main.py @@ -105,12 +105,13 @@ def getSystemVersion(): def getBootTime(): # 取系统启动时间 - uptime = mw.readFile('/proc/uptime') - if uptime == False: + if os.path.exists('/proc/uptime'): + uptime = mw.readFile('/proc/uptime') + run_time = uptime.split()[0] + else: start_time = psutil.boot_time() run_time = time.time() - start_time - else: - run_time = uptime.split()[0] + tStr = float(run_time) min = tStr / 60 hours = min / 60 From accdb5834d8be41a5d665efeb11c577ff33f5571 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 6 Nov 2024 20:58:22 +0800 Subject: [PATCH 129/965] up --- web/admin/setting/temp_login.py | 55 ++---- web/admin/setup/sql/default.sql | 4 +- web/admin/site/__init__.py | 77 ++++---- web/core/mw.py | 31 ++++ web/static/app/site.js | 4 +- web/thisdb/__init__.py | 4 + web/thisdb/binding.py | 37 ++++ web/thisdb/domain.py | 30 ++++ web/thisdb/sites.py | 43 ++++- web/thisdb/temp_login.py | 50 +++++- web/thisdb/user.py | 2 +- web/utils/site.py | 303 +++++++++++++++++++++++++++++++- 12 files changed, 556 insertions(+), 84 deletions(-) create mode 100644 web/thisdb/binding.py create mode 100644 web/thisdb/domain.py diff --git a/web/admin/setting/temp_login.py b/web/admin/setting/temp_login.py index 51f12a268..95156b579 100644 --- a/web/admin/setting/temp_login.py +++ b/web/admin/setting/temp_login.py @@ -23,6 +23,7 @@ from admin.user_login_check import panel_login_required import core.mw as mw import utils.config as utils_config +import thisdb from .setting import blueprint @@ -34,24 +35,11 @@ def get_temp_login(): p = request.form.get('page', '1').strip() tojs = request.form.get('tojs', '').strip() - count = TempLogin.query.filter_by().count() - pagination = TempLogin.query.filter_by().order_by(TempLogin.expire.desc()).paginate(page=int(p), per_page=int(limit)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['salt'] = item.salt - t['state'] = item.state - t['expire'] = item.expire - t['login_time'] = item.login_time - t['login_addr'] = item.login_addr - t['add_time'] = item.add_time - rows.append(t) + info = thisdb.getTempLoginPage(int(p), int(limit)) data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'setTempAccessReq','p':p,'row':limit}) + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':'setTempAccessReq','p':p,'row':limit}) return data @blueprint.route('/set_temp_login', endpoint='set_temp_login', methods=['POST']) @@ -59,38 +47,27 @@ def get_temp_login(): def set_temp_login(): # if 'tmp_login_expire' in session: # return mw.returnData(False, '没有权限') + + + thisdb.clearTempLogin() + start_time = int(time.time()) - model.clearTempLogin() + expire = start_time+3600 + + rand_str = mw.getRandomString(48) + token = mw.md5(rand_str) - - token = mw.getRandomString(48) - salt = mw.getRandomString(12) - - expire=start_time+3600 - make_token = mw.md5(token + salt) - add_tl = TempLogin( - token=make_token, - salt=salt, - state=0, - login_time=0, - login_addr='', - expire=expire, - add_time=start_time) - r = db.session.add(add_tl) - db.session.commit() - - if r is None: + r = thisdb.addTempLogin(token, expire) + if r > 0: mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format(mw.formatDate(times=expire))) - return {'status': True, 'msg': "临时连接已生成", 'token': make_token, 'expire': expire} + return {'status': True, 'msg': "临时连接已生成", 'token': token, 'expire': expire} return mw.returnData(False, '连接生成失败') @blueprint.route('/remove_temp_login', endpoint='remove_temp_login', methods=['POST']) @panel_login_required def remove_temp_login(): tl_id = request.form.get('id', '10').strip() - - r = TempLogin.query.filter(TempLogin.id == tl_id).delete() - db.session.commit() + r = thisdb.deleteTempLoginById(tl_id) if r > 0: mw.writeLog('面板设置', '删除临时登录连接') return mw.returnData(True, '删除成功') diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index ec3bc47d0..794ede180 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -12,9 +12,9 @@ CREATE TABLE IF NOT EXISTS `backup` ( CREATE TABLE IF NOT EXISTS `binding` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `pid` INTEGER, + `port` INTEGER, `domain` TEXT, `path` TEXT, - `port` INTEGER, `add_time` TEXT ); @@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS `firewall` ( ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp'; -INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`) VALUES +INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`, `update_time`) VALUES (1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), (2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00','0000-00-00 00:00:00'); diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 3bdf5979d..7752645d5 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -17,6 +17,7 @@ from admin.model import Sites from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites import utils.site as site import core.mw as mw import thisdb @@ -42,7 +43,7 @@ def list(): data['page'] = mw.getPage({'count':info['count'],'tojs':'getWeb','p':p, 'row':limit}) return data -#添加站点 +# 添加站点 @blueprint.route('/add', endpoint='add',methods=['POST']) @panel_login_required def add(): @@ -51,7 +52,15 @@ def add(): path = request.form.get('path', '') version = request.form.get('version', '') port = request.form.get('port', '') - return [] + return MwSites.instance().add(webname, port, ps, path, version) + +# 站点删除 +@blueprint.route('/delete', endpoint='delete',methods=['POST']) +@panel_login_required +def delete(): + site_id = request.form.get('id', '') + path = request.form.get('path', '') + return MwSites.instance().delete(site_id, path) # 获取站点类型 @blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) @@ -87,9 +96,39 @@ def check_web_status(): @blueprint.route('/get_php_version', endpoint='get_php_version',methods=['POST']) @panel_login_required def get_php_version(): - return site.getPhpVersion() + return MwSites.instance().getPhpVersion() +# 设置网站到期 +@blueprint.route('/set_end_date', endpoint='set_end_date',methods=['POST']) +@panel_login_required +def set_end_date(): + site_id = request.form.get('id', '') + edate = request.form.get('edate', '') + return MwSites.instance().setEndDate(site_id, edate) + + +# 设置网站备注 +@blueprint.route('/set_ps', endpoint='set_ps',methods=['POST']) +@panel_login_required +def set_ps(): + site_id = request.form.get('id', '') + ps = request.form.get('ps', '') + return MwSites.instance().setPs(site_id, ps) + + +# 设置默认网站信息 +@blueprint.route('/get_default_site', endpoint='get_default_site',methods=['POST']) +@panel_login_required +def get_default_site(): + return MwSites.instance().getDefaultSite() + +@blueprint.route('/set_default_site', endpoint='set_default_site',methods=['POST']) +@panel_login_required +def set_default_site(): + name = request.form.get('name', '') + return MwSites.instance().setDefaultSite(name) + @blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) @panel_login_required def get_cli_php_version(): @@ -98,7 +137,7 @@ def get_cli_php_version(): return mw.returnData(False, '未安装PHP,无法设置') php_bin = '/usr/bin/php' - php_versions = site.getPhpVersion() + php_versions = MwSites.instance().getPhpVersion() php_versions = php_versions[1:] if len(php_versions) < 1: @@ -117,35 +156,7 @@ def get_cli_php_version(): def set_cli_php_version(): if mw.isAppleSystem(): return mw.returnData(False, "开发机不可设置!") - version = request.form.get('version', '') - - php_bin = '/usr/bin/php' - php_bin_src = "/www/server/php/%s/bin/php" % version - php_ize = '/usr/bin/phpize' - php_ize_src = "/www/server/php/%s/bin/phpize" % version - php_fpm = '/usr/bin/php-fpm' - php_fpm_src = "/www/server/php/%s/sbin/php-fpm" % version - php_pecl = '/usr/bin/pecl' - php_pecl_src = "/www/server/php/%s/bin/pecl" % version - php_pear = '/usr/bin/pear' - php_pear_src = "/www/server/php/%s/bin/pear" % version - if not os.path.exists(php_bin_src): - return mw.returnData(False, '指定PHP版本未安装!') - - is_chattr = mw.execShell('lsattr /usr|grep /usr/bin')[0].find('-i-') - if is_chattr != -1: - mw.execShell('chattr -i /usr/bin') - mw.execShell("rm -f " + php_bin + ' ' + php_ize + ' ' + - php_fpm + ' ' + php_pecl + ' ' + php_pear) - mw.execShell("ln -sf %s %s" % (php_bin_src, php_bin)) - mw.execShell("ln -sf %s %s" % (php_ize_src, php_ize)) - mw.execShell("ln -sf %s %s" % (php_fpm_src, php_fpm)) - mw.execShell("ln -sf %s %s" % (php_pecl_src, php_pecl)) - mw.execShell("ln -sf %s %s" % (php_pear_src, php_pear)) - if is_chattr != -1: - mw.execShell('chattr +i /usr/bin') - mw.writeLog('面板设置', '设置PHP-CLI版本为: %s' % version) - return mw.returnData(True, '设置成功!') + return MwSites.instance().setCliPhpVersion(version) diff --git a/web/core/mw.py b/web/core/mw.py index 603456371..c4c3e2450 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -879,6 +879,33 @@ def isRestart(): return False return True +def getAcmeDir(): + acme = '/root/.acme.sh' + if isAppleSystem(): + cmd = "who | sed -n '2, 1p' |awk '{print $1}'" + user = execShell(cmd)[0].strip() + acme = '/Users/' + user + '/.acme.sh' + if not os.path.exists(acme): + acme = '/.acme.sh' + return acme + + +def getAcmeDomainDir(domain): + acme_dir = getAcmeDir() + acme_domain = acme_dir + '/' + domain + acme_domain_ecc = acme_domain + '_ecc' + if os.path.exists(acme_domain_ecc): + acme_domain = acme_domain_ecc + return acme_domain + + +def fileNameCheck(filename): + f_strs = [';', '&', '<', '>'] + for fs in f_strs: + if filename.find(fs) != -1: + return False + return True + def triggerTask(): isTask = getPanelDir() + '/logs/panel_task.lock' writeFile(isTask, 'True') @@ -925,6 +952,10 @@ def checkWebConfig(): def restartWeb(): return opWeb("reload") +def deleteFile(file): + if os.path.exists(file): + os.remove(file) + def isInstalledWeb(): path = getServerDir() + '/openresty/nginx/sbin/nginx' if os.path.exists(path): diff --git a/web/static/app/site.js b/web/static/app/site.js index 815774ad8..cf7e05ae7 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -72,14 +72,14 @@ $('#site_search').click(function(){ var idname = data.data[i].name.replace(/\./g,'_'); body = "\ - " + shortwebname + "\ + " + shortwebname + "\ " + status + "\ " + backup + "\ " + shortpath + "\ " + web_end_time + "\ " + data.data[i].ps + "\ \ - 设置\ + 设置\ | 删除\ " diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 8958fa60f..3b6f2b8a4 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -11,8 +11,12 @@ from .init import * from .option import * from .user import * + from .sites import * +from .domain import * + from .tasks import * from .logs import * from .crontab import * from .firewall import * +from .temp_login import * diff --git a/web/thisdb/binding.py b/web/thisdb/binding.py new file mode 100644 index 000000000..5b536b465 --- /dev/null +++ b/web/thisdb/binding.py @@ -0,0 +1,37 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,pid,domain,port,path,add_time' + +def addBinding(pid, domain, port, path): + now_time = mw.getDateFromNow() + insert_data = { + 'pid': pid, + 'domain': domain, + 'port':port, + 'path':path, + 'add_time': now_time, + } + return mw.M('binding').insert(insert_data) + + +def getBindingListBySiteId(site_id): + task_list = mw.M('binding').where('pid=?', (site_id,)).field(__FIELD).select() + return task_list + + + +def deleteBindingId(domain_id): + return mw.M('binding').where("id=?", (domain_id,)).delete() + +def deleteBindingBySiteId(site_id): + return mw.M('binding').where("pid=?", (site_id,)).delete() diff --git a/web/thisdb/domain.py b/web/thisdb/domain.py new file mode 100644 index 000000000..ec7d40115 --- /dev/null +++ b/web/thisdb/domain.py @@ -0,0 +1,30 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,pid,name,port,add_time' + +def addDomain(pid, name, port): + now_time = mw.getDateFromNow() + insert_data = { + 'pid': pid, + 'name': name, + 'port':port, + 'add_time': now_time, + } + return mw.M('domain').insert(insert_data) + + +def deleteDomainId(domain_id): + return mw.M('domain').where("id=?", (domain_id,)).delete() + +def deleteDomainBySiteId(site_id): + return mw.M('domain').where("pid=?", (site_id,)).delete() diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index e20f30071..332f6b1a5 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -10,10 +10,35 @@ import core.mw as mw +__FIELD = 'id,name,path,status,ps,edate,type_id,add_time,update_time' + def getSitesCount(): return mw.M('sites').count() +def getSitesById(site_id): + return mw.M('sites').field(__FIELD).where("id=?", (site_id,)).find() + +def addSites(name, path): + now_time = mw.getDateFromNow() + insert_data = { + 'name': name, + 'path': path, + 'status': 1, + 'ps':name, + 'type_id':0, + 'edate':'0000-00-00', + 'add_time': now_time, + 'update_time': now_time + } + return mw.M('sites').insert(insert_data) + + +def isSitesExist(name): + if mw.M('sites').where("name=?", (name,)).count() > 0: + return True + return False + def getSitesList( page:int | None = 1, size:int | None = 10, @@ -29,7 +54,7 @@ def getSitesList( sql_where = " type_id=" + str(type_id) - dbM = dbC = mw.M('sites').field('id,name,path,status,ps,edate,type_id,add_time,update_time') + dbM = dbC = mw.M('sites').field(__FIELD) if sql_where != '': count = dbC.where(sql_where).count() @@ -46,3 +71,19 @@ def getSitesList( data['count'] = count return data + +def deleteSitesById(site_id): + return mw.M('sites').where("id=?", (site_id,)).delete() + +def setSitesData(site_id, + edate: str | None = None, + ps: str | None = None, +): + update_data = {} + if edate is not None: + update_data['edate'] = edate + if ps is not None: + update_data['ps'] = ps + + return mw.M('sites').where('id=?',(site_id,)).update(update_data) + diff --git a/web/thisdb/temp_login.py b/web/thisdb/temp_login.py index ab75c3923..b6ac4e840 100644 --- a/web/thisdb/temp_login.py +++ b/web/thisdb/temp_login.py @@ -10,19 +10,61 @@ import time -from admin.model import db, TempLogin - import core.mw as mw +__FIELD = 'id,token,salt,state,login_time,login_addr,logout_time,expire,add_time' + +def addTempLogin( + token:str | None = None, + expire:int | None = None +): + if expire is None: + start_time = int(time.time()) + expire=start_time+3600 + + if token is None: + salt = mw.getRandomString(12) + r = mw.getRandomString(48) + token = mw.md5(r + salt) + + now_time = mw.formatDate() + insert_data = { + 'token':token, + 'salt':'0', + 'state':0, + 'login_time':0, + 'login_addr':'', + 'expire':expire, + 'add_time':now_time, + } + + return mw.M('temp_login').insert(insert_data) + +def getTempLoginPage( + page: int | None = 1, + size: int | None = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + tl_list = mw.M('temp_login').where('', ()).field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('temp_login').where('', ()).count() + + rdata = {} + rdata['list'] = tl_list + rdata['count'] = count + return rdata + def getTempLoginByToken(token, ) -> None: ''' 获取用户信息通过用户名 ''' - field = 'id,addtime,expire,login_time,login_addr,state,add_time' - data = mw.M('temp_login').where('token=?', (token,)).field(field).order('id asc').select() + data = mw.M('temp_login').where('token=?', (token,)).field(__FIELD).order('id asc').find() return data +def deleteTempLoginById(id) : + return mw.M('temp_login').where('id=?', (id,)).delete() + def clearTempLogin()->bool: ''' 清空过期数据 diff --git a/web/thisdb/user.py b/web/thisdb/user.py index ac94b222b..3cbaa4bc2 100644 --- a/web/thisdb/user.py +++ b/web/thisdb/user.py @@ -85,7 +85,7 @@ def getUserByRoot() -> None: def updateUserLoginTime(): now_time = mw.formatDate() - mw.M('users').field(__field).where('id=?', (1,)).update({'login_time',now_time}) + mw.M('users').field(__field).where('id=?', (1,)).update({'login_time':now_time}) return True def setUserByRoot( diff --git a/web/utils/site.py b/web/utils/site.py index 0d76ec0ff..c8562459a 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -10,13 +10,14 @@ import os import sys +import re import json +import time import threading import multiprocessing -from admin import model - import core.mw as mw +import thisdb class sites(object): @@ -31,6 +32,304 @@ class sites(object): sites._instance = sites(*args, **kwargs) return sites._instance + def __init__(self): + # nginx conf + self.setupPath = mw.getServerDir() + '/web_conf' + self.logsPath = mw.getLogsDir() + + self.vhostPath = vhost = self.setupPath + '/nginx/vhost' + if not os.path.exists(vhost): + mw.execShell("mkdir -p " + vhost + " && chmod -R 755 " + vhost) + self.rewritePath = rewrite = self.setupPath + '/nginx/rewrite' + if not os.path.exists(rewrite): + mw.execShell("mkdir -p " + rewrite + " && chmod -R 755 " + rewrite) + + self.passPath = passwd = self.setupPath + '/nginx/pass' + if not os.path.exists(passwd): + mw.execShell("mkdir -p " + passwd + " && chmod -R 755 " + passwd) + + self.redirectPath = redirect = self.setupPath + '/nginx/redirect' + if not os.path.exists(redirect): + mw.execShell("mkdir -p " + redirect +" && chmod -R 755 " + redirect) + + self.proxyPath = proxy = self.setupPath + '/nginx/proxy' + if not os.path.exists(proxy): + mw.execShell("mkdir -p " + proxy + " && chmod -R 755 " + proxy) + + # ssl conf + self.sslDir = self.setupPath + '/ssl' + self.sslLetsDir = self.setupPath + '/letsencrypt' + if not os.path.exists(self.sslLetsDir): + mw.execShell("mkdir -p " + self.sslLetsDir +" && chmod -R 755 " + self.sslLetsDir) + + # 域名编码转换 + def toPunycode(self, domain): + 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 + 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 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 getPath(self, path): + if path[-1] == '/': + return path[0:-1] + return path + + 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 add(self, webname, port, ps, path, version): + site_root_dir = mw.getWwwDir() + if site_root_dir == path.rstrip('/'): + return mw.returnData(False, '不要以网站根目录创建站点!') + + print(webname, port, ps, path, version) + 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 thisdb.isSitesExist(self.siteName): + return mw.returnData(False, '您添加的站点[%s]已存在!' % self.siteName) + + pid = thisdb.addSites(self.siteName, self.sitePath) + if pid < 1: + return mw.returnData(False, '添加失败!') + + + self.createRootDir(self.sitePath) + self.nginxAddConf() + + # 主域名配置 + thisdb.addDomain(pid, self.siteName, self.sitePort) + + # 绑定域名配置 + + # 添加更多域名 + # for domain in siteMenu['domainlist']: + # thisdb.addDomain(pid, self.siteName, self.sitePort) + + mw.restartWeb() + # return mw.returnData(False, '开发中!') + return mw.returnData(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, site_id, path): + info = thisdb.getSitesById(site_id) + webname = info['name'] + 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) + + thisdb.deleteSitesById(site_id) + thisdb.deleteDomainBySiteId(site_id) + + binding_list = thisdb.getBindingListBySiteId(site_id) + for x in binding_list: + tag = mw.getLogsDir() + "/" + webname + "_" + x['domain'] + wlog = tag + ".log" + wlog_error = tag + ".error.log" + + if os.path.exists(wlog): + mw.execShell('rm -rf ' + wlog) + if os.path.exists(wlog_error): + mw.execShell('rm -rf ' + wlog_error) + + thisdb.deleteBindingBySiteId(site_id) + mw.restartWeb() + return mw.returnData(True, '站点【%s】删除成功!' % webname) + + 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) + + # 设置网站过期 + def setEndDate(self, site_id, end_date): + info = thisdb.getSitesById(site_id) + thisdb.setSitesData(site_id, edate=end_date) + mw.writeLog('网站', '设置成功,站点【{1}】到期【{2}】后将自动停止!', (info['name'], end_date,)) + return mw.returnData(True, '设置成功,站点到期后将自动停止!') + + # 设置网站备注 + def setPs(self, site_id, ps): + if thisdb.setSitesData(site_id, ps=ps): + return mw.returnData(True, '修改成功!') + return mw.returnData(False, '修改失败!') + + # 获取默认站点 + def getDefaultSite(self): + data = {} + data['sites'] = mw.M('sites').field('name').order('id desc').select() + data['default_site'] = thisdb.getOption('default_site', default='') + return mw.getJson(data) + + def setDefaultSite(self, name): + # 清理旧的 + default_site = thisdb.getOption('default_site', default='') + if default_site: + path = self.getHostConf(default_site) + if os.path.exists(path): + conf = mw.readFile(path) + rep = r"listen\s+80.+;" + conf = re.sub(rep, 'listen 80;', conf, 1) + rep = r"listen\s+443.+;" + conf = re.sub(rep, 'listen 443 ssl;', conf, 1) + mw.writeFile(path, conf) + + path = self.getHostConf(name) + if os.path.exists(path): + conf = mw.readFile(path) + rep = r"listen\s+80\s*;" + conf = re.sub(rep, 'listen 80 default_server;', conf, 1) + rep = r"listen\s+443\s*ssl\s*\w*\s*;" + conf = re.sub(rep, 'listen 443 ssl default_server;', conf, 1) + mw.writeFile(path, conf) + + thisdb.setOption('default_site', name) + mw.restartWeb() + return mw.returnJson(True, '设置成功!') + + def setCliPhpVersion(self, version): + php_bin = '/usr/bin/php' + php_bin_src = "/www/server/php/%s/bin/php" % version + php_ize = '/usr/bin/phpize' + php_ize_src = "/www/server/php/%s/bin/phpize" % version + php_fpm = '/usr/bin/php-fpm' + php_fpm_src = "/www/server/php/%s/sbin/php-fpm" % version + php_pecl = '/usr/bin/pecl' + php_pecl_src = "/www/server/php/%s/bin/pecl" % version + php_pear = '/usr/bin/pear' + php_pear_src = "/www/server/php/%s/bin/pear" % version + if not os.path.exists(php_bin_src): + return mw.returnData(False, '指定PHP版本未安装!') + + is_chattr = mw.execShell('lsattr /usr|grep /usr/bin')[0].find('-i-') + if is_chattr != -1: + mw.execShell('chattr -i /usr/bin') + mw.execShell("rm -f " + php_bin + ' ' + php_ize + ' ' + php_fpm + ' ' + php_pecl + ' ' + php_pear) + mw.execShell("ln -sf %s %s" % (php_bin_src, php_bin)) + mw.execShell("ln -sf %s %s" % (php_ize_src, php_ize)) + mw.execShell("ln -sf %s %s" % (php_fpm_src, php_fpm)) + mw.execShell("ln -sf %s %s" % (php_pecl_src, php_pecl)) + mw.execShell("ln -sf %s %s" % (php_pear_src, php_pear)) + if is_chattr != -1: + mw.execShell('chattr +i /usr/bin') + mw.writeLog('面板设置', '设置PHP-CLI版本为: %s' % version) + return mw.returnData(True, '设置成功!') + def getPhpVersion(self): phpVersions = ('00', '52', '53', '54', '55', From e994cd80f11bb26bc0e65ba01c775e09549af19a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 7 Nov 2024 03:09:47 +0800 Subject: [PATCH 130/965] Update panel_tools.py --- panel_tools.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index cd2e3734f..ef4416085 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -23,9 +23,11 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) +from utils.firewall import firewall as MwFirewall import core.mw as mw import thisdb + INIT_DIR = "/etc/rc.d/init.d" if mw.isAppleSystem(): INIT_DIR = mw.getPanelDir() + "/scripts/init.d" @@ -98,10 +100,9 @@ def mwcli(mw_input=0): in_port = mw_input_cmd("请输入新的面板端口:") in_port_int = int(in_port.strip()) if in_port_int < 65536 and in_port_int > 0: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs( - in_port, 'WEB面板[TOOLS修改]', 'port') - mw.writeFile('data/port.pl', in_port) + MwFirewall.instance().addAcceptPort(in_port, 'WEB面板[TOOLS修改]', 'port') + panel_port = mw.getPanelDir() + '/data/port.pl' + mw.writeFile(panel_port, in_port) os.system(INIT_CMD + " restart_panel") os.system(INIT_CMD + " default") else: @@ -181,7 +182,7 @@ def mwcli(mw_input=0): if not run_cmd: mw.echoInfo("未检测到防火墙!") elif mw_input == 100: - php_conf = 'plugins/php/info.json' + php_conf = mw.getPanelDir() + '/plugins/php/info.json' if os.path.exists(php_conf): cont = mw.readFile(php_conf) cont = re.sub("\"53\"", "\"52\",\"53\"", cont) @@ -189,7 +190,7 @@ def mwcli(mw_input=0): mw.writeFile(php_conf, cont) mw.echoInfo("执行PHP52显示成功!") elif mw_input == 101: - php_conf = 'plugins/php/info.json' + php_conf = mw.getPanelDir() + '/plugins/php/info.json' if os.path.exists(php_conf): cont = mw.readFile(php_conf) cont = re.sub("\"52\",", "", cont) @@ -203,7 +204,7 @@ def mwcli(mw_input=0): def open_ssh_port(): - import firewall_api + find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1" cmd_data = mw.execShell(find_ssh_port_cmd) ssh_port = cmd_data[0].replace("Port ", '').strip() @@ -211,13 +212,13 @@ def open_ssh_port(): ssh_port = '22' print("|-SSH端口: "+ str(ssh_port)) - firewall_api.firewall_api().addAcceptPortArgs(ssh_port, 'SSH远程管理服务', 'port') + MwFirewall.instance().addAcceptPort(ssh_port, 'SSH远程管理服务', 'port') return True def set_panel_pwd(password, ncli=False): - info = model.getUserByRoot() - model.setUserByRoot(password=password) + info = thisdb.getUserByRoot() + thisdb.setUserByRoot(password=password) if ncli: print("|-username: " + info['name']) print("|-password: " + password) From 881facdfa87fd6688cd30abeceff4ca0bfec8b5e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 7 Nov 2024 03:50:50 +0800 Subject: [PATCH 131/965] update --- web/admin/site/__init__.py | 33 ++++++++++++++++++++++++++++++ web/thisdb/__init__.py | 1 + web/thisdb/domain.py | 3 +++ web/utils/site.py | 41 +++++++++++++++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 7752645d5..9695c359c 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -76,6 +76,31 @@ def get_root_dir(): data['dir'] = mw.getWwwDir() return data +# 获取站点配置 +@blueprint.route('/get_host_conf', endpoint='get_host_conf',methods=['POST']) +@panel_login_required +def get_host_conf(): + siteName = request.form.get('siteName', '') + host = MwSites.instance().getHostConf(siteName) + return {'host': host} + + +# 获取站点PHP版本 +@blueprint.route('/get_site_php_version', endpoint='get_site_php_version',methods=['POST']) +@panel_login_required +def get_site_php_version(): + siteName = request.form.get('siteName', '') + return MwSites.instance().getSitePhpVersion(siteName) + + +# 设置站点PHP版本 +@blueprint.route('/set_php_version', endpoint='set_php_version',methods=['POST']) +@panel_login_required +def set_php_version(): + siteName = request.form.get('siteName', '') + version = request.form.get('version', '') + return MwSites.instance().setPhpVersion(siteName,version) + # 检查OpenResty安装/启动状态 @blueprint.route('/check_web_status', endpoint='check_web_status',methods=['POST']) @panel_login_required @@ -123,6 +148,14 @@ def set_ps(): def get_default_site(): return MwSites.instance().getDefaultSite() +# 站点删除 +@blueprint.route('/get_domain', endpoint='get_domain',methods=['POST']) +@panel_login_required +def get_domain(): + site_id = request.form.get('id', '') + return MwSites.instance().getDomain(site_id) + + @blueprint.route('/set_default_site', endpoint='set_default_site',methods=['POST']) @panel_login_required def set_default_site(): diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 3b6f2b8a4..451473e59 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -14,6 +14,7 @@ from .user import * from .sites import * from .domain import * +from .binding import * from .tasks import * from .logs import * diff --git a/web/thisdb/domain.py b/web/thisdb/domain.py index ec7d40115..0bb9ef126 100644 --- a/web/thisdb/domain.py +++ b/web/thisdb/domain.py @@ -22,6 +22,9 @@ def addDomain(pid, name, port): } return mw.M('domain').insert(insert_data) +def getDomainByPid(pid): + return mw.M('domain').where("pid=?", (pid,)).field(__FIELD).select() + def deleteDomainId(domain_id): return mw.M('domain').where("id=?", (domain_id,)).delete() diff --git a/web/utils/site.py b/web/utils/site.py index c8562459a..c66e94c21 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -200,6 +200,7 @@ class sites(object): def delete(self, site_id, path): info = thisdb.getSitesById(site_id) + print(info) webname = info['name'] self.deleteWSLogs(webname) @@ -239,7 +240,8 @@ class sites(object): return mw.returnData(True, '站点【%s】删除成功!' % webname) def nginxAddConf(self): - source_tpl = mw.getRunDir() + '/data/tpl/nginx.conf' + source_tpl = mw.getPanelDir() + '/data/tpl/nginx.conf' + print(source_tpl) vhost_file = self.vhostPath + '/' + self.siteName + '.conf' content = mw.readFile(source_tpl) @@ -276,6 +278,43 @@ class sites(object): data['default_site'] = thisdb.getOption('default_site', default='') return mw.getJson(data) + + # 获取域名列表 + def getDomain(self, pid): + return thisdb.getDomainByPid(pid) + + + 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 data + + + def setPhpVersion(self, siteName, version): + # nginx + file = self.getHostConf(siteName) + conf = mw.readFile(file) + if conf: + rep = r"enable-php-(.*)\.conf" + tmp = re.search(rep, conf).group() + conf = conf.replace(tmp, 'enable-php-' + version + '.conf') + mw.writeFile(file, conf) + + msg = mw.getInfo('成功切换网站[{1}]的PHP版本为PHP-{2}', (siteName, version)) + mw.writeLog("网站管理", msg) + mw.restartWeb() + return mw.returnJson(True, msg) + + def setDefaultSite(self, name): # 清理旧的 default_site = thisdb.getOption('default_site', default='') From 8609033d37cdfd45cc02123d9314a97ac0840f26 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 7 Nov 2024 03:53:09 +0800 Subject: [PATCH 132/965] Update binding.py --- web/thisdb/binding.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/thisdb/binding.py b/web/thisdb/binding.py index 5b536b465..ea968812d 100644 --- a/web/thisdb/binding.py +++ b/web/thisdb/binding.py @@ -10,6 +10,8 @@ import core.mw as mw +# 站点-子目录绑定 + __FIELD = 'id,pid,domain,port,path,add_time' def addBinding(pid, domain, port, path): From 35c42d94c6b3089989c95b888c45a58087929831 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 7 Nov 2024 03:54:37 +0800 Subject: [PATCH 133/965] Update site.py --- web/utils/site.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index c66e94c21..af7b00f3d 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -262,7 +262,7 @@ class sites(object): def setEndDate(self, site_id, end_date): info = thisdb.getSitesById(site_id) thisdb.setSitesData(site_id, edate=end_date) - mw.writeLog('网站', '设置成功,站点【{1}】到期【{2}】后将自动停止!', (info['name'], end_date,)) + mw.writeLog('网站管理', '设置成功,站点【{1}】到期【{2}】后将自动停止!', (info['name'], end_date,)) return mw.returnData(True, '设置成功,站点到期后将自动停止!') # 设置网站备注 @@ -276,8 +276,7 @@ class sites(object): data = {} data['sites'] = mw.M('sites').field('name').order('id desc').select() data['default_site'] = thisdb.getOption('default_site', default='') - return mw.getJson(data) - + return data # 获取域名列表 def getDomain(self, pid): From 1b4785ec527a50e3668fbf387b498d66f1a2d02f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 7 Nov 2024 03:54:48 +0800 Subject: [PATCH 134/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index af7b00f3d..a7c60fe0b 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -311,7 +311,7 @@ class sites(object): msg = mw.getInfo('成功切换网站[{1}]的PHP版本为PHP-{2}', (siteName, version)) mw.writeLog("网站管理", msg) mw.restartWeb() - return mw.returnJson(True, msg) + return mw.returnData(True, msg) def setDefaultSite(self, name): From fa614fcf8821277d6f3f86435c2d5c4df317e528 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 7 Nov 2024 03:55:16 +0800 Subject: [PATCH 135/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index a7c60fe0b..4636a6381 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -338,7 +338,7 @@ class sites(object): thisdb.setOption('default_site', name) mw.restartWeb() - return mw.returnJson(True, '设置成功!') + return mw.returnData(True, '设置成功!') def setCliPhpVersion(self, version): php_bin = '/usr/bin/php' From 43420b7c3876a4232f62f32331eefaa57af03d99 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 18:15:31 +0800 Subject: [PATCH 136/965] update --- web/admin/setting/__init__.py | 1 + web/admin/setting/notify_email.py | 79 ++++++++++++++++++++++++++++++ web/admin/setup/option.py | 4 ++ web/static/app/config.js | 27 +++++----- web/templates/default/setting.html | 2 +- web/utils/config.py | 4 ++ 6 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 web/admin/setting/notify_email.py diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py index adfb25a3e..c516d49bf 100644 --- a/web/admin/setting/__init__.py +++ b/web/admin/setting/__init__.py @@ -12,6 +12,7 @@ from .setting import * from .temp_login import * from .timezone import * from .secondary_verifiy import * +from .notify_email import * diff --git a/web/admin/setting/notify_email.py b/web/admin/setting/notify_email.py new file mode 100644 index 000000000..26437672e --- /dev/null +++ b/web/admin/setting/notify_email.py @@ -0,0 +1,79 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from .setting import blueprint +import thisdb + +# 获取邮件信息 +@blueprint.route('/get_notify_email', endpoint='get_notify_email', methods=['POST']) +@panel_login_required +def get_notify_email(): + notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + + decrypt_data = mw.deDoubleCrypt('email', notify_email['cfg']) + notify_email['email'] = json.loads(decrypt_data) + return mw.returnData(True,'ok',notify_email) + + +# 设置邮件信息 +@blueprint.route('/set_notify_email', endpoint='set_notify_email', methods=['POST']) +@panel_login_required +def set_notify_email(): + tag = request.form.get('tag', '').strip() + data = request.form.get('data', '').strip() + + crypt_data = mw.enDoubleCrypt(tag, data) + + notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + notify_email['cfg'] = crypt_data + + thisdb.setOption('notify_email', json.dumps(notify_email), type='notify') + return mw.returnData(True,'设置成功') + + +# 设置邮件测试 +@blueprint.route('/set_notify_email_test', endpoint='set_notify_email_test', methods=['POST']) +@panel_login_required +def set_notify_email_test(): + return mw.returnData(True,'设置成功') + + +# 切换邮件开关 +@blueprint.route('/set_notify_email_enable', endpoint='set_notify_email_enable', methods=['POST']) +@panel_login_required +def set_notify_email_enable(): + tag = request.form.get('tag', '').strip() + data = request.form.get('data', '').strip() + + notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + + if notify_email['open']: + op_action = '关闭' + notify_email['open'] = False + else: + op_action = '开启' + notify_email['open'] = True + + thisdb.setOption('notify_email', json.dumps(notify_email), type='notify') + return mw.returnData(True, op_action+'成功') \ No newline at end of file diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 9a433961e..8a3439d0d 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -57,5 +57,9 @@ def init_option(): thisdb.setOption('backup_path', mw.getFatherDir()+'/backup') # 默认站点目录 thisdb.setOption('site_path', mw.getFatherDir()+'/wwwroot') + + + # 邮件通知 + thisdb.setOption('notify_email', json.dumps({'open':False}), type='notify') return True \ No newline at end of file diff --git a/web/static/app/config.js b/web/static/app/config.js index 542437619..c8594eaff 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -543,7 +543,7 @@ function setPanelSSL(){ function setNotifyApi(tag, obj){ var enable = $(obj).prop("checked"); // console.log(tag,obj,enable); - $.post('/setting/set_notify_enable', {'tag':tag, 'enable':enable},function(rdata){ + $.post('/setting/set_notify_email_enable', {'tag':tag, 'enable':enable},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){} } ,{icon:rdata.status?1:2}, 1000); @@ -637,7 +637,7 @@ function getTgbot(){ function getEmailCfg(){ var loadT = layer.msg('正在获取邮件配置信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/setting/get_notify',{},function(data){ + $.post('/setting/get_notify_email',{},function(data){ layer.close(loadT); var smtp_host = 'smtp.163.com'; @@ -651,14 +651,13 @@ function getEmailCfg(){ if (data.status){ if (typeof(data['data']['email']) !='undefined'){ - smtp_host = data['data']['email']['data']['smtp_host']; - smtp_port = data['data']['email']['data']['smtp_port']; - username = data['data']['email']['data']['username']; - password = data['data']['email']['data']['password']; - to_mail_addr = data['data']['email']['data']['to_mail_addr']; + smtp_host = data['data']['email']['smtp_host']; + smtp_port = data['data']['email']['smtp_port']; + username = data['data']['email']['username']; + password = data['data']['email']['password']; + to_mail_addr = data['data']['email']['to_mail_addr']; - - var smtp_ssl = data['data']['email']['data']['smtp_ssl']; + var smtp_ssl = data['data']['email']['smtp_ssl']; if (smtp_ssl == 'ssl'){ smtp_ssl_no = ''; smtp_ssl_yes = 'checked'; @@ -742,13 +741,13 @@ function getEmailCfg(){ return false; } - $.post('/config/set_notify',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_email',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); } },{icon:rdata.status?1:2},2000); - }); + },'json'); }, btn3:function(index){ @@ -791,17 +790,17 @@ function getEmailCfg(){ layer.msg('验证测试不能为空!', {icon:2}); return false; } - $.post('/config/set_notify_test',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_email_test',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); } },{icon:rdata.status?1:2},2000); - }); + },'json'); return false; } }); - }); + },'json'); } function getPanelSSL(){ diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index 588d1eeb0..1e908c07d 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -172,7 +172,7 @@

    邮件通知 - + 邮件通知 diff --git a/web/utils/config.py b/web/utils/config.py index 89ffabb85..5d6737b2d 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -78,5 +78,9 @@ def getGlobalVar(): data['hook_menu'] = thisdb.getOptionByJson('hook_menu',type='hook',default=[]) data['hook_global_static'] = thisdb.getOptionByJson('hook_global_static',type='hook',default=[]) + + + # 邮件通知设置 + data['notify_email'] = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') return data \ No newline at end of file From d5a8efff0e2f2e9c8a5728b27b610194f76d0e70 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 18:22:01 +0800 Subject: [PATCH 137/965] update --- web/admin/setting/notify_email.py | 8 +- web/core/mw.py | 168 ++++++++++++++++++++++++++++++ web/utils/email.py | 43 ++++++++ 3 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 web/utils/email.py diff --git a/web/admin/setting/notify_email.py b/web/admin/setting/notify_email.py index 26437672e..a71bceb10 100644 --- a/web/admin/setting/notify_email.py +++ b/web/admin/setting/notify_email.py @@ -56,8 +56,14 @@ def set_notify_email(): @blueprint.route('/set_notify_email_test', endpoint='set_notify_email_test', methods=['POST']) @panel_login_required def set_notify_email_test(): - return mw.returnData(True,'设置成功') + tag = request.form.get('tag', '').strip() + tag_data = request.form.get('data', '').strip() + data = json.loads(tag_data) + test_pass = mw.emailNotifyTest(data) + if test_pass: + return mw.returnData(True, '验证成功') + return mw.returnData(False, '验证失败') # 切换邮件开关 @blueprint.route('/set_notify_email_enable', endpoint='set_notify_email_enable', methods=['POST']) diff --git a/web/core/mw.py b/web/core/mw.py index c4c3e2450..deab5a480 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -1106,6 +1106,174 @@ def getMyORM(): # 数据库 START # --------------------------------------------------------------------------------- +##################### notify start ######################################### + + +def initNotifyConfig(): + p = getNotifyPath() + if not os.path.exists(p): + writeFile(p, '{}') + return True + + +def getNotifyPath(): + path = 'data/notify.json' + return path + + +def getNotifyData(is_parse=False): + initNotifyConfig() + notify_file = getNotifyPath() + notify_data = readFile(notify_file) + + data = json.loads(notify_data) + + if is_parse: + tag_list = ['tgbot', 'email'] + for t in tag_list: + if t in data and 'cfg' in data[t]: + data[t]['data'] = json.loads(deDoubleCrypt(t, data[t]['cfg'])) + return data + + +def writeNotify(data): + p = getNotifyPath() + return writeFile(p, json.dumps(data)) + + +def tgbotNotifyChatID(): + data = getNotifyData(True) + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + return t['chat_id'] + return '' + + +def tgbotNotifyObject(): + data = getNotifyData(True) + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + import telebot + bot = telebot.TeleBot(app_token) + return True, bot + return False, None + + +def tgbotNotifyMessage(app_token, chat_id, msg): + import telebot + bot = telebot.TeleBot(app_token) + try: + data = bot.send_message(chat_id, msg) + return True + except Exception as e: + writeFileLog(str(e)) + return False + + +def tgbotNotifyHttpPost(app_token, chat_id, msg): + try: + url = 'https://api.telegram.org/bot' + app_token + '/sendMessage' + post_data = { + 'chat_id': chat_id, + 'text': msg, + } + rdata = httpPost(url, post_data) + return True + except Exception as e: + writeFileLog(str(e)) + return False + + +def tgbotNotifyTest(app_token, chat_id): + msg = 'MW-通知验证测试OK' + return tgbotNotifyHttpPost(app_token, chat_id, msg) + + +def emailNotifyMessage(data): + ''' + 邮件通知 + ''' + import utils.email as email + try: + if data['smtp_ssl'] == 'ssl': + email.sendSSL(data['smtp_host'], data['smtp_port'], + data['username'], data['password'], + data['to_mail_addr'], data['subject'], data['content']) + else: + email.send(data['smtp_host'], data['smtp_port'], + data['username'], data['password'], + data['to_mail_addr'], data['subject'], data['content']) + return True + except Exception as e: + print(getTracebackInfo()) + return False + + +def emailNotifyTest(data): + # print(data) + data['subject'] = 'MW通知测试' + data['content'] = data['mail_test'] + return emailNotifyMessage(data) + + +def notifyMessageTry(msg, stype='common', trigger_time=300, is_write_log=True): + + lock_file = getPanelTmp() + '/notify_lock.json' + if not os.path.exists(lock_file): + writeFile(lock_file, '{}') + + lock_data = json.loads(readFile(lock_file)) + if stype in lock_data: + diff_time = time.time() - lock_data[stype]['do_time'] + if diff_time >= trigger_time: + lock_data[stype]['do_time'] = time.time() + else: + return False + else: + lock_data[stype] = {'do_time': time.time()} + + writeFile(lock_file, json.dumps(lock_data)) + + if is_write_log: + writeLog("通知管理[" + stype + "]", msg) + + data = getNotifyData(True) + # tag_list = ['tgbot', 'email'] + # tagbot + do_notify = False + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + i = sys.version_info + + # telebot 在python小于3.7无法使用 + if i[0] < 3 or i[1] < 7: + do_notify = tgbotNotifyHttpPost( + t['app_token'], t['chat_id'], msg) + else: + do_notify = tgbotNotifyMessage( + t['app_token'], t['chat_id'], msg) + + if 'email' in data and 'enable' in data['email']: + if data['email']['enable']: + t = data['email']['data'] + t['subject'] = 'MW通知' + t['content'] = msg + do_notify = emailNotifyMessage(t) + return do_notify + + +def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True): + try: + return notifyMessageTry(msg, stype, trigger_time, is_write_log) + except Exception as e: + writeFileLog(getTracebackInfo()) + return False + + +##################### notify end ######################################### # --------------------------------------------------------------------------------- # 打印相关 START diff --git a/web/utils/email.py b/web/utils/email.py new file mode 100644 index 000000000..6ba7b62d5 --- /dev/null +++ b/web/utils/email.py @@ -0,0 +1,43 @@ +# coding: utf-8 + +import smtplib + +from email import encoders +from email.header import Header +from email.mime.text import MIMEText +from email.utils import parseaddr, formataddr + + +def _format_addr(s): + name, addr = parseaddr(s) + return formataddr((Header(name, 'utf-8').encode(), addr)) + + +def send(smtp_host, smtp_port, username, password, to_mail, subject, content): + + smtp = smtplib.SMTP() + smtp.connect(smtp_host, port=smtp_port) + smtp.login(user=username, password=password) + + msg = MIMEText(content, 'plain', 'utf-8') + msg['From'] = _format_addr(username) + msg['To'] = _format_addr(to_mail) + msg['Subject'] = Header(subject, 'utf-8').encode() + + smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string()) + return True + + +def sendSSL(smtp_host, smtp_port, username, password, to_mail, subject, content): + + smtp = smtplib.SMTP_SSL(smtp_host, port=smtp_port) + smtp.login(user=username, password=password) + + msg = MIMEText(content, 'plain', 'utf-8') + msg['From'] = _format_addr(username) + msg['To'] = _format_addr(to_mail) + msg['Subject'] = Header(subject, 'utf-8').encode() + + smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string()) + smtp.quit() + return True From 8f1ec01b585d5d622a06c68ccda5b394222d8f59 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 18:26:46 +0800 Subject: [PATCH 138/965] update --- web/admin/setting/notify_email.py | 4 ++-- web/core/mw.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/web/admin/setting/notify_email.py b/web/admin/setting/notify_email.py index a71bceb10..ce38e1cb4 100644 --- a/web/admin/setting/notify_email.py +++ b/web/admin/setting/notify_email.py @@ -61,9 +61,9 @@ def set_notify_email_test(): data = json.loads(tag_data) test_pass = mw.emailNotifyTest(data) - if test_pass: + if test_pass == True: return mw.returnData(True, '验证成功') - return mw.returnData(False, '验证失败') + return mw.returnData(False, '验证失败:'+test_pass) # 切换邮件开关 @blueprint.route('/set_notify_email_enable', endpoint='set_notify_email_enable', methods=['POST']) diff --git a/web/core/mw.py b/web/core/mw.py index deab5a480..3dc278889 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -1198,16 +1198,19 @@ def emailNotifyMessage(data): import utils.email as email try: if data['smtp_ssl'] == 'ssl': - email.sendSSL(data['smtp_host'], data['smtp_port'], + r = email.sendSSL(data['smtp_host'], data['smtp_port'], data['username'], data['password'], data['to_mail_addr'], data['subject'], data['content']) else: - email.send(data['smtp_host'], data['smtp_port'], + r = email.send(data['smtp_host'], data['smtp_port'], data['username'], data['password'], data['to_mail_addr'], data['subject'], data['content']) + + print(r) return True except Exception as e: print(getTracebackInfo()) + return str(e) return False From 8ed2f6848b93d87b66e81f4e4ada2328a5a4e654 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 19:26:16 +0800 Subject: [PATCH 139/965] update --- web/admin/site/__init__.py | 42 ++++++++++++++ web/static/app/site.js | 2 +- web/utils/site.py | 111 +++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 9695c359c..060f6dede 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -76,6 +76,48 @@ def get_root_dir(): data['dir'] = mw.getWwwDir() return data +# 获取站点默认文档 +@blueprint.route('/get_index', endpoint='get_index',methods=['POST']) +@panel_login_required +def get_index(): + site_id = request.form.get('id', '') + data = {} + index = MwSites.instance().getIndex(site_id) + data['index'] = index + return data + +# 获取站点默认文档 +@blueprint.route('/set_index', endpoint='set_index',methods=['POST']) +@panel_login_required +def set_index(): + site_id = request.form.get('id', '') + index = request.form.get('index', '') + return MwSites.instance().setIndex(site_id, index) + +# 获取站点默认文档 +@blueprint.route('/get_limit_net', endpoint='get_limit_net',methods=['POST']) +@panel_login_required +def get_limit_net(): + site_id = request.form.get('id', '') + return MwSites.instance().getLimitNet(site_id) + +# 获取站点默认文档 +@blueprint.route('/set_limit_net', endpoint='set_limit_net',methods=['POST']) +@panel_login_required +def set_limit_net(): + site_id = request.form.get('id', '') + perserver = request.form.get('perserver', '') + perip = request.form.get('perip', '') + limit_rate = request.form.get('limit_rate', '') + return MwSites.instance().setLimitNet(site_id, perserver,perip,limit_rate) + +# 获取站点默认文档 +@blueprint.route('/close_limit_net', endpoint='close_limit_net',methods=['POST']) +@panel_login_required +def close_limit_net(): + site_id = request.form.get('id', '') + return MwSites.instance().closeLimitNet(site_id) + # 获取站点配置 @blueprint.route('/get_host_conf', endpoint='get_host_conf',methods=['POST']) @panel_login_required diff --git a/web/static/app/site.js b/web/static/app/site.js index cf7e05ae7..1284891a8 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1246,7 +1246,7 @@ function saveLimitNet(id, type){ 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){ + $.post('/site/set_limit_net',data,function(rdata){ layer.close(loadT); limitNet(id); layer.msg(rdata.msg,{icon:rdata.status?1:2}); diff --git a/web/utils/site.py b/web/utils/site.py index 4636a6381..c1d05a092 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -115,6 +115,7 @@ class sites(object): def getIndexConf(self): return mw.getServerDir() + '/openresty/nginx/conf/nginx.conf' + # 路径处理 def getPath(self, path): if path[-1] == '/': @@ -297,6 +298,116 @@ class sites(object): data['phpversion'] = def_pver return data + def getIndex(self, site_id): + info = thisdb.getSitesById(site_id) + file = self.getHostConf(info['name']) + conf = mw.readFile(file) + rep = r"\s+index\s+(.+);" + tmp = re.search(rep, conf).groups() + return tmp[0].replace(' ', ',') + + def setIndex(self, site_id, index): + if index.find('.') == -1: + return mw.returnData(False, '默认文档格式不正确,例:index.html') + + index = index.replace(' ', '') + index = index.replace(',,', ',') + + if len(index) < 3: + return mw.returnData(False, '默认文档不能为空!') + + info = thisdb.getSitesById(site_id) + siteName = info['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('网站管理', '站点[{1}]设置{2}成功', (siteName, index_l)) + return mw.returnJson(True, '设置成功!') + + def getLimitNet(self, site_id): + info = thisdb.getSitesById(site_id) + siteName = info['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 data + + def setLimitNet(self, site_id, perserver, perip, limit_rate): + str_perserver = 'limit_conn perserver ' + perserver + ';' + str_perip = 'limit_conn perip ' + perip + ';' + str_limit_rate = 'limit_rate ' + limit_rate + 'k;' + + info = thisdb.getSitesById(site_id) + siteName = info['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('网站管理', '网站[{1}]流量限制已开启!', (siteName,)) + return mw.returnData(True, '设置成功!') + + def closeLimitNet(self, site_id): + info = thisdb.getSitesById(site_id) + siteName = info['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('网站管理', '网站[{1}]流量限制已关闭!', (siteName,)) + return mw.returnData(True, '已关闭流量限制!') def setPhpVersion(self, siteName, version): # nginx From 5c24373a3200c3264f2bf58efb149c379ce217c2 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 19:35:48 +0800 Subject: [PATCH 140/965] Update __init__.py --- web/admin/site/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 060f6dede..894ee36b5 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -224,7 +224,7 @@ def get_cli_php_version(): if link_re.find(v['version']) != -1: return mw.returnData({"select": v, "versions": php_versions}) - return mw.getJson({"select": php_versions[0],"versions": php_versions}) + return {"select": php_versions[0],"versions": php_versions} @blueprint.route('/set_cli_php_version', endpoint='set_cli_php_version',methods=['POST']) @panel_login_required From e1e7fd51cd06bba576dc032c6ef3cdb2418b953e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 19:56:37 +0800 Subject: [PATCH 141/965] update --- web/admin/site/__init__.py | 31 +++++++++++++++++++++++-------- web/core/db.py | 7 +++++-- web/thisdb/domain.py | 3 ++- web/utils/site.py | 12 +++++++++++- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 894ee36b5..ebefe1e3f 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -183,6 +183,28 @@ def set_ps(): ps = request.form.get('ps', '') return MwSites.instance().setPs(site_id, ps) +# 站点绑定域名 +@blueprint.route('/get_domain', endpoint='get_domain',methods=['POST']) +@panel_login_required +def get_domain(): + site_id = request.form.get('pid', '') + return MwSites.instance().getDomain(site_id) + +# 获取默认为静态列表 +@blueprint.route('/get_rewrite_list', endpoint='get_rewrite_list',methods=['POST']) +@panel_login_required +def get_rewrite_list(): + return MwSites.instance().getRewriteList() + +# 获取默认为静态列表 +@blueprint.route('/get_rewrite_conf', endpoint='get_rewrite_conf',methods=['POST']) +@panel_login_required +def get_rewrite_conf(): + siteName = request.form.get('siteName', '') + rewrite = MwSites.instance().getRewriteConf(siteName) + return {'rewrite': rewrite} + + # 设置默认网站信息 @blueprint.route('/get_default_site', endpoint='get_default_site',methods=['POST']) @@ -190,14 +212,7 @@ def set_ps(): def get_default_site(): return MwSites.instance().getDefaultSite() -# 站点删除 -@blueprint.route('/get_domain', endpoint='get_domain',methods=['POST']) -@panel_login_required -def get_domain(): - site_id = request.form.get('id', '') - return MwSites.instance().getDomain(site_id) - - +# 设置默认站 @blueprint.route('/set_default_site', endpoint='set_default_site',methods=['POST']) @panel_login_required def set_default_site(): diff --git a/web/core/db.py b/web/core/db.py index 6550f77c8..0d63b222b 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -147,8 +147,11 @@ class Sql(): try: sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - # print(sql) - # print(self.__OPT_PARAM) + + if self.__debug: + print(sql) + print(self.__OPT_PARAM) + result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) data = result.fetchall() if len(data) == 0: diff --git a/web/thisdb/domain.py b/web/thisdb/domain.py index 0bb9ef126..fb163a624 100644 --- a/web/thisdb/domain.py +++ b/web/thisdb/domain.py @@ -23,7 +23,8 @@ def addDomain(pid, name, port): return mw.M('domain').insert(insert_data) def getDomainByPid(pid): - return mw.M('domain').where("pid=?", (pid,)).field(__FIELD).select() + # .debug(True) + return mw.M('domain').field(__FIELD).where("pid=?", (pid,)).select() def deleteDomainId(domain_id): diff --git a/web/utils/site.py b/web/utils/site.py index c1d05a092..e27219977 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -281,8 +281,18 @@ class sites(object): # 获取域名列表 def getDomain(self, pid): - return thisdb.getDomainByPid(pid) + data = thisdb.getDomainByPid(pid) + return data + def getRewriteList(self): + rewriteList = {} + rewriteList['rewrite'] = [] + rewriteList['rewrite'].append('0.当前') + rewrite_nginx_dir = mw.getPanelDir() + '/rewrite/nginx' + for ds in os.listdir(rewrite_nginx_dir): + rewriteList['rewrite'].append(ds[0:len(ds) - 5]) + rewriteList['rewrite'] = sorted(rewriteList['rewrite']) + return rewriteList def getSitePhpVersion(self, siteName): conf = mw.readFile(self.getHostConf(siteName)) From 03e37a2b8887f9b4ae395f6b1b92c9b408c5cf26 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 20:06:42 +0800 Subject: [PATCH 142/965] Update __init__.py --- web/admin/site/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index ebefe1e3f..373d29cce 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -196,7 +196,7 @@ def get_domain(): def get_rewrite_list(): return MwSites.instance().getRewriteList() -# 获取默认为静态列表 +# 获取站点Rewrite配置 @blueprint.route('/get_rewrite_conf', endpoint='get_rewrite_conf',methods=['POST']) @panel_login_required def get_rewrite_conf(): @@ -204,8 +204,6 @@ def get_rewrite_conf(): rewrite = MwSites.instance().getRewriteConf(siteName) return {'rewrite': rewrite} - - # 设置默认网站信息 @blueprint.route('/get_default_site', endpoint='get_default_site',methods=['POST']) @panel_login_required From 260c37f63b6a4d477f89ce9c04137582ffca0889 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 8 Nov 2024 21:29:01 +0800 Subject: [PATCH 143/965] update --- web/admin/files/__init__.py | 18 +------ web/admin/site/__init__.py | 30 ++++++++++++ web/core/mw.py | 37 ++++++++++++++ web/utils/file.py | 19 +++++++ web/utils/site.py | 98 +++++++++++++++++++++++++++++++++++++ 5 files changed, 185 insertions(+), 17 deletions(-) diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py index 42d0b3a20..12c7ce63a 100644 --- a/web/admin/files/__init__.py +++ b/web/admin/files/__init__.py @@ -42,23 +42,7 @@ def save_body(): path = request.form.get('path', '') data = request.form.get('data', '') encoding = request.form.get('encoding', '') - if not os.path.exists(path): - return mw.returnData(False, '文件不存在') - try: - if encoding == 'ascii': - encoding = 'utf-8' - - data = data.encode(encoding, errors='ignore').decode(encoding) - fp = open(path, 'w+', encoding=encoding) - fp.write(data) - fp.close() - - if path.find("web_conf") > 0: - mw.restartWeb() - mw.writeLog('文件管理', '文件[{1}]保存成功', (path,)) - return mw.returnData(True, '文件保存成功') - except Exception as ex: - return mw.returnData(False, '文件保存错误:' + str(ex)) + return file.saveBody(path,data,encoding) # 获取文件内容(最新行数) @blueprint.route('/get_last_body', endpoint='get_file_last_body', methods=['POST']) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 373d29cce..076608ccc 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -126,6 +126,14 @@ def get_host_conf(): host = MwSites.instance().getHostConf(siteName) return {'host': host} +# 设置站点配置 +@blueprint.route('/save_host_conf', endpoint='save_host_conf',methods=['POST']) +@panel_login_required +def save_host_conf(): + path = request.form.get('path', '') + data = request.form.get('data', '') + encoding = request.form.get('encoding', '') + return MwSites.instance().saveHostConf(path,data,encoding) # 获取站点PHP版本 @blueprint.route('/get_site_php_version', endpoint='get_site_php_version',methods=['POST']) @@ -204,6 +212,28 @@ def get_rewrite_conf(): rewrite = MwSites.instance().getRewriteConf(siteName) return {'rewrite': rewrite} +# 获取防盗链信息 +@blueprint.route('/get_security', endpoint='get_security',methods=['POST']) +@panel_login_required +def get_security(): + site_id = request.form.get('id', '') + return MwSites.instance().getSecurity(site_id) + +# 设置防盗链 +@blueprint.route('/set_security', endpoint='set_security',methods=['POST']) +@panel_login_required +def set_security(): + fix = request.form.get('fix', '') + domains = request.form.get('domains', '') + status = request.form.get('status', '') + name = request.form.get('name', '') + none = request.form.get('none', '') + site_id = request.form.get('id', '') + return MwSites.instance().setSecurity(site_id, fix, domains, status, none) + + + + # 设置默认网站信息 @blueprint.route('/get_default_site', endpoint='get_default_site',methods=['POST']) @panel_login_required diff --git a/web/core/mw.py b/web/core/mw.py index 3dc278889..50f6561ff 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -213,6 +213,43 @@ def writeFile(filename, content, mode='w+'): return False +def backFile(file, act=None): + """ + @name 备份配置文件 + @param file 需要备份的文件 + @param act 如果存在,则备份一份作为默认配置 + """ + file_type = "_bak" + if act: + file_type = "_def" + + # print("cp -p {0} {1}".format(file, file + file_type)) + execShell("cp -p {0} {1}".format(file, file + file_type)) + + +def removeBackFile(file, act=None): + """ + @name 删除备份配置文件 + @param file 需要删除备份文件 + @param act 如果存在,则还原默认配置 + """ + file_type = "_bak" + if act: + file_type = "_def" + execShell("rm -rf {0}".format(file + file_type)) + + +def restoreFile(file, act=None): + """ + @name 还原配置文件 + @param file 需要还原的文件 + @param act 如果存在,则还原默认配置 + """ + file_type = "_bak" + if act: + file_type = "_def" + execShell("cp -p {1} {0}".format(file, file + file_type)) + def systemdCfgDir(): # ubuntu cfg_dir = '/lib/systemd/system' diff --git a/web/utils/file.py b/web/utils/file.py index 69203efeb..d962bf9f6 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -47,6 +47,25 @@ def getFileBody(path): return mw.returnData(False, '文件未正常打开!') return mw.returnData(True, 'OK', data) +def saveBody(path, data, encoding): + if not os.path.exists(path): + return mw.returnData(False, '文件不存在') + try: + if encoding == 'ascii': + encoding = 'utf-8' + + data = data.encode(encoding, errors='ignore').decode(encoding) + fp = open(path, 'w+', encoding=encoding) + fp.write(data) + fp.close() + + if path.find("web_conf") > 0: + mw.restartWeb() + mw.writeLog('文件管理', '文件[{1}]保存成功', (path,)) + return mw.returnData(True, '文件保存成功') + except Exception as ex: + return mw.returnData(False, '文件保存错误:' + str(ex)) + # 获取文件权限描述 def getFileStatsDesc( filename: str | None = None, diff --git a/web/utils/site.py b/web/utils/site.py index e27219977..51baf563d 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -94,6 +94,21 @@ class sites(object): def getHostConf(self, siteName): return self.vhostPath + '/' + siteName + '.conf' + def saveHostConf(self, path, data, encoding): + import utils.file as file + mw.backFile(path) + rdata = file.saveBody(path, data, encoding) + + if rdata['status']: + isError = mw.checkWebConfig() + if isError != True: + mw.restoreFile(path) + msg = 'ERROR: 检测到配置文件有错误,请先排除后再操作

    ' + isError.replace("\n", '
    ') + '
    ' + return mw.returnData(False, msg) + mw.restartWeb() + mw.removeBackFile(path) + return rdata + def getRewriteConf(self, siteName): return self.rewritePath + '/' + siteName + '.conf' @@ -294,6 +309,89 @@ class sites(object): rewriteList['rewrite'] = sorted(rewriteList['rewrite']) return rewriteList + def getSecurity(self, site_id): + info = thisdb.getSitesById(site_id) + name = info['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 + else: + data['fix'] = 'gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2' + domains = thisdb.getDomainByPid(site_id) + tmp = [] + for domain in domains: + tmp.append(domain['name']) + data['domains'] = ','.join(tmp) + data['status'] = False + data['none'] = False + return data + + def setSecurity(self, site_id, fix, domains, status, none=''): + info = thisdb.getSitesById(site_id) + name = info['name'] + + if len(fix) < 2: + return mw.returnData(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.returnData(True, '设置成功!') + def getSitePhpVersion(self, siteName): conf = mw.readFile(self.getHostConf(siteName)) rep = r"enable-php-(.*)\.conf" From f1a74e6275e62616eb1145163125e5634defbbae Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 9 Nov 2024 01:27:32 +0800 Subject: [PATCH 144/965] update --- web/admin/site/__init__.py | 272 +---------------------------------- web/admin/site/site.py | 272 +++++++++++++++++++++++++++++++++++ web/admin/site/site_types.py | 40 ++++++ web/thisdb/__init__.py | 1 + web/thisdb/site_types.py | 19 +++ web/utils/site.py | 12 ++ 6 files changed, 346 insertions(+), 270 deletions(-) create mode 100644 web/admin/site/site.py create mode 100644 web/admin/site/site_types.py create mode 100644 web/thisdb/site_types.py diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 076608ccc..bacb102b1 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -8,273 +8,5 @@ # Author: midoks # --------------------------------------------------------------------------------- -import os - -from flask import Blueprint, render_template -from flask import request - -from admin.model import Sites -from admin.user_login_check import panel_login_required - -from utils.plugin import plugin as MwPlugin -from utils.site import sites as MwSites -import utils.site as site -import core.mw as mw -import thisdb - -blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') -@blueprint.route('/index', endpoint='index') -def index(): - return render_template('site.html') - -# 站点列表 -@blueprint.route('/list', endpoint='list', methods=['GET','POST']) -@panel_login_required -def list(): - p = request.form.get('p', '1') - limit = request.form.get('limit', '10') - type_id = request.form.get('type_id', '0').strip() - search = request.form.get('search', '').strip() - - info = thisdb.getSitesList(page=int(p),size=int(limit),type_id=int(type_id), search=search) - - data = {} - data['data'] = info['list'] - data['page'] = mw.getPage({'count':info['count'],'tojs':'getWeb','p':p, 'row':limit}) - return data - -# 添加站点 -@blueprint.route('/add', endpoint='add',methods=['POST']) -@panel_login_required -def add(): - 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 MwSites.instance().add(webname, port, ps, path, version) - -# 站点删除 -@blueprint.route('/delete', endpoint='delete',methods=['POST']) -@panel_login_required -def delete(): - site_id = request.form.get('id', '') - path = request.form.get('path', '') - return MwSites.instance().delete(site_id, path) - -# 获取站点类型 -@blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) -@panel_login_required -def get_site_types(): - return [] - -# 获取站点根目录 -@blueprint.route('/get_root_dir', endpoint='get_root_dir',methods=['POST']) -@panel_login_required -def get_root_dir(): - data = {} - data['dir'] = mw.getWwwDir() - return data - -# 获取站点默认文档 -@blueprint.route('/get_index', endpoint='get_index',methods=['POST']) -@panel_login_required -def get_index(): - site_id = request.form.get('id', '') - data = {} - index = MwSites.instance().getIndex(site_id) - data['index'] = index - return data - -# 获取站点默认文档 -@blueprint.route('/set_index', endpoint='set_index',methods=['POST']) -@panel_login_required -def set_index(): - site_id = request.form.get('id', '') - index = request.form.get('index', '') - return MwSites.instance().setIndex(site_id, index) - -# 获取站点默认文档 -@blueprint.route('/get_limit_net', endpoint='get_limit_net',methods=['POST']) -@panel_login_required -def get_limit_net(): - site_id = request.form.get('id', '') - return MwSites.instance().getLimitNet(site_id) - -# 获取站点默认文档 -@blueprint.route('/set_limit_net', endpoint='set_limit_net',methods=['POST']) -@panel_login_required -def set_limit_net(): - site_id = request.form.get('id', '') - perserver = request.form.get('perserver', '') - perip = request.form.get('perip', '') - limit_rate = request.form.get('limit_rate', '') - return MwSites.instance().setLimitNet(site_id, perserver,perip,limit_rate) - -# 获取站点默认文档 -@blueprint.route('/close_limit_net', endpoint='close_limit_net',methods=['POST']) -@panel_login_required -def close_limit_net(): - site_id = request.form.get('id', '') - return MwSites.instance().closeLimitNet(site_id) - -# 获取站点配置 -@blueprint.route('/get_host_conf', endpoint='get_host_conf',methods=['POST']) -@panel_login_required -def get_host_conf(): - siteName = request.form.get('siteName', '') - host = MwSites.instance().getHostConf(siteName) - return {'host': host} - -# 设置站点配置 -@blueprint.route('/save_host_conf', endpoint='save_host_conf',methods=['POST']) -@panel_login_required -def save_host_conf(): - path = request.form.get('path', '') - data = request.form.get('data', '') - encoding = request.form.get('encoding', '') - return MwSites.instance().saveHostConf(path,data,encoding) - -# 获取站点PHP版本 -@blueprint.route('/get_site_php_version', endpoint='get_site_php_version',methods=['POST']) -@panel_login_required -def get_site_php_version(): - siteName = request.form.get('siteName', '') - return MwSites.instance().getSitePhpVersion(siteName) - - -# 设置站点PHP版本 -@blueprint.route('/set_php_version', endpoint='set_php_version',methods=['POST']) -@panel_login_required -def set_php_version(): - siteName = request.form.get('siteName', '') - version = request.form.get('version', '') - return MwSites.instance().setPhpVersion(siteName,version) - -# 检查OpenResty安装/启动状态 -@blueprint.route('/check_web_status', endpoint='check_web_status',methods=['POST']) -@panel_login_required -def check_web_status(): - ''' - 创建站点检查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.returnData(False, '请启动OpenResty服务!') - return mw.returnData(True, 'OK') - -# 获取PHP版本 -@blueprint.route('/get_php_version', endpoint='get_php_version',methods=['POST']) -@panel_login_required -def get_php_version(): - return MwSites.instance().getPhpVersion() - - -# 设置网站到期 -@blueprint.route('/set_end_date', endpoint='set_end_date',methods=['POST']) -@panel_login_required -def set_end_date(): - site_id = request.form.get('id', '') - edate = request.form.get('edate', '') - return MwSites.instance().setEndDate(site_id, edate) - - -# 设置网站备注 -@blueprint.route('/set_ps', endpoint='set_ps',methods=['POST']) -@panel_login_required -def set_ps(): - site_id = request.form.get('id', '') - ps = request.form.get('ps', '') - return MwSites.instance().setPs(site_id, ps) - -# 站点绑定域名 -@blueprint.route('/get_domain', endpoint='get_domain',methods=['POST']) -@panel_login_required -def get_domain(): - site_id = request.form.get('pid', '') - return MwSites.instance().getDomain(site_id) - -# 获取默认为静态列表 -@blueprint.route('/get_rewrite_list', endpoint='get_rewrite_list',methods=['POST']) -@panel_login_required -def get_rewrite_list(): - return MwSites.instance().getRewriteList() - -# 获取站点Rewrite配置 -@blueprint.route('/get_rewrite_conf', endpoint='get_rewrite_conf',methods=['POST']) -@panel_login_required -def get_rewrite_conf(): - siteName = request.form.get('siteName', '') - rewrite = MwSites.instance().getRewriteConf(siteName) - return {'rewrite': rewrite} - -# 获取防盗链信息 -@blueprint.route('/get_security', endpoint='get_security',methods=['POST']) -@panel_login_required -def get_security(): - site_id = request.form.get('id', '') - return MwSites.instance().getSecurity(site_id) - -# 设置防盗链 -@blueprint.route('/set_security', endpoint='set_security',methods=['POST']) -@panel_login_required -def set_security(): - fix = request.form.get('fix', '') - domains = request.form.get('domains', '') - status = request.form.get('status', '') - name = request.form.get('name', '') - none = request.form.get('none', '') - site_id = request.form.get('id', '') - return MwSites.instance().setSecurity(site_id, fix, domains, status, none) - - - - -# 设置默认网站信息 -@blueprint.route('/get_default_site', endpoint='get_default_site',methods=['POST']) -@panel_login_required -def get_default_site(): - return MwSites.instance().getDefaultSite() - -# 设置默认站 -@blueprint.route('/set_default_site', endpoint='set_default_site',methods=['POST']) -@panel_login_required -def set_default_site(): - name = request.form.get('name', '') - return MwSites.instance().setDefaultSite(name) - -@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) -@panel_login_required -def get_cli_php_version(): - php_dir = mw.getServerDir() + '/php' - if not os.path.exists(php_dir): - return mw.returnData(False, '未安装PHP,无法设置') - - php_bin = '/usr/bin/php' - php_versions = MwSites.instance().getPhpVersion() - php_versions = php_versions[1:] - - if len(php_versions) < 1: - return mw.returnData(False, '未安装PHP,无法设置') - - if os.path.exists(php_bin) and os.path.islink(php_bin): - link_re = os.readlink(php_bin) - for v in php_versions: - if link_re.find(v['version']) != -1: - return mw.returnData({"select": v, "versions": php_versions}) - - return {"select": php_versions[0],"versions": php_versions} - -@blueprint.route('/set_cli_php_version', endpoint='set_cli_php_version',methods=['POST']) -@panel_login_required -def set_cli_php_version(): - if mw.isAppleSystem(): - return mw.returnData(False, "开发机不可设置!") - version = request.form.get('version', '') - return MwSites.instance().setCliPhpVersion(version) - - +from .site import * +from .site_types import * \ No newline at end of file diff --git a/web/admin/site/site.py b/web/admin/site/site.py new file mode 100644 index 000000000..8d628c74e --- /dev/null +++ b/web/admin/site/site.py @@ -0,0 +1,272 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.model import Sites +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') +@blueprint.route('/index', endpoint='index') +def index(): + return render_template('site.html') + +# 站点列表 +@blueprint.route('/list', endpoint='list', methods=['GET','POST']) +@panel_login_required +def list(): + p = request.form.get('p', '1') + limit = request.form.get('limit', '10') + type_id = request.form.get('type_id', '0').strip() + search = request.form.get('search', '').strip() + + info = thisdb.getSitesList(page=int(p),size=int(limit),type_id=int(type_id), search=search) + + data = {} + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':'getWeb','p':p, 'row':limit}) + return data + +# 添加站点 +@blueprint.route('/add', endpoint='add',methods=['POST']) +@panel_login_required +def add(): + 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 MwSites.instance().add(webname, port, ps, path, version) + +# 站点删除 +@blueprint.route('/delete', endpoint='delete',methods=['POST']) +@panel_login_required +def delete(): + site_id = request.form.get('id', '') + path = request.form.get('path', '') + return MwSites.instance().delete(site_id, path) + +# 获取站点根目录 +@blueprint.route('/get_root_dir', endpoint='get_root_dir',methods=['POST']) +@panel_login_required +def get_root_dir(): + data = {} + data['dir'] = mw.getWwwDir() + return data + +# 获取站点默认文档 +@blueprint.route('/get_index', endpoint='get_index',methods=['POST']) +@panel_login_required +def get_index(): + site_id = request.form.get('id', '') + data = {} + index = MwSites.instance().getIndex(site_id) + data['index'] = index + return data + +# 获取站点默认文档 +@blueprint.route('/set_index', endpoint='set_index',methods=['POST']) +@panel_login_required +def set_index(): + site_id = request.form.get('id', '') + index = request.form.get('index', '') + return MwSites.instance().setIndex(site_id, index) + +# 获取站点默认文档 +@blueprint.route('/get_limit_net', endpoint='get_limit_net',methods=['POST']) +@panel_login_required +def get_limit_net(): + site_id = request.form.get('id', '') + return MwSites.instance().getLimitNet(site_id) + +# 获取站点默认文档 +@blueprint.route('/set_limit_net', endpoint='set_limit_net',methods=['POST']) +@panel_login_required +def set_limit_net(): + site_id = request.form.get('id', '') + perserver = request.form.get('perserver', '') + perip = request.form.get('perip', '') + limit_rate = request.form.get('limit_rate', '') + return MwSites.instance().setLimitNet(site_id, perserver,perip,limit_rate) + +# 获取站点默认文档 +@blueprint.route('/close_limit_net', endpoint='close_limit_net',methods=['POST']) +@panel_login_required +def close_limit_net(): + site_id = request.form.get('id', '') + return MwSites.instance().closeLimitNet(site_id) + +# 获取站点配置 +@blueprint.route('/get_host_conf', endpoint='get_host_conf',methods=['POST']) +@panel_login_required +def get_host_conf(): + siteName = request.form.get('siteName', '') + host = MwSites.instance().getHostConf(siteName) + return {'host': host} + +# 设置站点配置 +@blueprint.route('/save_host_conf', endpoint='save_host_conf',methods=['POST']) +@panel_login_required +def save_host_conf(): + path = request.form.get('path', '') + data = request.form.get('data', '') + encoding = request.form.get('encoding', '') + return MwSites.instance().saveHostConf(path,data,encoding) + +# 获取站点PHP版本 +@blueprint.route('/get_site_php_version', endpoint='get_site_php_version',methods=['POST']) +@panel_login_required +def get_site_php_version(): + siteName = request.form.get('siteName', '') + return MwSites.instance().getSitePhpVersion(siteName) + + +# 设置站点PHP版本 +@blueprint.route('/set_php_version', endpoint='set_php_version',methods=['POST']) +@panel_login_required +def set_php_version(): + siteName = request.form.get('siteName', '') + version = request.form.get('version', '') + return MwSites.instance().setPhpVersion(siteName,version) + +# 检查OpenResty安装/启动状态 +@blueprint.route('/check_web_status', endpoint='check_web_status',methods=['POST']) +@panel_login_required +def check_web_status(): + ''' + 创建站点检查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.returnData(False, '请启动OpenResty服务!') + return mw.returnData(True, 'OK') + +# 获取PHP版本 +@blueprint.route('/get_php_version', endpoint='get_php_version',methods=['POST']) +@panel_login_required +def get_php_version(): + return MwSites.instance().getPhpVersion() + + +# 设置网站到期 +@blueprint.route('/set_end_date', endpoint='set_end_date',methods=['POST']) +@panel_login_required +def set_end_date(): + site_id = request.form.get('id', '') + edate = request.form.get('edate', '') + return MwSites.instance().setEndDate(site_id, edate) + + +# 设置网站备注 +@blueprint.route('/set_ps', endpoint='set_ps',methods=['POST']) +@panel_login_required +def set_ps(): + site_id = request.form.get('id', '') + ps = request.form.get('ps', '') + return MwSites.instance().setPs(site_id, ps) + +# 站点绑定域名 +@blueprint.route('/get_domain', endpoint='get_domain',methods=['POST']) +@panel_login_required +def get_domain(): + site_id = request.form.get('pid', '') + return MwSites.instance().getDomain(site_id) + +# 获取默认为静态列表 +@blueprint.route('/get_rewrite_list', endpoint='get_rewrite_list',methods=['POST']) +@panel_login_required +def get_rewrite_list(): + return MwSites.instance().getRewriteList() + +# 获取站点Rewrite配置 +@blueprint.route('/get_rewrite_conf', endpoint='get_rewrite_conf',methods=['POST']) +@panel_login_required +def get_rewrite_conf(): + siteName = request.form.get('siteName', '') + rewrite = MwSites.instance().getRewriteConf(siteName) + return {'rewrite': rewrite} + +# 获取防盗链信息 +@blueprint.route('/get_security', endpoint='get_security',methods=['POST']) +@panel_login_required +def get_security(): + site_id = request.form.get('id', '') + return MwSites.instance().getSecurity(site_id) + +# 设置防盗链 +@blueprint.route('/set_security', endpoint='set_security',methods=['POST']) +@panel_login_required +def set_security(): + fix = request.form.get('fix', '') + domains = request.form.get('domains', '') + status = request.form.get('status', '') + name = request.form.get('name', '') + none = request.form.get('none', '') + site_id = request.form.get('id', '') + return MwSites.instance().setSecurity(site_id, fix, domains, status, none) + + +# 设置默认网站信息 +@blueprint.route('/get_default_site', endpoint='get_default_site',methods=['POST']) +@panel_login_required +def get_default_site(): + return MwSites.instance().getDefaultSite() + +# 设置默认站 +@blueprint.route('/set_default_site', endpoint='set_default_site',methods=['POST']) +@panel_login_required +def set_default_site(): + name = request.form.get('name', '') + return MwSites.instance().setDefaultSite(name) + +@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) +@panel_login_required +def get_cli_php_version(): + php_dir = mw.getServerDir() + '/php' + if not os.path.exists(php_dir): + return mw.returnData(False, '未安装PHP,无法设置') + + php_bin = '/usr/bin/php' + php_versions = MwSites.instance().getPhpVersion() + php_versions = php_versions[1:] + + if len(php_versions) < 1: + return mw.returnData(False, '未安装PHP,无法设置') + + if os.path.exists(php_bin) and os.path.islink(php_bin): + link_re = os.readlink(php_bin) + for v in php_versions: + if link_re.find(v['version']) != -1: + return mw.returnData({"select": v, "versions": php_versions}) + + return {"select": php_versions[0],"versions": php_versions} + +@blueprint.route('/set_cli_php_version', endpoint='set_cli_php_version',methods=['POST']) +@panel_login_required +def set_cli_php_version(): + if mw.isAppleSystem(): + return mw.returnData(False, "开发机不可设置!") + version = request.form.get('version', '') + return MwSites.instance().setCliPhpVersion(version) + + diff --git a/web/admin/site/site_types.py b/web/admin/site/site_types.py new file mode 100644 index 000000000..62104549a --- /dev/null +++ b/web/admin/site/site_types.py @@ -0,0 +1,40 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取网站分类 +@blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) +@panel_login_required +def get_site_types(): + data = thisdb.getSiteTypesList() + data.insert(0, {"id": 0, "name": "默认分类"}) + return data + + +# 添加网站分类 +@blueprint.route('/add_site_type', endpoint='add_site_type',methods=['POST']) +@panel_login_required +def add_site_type(): + name = request.form.get('name', '').strip() + return MwSites.instance().addSiteTypes(name) \ No newline at end of file diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 451473e59..31924c2de 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -21,3 +21,4 @@ from .logs import * from .crontab import * from .firewall import * from .temp_login import * +from .site_types import * diff --git a/web/thisdb/site_types.py b/web/thisdb/site_types.py new file mode 100644 index 000000000..82d64267e --- /dev/null +++ b/web/thisdb/site_types.py @@ -0,0 +1,19 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +__FIELD = 'id,name' + +import core.mw as mw + +def getSiteTypesList(): + # .debug(True) + return mw.M('site_types').field(__FIELD).order("id asc").select() + + diff --git a/web/utils/site.py b/web/utils/site.py index 51baf563d..4c49b7c7a 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -587,6 +587,18 @@ class sites(object): mw.writeLog('面板设置', '设置PHP-CLI版本为: %s' % version) return mw.returnData(True, '设置成功!') + def addSiteTypes(name): + if not name: + return mw.returnData(False, "分类名称不能为空") + if len(name) > 18: + return mw.returnData(False, "分类名称长度不能超过6个汉字或18位字母") + if mw.M('site_types').count() >= 10: + return mw.returnData(False, '最多添加10个分类!') + if mw.M('site_types').where('name=?', (name,)).count() > 0: + return mw.returnData(False, "指定分类名称已存在!") + mw.M('site_types').add("name", (name,)) + return mw.returnData(True, '添加成功!') + def getPhpVersion(self): phpVersions = ('00', '52', '53', '54', '55', From 020c8da4262dc4534ede3288c26cdc3425a48c16 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 9 Nov 2024 17:18:57 +0800 Subject: [PATCH 145/965] update --- web/admin/setting/__init__.py | 1 + web/admin/setting/notify_tgbot.py | 85 ++++++++++++++++++++++++++++++ web/admin/setup/option.py | 4 +- web/core/mw.py | 29 ++++++++++ web/static/app/config.js | 27 ++++++---- web/templates/default/setting.html | 4 +- web/utils/config.py | 1 + 7 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 web/admin/setting/notify_tgbot.py diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py index c516d49bf..8e01f4bc0 100644 --- a/web/admin/setting/__init__.py +++ b/web/admin/setting/__init__.py @@ -13,6 +13,7 @@ from .temp_login import * from .timezone import * from .secondary_verifiy import * from .notify_email import * +from .notify_tgbot import * diff --git a/web/admin/setting/notify_tgbot.py b/web/admin/setting/notify_tgbot.py new file mode 100644 index 000000000..f3bc11a5d --- /dev/null +++ b/web/admin/setting/notify_tgbot.py @@ -0,0 +1,85 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from .setting import blueprint +import thisdb + +# 获取邮件信息 +@blueprint.route('/get_notify_tgbot', endpoint='get_notify_tgbot', methods=['POST']) +@panel_login_required +def get_notify_tgbot(): + notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + if 'cfg' in notify_tgbot: + decrypt_data = mw.deDoubleCrypt('tgbot', notify_tgbot['cfg']) + notify_tgbot['tgbot'] = json.loads(decrypt_data) + else: + notify_tgbot['tgbot'] = [] + return mw.returnData(True,'ok',notify_tgbot) + + +# 设置邮件信息 +@blueprint.route('/set_notify_tgbot', endpoint='set_notify_tgbot', methods=['POST']) +@panel_login_required +def set_notify_tgbot(): + data = request.form.get('data', '').strip() + + crypt_data = mw.enDoubleCrypt('tgbot', data) + + notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + notify_tgbot['cfg'] = crypt_data + + thisdb.setOption('notify_tgbot', json.dumps(notify_tgbot), type='notify') + return mw.returnData(True,'设置成功') + + +# 设置邮件测试 +@blueprint.route('/set_notify_tgbot_test', endpoint='set_notify_tgbot_test', methods=['POST']) +@panel_login_required +def set_notify_tgbot_test(): + tag_data = request.form.get('data', '').strip() + + tmp = json.loads(tag_data) + test_pass = mw.tgbotNotifyTest(tmp['app_token'], tmp['chat_id']) + if test_pass == True: + return mw.returnData(True, '验证成功') + return mw.returnData(False, '验证失败:'+test_pass) + +# 切换邮件开关 +@blueprint.route('/set_notify_tgbot_enable', endpoint='set_notify_tgbot_enable', methods=['POST']) +@panel_login_required +def set_notify_tgbot_enable(): + tag = request.form.get('tag', '').strip() + data = request.form.get('data', '').strip() + + notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + + if notify_tgbot['open']: + op_action = '关闭' + notify_tgbot['open'] = False + else: + op_action = '开启' + notify_tgbot['open'] = True + + thisdb.setOption('notify_tgbot', json.dumps(notify_tgbot), type='notify') + return mw.returnData(True, op_action+'成功') \ No newline at end of file diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 8a3439d0d..09968a64d 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -59,7 +59,9 @@ def init_option(): thisdb.setOption('site_path', mw.getFatherDir()+'/wwwroot') - # 邮件通知 + # 异步邮件通知 thisdb.setOption('notify_email', json.dumps({'open':False}), type='notify') + # 异步Telegram Bot 通知 + thisdb.setOption('notify_tgbot', json.dumps({'open':False}), type='notify') return True \ No newline at end of file diff --git a/web/core/mw.py b/web/core/mw.py index 50f6561ff..70f0e391a 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -721,6 +721,34 @@ def writeLog(stype, msg, args=()): # writeFileLog(getTracebackInfo()) return writeDbLog(stype, msg, args, uid) +def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3): + log_file = getPanelDir() + '/logs/debug.log' + if path != None: + log_file = path + + if os.path.exists(log_file): + size = os.path.getsize(log_file) + if size > limit_size: + log_file_rename = log_file + "_" + \ + time.strftime("%Y-%m-%d_%H%M%S") + '.log' + os.rename(log_file, log_file_rename) + logs = sorted(glob.glob(log_file + "_*")) + count = len(logs) + save_limit = count - save_limit + for i in range(count): + if i > save_limit: + break + os.remove(logs[i]) + # print('|---多余日志[' + logs[i] + ']已删除!') + + f = open(log_file, 'ab+') + msg += "\n" + if __name__ == '__main__': + print(msg) + f.write(msg.encode('utf-8')) + f.close() + return True + def writeDbLog(stype, msg, args=(), uid=1): try: import thisdb @@ -1220,6 +1248,7 @@ def tgbotNotifyHttpPost(app_token, chat_id, msg): return True except Exception as e: writeFileLog(str(e)) + return str(e) return False diff --git a/web/static/app/config.js b/web/static/app/config.js index c8594eaff..b3c662b9c 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -540,10 +540,19 @@ function setPanelSSL(){ }); } -function setNotifyApi(tag, obj){ +function setNotifyTgbot(obj){ var enable = $(obj).prop("checked"); - // console.log(tag,obj,enable); - $.post('/setting/set_notify_email_enable', {'tag':tag, 'enable':enable},function(rdata){ + $.post('/setting/set_notify_tgbot_enable', {'enable':enable},function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){} + } ,{icon:rdata.status?1:2}, 1000); + },'json'); +} + + +function setNotifyEmail(obj){ + var enable = $(obj).prop("checked"); + $.post('/setting/set_notify_email_enable', {'enable':enable},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){} } ,{icon:rdata.status?1:2}, 1000); @@ -552,16 +561,16 @@ function setNotifyApi(tag, obj){ function getTgbot(){ var loadT = layer.msg('正在获取TgBot信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/setting/get_notify',{},function(data){ + $.post('/setting/get_notify_tgbot',{},function(data){ layer.close(loadT); var app_token = ''; var chat_id = ''; if (data.status){ - if (typeof(data['data']['tgbot']) !='undefined'){ - app_token = data['data']['tgbot']['data']['app_token']; - chat_id = data['data']['tgbot']['data']['chat_id']; + if (data['data']['tgbot'].length != 0){ + app_token = data['data']['tgbot']['app_token']; + chat_id = data['data']['tgbot']['chat_id']; } } @@ -598,7 +607,7 @@ function getTgbot(){ return false; } - $.post('/setting/set_notify',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_tgbot',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); @@ -622,7 +631,7 @@ function getTgbot(){ return false; } - $.post('/setting/set_notify_test',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_tgbot_test',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index 1e908c07d..a831407d8 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -164,7 +164,7 @@

    TG机器人通知 - + Telegram Bot机器人通知【国内可能无法使用】 @@ -172,7 +172,7 @@

    邮件通知 - + 邮件通知 diff --git a/web/utils/config.py b/web/utils/config.py index 5d6737b2d..bc8307893 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -82,5 +82,6 @@ def getGlobalVar(): # 邮件通知设置 data['notify_email'] = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + data['notify_tgbot'] = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') return data \ No newline at end of file From 8233ba99df8d69df81e3575a2303aed48ed6249f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 9 Nov 2024 19:02:08 +0800 Subject: [PATCH 146/965] update --- web/admin/site/__init__.py | 3 ++- web/admin/site/site_default.py | 37 ++++++++++++++++++++++++++ web/admin/site/site_types.py | 47 +++++++++++++++++++++++++++++++++- web/static/app/site.js | 26 ++++++++++--------- 4 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 web/admin/site/site_default.py diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index bacb102b1..0a4953884 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -9,4 +9,5 @@ # --------------------------------------------------------------------------------- from .site import * -from .site_types import * \ No newline at end of file +from .site_types import * +from .site_default import * \ No newline at end of file diff --git a/web/admin/site/site_default.py b/web/admin/site/site_default.py new file mode 100644 index 000000000..c3cc30ca3 --- /dev/null +++ b/web/admin/site/site_default.py @@ -0,0 +1,37 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +@blueprint.route('/get_site_doc', endpoint='get_site_doc',methods=['POST']) +def get_site_doc(): + 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.returnData(True, 'ok', data) \ No newline at end of file diff --git a/web/admin/site/site_types.py b/web/admin/site/site_types.py index 62104549a..8c3202e1e 100644 --- a/web/admin/site/site_types.py +++ b/web/admin/site/site_types.py @@ -9,6 +9,7 @@ # --------------------------------------------------------------------------------- import os +import json from flask import Blueprint, render_template from flask import request @@ -37,4 +38,48 @@ def get_site_types(): @panel_login_required def add_site_type(): name = request.form.get('name', '').strip() - return MwSites.instance().addSiteTypes(name) \ No newline at end of file + return MwSites.instance().addSiteTypes(name) + + +# 添加网站分类 +@blueprint.route('/remove_site_type', endpoint='remove_site_type',methods=['POST']) +@panel_login_required +def remove_site_type(): + site_type_id = request.form.get('id', '') + if mw.M('site_types').where('id=?', (site_type_id,)).count() == 0: + return mw.returnData(False, "指定分类不存在!") + mw.M('site_types').where('id=?', (site_type_id,)).delete() + mw.M("sites").where("type_id=?", (site_type_id,)).save("type_id", (0,)) + return mw.returnData(True, "分类已删除!") + +# 添加网站分类 +@blueprint.route('/modify_site_type_name', endpoint='modify_site_type_name',methods=['POST']) +@panel_login_required +def modify_site_type_name(): + name = request.form.get('name', '').strip() + site_type_id = request.form.get('id', '') + if not name: + return mw.returnData(False, "分类名称不能为空") + if len(name) > 18: + return mw.returnData(False, "分类名称长度不能超过6个汉字或18位字母") + if mw.M('site_types').where('id=?', (site_type_id,)).count() == 0: + return mw.returnData(False, "指定分类不存在!") + mw.M('site_types').where('id=?', (site_type_id,)).setField('name', name) + return mw.returnData(True, "修改成功!") + +# 添加网站分类 +@blueprint.route('/set_site_type', endpoint='set_site_type',methods=['POST']) +@panel_login_required +def set_site_type(): + # 设置指定站点的分类 + site_ids = request.form.get('site_ids', '') + site_type_id = request.form.get('id', '') + site_ids = json.loads(site_ids) + for site_id in site_ids: + mw.M('sites').where('id=?', (site_id,)).setField('type_id', site_type_id) + return mw.returnData(True, "设置成功!") + + + + + diff --git a/web/static/app/site.js b/web/static/app/site.js index 1284891a8..0a4041353 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -2704,22 +2704,24 @@ function siteDefaultPage(){ closeBtn: 1, shift: 0, content: '

    \ - \ - \ - \ - \ -
    ' + \ + \ + \ + \ +
    ' }); } -function changeDefault(type){ +function changeDefault(type, obj){ + $(obj).attr('disabled', true); $.post('/site/get_site_doc','type='+type, function(rdata){ - showMsg('操作成功!',function(){ - if (rdata.status){ - vhref = rdata.data.path; - onlineEditFile(0,vhref); - } - },{icon:rdata.status?1:2}); + setTimeout(function(){ + $(obj).attr('disabled', false); + },3000); + if (rdata.status){ + var path = rdata.data.path; + onlineEditFile(0,path); + } },'json'); } From d50d8abab8c2d8029641398523696d726602b58f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 9 Nov 2024 23:25:16 +0800 Subject: [PATCH 147/965] update --- plugins/openresty/index.py | 7 +- web/admin/files/__init__.py | 8 + web/admin/site/php.py | 59 +++++++ web/admin/site/site.py | 96 ++++++++---- web/admin/site/site_types.py | 4 +- web/core/mw.py | 6 +- web/static/app/site.js | 55 +++---- web/thisdb/site_types.py | 9 ++ web/thisdb/sites.py | 4 + web/utils/file.py | 55 +++++++ web/utils/site.py | 292 ++++++++++++++++++++++++++++++++--- 11 files changed, 510 insertions(+), 85 deletions(-) create mode 100644 web/admin/site/php.py diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py index 778324890..2b2ea8b59 100755 --- a/plugins/openresty/index.py +++ b/plugins/openresty/index.py @@ -10,8 +10,9 @@ import re web_dir = os.getcwd() + "/web" -sys.path.append(web_dir) -os.chdir(web_dir) +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) import core.mw as mw @@ -132,7 +133,7 @@ def checkAuthEq(file, owner='root'): def confReplace(): - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() content = mw.readFile(getConfTpl()) content = content.replace('{$SERVER_PATH}', service_path) diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py index 12c7ce63a..d81227f80 100644 --- a/web/admin/files/__init__.py +++ b/web/admin/files/__init__.py @@ -82,6 +82,14 @@ def get_dir(): dir_list['page'] = mw.getPage({'p':page, 'row': row, 'tojs':'getFiles', 'count': dir_list['count']}, '1,2,3,4,5,6,7,8') return dir_list +# 创建目录 +@blueprint.route('/create_dir', endpoint='create_dir', methods=['POST']) +@panel_login_required +def create_dir(): + path = request.form.get('path', '') + return file.createDir(path) + + # 获取站点日志目录 @blueprint.route('/get_dir_size', endpoint='get_dir_size', methods=['POST']) @panel_login_required diff --git a/web/admin/site/php.py b/web/admin/site/php.py new file mode 100644 index 000000000..2425086c9 --- /dev/null +++ b/web/admin/site/php.py @@ -0,0 +1,59 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) +@panel_login_required +def get_cli_php_version(): + php_dir = mw.getServerDir() + '/php' + if not os.path.exists(php_dir): + return mw.returnData(False, '未安装PHP,无法设置') + + php_bin = '/usr/bin/php' + php_versions = MwSites.instance().getPhpVersion() + php_versions = php_versions[1:] + + if len(php_versions) < 1: + return mw.returnData(False, '未安装PHP,无法设置') + + if os.path.exists(php_bin) and os.path.islink(php_bin): + link_re = os.readlink(php_bin) + for v in php_versions: + if link_re.find(v['version']) != -1: + return mw.returnData({"select": v, "versions": php_versions}) + + return {"select": php_versions[0],"versions": php_versions} + +@blueprint.route('/set_cli_php_version', endpoint='set_cli_php_version',methods=['POST']) +@panel_login_required +def set_cli_php_version(): + if mw.isAppleSystem(): + return mw.returnData(False, "开发机不可设置!") + version = request.form.get('version', '') + return MwSites.instance().setCliPhpVersion(version) + + + + diff --git a/web/admin/site/site.py b/web/admin/site/site.py index 8d628c74e..c3e0972e6 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -167,7 +167,6 @@ def check_web_status(): def get_php_version(): return MwSites.instance().getPhpVersion() - # 设置网站到期 @blueprint.route('/set_end_date', endpoint='set_end_date',methods=['POST']) @panel_login_required @@ -206,6 +205,71 @@ def get_rewrite_conf(): rewrite = MwSites.instance().getRewriteConf(siteName) return {'rewrite': rewrite} +# 获取Rewrite模版名 +@blueprint.route('/get_rewrite_tpl', endpoint='get_rewrite_tpl',methods=['POST']) +@panel_login_required +def get_php_version(): + tplname = request.form.get('tplname', '') + return MwSites.instance().getRewriteTpl(tplname) + +# 获取网站目录 +@blueprint.route('/get_dir_user_ini', endpoint='get_dir_user_ini',methods=['POST']) +@panel_login_required +def get_dir_user_ini(): + site_id = request.form.get('id', '') + return MwSites.instance().getDirUserIni(site_id) + +# 设置防跨站攻击 +@blueprint.route('/set_dir_user_ini', endpoint='set_dir_user_ini',methods=['POST']) +@panel_login_required +def set_dir_user_ini(): + path = request.form.get('path', '') + run_path = request.form.get('run_path', '') + return MwSites.instance().setDirUserIni(path,run_path) + +# 网站日志开关 +@blueprint.route('/logs_open', endpoint='logs_open',methods=['POST']) +@panel_login_required +def logs_open(): + site_id = request.form.get('id', '') + return MwSites.instance().logsOpen(site_id) + +# 设置网站路径 +@blueprint.route('/set_path', endpoint='set_path',methods=['POST']) +@panel_login_required +def set_path(): + site_id = request.form.get('id', '') + path = request.form.get('path', '') + return MwSites.instance().setSitePath(site_id, path) + + +# 设置网站路径 +@blueprint.route('/set_site_run_path', endpoint='set_site_run_path',methods=['POST']) +@panel_login_required +def set_site_run_path(): + site_id = request.form.get('id', '') + run_path = request.form.get('run_path', '') + return MwSites.instance().setSiteRunPath(site_id, run_path) + + + + +# 设置网站 - 开启密码访问 +@blueprint.route('/set_has_pwd', endpoint='set_has_pwd',methods=['POST']) +@panel_login_required +def set_has_pwd(): + site_id = request.form.get('id', '') + username = request.form.get('username', '') + password = request.form.get('password', '') + return MwSites.instance().setHasPwd(site_id, username, password) + +# 设置网站 - 关闭密码访问 +@blueprint.route('/close_has_pwd', endpoint='close_has_pwd',methods=['POST']) +@panel_login_required +def close_has_pwd(): + site_id = request.form.get('id', '') + return MwSites.instance().closeHasPwd(site_id) + # 获取防盗链信息 @blueprint.route('/get_security', endpoint='get_security',methods=['POST']) @panel_login_required @@ -239,34 +303,4 @@ def set_default_site(): name = request.form.get('name', '') return MwSites.instance().setDefaultSite(name) -@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) -@panel_login_required -def get_cli_php_version(): - php_dir = mw.getServerDir() + '/php' - if not os.path.exists(php_dir): - return mw.returnData(False, '未安装PHP,无法设置') - - php_bin = '/usr/bin/php' - php_versions = MwSites.instance().getPhpVersion() - php_versions = php_versions[1:] - - if len(php_versions) < 1: - return mw.returnData(False, '未安装PHP,无法设置') - - if os.path.exists(php_bin) and os.path.islink(php_bin): - link_re = os.readlink(php_bin) - for v in php_versions: - if link_re.find(v['version']) != -1: - return mw.returnData({"select": v, "versions": php_versions}) - - return {"select": php_versions[0],"versions": php_versions} - -@blueprint.route('/set_cli_php_version', endpoint='set_cli_php_version',methods=['POST']) -@panel_login_required -def set_cli_php_version(): - if mw.isAppleSystem(): - return mw.returnData(False, "开发机不可设置!") - version = request.form.get('version', '') - return MwSites.instance().setCliPhpVersion(version) - diff --git a/web/admin/site/site_types.py b/web/admin/site/site_types.py index 8c3202e1e..41e3c4396 100644 --- a/web/admin/site/site_types.py +++ b/web/admin/site/site_types.py @@ -52,7 +52,7 @@ def remove_site_type(): mw.M("sites").where("type_id=?", (site_type_id,)).save("type_id", (0,)) return mw.returnData(True, "分类已删除!") -# 添加网站分类 +# 修改网站分类 @blueprint.route('/modify_site_type_name', endpoint='modify_site_type_name',methods=['POST']) @panel_login_required def modify_site_type_name(): @@ -67,7 +67,7 @@ def modify_site_type_name(): mw.M('site_types').where('id=?', (site_type_id,)).setField('name', name) return mw.returnData(True, "修改成功!") -# 添加网站分类 +# 设置网站分类 @blueprint.route('/set_site_type', endpoint='set_site_type',methods=['POST']) @panel_login_required def set_site_type(): diff --git a/web/core/mw.py b/web/core/mw.py index 70f0e391a..f723c7bbd 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -288,7 +288,11 @@ def md5(content): except Exception as ex: return False - +def hasPwd(password): + # 加密密码字符 + import crypt + return crypt.crypt(password, password) + def getFileMd5(filename): # 文件的MD5值 if not os.path.isfile(filename): diff --git a/web/static/app/site.js b/web/static/app/site.js index 0a4041353..63ed78bb5 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -351,30 +351,30 @@ function webAddPage(type) { //修改网站目录 function webPathEdit(id){ - $.post('/site/get_dir_user_ini','&id='+id, function(data){ - var userini = data['data']; - var webpath = userini['path']; - var siteName = userini['name']; - var runPath = userini['runPath']['runPath']; - var userinicheckeds = userini.userini?'checked':''; - var logscheckeds = userini.logs?'checked':''; - var opt = '' + $.post('/site/get_dir_user_ini','id='+id, function(data){ + var data = data['data']; + var site_path = data['path']; + var site_name = data['name']; + var run_path = data['run_path']['run_path']; + var user_ini_checked = data.user_ini?'checked':''; + var logs_checked = data.logs?'checked':''; + var opt = ''; var selected = ''; - for(var i=0;i'+ userini.runPath.dirs[i] +'' + opt += ''; } - var webPathHtml = "
    \ + var content = "
    \
    \ - \ - \ + \ + \
    \
    \ 网站目录\ - \ + \ \ \
    \ @@ -389,11 +389,11 @@ function webPathEdit(id){ " +'
    ' +'密码访问' - +'' + +'' +'' +'' +'
    ' - +'
    ' + +'
    ' +'

    授权账号

    ' +'

    访问密码

    ' +'

    重复密码

    ' @@ -401,14 +401,11 @@ function webPathEdit(id){ +'
    ' +'
    '; - $("#webedit-con").html(webPathHtml); + $("#webedit-con").html(content); $("#userini").change(function(){ - $.post('/site/set_dir_user_ini',{ - 'path':webpath, - 'runPath':runPath, - },function(userini){ - layer.msg(userini.msg+'

    注意:设置防跨站需要重启PHP才能生效!

    ',{icon:userini.status?1:2}); - tryRestartPHP(siteName); + $.post('/site/set_dir_user_ini',{'path':site_path,'run_path':run_path,},function(userini){ + layer.msg(data.msg+'

    注意:设置防跨站需要重启PHP才能生效!

    ',{icon:data.status?1:2}); + tryRestartPHP(site_name); },'json'); }); @@ -458,10 +455,9 @@ function setPathSafe(id){ function setSiteRunPath(id){ var NewPath = $("#runPath").val(); var loadT = layer.msg(lan.public.the,{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_site_run_path','id='+id+'&runPath='+NewPath,function(rdata){ + $.post('/site/set_site_run_path','id='+id+'&run_path='+NewPath,function(rdata){ layer.close(loadT); - var ico = rdata.status?1:2; - layer.msg(rdata.msg,{icon:ico}); + layer.msg(rdata.msg,{icon:rdata.status?1:2}); },'json'); } @@ -2898,7 +2894,7 @@ function tryRestartPHP(siteName){ var phpversion = data.phpversion; if (phpversion == "00"){ - return + return; } var php_sign = 'php'; @@ -2915,11 +2911,10 @@ function tryRestartPHP(siteName){ var reqData = {name: php_sign, func:'restart'} reqData['version'] = phpversion; - // console.log(reqData); var loadT = layer.msg('尝试自动重启PHP['+phpversion+']...', { icon: 16, time: 0, shade: 0.3 }); $.post('/plugins/run', reqData, function(data) { layer.close(loadT); - layer.msg(data.msg,{icon:data.status?1:2,time:3000,shade: [0.3, '#000']}); + layer.msg( 'PHP['+phpversion+']'+(data.status?'重启成功!':'重启失败!'),{icon:data.status?1:2,time:3000,shade: [0.3, '#000']}); },'json'); },'json'); } \ No newline at end of file diff --git a/web/thisdb/site_types.py b/web/thisdb/site_types.py index 82d64267e..54751f64d 100644 --- a/web/thisdb/site_types.py +++ b/web/thisdb/site_types.py @@ -12,6 +12,15 @@ __FIELD = 'id,name' import core.mw as mw +def addSiteTypes(name): + return mw.M('site_types').add("name", (name,)) + +def getSiteTypesCount(): + return mw.M('site_types').count() + +def getSiteTypesCountByName(name): + return mw.M('site_types').where('name=?', (name,)).count() + def getSiteTypesList(): # .debug(True) return mw.M('site_types').field(__FIELD).order("id asc").select() diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index 332f6b1a5..f212e1d02 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -78,6 +78,7 @@ def deleteSitesById(site_id): def setSitesData(site_id, edate: str | None = None, ps: str | None = None, + path: str | None = None, ): update_data = {} if edate is not None: @@ -85,5 +86,8 @@ def setSitesData(site_id, if ps is not None: update_data['ps'] = ps + if path is not None: + update_data['path'] = path + return mw.M('sites').where('id=?',(site_id,)).update(update_data) diff --git a/web/utils/file.py b/web/utils/file.py index d962bf9f6..691479979 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -17,6 +17,61 @@ from admin import model import core.mw as mw import thisdb +def setFileAccept(filename): + auth = 'www:www' + if mw.getOs() == 'darwin': + user = mw.execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() + auth = user + ':staff' + os.system('chown -R ' + auth + ' ' + filename) + os.system('chmod -R 755 ' + filename) + +def createDir(path): + try: + if not checkFileName(path): + return mw.returnData(False, '目录名中不能包含特殊字符!') + if os.path.exists(path): + return mw.returnData(False, '指定目录已存在!') + os.makedirs(path) + setFileAccept(path) + msg = mw.getInfo('创建目录[{1}]成功!', (path,)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '目录创建成功!') + except Exception as e: + print(e) + return mw.returnData(False, '目录创建失败!') + +# 检查敏感目录 +def checkDir(path): + path = path.replace('//', '/') + if path[-1:] == '/': + path = path[:-1] + + sense_dir = ('', + '/', + '/*', + '/www', + '/root', + '/boot', + '/bin', + '/etc', + '/home', + '/dev', + '/sbin', + '/var', + '/usr', + '/tmp', + '/sys', + '/proc', + '/media', + '/mnt', + '/opt', + '/lib', + '/srv', + '/selinux', + '/www/server', + mw.getRootDir()) + return not path in sense_dir + def getFileBody(path): if not os.path.exists(path): return mw.returnData(False, '文件不存在', (path,)) diff --git a/web/utils/site.py b/web/utils/site.py index 4c49b7c7a..e5452e005 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -187,7 +187,7 @@ class sites(object): return mw.returnData(True, '添加成功') - def deleteWSLogs(self, webname): + def deleteALlLogs(self, webname): assLogPath = mw.getLogsDir() + '/' + webname + '.log' errLogPath = mw.getLogsDir() + '/' + webname + '.error.log' confFile = self.setupPath + '/nginx/vhost/' + webname + '.conf' @@ -216,13 +216,12 @@ class sites(object): def delete(self, site_id, path): info = thisdb.getSitesById(site_id) - print(info) webname = info['name'] - self.deleteWSLogs(webname) + self.deleteALlLogs(webname) if path == '1': - rootPath = mw.getWwwDir() + '/' + webname - mw.execShell('rm -rf ' + rootPath) + web_root_path = mw.getWwwDir() + '/' + webname + mw.execShell('rm -rf ' + web_root_path) # ssl ssl_dir = self.sslDir + '/' + webname @@ -243,13 +242,13 @@ class sites(object): binding_list = thisdb.getBindingListBySiteId(site_id) for x in binding_list: tag = mw.getLogsDir() + "/" + webname + "_" + x['domain'] - wlog = tag + ".log" - wlog_error = tag + ".error.log" + site_log = tag + ".log" + site_error = tag + ".error.log" - if os.path.exists(wlog): - mw.execShell('rm -rf ' + wlog) - if os.path.exists(wlog_error): - mw.execShell('rm -rf ' + wlog_error) + if os.path.exists(site_log): + mw.execShell('rm -rf ' + site_log) + if os.path.exists(site_error): + mw.execShell('rm -rf ' + site_error) thisdb.deleteBindingBySiteId(site_id) mw.restartWeb() @@ -274,6 +273,22 @@ class sites(object): 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.getRewriteConf(self.siteName) + mw.writeFile(rewrite_file, '') + # 设置网站过期 def setEndDate(self, site_id, end_date): info = thisdb.getSitesById(site_id) @@ -299,6 +314,13 @@ class sites(object): data = thisdb.getDomainByPid(pid) return data + # 获取模版名内容 + def getRewriteTpl(self, tplname): + file = mw.getPanelDir() + '/rewrite/nginx/' + tplname + '.conf' + if not os.path.exists(file): + return mw.returnData(False, '模版不存在!') + return mw.returnData(True, 'OK', file) + def getRewriteList(self): rewriteList = {} rewriteList['rewrite'] = [] @@ -309,6 +331,237 @@ class sites(object): rewriteList['rewrite'] = sorted(rewriteList['rewrite']) return rewriteList + # 取日志状态 + 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 getSiteRunPath(self, site_name, site_path): + filename = self.getHostConf(site_name) + if os.path.exists(filename): + conf = mw.readFile(filename) + rep = r'\s*root\s*(.+);' + path = re.search(rep, conf).groups()[0] + + data = {} + if site_path == path: + data['path'] = '/' + else: + data['path'] = path.replace(site_path, '') + + dirnames = [] + dirnames.append('/') + for filename in os.listdir(site_path): + try: + file_path = site_path + '/' + filename + if os.path.islink(file_path): + continue + if os.path.isdir(file_path): + dirnames.append('/' + filename) + except: + pass + + data['dirs'] = dirnames + return data + + def getDirUserIni(self, site_id): + + info = thisdb.getSitesById(site_id) + + path = info['path'] + name = info['name'] + data = {} + data['logs'] = self.getLogsStatus(name) + data['run_path'] = self.getSiteRunPath(name, path) + + data['user_ini'] = False + if os.path.exists(path + '/.user.ini'): + data['user_ini'] = True + + if data['run_path']['path'] != '/': + user_ini = path + data['run_path']['path'] + '/.user.ini' + if os.path.exists(user_ini): + data['userini'] = True + + data['pass'] = self.getHasPwd(name) + data['path'] = path + data['name'] = name + return mw.returnData(True, 'OK', data) + + # 清除多余user.ini + def delUserInI(self, path, up=0): + filename = path + '/.user.ini' + if os.path.exists(filename): + mw.execShell("which chattr && chattr -i " + filename) + os.remove(filename) + + for f in os.listdir(path): + try: + npath = path + '/' + f + if os.path.isdir(npath): + if up < 100: + self.delUserInI(npath, up + 1) + + user_ini = npath + '/.user.ini' + print('ff:',user_ini) + if not os.path.exists(user_ini): + continue + mw.execShell('which chattr && chattr -i ' + user_ini) + os.remove(user_ini) + except: + continue + return True + + + # 设置目录防御 + def addDirUserIni(self, site_path, run_path): + new_path = site_path + run_path + filename = new_path + '/.user.ini' + if os.path.exists(filename): + return mw.returnData(True, '已打开防跨站设置!') + + open_path = 'open_basedir={}/:{}/'.format(new_path, site_path) + if run_path == '/' or run_path == '': + open_path = 'open_basedir={}/'.format(site_path) + + mw.writeFile(filename, open_path + ':/www/server/php:/tmp/:/proc/') + mw.execShell("which chattr && chattr +i " + filename) + + def setDirUserIni(self, site_path, run_path): + filename = site_path + '/.user.ini' + if os.path.exists(filename): + self.delUserInI(site_path) + return mw.returnData(True, '已清除防跨站设置!') + self.addDirUserIni(site_path, run_path) + return mw.returnData(True, '已打开防跨站设置!') + + def logsOpen(self, site_id): + info = thisdb.getSitesById(site_id) + name = info['name'] + + 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.returnData(True, '操作成功!') + + def setSitePath(self, site_id, path): + path = self.getPath(path) + if path == "" or site_id == '0': + return mw.returnData(False, "目录不能为空!") + + import utils.file as file + if not file.checkDir(path): + return mw.returnData(False, "不能以系统关键目录作为站点目录") + + info = thisdb.getSitesById(site_id) + if info['path'] == path: + return mw.returnData(False, "与原路径一致,无需修改!") + conf_file = self.getHostConf(info['name']) + content = mw.readFile(conf_file) + if content: + content = content.replace(info['path'], path) + mw.writeFile(file, content) + + thisdb.setSitesData(site_id, path=path) + msg = mw.getInfo('修改网站[{1}]物理路径成功!', (info['name'],)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, "设置成功!") + + # 设置当前站点运行目录 + def setSiteRunPath(self, site_id, run_path): + info = thisdb.getSitesById(site_id) + site_name = info['name'] + site_path = info['path'] + + new_path = site_path + run_path + + # 处理Nginx + filename = self.getHostConf(site_name) + 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, new_path) + mw.writeFile(filename, conf) + + self.setSitePath(site_path, run_path) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + # 设置目录加密 + def setHasPwd(self, site_id, username, password): + if len(username.strip()) == 0 or len(password.strip()) == 0: + return mw.returnData(False, '用户名或密码不能为空!') + + info = thisdb.getSitesById(site_id) + siteName = info['name'] + filename = self.passPath + '/' + siteName + '.pass' + passconf = username + ':' + mw.hasPwd(password) + + 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) + + msg = mw.getInfo('设置网站[{1}]为需要密码认证!', (siteName,)) + mw.writeLog("网站管理", msg) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + # 取消目录加密 + def closeHasPwd(self, site_id): + info = thisdb.getSitesById(site_id) + siteName = info['name'] + 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) + + msg = mw.getInfo('清除网站[{1}]的密码认证!', (siteName,)) + mw.writeLog("网站管理", msg) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + def getSecurity(self, site_id): info = thisdb.getSitesById(site_id) name = info['name'] @@ -323,10 +576,8 @@ class sites(object): 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) + 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 @@ -592,11 +843,16 @@ class sites(object): return mw.returnData(False, "分类名称不能为空") if len(name) > 18: return mw.returnData(False, "分类名称长度不能超过6个汉字或18位字母") - if mw.M('site_types').count() >= 10: + + all_count = thisdb.getSiteTypesCount() + if all_count >= 10: return mw.returnData(False, '最多添加10个分类!') - if mw.M('site_types').where('name=?', (name,)).count() > 0: + + name_count = thisdb.getSiteTypesCountByName(name) + if name_count > 0: return mw.returnData(False, "指定分类名称已存在!") - mw.M('site_types').add("name", (name,)) + + thisdb.addSiteTypes(name) return mw.returnData(True, '添加成功!') From 433ad507e7c7f1eae8fc060973b725effc44e190 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 9 Nov 2024 23:31:56 +0800 Subject: [PATCH 148/965] update --- web/admin/site/__init__.py | 4 +++- web/admin/site/logs.py | 45 ++++++++++++++++++++++++++++++++++++++ web/utils/site.py | 14 ++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 web/admin/site/logs.py diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 0a4953884..b4691d745 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -10,4 +10,6 @@ from .site import * from .site_types import * -from .site_default import * \ No newline at end of file +from .site_default import * +from .php import * +from .logs import * \ No newline at end of file diff --git a/web/admin/site/logs.py b/web/admin/site/logs.py new file mode 100644 index 000000000..4ae249cf5 --- /dev/null +++ b/web/admin/site/logs.py @@ -0,0 +1,45 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +@blueprint.route('/get_logs', endpoint='get_logs',methods=['POST']) +@panel_login_required +def get_logs(): + siteName = request.form.get('siteName', '') + return MwSites.instance().getLogs(siteName) + + +@blueprint.route('/get_error_logs', endpoint='get_error_logs',methods=['POST']) +@panel_login_required +def get_error_logs(): + siteName = request.form.get('siteName', '') + return MwSites.instance().getErrorLogs(siteName) + + + + + + + diff --git a/web/utils/site.py b/web/utils/site.py index e5452e005..d6785d6aa 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -314,6 +314,20 @@ class sites(object): data = thisdb.getDomainByPid(pid) return data + # 获取日志内容 + def getLogs(self, siteName): + logPath = mw.getLogsDir() + '/' + siteName + '.log' + if not os.path.exists(logPath): + return mw.returnData(False, '日志为空') + return mw.returnData(True, mw.getLastLine(logPath, 100)) + + # 获取错误日志内容 + def getErrorLogs(self, siteName): + logPath = mw.getLogsDir() + '/' + siteName + '.error.log' + if not os.path.exists(logPath): + return mw.returnData(False, '日志为空') + return mw.returnData(True, mw.getLastLine(logPath, 100)) + # 获取模版名内容 def getRewriteTpl(self, tplname): file = mw.getPanelDir() + '/rewrite/nginx/' + tplname + '.conf' From e5ed04ce319b0d9d40d8576f39280bbe31e4254b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 02:15:35 +0800 Subject: [PATCH 149/965] update --- web/admin/site/__init__.py | 5 +- web/admin/site/dir.py | 83 ++++++++++++ web/admin/site/redirect.py | 58 ++++++++ web/admin/site/site.py | 17 --- web/admin/site/ssl.py | 33 +++++ web/core/db.py | 3 +- web/static/app/site.js | 33 ++--- web/thisdb/binding.py | 18 ++- web/thisdb/domain.py | 6 +- web/utils/site.py | 265 ++++++++++++++++++++++++++++++++++++- 10 files changed, 469 insertions(+), 52 deletions(-) create mode 100644 web/admin/site/dir.py create mode 100644 web/admin/site/redirect.py create mode 100644 web/admin/site/ssl.py diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index b4691d745..f98b21bb5 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -12,4 +12,7 @@ from .site import * from .site_types import * from .site_default import * from .php import * -from .logs import * \ No newline at end of file +from .logs import * +from .dir import * +from .redirect import * +from .ssl import * \ No newline at end of file diff --git a/web/admin/site/dir.py b/web/admin/site/dir.py new file mode 100644 index 000000000..19c153f06 --- /dev/null +++ b/web/admin/site/dir.py @@ -0,0 +1,83 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取网站目录 +@blueprint.route('/get_dir_user_ini', endpoint='get_dir_user_ini',methods=['POST']) +@panel_login_required +def get_dir_user_ini(): + site_id = request.form.get('id', '') + return MwSites.instance().getDirUserIni(site_id) + +# 设置防跨站攻击 +@blueprint.route('/set_dir_user_ini', endpoint='set_dir_user_ini',methods=['POST']) +@panel_login_required +def set_dir_user_ini(): + path = request.form.get('path', '') + run_path = request.form.get('run_path', '') + return MwSites.instance().setDirUserIni(path,run_path) + +# 获取子目录绑定 +@blueprint.route('/get_dir_binding', endpoint='get_dir_binding',methods=['POST']) +@panel_login_required +def get_dir_binding(): + site_id = request.form.get('id', '') + return MwSites.instance().getDirBinding(site_id) + + +# 添加子目录绑定 +@blueprint.route('/add_dir_bind', endpoint='add_dir_bind',methods=['POST']) +@panel_login_required +def add_dir_bind(): + site_id = request.form.get('id', '') + domain = request.form.get('domain', '') + dir_name = request.form.get('dir_name', '') + return MwSites.instance().addDirBind(site_id,domain,dir_name) + + +# 获取目录绑定rewrite +@blueprint.route('/get_dir_bind_rewrite', endpoint='get_dir_bind_rewrite',methods=['POST']) +@panel_login_required +def get_dir_bind_rewrite(): + binding_id = request.form.get('id', '') + add = request.form.get('add', '') + return MwSites.instance().getDirBindingRewrite(binding_id,add) + + +# 获取目录绑定rewrite +@blueprint.route('/del_dir_bind', endpoint='del_dir_bind',methods=['POST']) +@panel_login_required +def del_dir_bind(): + binding_id = request.form.get('id', '') + return MwSites.instance().delDirBinding(binding_id) + + + + + + + + + diff --git a/web/admin/site/redirect.py b/web/admin/site/redirect.py new file mode 100644 index 000000000..c74983ed4 --- /dev/null +++ b/web/admin/site/redirect.py @@ -0,0 +1,58 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取重定向列表 +@blueprint.route('/get_redirect', endpoint='get_redirect', methods=['POST']) +@panel_login_required +def get_redirect(): + site_name = request.form.get("siteName", '') + return MwSites.instance().getRedirect(site_name) + +# 设置重定向列表 +@blueprint.route('/set_redirect', endpoint='set_redirect', methods=['POST']) +@panel_login_required +def set_redirect(): + site_name = request.form.get("siteName", '') + site_from = request.form.get("from", '') + to = request.form.get("to", '') # redirect to + type = request.form.get("type", '') # path / domain + r_type = request.form.get("r_type", '') # redirect type + keep_path = request.form.get("keep_path", '') # keep path + return MwSites.instance().setRedirect(site_name, site_from, to, type, r_type, keep_path) + +# 设置重定向配置 +@blueprint.route('/get_redirect_conf', endpoint='get_redirect_conf', methods=['POST']) +@panel_login_required +def get_redirect_conf(): + site_name = request.form.get("siteName", '') + rid = request.form.get("id", '') + return MwSites.instance().getRedirectConf(site_name, rid) + + + + + + diff --git a/web/admin/site/site.py b/web/admin/site/site.py index c3e0972e6..6cbde59e0 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -212,21 +212,6 @@ def get_php_version(): tplname = request.form.get('tplname', '') return MwSites.instance().getRewriteTpl(tplname) -# 获取网站目录 -@blueprint.route('/get_dir_user_ini', endpoint='get_dir_user_ini',methods=['POST']) -@panel_login_required -def get_dir_user_ini(): - site_id = request.form.get('id', '') - return MwSites.instance().getDirUserIni(site_id) - -# 设置防跨站攻击 -@blueprint.route('/set_dir_user_ini', endpoint='set_dir_user_ini',methods=['POST']) -@panel_login_required -def set_dir_user_ini(): - path = request.form.get('path', '') - run_path = request.form.get('run_path', '') - return MwSites.instance().setDirUserIni(path,run_path) - # 网站日志开关 @blueprint.route('/logs_open', endpoint='logs_open',methods=['POST']) @panel_login_required @@ -252,8 +237,6 @@ def set_site_run_path(): return MwSites.instance().setSiteRunPath(site_id, run_path) - - # 设置网站 - 开启密码访问 @blueprint.route('/set_has_pwd', endpoint='set_has_pwd',methods=['POST']) @panel_login_required diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py new file mode 100644 index 000000000..7c9b0ad82 --- /dev/null +++ b/web/admin/site/ssl.py @@ -0,0 +1,33 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + + + + + + + + diff --git a/web/core/db.py b/web/core/db.py index 0d63b222b..2cd72efa8 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -191,7 +191,8 @@ class Sql(): self.__close() return data except Exception as ex: - return "error: " + str(ex) + # return "error: " + str(ex) + return [] def inquiry(self, input_field=''): # 查询数据集 diff --git a/web/static/app/site.js b/web/static/app/site.js index 63ed78bb5..798e536d1 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1347,13 +1347,13 @@ function showRewrite(rdata){ //添加子目录绑定 function addDirBinding(id){ var domain = $("input[name='domain']").val(); - var dirName = $("select[name='dirName']").val(); - if(domain == '' || dirName == '' || dirName == null){ + var dir_name = $("select[name='dirName']").val(); + if(domain == '' || dir_name == '' || dir_name == null){ layer.msg(lan.site.d_s_empty,{icon:2}); return; } - var data = 'id='+id+'&domain='+domain+'&dirName='+dirName; + var data = 'id='+id+'&domain='+domain+'&dir_name='+dir_name; $.post('/site/add_dir_bind',data,function(rdata){ dirBinding(id); layer.msg(rdata.msg,{icon:rdata.status?1:2}); @@ -1421,6 +1421,7 @@ function to301(siteName, type, obj){ $('.btn-colse-prosy').click(function() { layer.close(redirect_form); }); + $('.btn-submit-redirect').click(function() { var keep_path = $('[name="keep_path"]').prop('checked') ? 1 : 0; var r_type = $('[name="r_type"]').val(); @@ -1428,24 +1429,14 @@ function to301(siteName, type, obj){ var from = $('[name="from"]').val(); var to = $('[name="to"]').val(); - $.post('/site/set_redirect', { - siteName: siteName, - type: type, - r_type: r_type, - from: from, - to: to, - keep_path: keep_path - }, function(res) { - res = JSON.parse(res); - if (res.status) { + $.post('/site/set_redirect', {siteName: siteName,type: type,r_type: r_type,from: from,to: to,keep_path: keep_path}, function(data) { + if (data.status) { layer.close(redirect_form); - to301(siteName) + to301(siteName); } else { - layer.msg(res.msg, { - icon: 2 - }); + layer.msg(data.msg, {icon: 2}); } - }); + },'json'); }); }, 100); } @@ -1455,14 +1446,13 @@ function to301(siteName, type, obj){ siteName: siteName, id: obj, }, function(res) { - res = JSON.parse(res); if (res.status == true) { layer.msg('删除成功', {time: 1000,icon: 1}); to301(siteName); } else { layer.msg(res.msg, {time: 1000,icon: 2}); } - }); + },'json'); return } @@ -1471,7 +1461,6 @@ function to301(siteName, type, obj){ var data = {siteName: siteName,id: obj}; $.post('/site/get_redirect_conf', data, function(res) { layer.close(laoding); - res = JSON.parse(res); if (res.status == true) { var mBody = "
    \ \ @@ -2326,7 +2315,7 @@ function opSSL(type, id, siteName, callback){ var key = ''; var csr = ''; var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('site/get_ssl','site_name='+siteName,function(data){ + $.post('/site/get_ssl','site_name='+siteName,function(data){ layer.close(loadT); var rdata = data['data']; diff --git a/web/thisdb/binding.py b/web/thisdb/binding.py index ea968812d..689989530 100644 --- a/web/thisdb/binding.py +++ b/web/thisdb/binding.py @@ -14,6 +14,10 @@ import core.mw as mw __FIELD = 'id,pid,domain,port,path,add_time' +def getBindingCountByDomain(name): + # .debug(True) + return mw.M('binding').where("domain=?", (name,)).count() + def addBinding(pid, domain, port, path): now_time = mw.getDateFromNow() insert_data = { @@ -25,15 +29,17 @@ def addBinding(pid, domain, port, path): } return mw.M('binding').insert(insert_data) - def getBindingListBySiteId(site_id): - task_list = mw.M('binding').where('pid=?', (site_id,)).field(__FIELD).select() - return task_list + # .debug(True) + binding_list = mw.M('binding').field(__FIELD).where('pid=?', (site_id,)).select() + return binding_list + +def getBindingById(site_id): + return mw.M('binding').where("id=?", (site_id,)).field(__FIELD).find() - -def deleteBindingId(domain_id): - return mw.M('binding').where("id=?", (domain_id,)).delete() +def deleteBindingById(binding_id): + return mw.M('binding').where("id=?", (binding_id,)).delete() def deleteBindingBySiteId(site_id): return mw.M('binding').where("pid=?", (site_id,)).delete() diff --git a/web/thisdb/domain.py b/web/thisdb/domain.py index fb163a624..bdf3e47d2 100644 --- a/web/thisdb/domain.py +++ b/web/thisdb/domain.py @@ -12,6 +12,10 @@ import core.mw as mw __FIELD = 'id,pid,name,port,add_time' +def getDomainCountByName(name): + # .debug(True) + return mw.M('domain').where("name=?", (name,)).count() + def addDomain(pid, name, port): now_time = mw.getDateFromNow() insert_data = { @@ -25,7 +29,7 @@ def addDomain(pid, name, port): def getDomainByPid(pid): # .debug(True) return mw.M('domain').field(__FIELD).where("pid=?", (pid,)).select() - + def deleteDomainId(domain_id): return mw.M('domain').where("id=?", (domain_id,)).delete() diff --git a/web/utils/site.py b/web/utils/site.py index d6785d6aa..807b03993 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -124,8 +124,8 @@ class sites(object): def getProxyPath(self, siteName): return "{}/{}".format(self.proxyPath, siteName) - def getDirBindRewrite(self, siteName, dirname): - return self.rewritePath + '/' + siteName + '_' + dirname + '.conf' + def getDirBindRewrite(self, site_name, dir_name): + return self.rewritePath + '/' + site_name + '_' + dir_name + '.conf' def getIndexConf(self): return mw.getServerDir() + '/openresty/nginx/conf/nginx.conf' @@ -256,8 +256,7 @@ class sites(object): def nginxAddConf(self): source_tpl = mw.getPanelDir() + '/data/tpl/nginx.conf' - print(source_tpl) - vhost_file = self.vhostPath + '/' + self.siteName + '.conf' + vhost_file = self.getHostConf(self.siteName) content = mw.readFile(source_tpl) content = content.replace('{$PORT}', self.sitePort) @@ -463,6 +462,154 @@ class sites(object): self.addDirUserIni(site_path, run_path) return mw.returnData(True, '已打开防跨站设置!') + + def getDirBinding(self, site_id): + info = thisdb.getSitesById(site_id) + path = info['path'] + if not os.path.exists(path): + checks = ['/', '/usr', '/etc'] + if path in checks: + data = {} + data['dirs'] = [] + data['binding'] = [] + return mw.returnData(True, 'OK', data) + os.system('mkdir -p ' + path) + os.system('chmod 755 ' + path) + os.system('chown www:www ' + path) + siteName = info['name'] + mw.writeLog('网站管理', '站点[' + siteName + '],根目录[' + path + ']不存在,已重新创建!') + + dirnames = [] + for filename in os.listdir(path): + try: + filePath = path + '/' + filename + if os.path.islink(filePath): + continue + if os.path.isdir(filePath): + dirnames.append(filename) + except: + pass + + data = {} + data['dirs'] = dirnames + data['binding'] = thisdb.getBindingListBySiteId(site_id) + return mw.returnJson(True, 'OK', data) + + def addDirBind(self, site_id, domain, dir_name): + domain_split = domain.split(':') + domain = domain_split[0] + port = '80' + if len(domain_split) > 1: + port = domain_split[1] + if dir_name == '': + mw.returnData(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.returnData(False, '主域名格式不正确!') + + info = thisdb.getSitesById(site_id) + webdir = info['path'] + '/' + dir_name + + if thisdb.getBindingCountByDomain(domain): + return mw.returnData(False, '您添加的域名在子目录已存在!') + + if thisdb.getDomainCountByName(domain) > 0: + return mw.returnData(False, '您添加的域名已存在!') + + filename = self.getHostConf(info['name']) + conf = mw.readFile(filename) + if conf: + rep = r"enable-php-([0-9]{2,3})\.conf" + domain_split = re.search(rep, conf).groups() + version = domain_split[0] + + source_dirbind_tpl = mw.getPanelDir() + '/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}', info['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) + msg = 'ERROR:
    ' + isError.replace("\n", '
    ') + '
    ' + return mw.returnData(False, msg) + + thisdb.addBinding(site_id,domain,port,dir_name) + msg = mw.getInfo('网站[{1}]子目录[{2}]绑定到[{3}]',(info['name'], dir_name, domain)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + mw.removeBackFile(filename) + return mw.returnData(True, '添加成功!') + + # 取子目录Rewrite + def getDirBindingRewrite(self, binding_id, add): + binding_info = thisdb.getBindingById(binding_id) + info = thisdb.getSitesById(binding_info['pid']) + + filename = self.getDirBindRewrite(info['name'], binding_info['path']) + if add == '1': + mw.writeFile(filename, '') + file = self.getHostConf(info['name']) + conf = mw.readFile(file) + domain = binding_info['domain'] + rep = "\n#BINDING-" + domain + "-START(.|\n)+BINDING-" + domain + "-END" + tmp = re.search(rep, conf).group() + dirConf = tmp.replace('rewrite/' + info['name'] + '.conf;', 'rewrite/' + info['name'] + '_' + binding_info['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 data + + def delDirBinding(self, binding_id): + + binding_info = thisdb.getBindingById(binding_id) + info = thisdb.getSitesById(binding_info['pid']) + + filename = self.getHostConf(info['name']) + conf = mw.readFile(filename) + if conf: + rep = r"\s*.+BINDING-" + binding_info['domain'] + "-START(.|\n)+BINDING-" + binding_info['domain'] + "-END" + conf = re.sub(rep, '', conf) + mw.writeFile(filename, conf) + + filename = self.getDirBindRewrite(info['name'], binding_info['path']) + if os.path.exists(filename): + os.remove(filename) + + msg = mw.getInfo('删除网站[{1}]子目录[{2}]绑定',(info['name'], binding_info['path'])) + mw.writeLog('网站管理', msg) + mw.restartWeb() + + thisdb.deleteBindingById(binding_id) + return mw.returnJson(True, '删除成功!') + + def logsOpen(self, site_id): info = thisdb.getSitesById(site_id) name = info['name'] @@ -782,6 +929,116 @@ class sites(object): mw.writeLog('网站管理', '网站[{1}]流量限制已关闭!', (siteName,)) return mw.returnData(True, '已关闭流量限制!') + + # 获取重定向配置 + def getRedirect(self, siteName): + redirect_file = self.getRedirectDataPath(siteName) + if not os.path.exists(redirect_file): + mw.execShell("mkdir {}/{}".format(self.redirectPath, siteName)) + return mw.returnData(True, "no exists!", {"result": [], "count": 0}) + + content = mw.readFile(redirect_file) + data = json.loads(content) + # 处理301信息 + return mw.returnData(True, "ok", {"result": data, "count": len(data)}) + + # 操作 重定向配置 + def operateRedirectConf(self, siteName, method='start'): + vhost_file = self.getHostConf(siteName) + 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 setRedirect(self, siteName, site_from, to, type, r_type, keep_path): + if siteName == '' or site_from == '' or to == '' or type == '' or r_type == '': + return mw.returnData(False, "必填项不能为空!") + + redirect_file = self.getRedirectDataPath(siteName) + content = mw.readFile(redirect_file) if os.path.exists(redirect_file) else "" + data = json.loads(content) if content != "" else [] + + _r_type = 0 if r_type == "301" else 1 + _type_code = 0 if type == "path" else 1 + _keep_path = 1 if keep_path == "1" else 0 + + # check if domain exists in site + if _type_code == 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.returnData(False, "域名不存在!") + + file_content = "" + # path + if _type_code == 0: + redirect_type = "permanent" if _r_type == 0 else "redirect" + if not site_from.startswith("/"): + site_from = "/{}".format(site_from) + if _keep_path == 1: + to = "{}$1".format(to) + site_from = "{}(.*)".format(site_from) + file_content = "rewrite ^{} {} {};".format(site_from, to, redirect_type) + # domain + else: + if _keep_path == 1: + _to = "{}$request_uri".format(_to) + + redirect_type = "301" if _type_code == 0 else "302" + _if = "if ($host ~ '^{}')".format(site_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"] == site_from: + return mw.returnData(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.returnData(False, "错误的目标地址") + + # write data json file + data.append({"r_from": site_from, "type": _type_code, "r_type": _type_code,"r_to": to, 'keep_path': _keep_path, 'id': _id}) + mw.writeFile(redirect_file, json.dumps(data)) + mw.writeFile("{}/{}.conf".format(self.getRedirectPath(siteName), _id), file_content) + + self.operateRedirectConf(siteName, 'start') + mw.restartWeb() + return mw.returnData(True, "设置成功") + + def getRedirectConf(self,siteName, rid): + if rid == '' or siteName == '': + return mw.returnData(False, "必填项不能为空!") + + path = self.getRedirectPath(siteName) + conf = "{}/{}.conf".format(path, rid) + data = mw.readFile(conf) + if data == False: + return mw.returnData(False, "获取失败!") + return mw.returnData(True, "ok", {"result": data}) + def setPhpVersion(self, siteName, version): # nginx file = self.getHostConf(siteName) From e6af36a34b9614f201c07948ca85b9c561d78845 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 02:26:15 +0800 Subject: [PATCH 150/965] Update panel_task.py --- panel_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index d60b4bc97..afb5f96b3 100755 --- a/panel_task.py +++ b/panel_task.py @@ -224,7 +224,7 @@ def startPHPVersion(version): return True # 尝试重启服务 cgi = '/tmp/php-cgi-' + version + '.sock' - pid = sdir + '/php/' + version + '/var/run/php-fpm.pid' + pid = server_dir + '/php/' + version + '/var/run/php-fpm.pid' data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'") if data[0] != '': os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ") From c2a8b9523143ef19b122de2850fff86e7d1bfb79 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 13:19:03 +0800 Subject: [PATCH 151/965] update --- panel_tools.py | 2 +- web/admin/setting/setting.py | 31 +++++- web/static/app/config.js | 187 +++++++++++++++++------------------ web/thisdb/user.py | 7 ++ 4 files changed, 130 insertions(+), 97 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index ef4416085..731174d94 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -23,7 +23,7 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) -from utils.firewall import firewall as MwFirewall +from utils.firewall import Firewall as MwFirewall import core.mw as mw import thisdb diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 3b2d81a0f..727ef6169 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -15,8 +15,7 @@ import os from flask import Blueprint, render_template from flask import request -from admin import model - +from admin import session from admin.user_login_check import panel_login_required @@ -203,6 +202,34 @@ def set_ipv6_status(): mw.restartMw() return mw.returnData(True, '设置成功!') +# 设置面板用户 +@blueprint.route('/set_name', endpoint='set_name', methods=['POST']) +@panel_login_required +def set_name(): + name1 = request.form.get('name1', '') + name2 = request.form.get('name2', '') + if name1 != name2: + return mw.returnData(False, '两次输入的用户名不一致,请重新输入!') + if len(name1) < 3: + return mw.returnData(False, '用户名长度不能少于3位') + thisdb.setUserByName(session['username'], name1) + session['username'] = name1 + return mw.returnData(True, '用户修改成功!') + +@blueprint.route('/set_password', endpoint='set_password', methods=['POST']) +@panel_login_required +def set_password(): + password1 = request.form.get('password1', '') + password2 = request.form.get('password2', '') + if password1 != password2: + return mw.returnData(False, '两次输入的密码不一致,请重新输入!') + if len(password1) < 5: + return mw.returnData(False, '用户密码不能小于5位!') + + thisdb.setUserPwdByName(session['username'], password1) + return mw.returnData(True, '密码修改成功!') + + # 设置站点状态 @blueprint.route('/set_port', endpoint='set_port', methods=['POST']) @panel_login_required diff --git a/web/static/app/config.js b/web/static/app/config.js index b3c662b9c..ac64e1822 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -310,126 +310,125 @@ function modifyAuthPath() { }); } -function setPassword(a) { - if(a == 1) { - p1 = $("#p1").val(); - p2 = $("#p2").val(); - if(p1 == "" || p1.length < 8) { - layer.msg('面板密码不能少于8位!', {icon: 2}); - return - } - - //准备弱口令匹配元素 - var checks = ['admin888','123123123','12345678','45678910','87654321','asdfghjkl','password','qwerqwer']; - pchecks = 'abcdefghijklmnopqrstuvwxyz1234567890'; - for(var i=0;i\ + btn:["修改","关闭","随机"], + content: "
    \
    \ 密码\ -
    \ +
    \
    \
    \ 重复\ -
    \ +
    \
    \ -
    \ - 随机\ - \ - \ -
    \ -
    " +
    ", + yes:function(){ + var p1 = $("#p1").val(); + var p2 = $("#p2").val(); + if(p1 == "" || p1.length < 8) { + layer.msg('面板密码不能少于8位!', {icon: 2}); + return + } + + //准备弱口令匹配元素 + var checks = ['admin888','123123123','12345678','45678910','87654321','asdfghjkl','password','qwerqwer']; + pchecks = 'abcdefghijklmnopqrstuvwxyz1234567890'; + for(var i=0;i\ + btn:["确定","取消","随机"], + content: "
    \
    用户名\ -
    \ +
    \
    \
    \ 重复\ -
    \ +
    \
    \ -
    \ - \ - \ -
    \ -
    " +
    ", + yes: function(){ + p1 = $("#p1").val(); + p2 = $("#p2").val(); + if(p1 == "" || p1.length < 3) { + layer.msg('用户名长度不能少于3位', {icon: 2}); + return; + } + if(p1 != p2) { + layer.msg('两次输入的用户名不一致', {icon: 2}); + return; + } + $.post("/setting/set_name", "name1=" + encodeURIComponent(p1) + "&name2=" + encodeURIComponent(p2), function(b) { + if(b.status) { + layer.closeAll(); + layer.msg(b.msg, {icon: 1}); + $("input[name='username_']").val(p1) + } else { + layer.msg(b.msg, {icon: 2}); + } + },'json'); + return + }, + btn3:function(){ + var pwd = randomStrPwd(12); + $("#p1").val(pwd); + $("#p2").val(pwd); + layer.msg('请在修改前记录好您的用户名!',{time:2000}); + return false; + } }) } diff --git a/web/thisdb/user.py b/web/thisdb/user.py index 3cbaa4bc2..ebf74532a 100644 --- a/web/thisdb/user.py +++ b/web/thisdb/user.py @@ -88,6 +88,13 @@ def updateUserLoginTime(): mw.M('users').field(__field).where('id=?', (1,)).update({'login_time':now_time}) return True +def setUserByName(name, new_name): + return mw.M('users').where("name=?", (name,)).setField('name', new_name.strip()) + +def setUserPwdByName(name, password): + pwd = mw.md5(password) + return mw.M('users').where("name=?", (name,)).setField('password', pwd) + def setUserByRoot( name: str | None = None, password: str | None = None, From 23b3f213b6a75e6b58c47c2eecc74262b52387ea Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 14:15:53 +0800 Subject: [PATCH 152/965] Update panel_tools.py --- panel_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_tools.py b/panel_tools.py index 731174d94..8697b747b 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -237,7 +237,7 @@ def show_panel_pwd(): if mw.md5(pwd) == info['password']: print('|-password: ' + pwd) return - print("* password has been changed!") + print("*-password has been changed!") def show_panel_adminpath(): admin_path = thisdb.getOption('admin_path') From 3a1a3f657911018ad36c4e9cea82044fdc25aee5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 17:32:00 +0800 Subject: [PATCH 153/965] update --- web/admin/site/__init__.py | 1 + web/admin/site/proxy.py | 69 ++++++++++ web/admin/site/redirect.py | 15 ++- web/utils/site.py | 253 ++++++++++++++++++++++++++++++++++++- 4 files changed, 335 insertions(+), 3 deletions(-) create mode 100644 web/admin/site/proxy.py diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index f98b21bb5..f7b5c7397 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -15,4 +15,5 @@ from .php import * from .logs import * from .dir import * from .redirect import * +from .proxy import * from .ssl import * \ No newline at end of file diff --git a/web/admin/site/proxy.py b/web/admin/site/proxy.py new file mode 100644 index 000000000..704a9da4a --- /dev/null +++ b/web/admin/site/proxy.py @@ -0,0 +1,69 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取代理列表 +@blueprint.route('/get_proxy_list', endpoint='get_proxy_list', methods=['POST']) +@panel_login_required +def get_proxy_list(): + site_name = request.form.get("siteName", '') + return MwSites.instance().getProxyList(site_name) + +# 获取代理列表 +@blueprint.route('/set_proxy', endpoint='set_proxy', methods=['POST']) +@panel_login_required +def set_proxy(): + site_name = request.form.get('siteName', '') + site_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', '') + pid = request.form.get('id', '') + return MwSites.instance().setProxy(site_name,site_from,to,host,name,open_proxy, open_cache,cache_time, pid) + +# 获取代理配置 +@blueprint.route('/get_proxy_conf', endpoint='get_proxy_conf', methods=['POST']) +@panel_login_required +def get_proxy_conf(): + site_name = request.form.get("siteName", '') + rid = request.form.get("id", '') + return MwSites.instance().getProxyConf(site_name, rid) + + +# 删除代理配置 +@blueprint.route('/del_proxy', endpoint='del_proxy', methods=['POST']) +@panel_login_required +def del_proxy(): + site_name = request.form.get("siteName", '') + rid = request.form.get("id", '') + return MwSites.instance().delProxy(site_name, rid) + + + + diff --git a/web/admin/site/redirect.py b/web/admin/site/redirect.py index c74983ed4..ad6bdca19 100644 --- a/web/admin/site/redirect.py +++ b/web/admin/site/redirect.py @@ -43,13 +43,24 @@ def set_redirect(): keep_path = request.form.get("keep_path", '') # keep path return MwSites.instance().setRedirect(site_name, site_from, to, type, r_type, keep_path) -# 设置重定向配置 +# 获取重定向配置 @blueprint.route('/get_redirect_conf', endpoint='get_redirect_conf', methods=['POST']) @panel_login_required def get_redirect_conf(): site_name = request.form.get("siteName", '') rid = request.form.get("id", '') - return MwSites.instance().getRedirectConf(site_name, rid) + return MwSites.instance().getRedirectConf(site_name, rid) + +# 删除重定向配置 +@blueprint.route('/del_redirect', endpoint='del_redirect', methods=['POST']) +@panel_login_required +def del_redirect(): + site_name = request.form.get("siteName", '') + rid = request.form.get("id", '') + return MwSites.instance().delRedirect(site_name, rid) + + + diff --git a/web/utils/site.py b/web/utils/site.py index 807b03993..f876760f2 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1028,7 +1028,7 @@ class sites(object): mw.restartWeb() return mw.returnData(True, "设置成功") - def getRedirectConf(self,siteName, rid): + def getRedirectConf(self, siteName, rid): if rid == '' or siteName == '': return mw.returnData(False, "必填项不能为空!") @@ -1039,6 +1039,257 @@ class sites(object): return mw.returnData(False, "获取失败!") return mw.returnData(True, "ok", {"result": data}) + # 删除指定重定向 + def delRedirect(self,siteName, rid): + if rid == '' or siteName == '': + return mw.returnData(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"] == rid: + 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), rid)) + except Exception as e: + return mw.returnData(False, "删除失败:"+str(e)) + return mw.returnData(True, "删除成功!") + + # 读取 网站 反向代理列表 + def getProxyList(self, site_name): + data_path = self.getProxyDataPath(site_name) + + if not os.path.exists(data_path): + mw.execShell("mkdir {}/{}".format(self.proxyPath, site_name)) + return mw.returnData(True, "", {"result": [], "count": 0}) + + content = mw.readFile(data_path) + data = json.loads(content) + tmp = [] + for proxy in data: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + 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.returnData(True, "ok", {"result": data, "count": len(data)}) + + # 操作 反向代理配置 + def operateProxyConf(self, site_name, method='start'): + vhost_file = self.getHostConf(site_name) + content = mw.readFile(vhost_file) + + proxy_cnf = '''#PROXY-START + include %s/*.conf; + #PROXY-END''' % (self.getProxyPath(site_name)) + + 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 setProxy(self, site_name, site_from, to, host, name, open_proxy, open_cache, cache_time, pid): + from urllib.parse import urlparse + if site_name == "" or site_from == "" or to == "" or host == "" or name == "": + return mw.returnData(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.returnData(False, "错误的目标地址!") + + # get host from url + try: + if host == "$host": + host_tmp = urlparse(_to) + host = host_tmp.netloc + except: + return mw.returnData(False, "错误的目标地址") + + proxy_site_path = self.getProxyDataPath(site_name) + 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 pid == "": + pid = mw.md5("{}".format(name)) + else: + proxy_action = 'edit' + + if proxy_action == "add": + for item in data: + if item["name"] == name: + return mw.returnData(False, "名称重复!!") + if item["from"] == site_from: + return mw.returnData(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 = "\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 = "\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 site_from[0] != '/': + site_from = '/' + site_from + tpl = tpl.replace("{from}", site_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(site_name), pid) + conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(site_name), pid) + mw.writeFile(conf_proxy, tpl) + + rule_test = mw.checkWebConfig() + if rule_test != True: + os.remove(conf_proxy) + return mw.returnData(False, "OpenResty配置测试不通过, 请重试: {}".format(rule_test)) + + if proxy_action == "add": + # 添加代理 + pid = mw.md5("{}".format(name)) + for item in data: + if item["name"] == name: + return mw.returnData(False, "名称重复!") + if item["from"] == site_from: + return mw.returnData(False, "代理目录已存在!") + data.append({ + "name": name, + "from": site_from, + "to": to, + "host": host, + "open_cache": open_cache, + "open_proxy": open_proxy, + "cache_time": cache_time, + "id": pid, + }) + else: + # 修改代理 + dindex = -1 + for x in range(len(data)): + if data[x]["id"] == pid: + dindex = x + break + if dindex < 0: + return mw.returnData(False, "异常请求") + data[dindex]['from'] = site_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(site_name, 'start') + mw.restartWeb() + return mw.returnData(True, "ok", {"hash": pid}) + + def getProxyConf(self, site_name, pid): + if pid == '' or site_name == '': + return mw.returnData(False, "必填项不能为空!") + + conf_file = "{}/{}/{}.conf".format(self.proxyPath, site_name, pid) + if not os.path.exists(conf_file): + conf_file = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, pid) + + if not os.path.exists(conf_file): + return mw.returnData(False, "获取失败!") + + data = mw.readFile(conf_file) + return mw.returnData(True, "ok", {"result": data}) + + def delProxy(self, site_name, pid): + if pid == '' or site_name == '': + return mw.returnData(False, "必填项不能为空!") + + try: + data_path = self.getProxyDataPath(site_name) + 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"] == pid: + data.remove(item) + break + # write database + mw.writeFile(data_path, json.dumps(data)) + + # data is empty,should stop + if len(data) == 0: + self.operateProxyConf(site_name, 'stop') + # remove conf file + cmd = "rm -rf {}/{}.conf*".format(self.getProxyPath(site_name), pid) + mw.execShell(cmd) + except: + return mw.returnData(False, "删除反代失败!") + + mw.restartWeb() + return mw.returnData(True, "删除反代成功!") + + def setPhpVersion(self, siteName, version): # nginx file = self.getHostConf(siteName) From 9945425f99cb99c68ab2efd6c0f46646b11dd949 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 18:28:34 +0800 Subject: [PATCH 154/965] update --- class/core/mw.py | 6 ++--- web/admin/site/ssl.py | 9 +++++++ web/core/mw.py | 62 +++++++++++++++++++++++++++++++++++++++++++ web/utils/site.py | 58 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/class/core/mw.py b/class/core/mw.py index 99fe840ab..40bc1771f 100755 --- a/class/core/mw.py +++ b/class/core/mw.py @@ -1832,11 +1832,9 @@ def getCertName(certPath): if hasattr(issuer, 'O'): result['issuer'] = issuer.O # 取到期时间 - result['notAfter'] = strfDate( - bytes.decode(x509.get_notAfter())[:-1]) + result['notAfter'] = strfDate(bytes.decode(x509.get_notAfter())[:-1]) # 取申请时间 - result['notBefore'] = strfDate( - bytes.decode(x509.get_notBefore())[:-1]) + result['notBefore'] = strfDate(bytes.decode(x509.get_notBefore())[:-1]) # 取可选名称 result['dns'] = [] for i in range(x509.get_extension_count()): diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 7c9b0ad82..23d98e958 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -25,6 +25,15 @@ import thisdb from .site import blueprint +# 删除代理配置 +@blueprint.route('/get_ssl', endpoint='get_ssl', methods=['POST']) +@panel_login_required +def get_ssl(): + site_name = request.form.get('site_name', '') + ssl_type = request.form.get('ssl_type', '') + return MwSites.instance().getSsl(site_name, ssl_type) + + diff --git a/web/core/mw.py b/web/core/mw.py index f723c7bbd..60342257e 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -1175,6 +1175,68 @@ def getMyORM(): # 数据库 START # --------------------------------------------------------------------------------- +##################### ssl start ######################################### +# 获取证书名称 +def getCertName(certPath): + if not os.path.exists(certPath): + return None + try: + import OpenSSL + result = {} + x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, readFile(certPath)) + # 取产品名称 + issuer = x509.get_issuer() + result['issuer'] = '' + if hasattr(issuer, 'CN'): + result['issuer'] = issuer.CN + if not result['issuer']: + is_key = [b'0', '0'] + issue_comp = issuer.get_components() + if len(issue_comp) == 1: + is_key = [b'CN', 'CN'] + for iss in issue_comp: + if iss[0] in is_key: + result['issuer'] = iss[1].decode() + break + if not result['issuer']: + if hasattr(issuer, 'O'): + result['issuer'] = issuer.O + # 取到期时间 + result['notAfter'] = strfDate( + bytes.decode(x509.get_notAfter())[:-1]) + # 取申请时间 + result['notBefore'] = strfDate( + bytes.decode(x509.get_notBefore())[:-1]) + # 取可选名称 + result['dns'] = [] + for i in range(x509.get_extension_count()): + s_name = x509.get_extension(i) + if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: + s_dns = str(s_name).split(',') + for d in s_dns: + result['dns'].append(d.split(':')[1]) + subject = x509.get_subject().get_components() + # 取主要认证名称 + if len(subject) == 1: + result['subject'] = subject[0][1].decode() + else: + if not result['dns']: + for sub in subject: + if sub[0] == b'CN': + result['subject'] = sub[1].decode() + break + if 'subject' in result: + result['dns'].append(result['subject']) + else: + result['subject'] = result['dns'][0] + result['endtime'] = int(int(time.mktime(time.strptime( + result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400) + return result + except Exception as e: + writeFileLog(getTracebackInfo()) + return None +##################### ssl end ######################################### + ##################### notify start ######################################### diff --git a/web/utils/site.py b/web/utils/site.py index f876760f2..8445e960d 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1290,6 +1290,64 @@ location ^~ {from} {\n\ return mw.returnData(True, "删除反代成功!") + + # 是否跳转到https + def isToHttps(self, site_name): + file = self.getHostConf(site_name) + 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 getSsl(self, site_name, 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.returnData(True, 'OK', data) + + def setPhpVersion(self, siteName, version): # nginx file = self.getHostConf(siteName) From 08336eb2ee48e1d9fdf14496b8274f245caf1516 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 19:13:53 +0800 Subject: [PATCH 155/965] Update firewall.html --- web/templates/default/firewall.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/templates/default/firewall.html b/web/templates/default/firewall.html index e0b1e5224..8aa674fea 100755 --- a/web/templates/default/firewall.html +++ b/web/templates/default/firewall.html @@ -92,7 +92,7 @@ - + -
    +
    + 启用防火墙 +
    + +
    - +
    @@ -102,10 +103,10 @@ 说明: 支持放行端口范围,如: 3000:3500
    -
    +
    -
    +
    diff --git a/web/utils/file.py b/web/utils/file.py index 691479979..de7a9325e 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -322,7 +322,7 @@ def getDirSize(filePath, size=0): # 获取目录大小(bash) def getDirSizeByBash(path): tmp = mw.execShell('du -sh ' + path) - return tmp[0].split()[0] + return tmp[0].split()[0].lower() # 计算文件数量 def getCount(path, search = None): @@ -493,3 +493,13 @@ def setMode(path): p_stat = os.stat(s_path) os.chown(path, p_stat.st_uid, p_stat.st_gid) os.chmod(path, p_stat.st_mode) + + +def closeLogs(): + log_file = mw.getLogsDir() + os.system('rm -rf ' + log_file + '/*') + mw.opWeb('reload') + # os.system('kill -USR1 `cat ' + mw.getServerDir() +'/openresty/nginx/logs/nginx.pid`') + mw.writeLog('文件管理', '网站日志已被清空!') + tmp = getDirSizeByBash(log_file) + return mw.returnData(True, tmp) From a9f10842e7158a985ab5901c2130f577df3164f0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 20:35:58 +0800 Subject: [PATCH 158/965] Update files.js --- web/static/app/files.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/web/static/app/files.js b/web/static/app/files.js index 5b0a5aec1..6bb9abf32 100755 --- a/web/static/app/files.js +++ b/web/static/app/files.js @@ -1,15 +1,8 @@ -// console.log(lan); - //判断磁盘数量超出宽度 function isDiskWidth(){ - var comlistWidth = $("#comlist").width(); - var bodyWidth = $(".file-box").width(); - if(comlistWidth + 530 > bodyWidth){ - $("#comlist").css({"width":bodyWidth-530+"px","height":"34px","overflow":"auto"}); - } - else{ - $("#comlist").removeAttr("style"); - } + var comlist_width = $("#comlist").width(); + var body_width = $(".file-box").width(); + $("#comlist").css({"width":(body_width-520)+"px","height":"34px","overflow":"auto"}); } //打开回收站 @@ -227,7 +220,9 @@ function getFileName(name){ function reisImage(fileName){ var exts = ['jpg','jpeg','png','bmp','gif','tiff','ico']; for(var i=0; i Date: Sun, 10 Nov 2024 20:36:48 +0800 Subject: [PATCH 159/965] Update files.js --- web/static/app/files.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/static/app/files.js b/web/static/app/files.js index 6bb9abf32..85e1ef806 100755 --- a/web/static/app/files.js +++ b/web/static/app/files.js @@ -1051,7 +1051,9 @@ function createDir(type, path) { ', success:function(){ $("#newDirName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#createDirBtn").click(); + if(e.keyCode == 13) { + $("#createDirBtn").click(); + } }); } }); From af29e865e82584b6c1d8f5e241cce17d727555d9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 22:43:08 +0800 Subject: [PATCH 160/965] update --- web/admin/setup/sql/default.sql | 20 ++++++++--- web/static/app/config.js | 14 ++++---- web/thisdb/__init__.py | 6 ++-- web/thisdb/app.py | 60 +++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 web/thisdb/app.py diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index 794ede180..074484838 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -99,8 +99,8 @@ CREATE TABLE IF NOT EXISTS `users` ( `login_time` TEXT, `phone` TEXT, `email` TEXT, - `add_time` INTEGER, - `update_time` INTEGER + `add_time` TEXT, + `update_time` TEXT ); CREATE TABLE IF NOT EXISTS `tasks` ( @@ -111,7 +111,7 @@ CREATE TABLE IF NOT EXISTS `tasks` ( `end` INTEGER, `cmd` TEXT, `status` INTEGER, - `add_time` INTEGER + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `temp_login` ( @@ -123,7 +123,7 @@ CREATE TABLE IF NOT EXISTS `temp_login` ( `login_addr` REAL, `logout_time` INTEGER, `expire` INTEGER, - `add_time` INTEGER + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `panel` ( @@ -133,7 +133,17 @@ CREATE TABLE IF NOT EXISTS `panel` ( `username` TEXT, `password` TEXT, `click` INTEGER, - `add_time` INTEGER + `add_time` TEXT +); + +CREATE TABLE IF NOT EXISTS `app` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `app_id` TEXT, + `app_secret` TEXT, + `white_list` TEXT, + `status` INTEGER, + `add_time` TEXT, + `update_time` TEXT ); CREATE TABLE IF NOT EXISTS `option` ( diff --git a/web/static/app/config.js b/web/static/app/config.js index ac64e1822..30850558e 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -268,13 +268,13 @@ function modifyAuthPath() { btn:['提交','关闭', '随机生成'], shadeClose: false, content: '
    \ -
    \ - 入口地址\ -
    \ - \ -
    \ -
    \ -
    ', +
    \ + 入口地址\ +
    \ + \ +
    \ +
    \ + ', yes:function(index){ var auth_path = $("input[name='auth_path_set']").val(); if (auth_path == '/' || auth_path == ''){ diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 31924c2de..33f664b8b 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -10,9 +10,9 @@ from .init import * from .option import * -from .user import * from .sites import * +from .site_types import * from .domain import * from .binding import * @@ -20,5 +20,7 @@ from .tasks import * from .logs import * from .crontab import * from .firewall import * + from .temp_login import * -from .site_types import * +from .user import * +from .app import * diff --git a/web/thisdb/app.py b/web/thisdb/app.py new file mode 100644 index 000000000..8f34d7ed3 --- /dev/null +++ b/web/thisdb/app.py @@ -0,0 +1,60 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,app_id,app_secret,status,add_time,update_time' + +def addApp(app_id,app_secret): + now_time = mw.getDateFromNow() + add_data = { + 'app_id': app_id, + 'app_secret': app_secret, + 'status': 1, + 'add_time': now_time, + 'update_time': now_time + } + return mw.M('app').insert(add_data) + +def getAppList( + page:int | None = 1, + size:int | None = 10, +): + m = mw.M('app').field(__FIELD) + + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + app_list = m.limit(limit).order('id desc').select() + count = m.count() + + data = {} + data['list'] = app_list + data['count'] = count + return data + +def getAppById(aid): + return mw.M('app').field(__FIELD).where("id=?", (aid,)).find() + +def deleteAppById(aid): + return mw.M('app').where("id=?", (aid,)).delete() + +def setAppData(aid, + status: str | None = None, + app_id: str | None = None, + app_secret: str | None = None, +): + up_data = {} + if status is not None: + up_data['status'] = status + if app_id is not None: + up_data['app_id'] = app_id + if app_secret is not None: + up_data['app_secret'] = app_secret + return mw.M('app').where('id=?',(aid,)).update(up_data) From f511b8e44f02ebcea616fb12b929507c100923ea Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 10 Nov 2024 22:48:41 +0800 Subject: [PATCH 161/965] Update app.py --- web/thisdb/app.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/web/thisdb/app.py b/web/thisdb/app.py index 8f34d7ed3..be4f5ace0 100644 --- a/web/thisdb/app.py +++ b/web/thisdb/app.py @@ -13,7 +13,7 @@ import core.mw as mw __FIELD = 'id,app_id,app_secret,status,add_time,update_time' def addApp(app_id,app_secret): - now_time = mw.getDateFromNow() + now_time = mw.getDateFromNow() add_data = { 'app_id': app_id, 'app_secret': app_secret, @@ -24,23 +24,23 @@ def addApp(app_id,app_secret): return mw.M('app').insert(add_data) def getAppList( - page:int | None = 1, + page:int | None = 1, size:int | None = 10, -): - m = mw.M('app').field(__FIELD) +): + m = mw.M('app').field(__FIELD) - start = (int(page) - 1) * (int(size)) + start = (int(page) - 1) * (int(size)) limit = str(start) + ',' +str(size) - app_list = m.limit(limit).order('id desc').select() - count = m.count() + app_list = m.limit(limit).order('id desc').select() + count = m.count() - data = {} + data = {} data['list'] = app_list data['count'] = count return data def getAppById(aid): - return mw.M('app').field(__FIELD).where("id=?", (aid,)).find() + return mw.M('app').field(__FIELD).where("id=?", (aid,)).find() def deleteAppById(aid): return mw.M('app').where("id=?", (aid,)).delete() From 111aaa768a94dfaa31e2bdca92d4fd0c1dea6042 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 11 Nov 2024 00:48:54 +0800 Subject: [PATCH 162/965] update --- panel_task.py | 3 + web/admin/__init__.py | 22 +---- web/admin/setting/__init__.py | 9 +- web/admin/setting/app.py | 55 +++++++++++ web/admin/setup/__init__.py | 16 +++ web/admin/setup/sql/default.sql | 10 +- web/static/app/config.js | 152 +++++++++++++++++++++++++++++ web/templates/default/setting.html | 2 +- web/thisdb/app.py | 5 +- 9 files changed, 239 insertions(+), 35 deletions(-) create mode 100644 web/admin/setting/app.py diff --git a/panel_task.py b/panel_task.py index afb5f96b3..97c64e853 100755 --- a/panel_task.py +++ b/panel_task.py @@ -27,6 +27,9 @@ sys.path.append(web_dir) import core.mw as mw import thisdb +from admin import setup +setup.init() + g_log_file = mw.getPanelTaskLog() isTask = mw.getMWLogs() + '/panelTask.pl' diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 359557fea..8d40a7f32 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -76,27 +76,7 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True # 初始化db # sys_db.init_app(app) # Migrate(app, sys_db) - - -# 检查数据库是否存在。如果没有就创建它。 -setup_db_required = False -if not os.path.isfile(config.SQLITE_PATH): - setup_db_required = True - -# with app.app_context(): -# sys_db.create_all() - -# with app.app_context(): -# if setup_db_required: -# sys_db.create_all() - -if setup_db_required: - # 初始化用户信息 - thisdb.initPanelData() - setup.init_admin_user() - setup.init_option() -setup.init_cmd() -setup.init_db_system() +setup.init() app.config['BASIC_AUTH_OPEN'] = False diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py index 8e01f4bc0..1627e3b8f 100644 --- a/web/admin/setting/__init__.py +++ b/web/admin/setting/__init__.py @@ -9,15 +9,12 @@ # --------------------------------------------------------------------------------- from .setting import * + from .temp_login import * from .timezone import * from .secondary_verifiy import * + from .notify_email import * from .notify_tgbot import * +from .app import * - - - - - - \ No newline at end of file diff --git a/web/admin/setting/app.py b/web/admin/setting/app.py new file mode 100644 index 000000000..7fd802822 --- /dev/null +++ b/web/admin/setting/app.py @@ -0,0 +1,55 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from .setting import blueprint +import thisdb + + +# 获取APP列表 +@blueprint.route('/get_app_list', endpoint='get_app_list', methods=['POST']) +@panel_login_required +def get_app_list(): + limit = request.form.get('limit', '5').strip() + page = request.form.get('page', '1').strip() + tojs = request.form.get('tojs', '').strip() + + info = thisdb.getAppList(page=int(page),size=int(limit)) + data = {} + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':tojs,'p':page,'row':limit}) + return data + + +# 获取APP列表 +@blueprint.route('/add_app', endpoint='add_app', methods=['POST']) +@panel_login_required +def add_app(): + app_id = request.form.get('app_id', '').strip() + app_secret = request.form.get('app_secret', '1').strip() + limit_addr = request.form.get('limit_addr', '').strip() + + rid = thisdb.addApp(app_id,app_secret,limit_addr) + if rid > 0: + return mw.returnData(True, '添加成功!') + return mw.returnData(False, '添加失败!') diff --git a/web/admin/setup/__init__.py b/web/admin/setup/__init__.py index e8ab0677a..6747b01d4 100644 --- a/web/admin/setup/__init__.py +++ b/web/admin/setup/__init__.py @@ -8,8 +8,24 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os + from .user import init_admin_user from .option import init_option from .init_db_system import init_db_system from .init_cmd import init_cmd + +import thisdb +import config + +def init(): + # 检查数据库是否存在。如果没有就创建它。 + if not os.path.isfile(config.SQLITE_PATH): + # 初始化用户信息 + thisdb.initPanelData() + init_admin_user() + init_option() + init_cmd() + init_db_system() + diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index 074484838..ab2b3baae 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -37,6 +37,7 @@ CREATE TABLE IF NOT EXISTS `crontab` ( `add_time` TEXT, `update_time` TEXT ); +CREATE UNIQUE INDEX name_idx ON crontab(name); CREATE TABLE IF NOT EXISTS `firewall` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -46,15 +47,12 @@ CREATE TABLE IF NOT EXISTS `firewall` ( `add_time` TEXT, `update_time` TEXT ); - -ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp'; +CREATE UNIQUE INDEX port_idx ON firewall(port); INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`, `update_time`) VALUES (1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), (2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00','0000-00-00 00:00:00'); - - CREATE TABLE IF NOT EXISTS `logs` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, @@ -77,11 +75,13 @@ CREATE TABLE IF NOT EXISTS `sites` ( `add_time` TEXT, `update_time` TEXT ); +CREATE UNIQUE INDEX name_idx ON sites(name); CREATE TABLE IF NOT EXISTS `site_types` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT ); +CREATE UNIQUE INDEX name_idx ON site_types(name); CREATE TABLE IF NOT EXISTS `domain` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -145,6 +145,7 @@ CREATE TABLE IF NOT EXISTS `app` ( `add_time` TEXT, `update_time` TEXT ); +CREATE UNIQUE INDEX app_id_idx ON app(app_id); CREATE TABLE IF NOT EXISTS `option` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -152,5 +153,4 @@ CREATE TABLE IF NOT EXISTS `option` ( `type` TEXT, `value` TEXT ); - CREATE UNIQUE INDEX name_idx ON option(name); diff --git a/web/static/app/config.js b/web/static/app/config.js index 30850558e..8db5b183e 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -1472,3 +1472,155 @@ function setPanelApi(){ } ,{icon:rdata.status?1:2}, 1000); },'json'); } + + +function getAppList(page) { + if (typeof(page) == 'undefined'){ + page = 1; + } + + $.post('/setting/get_app_list', {page:page}, function(rdata){ + console.log(rdata); + if ( typeof(rdata.status) !='undefined' && !rdata.status){ + showMsg(rdata.msg,function(){ + layer.closeAll(); + },{icon:2}, 2000); + return; + } + + var tbody = ''; + for (var i = 0; i < rdata.data.length; i++) { + + tbody += ''; + tbody += '' + (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 += ''; + } + + $('#temp_login_view_tbody').html(tbody); + $('.temp_login_view_page').html(rdata.page); + },'json'); +} + +function addApp(){ + var msg = '注意1:滥用临时授权可能导致安全风险。
    注意2:请勿在公共场合发布临时授权连接

    即将创建临时授权连接,继续吗?'; + layer.confirm(msg,{title:'风险提示',closeBtn:1,icon:13,}, function(create_layer) { + layer.close(create_layer); + layer.open({ + area: '570px', + title: '添加应用', + shift: 0, + type: 1, + content: '
    \ +
    \ + 应用ID\ +
    \ + \ + \ +
    \ +
    \ +
    \ + 应用密钥\ +
    \ + \ + \ +
    \ +
    \ +
    \ + IP白名单
    (每行1个)
    \ +
    \ +
    \ +
    \ + \ +
    \ +
    \ +
      \ +
    • 开启API后,必需在IP白名单列表中的IP才能访问面板API接口
    • \ +
    • 请谨慎在生产环境开启,这可能增加服务器安全风险;
    • \ +
    \ +
    ', + success:function(){ + $('input[name="app_id"]').val(getRandomString(10)); + $('input[name="app_secret"]').val(getRandomString(20)); + + $('.app_id').click(function(){ + $('input[name="app_id"]').val(getRandomString(10)); + }); + + $('.app_secret').click(function(){ + $('input[name="app_secret"]').val(getRandomString(20)); + }); + + $('.save_app_data').click(function(){ + var app_id = $('input[name="app_id"]').val(); + var app_secret = $('input[name="app_secret"]').val(); + var limit_addr = $('textarea[name="api_limit_addr"]').val(); + $.post('/setting/add_app', {'app_id':app_id,'app_secret':app_secret,'limit_addr':limit_addr},function(rdata){ + showMsg(rdata.msg, function(){} ,{icon:rdata.status?1:2}, 2000); + },'json'); + }); + + } + }); + }); +} + +function appPage(){ + layer.open({ + area: ['700px', '380px'], + title: 'APP应用管理', + closeBtn:1, + shift: 0, + type: 1, + content: "", + success:function(){ + getAppList(); + $('.app_add').click(function(){ + addApp(); + }); + } + }); +} diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index a831407d8..61d4a659a 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -131,7 +131,7 @@ API接口 - + 提供面板API接口访问的支持

    diff --git a/web/thisdb/app.py b/web/thisdb/app.py index be4f5ace0..4350f194a 100644 --- a/web/thisdb/app.py +++ b/web/thisdb/app.py @@ -10,14 +10,15 @@ import core.mw as mw -__FIELD = 'id,app_id,app_secret,status,add_time,update_time' +__FIELD = 'id,app_id,app_secret,white_list,status,add_time,update_time' -def addApp(app_id,app_secret): +def addApp(app_id,app_secret,white_list): now_time = mw.getDateFromNow() add_data = { 'app_id': app_id, 'app_secret': app_secret, 'status': 1, + 'white_list':white_list, 'add_time': now_time, 'update_time': now_time } From 89d6bbd3172099328a2ecd4dbd5d2861e39f79ea Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 11 Nov 2024 00:56:03 +0800 Subject: [PATCH 163/965] Update default.sql --- web/admin/setup/sql/default.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index ab2b3baae..a9b8cb531 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -138,6 +138,7 @@ CREATE TABLE IF NOT EXISTS `panel` ( CREATE TABLE IF NOT EXISTS `app` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, `app_id` TEXT, `app_secret` TEXT, `white_list` TEXT, @@ -145,6 +146,7 @@ CREATE TABLE IF NOT EXISTS `app` ( `add_time` TEXT, `update_time` TEXT ); +CREATE UNIQUE INDEX name_idx ON app(name); CREATE UNIQUE INDEX app_id_idx ON app(app_id); CREATE TABLE IF NOT EXISTS `option` ( From 8f1396bb8b75db033c0021d4b87e526692a9f79f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 11 Nov 2024 22:18:32 +0800 Subject: [PATCH 164/965] up --- web/admin/setting/app.py | 13 ++- web/static/app/config.js | 182 +++++++++++++++++++-------------------- 2 files changed, 101 insertions(+), 94 deletions(-) diff --git a/web/admin/setting/app.py b/web/admin/setting/app.py index 7fd802822..3be1210cd 100644 --- a/web/admin/setting/app.py +++ b/web/admin/setting/app.py @@ -32,7 +32,7 @@ import thisdb def get_app_list(): limit = request.form.get('limit', '5').strip() page = request.form.get('page', '1').strip() - tojs = request.form.get('tojs', '').strip() + tojs = request.form.get('tojs', 'getAppList').strip() info = thisdb.getAppList(page=int(page),size=int(limit)) data = {} @@ -53,3 +53,14 @@ def add_app(): if rid > 0: return mw.returnData(True, '添加成功!') return mw.returnData(False, '添加失败!') + + +# 获取APP列表 +@blueprint.route('/delete_app', endpoint='delete_app', methods=['POST']) +@panel_login_required +def delete_app(): + aid = request.form.get('id', '').strip() + rid = thisdb.deleteAppById(aid) + if rid > 0: + return mw.returnData(True, '删除成功!') + return mw.returnData(False, '删除失败!') diff --git a/web/static/app/config.js b/web/static/app/config.js index 8db5b183e..452d4624d 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -1474,146 +1474,142 @@ function setPanelApi(){ } +function deleteApp(id){ + layer.confirm('您确定要删除吗?',{title:'删除应用',closeBtn:2,icon:13,cancel:function(){}}, function() { + $.post('/setting/delete_app', {'id':id},function(rdata){ + layer.msg(rdata.msg,{icon:rdata.status?1:2}); + if (rdata.status){ + getAppList(); + } + },'json'); + }); +} + function getAppList(page) { + if (typeof(page) == 'undefined'){ page = 1; } $.post('/setting/get_app_list', {page:page}, function(rdata){ - console.log(rdata); - if ( typeof(rdata.status) !='undefined' && !rdata.status){ - showMsg(rdata.msg,function(){ - layer.closeAll(); - },{icon:2}, 2000); - return; - } - var tbody = ''; for (var i = 0; i < rdata.data.length; i++) { + var row = rdata.data[i]; tbody += ''; - tbody += '' + (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 += '' + row['app_id'] +''; + tbody += '' + row['app_secret'] +''; + tbody += '' + row['white_list'] +''; - tbody += '' + (getLocalTime(rdata.data[i]['login_time'])||'未登陆') +''; - tbody += '' + getLocalTime(rdata.data[i]['expire']) +''; - - tbody += ''; - - if (rdata.data[i]['state'] == '1' ){ - tbody += '操作日志'; - } else{ - tbody += '删除'; + if (row['status'] == 1){ + tbody += '已开启'; + } else { + tbody += '已关闭'; } + tbody += '' + row['add_time'] +''; + + tbody += ''; + tbody += '删除'; tbody += ''; tbody += ''; } - $('#temp_login_view_tbody').html(tbody); - $('.temp_login_view_page').html(rdata.page); + $('#app_list_body tbody').html(tbody); + $('#app_list_body .page').html(rdata.page); },'json'); } function addApp(){ - var msg = '注意1:滥用临时授权可能导致安全风险。
    注意2:请勿在公共场合发布临时授权连接

    即将创建临时授权连接,继续吗?'; - layer.confirm(msg,{title:'风险提示',closeBtn:1,icon:13,}, function(create_layer) { - layer.close(create_layer); - layer.open({ - area: '570px', - title: '添加应用', - shift: 0, - type: 1, - content: '
    \ -
    \ - 应用ID\ -
    \ - \ - \ -
    \ + layer.open({ + area: '570px', + title: '添加应用', + shift: 0, + type: 1, + content: '
    \ +
    \ + 应用ID\ +
    \ + \ + \
    \ -
    \ - 应用密钥\ -
    \ - \ - \ -
    \ +
    \ +
    \ + 应用密钥\ +
    \ + \ + \
    \ -
    \ - IP白名单
    (每行1个)
    \ -
    \ -
    \ -
    \ - \ -
    \ -
    \ -
      \ -
    • 开启API后,必需在IP白名单列表中的IP才能访问面板API接口
    • \ -
    • 请谨慎在生产环境开启,这可能增加服务器安全风险;
    • \ -
    \ -
    ', - success:function(){ +
    \ +
    \ + IP白名单
    (每行1个)
    \ +
    \ +
    \ +
    \ + \ +
    \ +
    \ +
      \ +
    • 开启API后,必需在IP白名单列表中的IP才能访问面板API接口
    • \ +
    • 请谨慎在生产环境开启,这可能增加服务器安全风险;
    • \ +
    \ +
    ', + success:function(obj,cur_layer){ + $('input[name="app_id"]').val(getRandomString(10)); + $('input[name="app_secret"]').val(getRandomString(20)); + + $('.app_id').click(function(){ $('input[name="app_id"]').val(getRandomString(10)); + }); + + $('.app_secret').click(function(){ $('input[name="app_secret"]').val(getRandomString(20)); + }); - $('.app_id').click(function(){ - $('input[name="app_id"]').val(getRandomString(10)); - }); + $('.save_app_data').click(function(){ + var app_id = $('input[name="app_id"]').val(); + var app_secret = $('input[name="app_secret"]').val(); + var limit_addr = $('textarea[name="api_limit_addr"]').val(); + $.post('/setting/add_app', {'app_id':app_id,'app_secret':app_secret,'limit_addr':limit_addr},function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){ + getAppList(); + layer.close(cur_layer); + } + } ,{icon:rdata.status?1:2}, 2000); + },'json'); + }); - $('.app_secret').click(function(){ - $('input[name="app_secret"]').val(getRandomString(20)); - }); - - $('.save_app_data').click(function(){ - var app_id = $('input[name="app_id"]').val(); - var app_secret = $('input[name="app_secret"]').val(); - var limit_addr = $('textarea[name="api_limit_addr"]').val(); - $.post('/setting/add_app', {'app_id':app_id,'app_secret':app_secret,'limit_addr':limit_addr},function(rdata){ - showMsg(rdata.msg, function(){} ,{icon:rdata.status?1:2}, 2000); - },'json'); - }); - - } - }); + } }); } function appPage(){ layer.open({ - area: ['700px', '380px'], + area: ['900px', '380px'], title: 'APP应用管理', closeBtn:1, shift: 0, type: 1, content: " diff --git a/web/thisdb/app.py b/web/thisdb/app.py index 4350f194a..f75b2faf7 100644 --- a/web/thisdb/app.py +++ b/web/thisdb/app.py @@ -46,6 +46,13 @@ def getAppById(aid): def deleteAppById(aid): return mw.M('app').where("id=?", (aid,)).delete() +def toggleAppStatus(aid): + info = mw.M('app').field(__FIELD).where("id=?", (aid,)).find() + if info['status'] == 1: + return mw.M('app').where('id=?',(aid,)).update({'status':0}) + return mw.M('app').where('id=?',(aid,)).update({'status':1}) + + def setAppData(aid, status: str | None = None, app_id: str | None = None, diff --git a/web/utils/file.py b/web/utils/file.py index de7a9325e..02adcaac4 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -11,8 +11,7 @@ import os import pwd import time - -from admin import model +import shutil import core.mw as mw import thisdb @@ -25,6 +24,23 @@ def setFileAccept(filename): os.system('chown -R ' + auth + ' ' + filename) os.system('chmod -R 755 ' + filename) +def createFile(file_path): + try: + if not checkFileName(file_path): + return mw.returnData(False, '文件名中不能包含特殊字符!') + if os.path.exists(file_path): + return mw.returnData(False, '指定文件已存在!') + _path = os.path.dirname(file_path) + if not os.path.exists(_path): + os.makedirs(_path) + open(file_path, 'w+').close() + setFileAccept(file) + msg = mw.getInfo('创建文件[{1}]成功!', (file_path,)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '文件创建成功!') + except Exception as e: + return mw.returnData(True, '文件创建失败:'+str(e)) + def createDir(path): try: if not checkFileName(path): diff --git a/web/utils/ssh/ssh_local.py b/web/utils/ssh/ssh_local.py new file mode 100644 index 000000000..2201881b7 --- /dev/null +++ b/web/utils/ssh/ssh_local.py @@ -0,0 +1,120 @@ +# coding: utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +# --------------------------------------------------------------------------------- +# SSH终端操作 +# --------------------------------------------------------------------------------- + +import json +import time +import os +import sys +import socket +import threading +import re + +from io import BytesIO, StringIO + +import core.mw as mw +import paramiko + +from flask_socketio import SocketIO, emit, send + + +class ssh_local(object): + + __debug_file = 'logs/ssh_local.log' + __log_type = 'SSH终端' + + __ssh = None + + # lock + _instance_lock = threading.Lock() + + def __init__(self): + pass + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(ssh_local, "_instance"): + with ssh_local._instance_lock: + if not hasattr(ssh_local, "_instance"): + ssh_local._instance = ssh_local(*args, **kwargs) + return ssh_local._instance + + def debug(self, msg): + msg = "{} - {}:{} => {} \n".format(mw.formatDate(), + self.__host, self.__port, msg) + if not mw.isDebugMode(): + return + mw.writeFile(self.__debug_file, msg, 'a+') + + def returnMsg(self, status, msg): + return {'status': status, 'msg': msg} + + + def connectSsh(self): + import paramiko + ssh = paramiko.SSHClient() + mw.createSshInfo() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + port = mw.getSSHPort() + try: + ssh.connect('127.0.0.1', port, timeout=5) + except Exception as e: + ssh.connect('localhost', port, timeout=5) + except Exception as e: + ssh.connect(mw.getHostAddr(), port, timeout=30) + except Exception as e: + return False + + shell = ssh.invoke_shell(term='xterm', width=83, height=21) + shell.setblocking(0) + return shell + + def send(self): + pass + + def close(self): + try: + if self.__ssh: + self.__ssh.close() + except: + pass + + def wsSend(self, recv): + try: + t = recv.decode("utf-8") + return emit('server_response', {'data': t}) + except Exception as e: + return emit('server_response', {'data': recv}) + + def wsSendConnect(self): + return emit('connect', {'data': 'ok'}) + + def wsSendReConnect(self): + return emit('reconnect', {'data': 'ok'}) + + + def run(self, info): + if not self.__ssh: + self.__ssh = self.connectSsh() + + if self.__ssh.exit_status_ready(): + self.__ssh = self.connectSsh() + + self.__ssh.send(info) + try: + time.sleep(0.005) + recv = self.__ssh.recv(8192) + return self.wsSend(recv) + except Exception as ex: + return self.wsSend('') diff --git a/web/utils/ssh/ssh_terminal.py b/web/utils/ssh/ssh_terminal.py new file mode 100644 index 000000000..4419860dc --- /dev/null +++ b/web/utils/ssh/ssh_terminal.py @@ -0,0 +1,467 @@ +# coding: utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +# --------------------------------------------------------------------------------- +# SSH终端操作 +# --------------------------------------------------------------------------------- + +import json +import time +import os +import sys +import socket +import threading +import re + +from io import BytesIO, StringIO + +import core.mw as mw +import paramiko + +from flask_socketio import SocketIO, emit, send + + +class ssh_terminal(object): + + __debug_file = 'logs/ssh_terminal.log' + __log_type = 'SSH终端' + + # websocketio 唯一标识 + __sid = '' + + __host = None + __type = '0' + __port = 22 + __user = None + __pass = None + __pkey = None + __key_passwd = None + + __rep_ssh_config = False + __rep_ssh_service = False + __sshd_config_backup = None + + __ssh = None + __tp = None + __ps = None + + __ssh_list = {} + __ssh_last_request_time = {} + + # lock + _instance_lock = threading.Lock() + + def __init__(self): + ht = threading.Thread(target=self.heartbeat) + ht.start() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(ssh_terminal, "_instance"): + with ssh_terminal._instance_lock: + if not hasattr(ssh_terminal, "_instance"): + ssh_terminal._instance = ssh_terminal(*args, **kwargs) + return ssh_terminal._instance + + def debug(self, msg): + msg = "{} - {}:{} => {} \n".format(mw.formatDate(), + self.__host, self.__port, msg) + if not mw.isDebugMode(): + return + mw.writeFile(self.__debug_file, msg, 'a+') + + def returnMsg(self, status, msg): + return {'status': status, 'msg': msg} + + def restartSsh(self, act='reload'): + ''' + 重启ssh 无参数传递 + ''' + version = mw.readFile('/etc/redhat-release') + if not os.path.exists('/etc/redhat-release'): + mw.execShell('service ssh ' + act) + elif version.find(' 7.') != -1 or version.find(' 8.') != -1: + mw.execShell("systemctl " + act + " sshd.service") + else: + mw.execShell("/etc/init.d/sshd " + act) + + def isRunning(self, rep=False): + try: + if rep and self.__rep_ssh_service: + self.restartSsh('stop') + return True + + status = self.getSshStatus() + if not status: + self.restartSsh('start') + self.__rep_ssh_service = True + return True + return False + except: + return False + + def setSshdConfig(self, rep=False): + self.isRunning(rep) + if rep and not self.__rep_ssh_config: + return False + + try: + sshd_config_file = '/etc/ssh/sshd_config' + if not os.path.exists(sshd_config_file): + return False + + sshd_config = mw.readFile(sshd_config_file) + if not sshd_config: + return False + + if rep: + if self.__sshd_config_backup: + mw.writeFile(sshd_config_file, self.__sshd_config_backup) + self.restartSsh() + return True + + pin = r'^\s*PubkeyAuthentication\s+(yes|no)' + pubkey_status = re.findall(pin, sshd_config, re.I) + if pubkey_status: + if pubkey_status[0] == 'yes': + pubkey_status = True + else: + pubkey_status = False + + pin = r'^\s*RSAAuthentication\s+(yes|no)' + rsa_status = re.findall(pin, sshd_config, re.I) + if rsa_status: + if rsa_status[0] == 'yes': + rsa_status = True + else: + rsa_status = False + + self._sshd_config_backup = sshd_config + is_write = False + if not pubkey_status: + sshd_config = re.sub( + r'\n#?PubkeyAuthentication\s\w+', '\nPubkeyAuthentication yes', sshd_config) + is_write = True + if not rsa_status: + sshd_config = re.sub( + r'\n#?RSAAuthentication\s\w+', '\nRSAAuthentication yes', sshd_config) + is_write = True + + if is_write: + mw.writeFile(sshd_config_file, sshd_config) + self.__rep_ssh_config = True + self.restartSsh() + else: + self.__sshd_config_backup = None + return True + except: + return False + + def setSid(self, sid): + self.__sid = sid + + def connect(self, sid): + # self.connectBySocket() + if self.__host in ['127.0.0.1', 'localhost']: + return self.connectLocalSsh(sid) + else: + return self.connectBySocket(sid) + + def connectLocalSsh(self, sid): + mw.createSshInfo() + self.__ps = paramiko.SSHClient() + self.__ps.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + self.__port = mw.getSSHPort() + try: + self.__ps.connect(self.__host, self.__port, timeout=60) + except Exception as e: + self.__ps.connect('127.0.0.1', self.__port) + except Exception as e: + self.__ps.connect('localhost', self.__port) + except Exception as e: + self.setSshdConfig(True) + self.__ps.close() + e = str(e) + if e.find('websocket error!') != -1: + return self.returnMsg(True, '连接成功') + if e.find('Authentication timeout') != -1: + self.debug("认证超时{}".format(e)) + return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e)) + if e.find('Connection reset by peer') != -1: + self.debug('目标服务器主动拒绝连接') + return self.returnMsg(False, '目标服务器主动拒绝连接') + if e.find('Error reading SSH protocol banner') != -1: + self.debug('协议头响应超时') + return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e) + if not e: + self.debug('SSH协议握手超时') + return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕") + err = mw.getTracebackInfo() + self.debug(err) + return self.returnMsg(False, "未知错误: {}".format(err)) + + self.debug('local-ssh:认证成功,正在构建会话通道') + ssh = self.__ps.invoke_shell( + term='xterm', width=83, height=21) + ssh.setblocking(0) + self.__ssh_list[sid] = ssh + mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format( + self.__host, self.__port)) + self.debug('local-ssh:通道已构建') + return self.returnMsg(True, '连接成功!') + + def connectBySocket(self, sid): + if not self.__host: + return self.returnMsg(False, '错误的连接地址') + if not self.__user: + self.__user = 'root' + if not self.__port: + self.__port = 22 + + self.setSshdConfig(True) + num = 0 + while num < 5: + num += 1 + try: + self.debug('正在尝试第{}次连接'.format(num)) + if self.__rep_ssh_config: + time.sleep(0.1) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(2 + num) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192) + sock.connect((self.__host, self.__port)) + break + except Exception as e: + if num == 5: + self.setSshdConfig(True) + self.debug('重试连接失败,{}'.format(e)) + return self.returnMsg(False, '连接目标服务器失败, {}:{}'.format(self.__host, self.__port)) + else: + time.sleep(0.2) + + # print(self.__host, sock) + self.__tp = paramiko.Transport(sock) + try: + self.__tp.start_client() + self.__tp.banner_timeout = 60 + if not self.__pass and not self.__pkey: + return self.returnMsg(False, '密码或私钥不能都为空: {}:{}'.format(self.__host, self.__port)) + + if self.__pkey != '' and self.__type != '0': + self.debug('正在认证私钥') + p_file = StringIO(str(self.__pkey.replace('\\n', '\n'))) + # p_file = "/tmp/t_ssh_pkey.txt" + # mw.writeFile(p_file, self.__pkey.replace('\\n', '\n')) + # mw.execShell('chmod 600 ' + p_file) + try: + p_file.seek(0) + pkey = paramiko.RSAKey.from_private_key(p_file) + except: + try: + p_file.seek(0) # 重置游标 + pkey = paramiko.Ed25519Key.from_private_key( + p_file) + except: + try: + p_file.seek(0) + pkey = paramiko.ECDSAKey.from_private_key(p_file) + except: + p_file.seek(0) + pkey = paramiko.DSSKey.from_private_key(p_file) + + self.__tp.auth_publickey(username=self.__user, key=pkey) + else: + try: + self.__tp.auth_none(self.__user) + except Exception as e: + e = str(e) + if e.find('keyboard-interactive') >= 0: + self._auth_interactive() + else: + self.debug('正在认证密码') + self.__tp.auth_password( + username=self.__user, password=self.__pass) + except Exception as e: + self.setSshdConfig(True) + self.__tp.close() + e = str(e) + # print(e) + if e.find('Authentication timeout') != -1: + self.debug("认证超时{}".format(e)) + return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e)) + if e.find('Authentication failed') != -1: + self.debug('认证失败{}'.format(e)) + return self.returnMsg(False, '帐号或密码错误: {}'.format(e + "," + self.__user + "@" + self.__host + ":" + str(self.__port))) + if e.find('Bad authentication type; allowed types') != -1: + self.debug('认证失败{}'.format(e)) + if self.__host in ['127.0.0.1', 'localhost'] and self.__pass == 'none': + return self.returnMsg(False, '帐号或密码错误: {}'.format("Authentication failed ," + self.__user + "@" + self.__host + ":" + str(self.__port))) + return self.returnMsg(False, '不支持的身份验证类型: {}'.format(e)) + if e.find('Connection reset by peer') != -1: + self.debug('目标服务器主动拒绝连接') + return self.returnMsg(False, '目标服务器主动拒绝连接') + if e.find('Error reading SSH protocol banner') != -1: + self.debug('协议头响应超时') + return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e) + if not e: + self.debug('SSH协议握手超时') + return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕") + err = mw.getTracebackInfo() + self.debug(err) + return self.returnMsg(False, "未知错误: {}".format(err)) + + self.debug('认证成功,正在构建会话通道') + + ssh = self.__tp.open_session() + ssh.get_pty(term='xterm', width=100, height=34) + ssh.invoke_shell() + self.__ssh_list[sid] = ssh + mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format( + self.__host, self.__port)) + self.debug('通道已构建') + return self.returnMsg(True, '连接成功.') + + def getSshInfo(self, file): + rdata = mw.readFile(file) + destr = mw.deDoubleCrypt('mdserver-web', rdata) + return json.loads(destr) + + def setAttr(self, sid, info): + self.__host = info['host'].strip() + + # 外部连接获取 + if not self.__host in ['127.0.0.1', 'localhost']: + dst_info = mw.getServerDir() + '/webssh/host/' + self.__host + '/info.json' + if os.path.exists(dst_info): + info = self.getSshInfo(dst_info) + + if 'type' in info: + self.__type = info['type'] + + if 'port' in info: + self.__port = int(info['port']) + if 'username' in info: + self.__user = info['username'] + if 'pkey' in info: + self.__pkey = info['pkey'] + if 'password' in info: + self.__pass = info['password'] + if 'pkey_passwd' in info: + self.__key_passwd = info['pkey_passwd'] + + # print(self.__host, self.__pass, self.__key_passwd) + try: + result = self.connect(sid) + # print(result) + except Exception as ex: + if str(ex).find("NoneType") == -1: + raise ex + return result + + def send(self): + pass + + def close(self): + try: + if self.__ssh: + self.__ssh.close() + if self.__tp: # 关闭宿主服务 + self.__tp.close() + if self.__ps: + self.__ps.close() + except: + pass + + def resize(self, sid, data): + try: + self.__ssh_list[sid].resize_pty( + width=data['cols'], height=data['rows']) + return True + except: + return False + + def wsSend(self, recv): + try: + t = recv.decode("utf-8") + return emit('server_response', {'data': t}) + except Exception as e: + return emit('server_response', {'data': recv}) + + def wsSendConnect(self): + return emit('connect', {'data': 'ok'}) + + def wsSendReConnect(self): + return emit('reconnect', {'data': 'ok'}) + + def heartbeat(self): + # limit_cos = 10 + while True: + time.sleep(3) + cur_time = time.time() + for x in list(self.__ssh_list.keys()): + ssh_last_time = self.__ssh_last_request_time[x] + sid_off_cos = cur_time - ssh_last_time + + # print("heartbeat off cos :", x, sid_off_cos) + if sid_off_cos > 3: + cur_ssh = self.__ssh_list[x] + if not cur_ssh: + del(self.__ssh_list[x]) + del(self.__ssh_last_request_time[x]) + continue + + if cur_ssh.exit_status_ready(): + del(self.__ssh_list[x]) + del(self.__ssh_last_request_time[x]) + continue + + cur_ssh.send("exit\r\n") + del(self.__ssh_list[x]) + del(self.__ssh_last_request_time[x]) + + def run(self, sid, info): + self.__sid = sid + if not self.__sid: + return self.wsSend('WebSocketIO无效') + + self.__ssh_last_request_time[sid] = time.time() + if not sid in self.__ssh_list: + if type(info) == dict and 'host' in info: + result = self.setAttr(sid, info) + if result['status']: + return self.wsSendConnect() + else: + return self.wsSend(result['msg']) + else: + return self.wsSendReConnect() + + result = self.returnMsg(False, '') + if sid in self.__ssh_list: + if 'resize' in info: + self.resize(sid, info) + result = self.returnMsg(True, '已连接') + if result['status']: + if type(info) == str: + time.sleep(0.1) + cur_ssh = self.__ssh_list[sid] + if cur_ssh.exit_status_ready(): + self.wsSend("logout\r\n") + del(self.__ssh_list[sid]) + return + cur_ssh.send(info) + try: + time.sleep(0.005) + recv = cur_ssh.recv(8192) + return self.wsSend(recv) + except Exception as ex: + return self.wsSend('') + else: + return self.wsSend(result['msg']) From 7b37a86acb75adc99d154ae2c5598baaeac06ab5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 00:09:20 +0800 Subject: [PATCH 166/965] update --- scripts/old/install.sh | 178 ++++++++++++++++++++++++++++++++++ web/admin/__init__.py | 6 ++ web/admin/dashboard/ssh.py | 2 + web/admin/plugins/__init__.py | 4 +- web/utils/plugin.py | 2 - 5 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 scripts/old/install.sh diff --git a/scripts/old/install.sh b/scripts/old/install.sh new file mode 100644 index 000000000..6bd55eaa9 --- /dev/null +++ b/scripts/old/install.sh @@ -0,0 +1,178 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH +# LANG=en_US.UTF-8 +is64bit=`getconf LONG_BIT` + +VERSION=0.17.3 + +LOG_FILE=/var/log/mw-install.log + +{ + +if [ -f /etc/motd ];then + echo "welcome to mdserver-web panel" > /etc/motd +fi + +startTime=`date +%s` + +_os=`uname` +echo "use system: ${_os}" + +if [ ${_os} == "Darwin" ]; then + OSNAME='macos' +elif grep -Eqi "openSUSE" /etc/*-release; then + OSNAME='opensuse' + zypper refresh + zypper install cron wget curl zip unzip +elif grep -Eqi "FreeBSD" /etc/*-release; then + OSNAME='freebsd' + pkg install -y wget curl zip unzip unrar rar +elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then + OSNAME='euler' + yum install -y wget curl zip unzip tar crontabs +elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then + OSNAME='rhel' + yum install -y wget curl zip unzip tar crontabs +elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then + OSNAME='rhel' + yum install -y wget curl zip unzip tar crontabs +elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then + OSNAME='rhel' + yum install -y wget curl zip unzip tar crontabs +elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then + OSNAME='rhel' + yum install -y wget curl zip unzip tar crontabs +elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then + OSNAME='amazon' + yum install -y wget curl zip unzip tar crontabs +elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/os-release; then + OSNAME='debian' + apt update -y + apt install -y wget curl zip unzip tar cron +elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/os-release; then + OSNAME='ubuntu' + apt update -y + apt install -y wget curl zip unzip tar cron +else + OSNAME='unknow' +fi + +if [ "$EUID" -ne 0 ] && [ "$OSNAME" != "macos" ];then + echo "Please run as root!" + exit +fi + + +# HTTP_PREFIX="https://" +# LOCAL_ADDR=common +# ping -c 1 github.com > /dev/null 2>&1 +# if [ "$?" != "0" ];then +# LOCAL_ADDR=cn +# HTTP_PREFIX="https://mirror.ghproxy.com/" +# fi + +HTTP_PREFIX="https://" +LOCAL_ADDR=common +cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") +if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then + LOCAL_ADDR=cn + HTTP_PREFIX="https://mirror.ghproxy.com/" +fi + +echo "local:${LOCAL_ADDR}" + +if [ $OSNAME != "macos" ];then + if id www &> /dev/null ;then + echo "" + else + groupadd www + useradd -g www -s /usr/sbin/nologin www + fi + + mkdir -p /www/server + mkdir -p /www/wwwroot + mkdir -p /www/wwwlogs + mkdir -p /www/backup/database + mkdir -p /www/backup/site + + # https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh + if [ ! -d /www/server/mdserver-web ];then + if [ "$LOCAL_ADDR" == "common" ];then + curl --insecure -sSLo /tmp/master.zip ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/tags/${VERSION}.zip + cd /tmp && unzip /tmp/master.zip + mv -f /tmp/mdserver-web-master /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web-master + else + # curl --insecure -sSLo /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip + wget --no-check-certificate -O /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip + cd /tmp && unzip /tmp/master.zip + mv -f /tmp/mdserver-web /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web + fi + + + fi + + # install acme.sh + if [ ! -d /root/.acme.sh ];then + if [ "$LOCAL_ADDR" != "common" ];then + curl --insecure -sSLo /tmp/acme.tar.gz https://gitee.com/neilpang/acme.sh/repository/archive/master.tar.gz + tar xvzf /tmp/acme.tar.gz -C /tmp + cd /tmp/acme.sh-master + bash acme.sh install + fi + + if [ ! -d /root/.acme.sh ];then + curl https://get.acme.sh | sh + fi + fi +fi + +echo "use system version: ${OSNAME}" +if [ "${OSNAME}" == "macos" ];then + curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install/macos.sh | bash +else + cd /www/server/mdserver-web && bash scripts/install/${OSNAME}.sh +fi + +if [ "${OSNAME}" == "macos" ];then + echo "macos end" + exit 0 +fi + +cd /www/server/mdserver-web && bash cli.sh start +isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'` +n=0 +while [ ! -f /etc/rc.d/init.d/mw ]; +do + echo -e ".\c" + sleep 1 + let n+=1 + if [ $n -gt 20 ];then + echo -e "start mw fail" + exit 1 + fi +done + +cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop +cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start +cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw default + +sleep 2 +if [ ! -e /usr/bin/mw ]; then + if [ -f /etc/rc.d/init.d/mw ];then + ln -s /etc/rc.d/init.d/mw /usr/bin/mw + fi +fi + +endTime=`date +%s` +((outTime=(${endTime}-${startTime})/60)) +echo -e "Time consumed:\033[32m $outTime \033[0mMinute!" + +} 1> >(tee $LOG_FILE) 2>&1 + +echo -e "\nInstall completed. If error occurs, please contact us with the log file mw-install.log ." +echo "安装完毕,如果出现错误,请带上同目录下的安装日志 mw-install.log 联系我们反馈." \ No newline at end of file diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 8d40a7f32..20495c45d 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -215,6 +215,12 @@ app.logger.debug("Python syspath: %s", sys.path) # OK socketio.init_app(app, cors_allowed_origins="*") +from gevent.pywsgi import WSGIServer +from geventwebsocket.handler import WebSocketHandler +http_server = WSGIServer(('0.0.0.0', config.DEFAULT_SERVER_PORT), app, handler_class=WebSocketHandler) +http_server.serve_forever() +socketio.run(app, host=HOST, port=PORT) + # def create_app(app_name = None): # # if not app_name: diff --git a/web/admin/dashboard/ssh.py b/web/admin/dashboard/ssh.py index f68d661ee..86bfd30b0 100644 --- a/web/admin/dashboard/ssh.py +++ b/web/admin/dashboard/ssh.py @@ -8,6 +8,8 @@ # Author: midoks # --------------------------------------------------------------------------------- +from flask import request + from admin import socketio from admin.common import isLogined diff --git a/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py index 77baaf6f1..8092e015a 100644 --- a/web/admin/plugins/__init__.py +++ b/web/admin/plugins/__init__.py @@ -17,10 +17,10 @@ from flask import request from utils.plugin import plugin as MwPlugin from admin.user_login_check import panel_login_required -from admin import model import core.mw as mw import utils.config as utils_config +import thisdb blueprint = Blueprint('plugins', __name__, url_prefix='/plugins', template_folder='../../templates/default') @@ -131,7 +131,7 @@ def menu(): pg = MwPlugin.instance() tag = request.args.get('tag', '') - hook_menu = model.getOptionByJson('hook_menu',type='hook',default=[]) + hook_menu = thisdb.getOptionByJson('hook_menu',type='hook',default=[]) content = '' for menu_data in hook_menu: if tag == menu_data['name'] and 'path' in menu_data: diff --git a/web/utils/plugin.py b/web/utils/plugin.py index 3c0bf683e..a9b4a9a55 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -14,8 +14,6 @@ import json import threading import multiprocessing -from admin import model - import core.mw as mw import thisdb From c182635fd6fbb9b37fc4b29981c39b292d4dd994 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 00:10:16 +0800 Subject: [PATCH 167/965] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index fbb8a397e..6338925f6 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,11 @@ curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_new.sh | bash ``` +### 旧版安装 +``` +curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash +``` + ### 捐赠地址 USDT(TRC20) TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya From 6e88b3b0c53f44f79cb7dfc3c2774755c6b7d67b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 00:15:08 +0800 Subject: [PATCH 168/965] Create update.sh --- scripts/old/update.sh | 117 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 scripts/old/update.sh diff --git a/scripts/old/update.sh b/scripts/old/update.sh new file mode 100644 index 000000000..079aa5924 --- /dev/null +++ b/scripts/old/update.sh @@ -0,0 +1,117 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH +# LANG=en_US.UTF-8 +is64bit=`getconf LONG_BIT` + +startTime=`date +%s` + +VERSION=0.17.3 + +_os=`uname` +echo "use system: ${_os}" + +if [ "$EUID" -ne 0 ] + then echo "Please run as root!" + exit +fi + +if [ ${_os} != "Darwin" ] && [ ! -d /www/server/mdserver-web/logs ]; then + mkdir -p /www/server/mdserver-web/logs +fi + +LOG_FILE=/var/log/mw-update.log + +{ + +if [ ${_os} == "Darwin" ]; then + OSNAME='macos' +elif grep -Eqi "openSUSE" /etc/*-release; then + OSNAME='opensuse' + zypper refresh +elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then + OSNAME='euler' +elif grep -Eqi "FreeBSD" /etc/*-release; then + OSNAME='freebsd' +elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then + OSNAME='amazon' + yum install -y wget zip unzip +elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then + OSNAME='debian' + apt install -y wget zip unzip +elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then + OSNAME='ubuntu' + apt install -y wget zip unzip +elif grep -Eqi "Raspbian" /etc/issue || grep -Eqi "Raspbian" /etc/*-release; then + OSNAME='raspbian' +else + OSNAME='unknow' +fi + + +HTTP_PREFIX="https://" +LOCAL_ADDR=common +cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") +if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then + LOCAL_ADDR=cn + HTTP_PREFIX="https://mirror.ghproxy.com/" +fi +echo "local:${LOCAL_ADDR}" + +CP_CMD=/usr/bin/cp +if [ -f /bin/cp ];then + CP_CMD=/bin/cp +fi + +if [ "$LOCAL_ADDR" != "common" ];then + # curl --insecure -sSLo /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip + wget --no-check-certificate -O /tmp/master.zip github.com/midoks/mdserver-web/archive/refs/tags/${VERSION}.zip + cd /tmp && unzip /tmp/master.zip + + $CP_CMD -rf /tmp/mdserver-web/* /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web +else + curl --insecure -sSLo /tmp/master.zip https://codeload.github.com/midoks/mdserver-web/zip/master + + cd /tmp && unzip /tmp/master.zip + $CP_CMD -rf /tmp/mdserver-web-master/* /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web-master +fi + + +#pip uninstall public +echo "use system version: ${OSNAME}" +cd /www/server/mdserver-web && bash scripts/update/${OSNAME}.sh + +bash /etc/rc.d/init.d/mw restart +bash /etc/rc.d/init.d/mw default + +if [ -f /usr/bin/mw ];then + rm -rf /usr/bin/mw +fi + +if [ ! -e /usr/bin/mw ]; then + if [ ! -f /usr/bin/mw ];then + ln -s /etc/rc.d/init.d/mw /usr/bin/mw + fi +fi + +endTime=`date +%s` +((outTime=($endTime-$startTime)/60)) +echo -e "Time consumed:\033[32m $outTime \033[0mMinute!" + +} 1> >(tee $LOG_FILE) 2>&1 \ No newline at end of file From 96fcdb8af304dd5a77c8114b44629188ce8dc020 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 00:15:37 +0800 Subject: [PATCH 169/965] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6338925f6..b4d210106 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,7 @@ curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev ### 旧版安装 ``` 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) From 990199ec894a33bf15cda7025716d3d9d35b2c31 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:00:06 +0800 Subject: [PATCH 170/965] update --- new_cli.sh | 5 +- web/admin/__init__.py | 100 ++++++++------------------------ web/admin/dashboard/__init__.py | 1 - web/admin/dashboard/ssh.py | 37 ------------ web/admin/ssh.py | 39 +++++++++++++ web/setting.py | 27 ++++----- 6 files changed, 80 insertions(+), 129 deletions(-) delete mode 100644 web/admin/dashboard/ssh.py create mode 100644 web/admin/ssh.py diff --git a/new_cli.sh b/new_cli.sh index 77c21ddf6..97b66ee1d 100755 --- a/new_cli.sh +++ b/new_cli.sh @@ -60,7 +60,7 @@ mw_start_debug(){ port=$(cat ${DIR}/data/port.pl) fi # gunicorn -b :${port} -k gevent -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app } mw_start_panel(){ @@ -68,7 +68,8 @@ mw_start_panel(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - cd ${DIR}/web && gunicorn -b :${port} -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app } mw_start_bgtask(){ diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 20495c45d..e7f47b7f6 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -17,36 +17,27 @@ import logging from datetime import timedelta from flask import Flask +from flask import request +from flask import Response from flask_socketio import SocketIO, emit, send from flask import Flask, abort, current_app, session, url_for from flask import Blueprint, render_template from flask import render_template_string -from flask import request -from flask import Response + from flask_migrate import Migrate from flask_caching import Cache from werkzeug.local import LocalProxy -# from admin import model from admin import setup -import thisdb - -from admin.model import db as sys_db - import core.mw as mw import config import utils.config as utils_config - -root_dir = mw.getRunDir() - -socketio = SocketIO(manage_session=False, async_mode='threading', - logger=False, engineio_logger=False, debug=False, - ping_interval=25, ping_timeout=120) +import thisdb app = Flask(__name__, template_folder='templates/default') @@ -79,6 +70,13 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True setup.init() +# webssh +socketio = SocketIO() +socketio.init_app(app) +from .ssh import websshRun +websshRun() + + app.config['BASIC_AUTH_OPEN'] = False try: basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) @@ -158,68 +156,11 @@ def inject_global_variables(): return dict(config=g_config, data=data) -# from flasgger import Swagger -# api = Api(app, version='1.0', title='API', description='API 文档') -# Swagger(app) - -# @app.route('/colors//') -# def colors(palette): -# """ -# 根据调色板名称返回颜色列表 -# --- -# parameters: -# - name: palette -# in: path -# type: string -# enum: ['all', 'rgb', 'cmyk'] -# required: true -# default: all -# definitions: -# Palette: -# type: object -# properties: -# palette_name: -# type: array -# items: -# $ref: '#/definitions/Color' -# Color: -# type: string -# responses: -# 200: -# description: 返回的颜色列表,可按调色板过滤 -# schema: -# $ref: '#/definitions/Palette' -# examples: -# rgb: ['red', 'green', 'blue'] -# """ -# all_colors = { -# 'cmyk': ['cyan', 'magenta', 'yellow', 'black'], -# 'rgb': ['red', 'green', 'blue'] -# } -# if palette == 'all': -# result = all_colorselse -# result = {palette: all_colors.get(palette)} -# return jsonify(result) - - - - -# Log the startup -app.logger.info('########################################################') -app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) -app.logger.info('########################################################') -app.logger.debug("Python syspath: %s", sys.path) - - - -# OK -socketio.init_app(app, cors_allowed_origins="*") - -from gevent.pywsgi import WSGIServer -from geventwebsocket.handler import WebSocketHandler -http_server = WSGIServer(('0.0.0.0', config.DEFAULT_SERVER_PORT), app, handler_class=WebSocketHandler) -http_server.serve_forever() -socketio.run(app, host=HOST, port=PORT) +# from gevent.pywsgi import WSGIServer +# from geventwebsocket.handler import WebSocketHandler +# http_server = WSGIServer(('0.0.0.0', config.DEFAULT_SERVER_PORT), app, handler_class=WebSocketHandler) +# http_server.serve_forever() +# socketio.run(app, host='0.0.0.0', port=PORT) # def create_app(app_name = None): # @@ -230,4 +171,11 @@ socketio.run(app, host=HOST, port=PORT) # cli_mode = False # if app_name.endswith('-cli'): # cli_mode = True -# return app \ No newline at end of file +# return app + + +# Log the startup +app.logger.info('########################################################') +app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) +app.logger.info('########################################################') +app.logger.debug("Python syspath: %s", sys.path) \ No newline at end of file diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py index 259b242db..77a962a50 100644 --- a/web/admin/dashboard/__init__.py +++ b/web/admin/dashboard/__init__.py @@ -11,4 +11,3 @@ from .dashboard import * from .login import * -from .ssh import * diff --git a/web/admin/dashboard/ssh.py b/web/admin/dashboard/ssh.py deleted file mode 100644 index 86bfd30b0..000000000 --- a/web/admin/dashboard/ssh.py +++ /dev/null @@ -1,37 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -from flask import request - -from admin import socketio -from admin.common import isLogined - - -@socketio.on('webssh_websocketio') -def webssh_websocketio(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return - import utils.ssh.ssh_terminal as ssh_terminal - shell_client = ssh_terminal.ssh_terminal.instance() - shell_client.run(request.sid, data) - return - - -@socketio.on('webssh') -def webssh(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return None - - import utils.ssh.ssh_local as ssh_local - shell = ssh_local.ssh_local.instance() - shell.run(data) - return \ No newline at end of file diff --git a/web/admin/ssh.py b/web/admin/ssh.py new file mode 100644 index 000000000..3da737368 --- /dev/null +++ b/web/admin/ssh.py @@ -0,0 +1,39 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +from flask_socketio import SocketIO, emit, send +from flask import request + +from admin import app,socketio +from .common import isLogined + +def websshRun(): + @socketio.on('webssh_websocketio') + def webssh_websocketio(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return + import utils.ssh.ssh_terminal as ssh_terminal + shell_client = ssh_terminal.ssh_terminal.instance() + shell_client.run(request.sid, data) + return + + + @socketio.on('webssh') + def webssh(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return None + + import utils.ssh.ssh_local as ssh_local + shell = ssh_local.ssh_local.instance() + shell.run(data) + return diff --git a/web/setting.py b/web/setting.py index d914c71a9..332e17387 100755 --- a/web/setting.py +++ b/web/setting.py @@ -33,25 +33,25 @@ if not os.path.exists(log_dir): os.mkdir(log_dir) # default port -mw_port = '7200' +panel_port = '7200' default_port_file = panel_dir+'/data/port.pl' if os.path.exists(default_port_file): - mw_port = mw.readFile(default_port_file) - mw_port.strip() -# else: -# import firewall_api -# import common -# common.initDB() -# mw_port = str(random.randint(10000, 65530)) -# firewall_api.firewall_api().addAcceptPortArgs(mw_port, 'WEB面板', 'port') -# mw.writeFile('data/port.pl', mw_port) + panel_port = mw.readFile(default_port_file) + panel_port.strip() +else: + from utils.firewall import Firewall as MwFirewall + import common + common.initDB() + mw_port = str(random.randint(10000, 65530)) + MwFirewall.instance().addAcceptPort(panel_port, 'WEB面板', 'port') + mw.writeFile('data/port.pl', panel_port) bind = [] default_ipv6_file = panel_dir+'/data/ipv6.pl' if os.path.exists(default_ipv6_file): - bind.append('[0:0:0:0:0:0:0:0]:%s' % mw_port) + bind.append('[0:0:0:0:0:0:0:0]:%s' % panel_port) else: - bind.append('0.0.0.0:%s' % mw_port) + bind.append('0.0.0.0:%s' % panel_port) if workers > 2: workers = 1 @@ -61,6 +61,7 @@ backlog = 512 reload = False daemon = True # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' +worker_class = 'eventlet' timeout = 600 keepalive = 60 preload_app = True @@ -69,4 +70,4 @@ access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' loglevel = 'info' errorlog = log_dir + '/panel_error.log' accesslog = log_dir + '/panel.log' -pidfile = log_dir + '/mw.pid' +pidfile = log_dir + '/panel.pid' From b03f7abcb825278eccefd6c4a49007a3c8779583 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:03:49 +0800 Subject: [PATCH 171/965] Update requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 20e997e8a..d50874f51 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ flask-caching>=1.10.1 cache==1.0.3 gevent>=22.10.2 gevent-websocket==0.10.1 +eventlet>=0.24.1 psutil==5.9.1 chardet==3.0.4 SQLAlchemy==2.* From 17a9f07f04bd91dbe599d549a88a7be95c92b60f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:06:31 +0800 Subject: [PATCH 172/965] Update app.py --- web/app.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/web/app.py b/web/app.py index aa0fe2680..cc166c571 100644 --- a/web/app.py +++ b/web/app.py @@ -9,21 +9,18 @@ # --------------------------------------------------------------------------------- import sys +import os +from admin import app, socketio +import config if sys.version_info < (3, 6): raise RuntimeError('This application must be run under Python 3.6 or later.') -import os - # 我们需要在sys.path中包含根目录,以确保我们可以找到在独立运行时运行时所需的一切。 if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) - -import config -from admin import app, socketio - # print(config.DEFAULT_SERVER,config.DEFAULT_SERVER_PORT) # app = create_app() def main(): From 49bfb5b3a8ccae6bea49934b8e5944787a483f2f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:12:15 +0800 Subject: [PATCH 173/965] update --- web/utils/ssh/ssh_local.py | 2 +- web/utils/ssh/ssh_terminal.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/web/utils/ssh/ssh_local.py b/web/utils/ssh/ssh_local.py index 2201881b7..48e4e663e 100644 --- a/web/utils/ssh/ssh_local.py +++ b/web/utils/ssh/ssh_local.py @@ -39,7 +39,7 @@ class ssh_local(object): _instance_lock = threading.Lock() def __init__(self): - pass + self.__debug_file = mw.getPanelDir()+ '/logs/ssh_terminal.log' @classmethod def instance(cls, *args, **kwargs): diff --git a/web/utils/ssh/ssh_terminal.py b/web/utils/ssh/ssh_terminal.py index 4419860dc..e683e60c0 100644 --- a/web/utils/ssh/ssh_terminal.py +++ b/web/utils/ssh/ssh_terminal.py @@ -30,7 +30,7 @@ from flask_socketio import SocketIO, emit, send class ssh_terminal(object): - __debug_file = 'logs/ssh_terminal.log' + __debug_file = 'logs/ssh_terminal.log' __log_type = 'SSH终端' # websocketio 唯一标识 @@ -59,6 +59,7 @@ class ssh_terminal(object): _instance_lock = threading.Lock() def __init__(self): + self.__debug_file = mw.getPanelDir()+ '/logs/ssh_terminal.log' ht = threading.Thread(target=self.heartbeat) ht.start() From 0da42e9ec82f5ae61531c47fb141552aedcdaef3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:21:14 +0800 Subject: [PATCH 174/965] Update __init__.py --- web/admin/firewall/__init__.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 435e706ae..006119c0b 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -66,7 +66,7 @@ def add_accept_port(): return MwFirewall.instance().addAcceptPort(port, ps, stype, protocol=protocol) -# 添加放行端口 +# 删除放行端口 @blueprint.route('/del_accept_port', endpoint='del_accept_port', methods=['POST']) @panel_login_required def del_accept_port(): @@ -76,7 +76,14 @@ def del_accept_port(): return MwFirewall.instance().delAcceptPort(firewall_id, port, protocol=protocol) - +# 设置防火墙状态 +@blueprint.route('/set_fw', endpoint='set_fw', methods=['POST']) +@panel_login_required +def set_fw(): + if mw.isAppleSystem(): + return mw.returnData(True, '开发机不能设置!') + status = request.form.get('status', '1') + return MwFirewall.instance().setFw(status) From 1616d4d191b2b1787c52a652fe6797491d31b2b3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:36:30 +0800 Subject: [PATCH 175/965] update --- web/admin/firewall/__init__.py | 2 -- web/admin/setup/__init__.py | 4 ++++ web/utils/firewall.py | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 006119c0b..d57c998c7 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -13,8 +13,6 @@ from flask import Blueprint, render_template from flask import request from admin.user_login_check import panel_login_required -from admin.model import db, Firewall - from utils.firewall import Firewall as MwFirewall diff --git a/web/admin/setup/__init__.py b/web/admin/setup/__init__.py index 6747b01d4..cd0d5d2b5 100644 --- a/web/admin/setup/__init__.py +++ b/web/admin/setup/__init__.py @@ -15,6 +15,7 @@ from .option import init_option from .init_db_system import init_db_system from .init_cmd import init_cmd +from utils.firewall import Firewall as MwFirewall import thisdb import config @@ -29,3 +30,6 @@ def init(): init_cmd() init_db_system() + # 自动识别防火墙配置 + MwFirewall.instance().aIF() + diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 9b39ac475..4e7bd014f 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -47,6 +47,16 @@ class Firewall(object): elif mw.isAppleSystem(): self.__isMac = True + # 自动识别防火墙配置 | Automatically identify firewall + def aIF(): + if self.__isFirewalld: + self.AIF_Firewalld() + + def AIF_Firewalld(): + # firewall-cmd --list-all | grep ' ports' + data = mw.execShell("firewall-cmd --list-all | grep ' ports'") + print(data) + def getList(self, page=1,size=10): info = thisdb.getFirewallList(page=page, size=size) From 687f16e1efd5bfec8c06dc479ad56f0958c64c45 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:37:59 +0800 Subject: [PATCH 176/965] Update firewall.py --- web/utils/firewall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 4e7bd014f..7b12014e3 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -48,11 +48,11 @@ class Firewall(object): self.__isMac = True # 自动识别防火墙配置 | Automatically identify firewall - def aIF(): + def aIF(self): if self.__isFirewalld: self.AIF_Firewalld() - def AIF_Firewalld(): + def AIF_Firewalld(self): # firewall-cmd --list-all | grep ' ports' data = mw.execShell("firewall-cmd --list-all | grep ' ports'") print(data) From 1cc8ac17f3e21140dec60d89afc0b20a6d6c850a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:42:02 +0800 Subject: [PATCH 177/965] update --- web/admin/__init__.py | 28 ++++++++++++++++++++++------ web/admin/ssh.py | 39 --------------------------------------- 2 files changed, 22 insertions(+), 45 deletions(-) delete mode 100644 web/admin/ssh.py diff --git a/web/admin/__init__.py b/web/admin/__init__.py index e7f47b7f6..d472efabd 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -63,20 +63,36 @@ app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) # app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 SQLite 数据库 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True - # 初始化db -# sys_db.init_app(app) -# Migrate(app, sys_db) setup.init() - # webssh socketio = SocketIO() socketio.init_app(app) -from .ssh import websshRun -websshRun() + +@socketio.on('webssh_websocketio') +def webssh_websocketio(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return + import utils.ssh.ssh_terminal as ssh_terminal + shell_client = ssh_terminal.ssh_terminal.instance() + shell_client.run(request.sid, data) + return +@socketio.on('webssh') +def webssh(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return None + + import utils.ssh.ssh_local as ssh_local + shell = ssh_local.ssh_local.instance() + shell.run(data) + return + +# BASIC AUTH app.config['BASIC_AUTH_OPEN'] = False try: basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) diff --git a/web/admin/ssh.py b/web/admin/ssh.py deleted file mode 100644 index 3da737368..000000000 --- a/web/admin/ssh.py +++ /dev/null @@ -1,39 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - - -from flask_socketio import SocketIO, emit, send -from flask import request - -from admin import app,socketio -from .common import isLogined - -def websshRun(): - @socketio.on('webssh_websocketio') - def webssh_websocketio(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return - import utils.ssh.ssh_terminal as ssh_terminal - shell_client = ssh_terminal.ssh_terminal.instance() - shell_client.run(request.sid, data) - return - - - @socketio.on('webssh') - def webssh(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return None - - import utils.ssh.ssh_local as ssh_local - shell = ssh_local.ssh_local.instance() - shell.run(data) - return From c9288e8dbeb2f5a6e6ab92f0d93f940015ac4ecc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 01:42:53 +0800 Subject: [PATCH 178/965] Update __init__.py --- web/admin/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index d472efabd..b3f26b3e0 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -14,25 +14,24 @@ import json import time import uuid import logging + from datetime import timedelta from flask import Flask from flask import request from flask import Response - -from flask_socketio import SocketIO, emit, send from flask import Flask, abort, current_app, session, url_for from flask import Blueprint, render_template from flask import render_template_string - - +from flask_socketio import SocketIO, emit, send from flask_migrate import Migrate from flask_caching import Cache from werkzeug.local import LocalProxy from admin import setup +from admin.common import isLogined import core.mw as mw import config @@ -40,6 +39,7 @@ import utils.config as utils_config import thisdb + app = Flask(__name__, template_folder='templates/default') # 缓存配置 From 98f7c4810ef294e5f6bd2cfd2a657a240f5fbbc0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 14:33:19 +0800 Subject: [PATCH 179/965] Update __init__.py --- web/admin/__init__.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index b3f26b3e0..b057e135a 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -171,13 +171,6 @@ def inject_global_variables(): } return dict(config=g_config, data=data) - -# from gevent.pywsgi import WSGIServer -# from geventwebsocket.handler import WebSocketHandler -# http_server = WSGIServer(('0.0.0.0', config.DEFAULT_SERVER_PORT), app, handler_class=WebSocketHandler) -# http_server.serve_forever() -# socketio.run(app, host='0.0.0.0', port=PORT) - # def create_app(app_name = None): # # if not app_name: From e6f1bbfabeb57460cb64f635ce15df1a3c288ace Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 15:40:58 +0800 Subject: [PATCH 180/965] Update __init__.py --- web/admin/__init__.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index b057e135a..776c95b9d 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -171,17 +171,6 @@ def inject_global_variables(): } return dict(config=g_config, data=data) -# def create_app(app_name = None): -# -# if not app_name: -# app_name = config.APP_NAME - -# # Check if app is created for CLI operations or Web -# cli_mode = False -# if app_name.endswith('-cli'): -# cli_mode = True -# return app - # Log the startup app.logger.info('########################################################') From 2d32d8dbb29f9e2ffebae73d1ccd311ee0f86e48 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 20:05:34 +0800 Subject: [PATCH 181/965] update --- web/admin/firewall/__init__.py | 8 ++++++++ web/utils/firewall.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index d57c998c7..331529267 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -53,6 +53,14 @@ def get_ssh_info(): def set_ping(): return MwFirewall.instance().setPing() + +# 修改ssh端口 +@blueprint.route('/set_ssh_port', endpoint='set_ssh_port', methods=['POST']) +@panel_login_required +def set_ssh_port(): + port = request.form.get('port', '1').strip() + return MwFirewall.instance().setSshPort(port) + # 添加放行端口 @blueprint.route('/add_accept_port', endpoint='add_accept_port', methods=['POST']) @panel_login_required diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 7b12014e3..bd67c8efe 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -170,6 +170,25 @@ class Firewall(object): mw.execShell('sysctl -p') return mw.returnData(True, '设置成功!') + def setSshPort(self, port): + if int(port) < 22 or int(port) > 65535: + return mw.returnData(False, '端口范围必需在22-65535之间!') + + ports = ['21', '25', '80', '443', '888'] + if port in ports: + return mw.returnData(False, '(' + port + ')' + '特殊端口不可设置!') + + file = '/etc/ssh/sshd_config' + conf = mw.readFile(file) + + rep = r"#*Port\s+([0-9]+)\s*\n" + conf = re.sub(rep, "Port " + port + "\n", conf) + mw.writeFile(file, conf) + + self.addAcceptPort(port, 'SSH端口修改', 'port') + self.reload() + return mw.returnData(True, '修改成功!') + def setFw(self, status): if self.__isIptables: self.setFwIptables(status) From 31ac0c2542daaeae59e329749ebc19436559071f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 20:21:12 +0800 Subject: [PATCH 182/965] Update firewall.py --- web/utils/firewall.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index bd67c8efe..0cdc41b12 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -55,7 +55,8 @@ class Firewall(object): def AIF_Firewalld(self): # firewall-cmd --list-all | grep ' ports' data = mw.execShell("firewall-cmd --list-all | grep ' ports'") - print(data) + all_port = data[0].strip() + print(all_port) def getList(self, page=1,size=10): info = thisdb.getFirewallList(page=page, size=size) From b10e1ba8cc945cf72d7495d2193b7a4eb8a02a83 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 20:24:48 +0800 Subject: [PATCH 183/965] update --- cli.sh | 60 +++++++++++++++------------ new_cli.sh | 116 ----------------------------------------------------- 2 files changed, 34 insertions(+), 142 deletions(-) delete mode 100755 new_cli.sh diff --git a/cli.sh b/cli.sh index a06e18550..97b66ee1d 100755 --- a/cli.sh +++ b/cli.sh @@ -3,31 +3,31 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/hom DIR=$(cd "$(dirname "$0")"; pwd) MDIR=$(dirname "$DIR") +export LC_ALL="en_US.UTF-8" + +# echo $DIR PATH=$PATH:$DIR/bin -if [ -f bin/activate ];then - source bin/activate +if [ -f ${DIR}/bin/activate ];then + source ${DIR}/bin/activate if [ "$?" != "0" ];then echo "load local python env fail!" fi fi -export LC_ALL="en_US.UTF-8" - - mw_start_task() { - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "starting mw-tasks... \c" - cd $DIR && python3 task.py >> ${DIR}/logs/task.log 2>&1 & + cd $DIR && python3 panel_task.py >> ${DIR}/logs/panel_task.log 2>&1 & sleep 0.3 - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' - tail -n 20 $DIR/logs/task.log + tail -n 20 $DIR/logs/panel_task.log echo '------------------------------------------------------' echo -e "\033[31mError: mw-tasks service startup failed.\033[0m" return; @@ -39,34 +39,42 @@ mw_start_task() } mw_start(){ - gunicorn -c setting.py app:app + cd ${DIR}/web && gunicorn -c setting.py app:app #安全启动 mw_start_task } mw_start_debug(){ - if [ ! -f $DIR/logs/task.log ];then - echo '' > $DIR/logs/task.log + if [ ! -f $DIR/logs/panel_task.log ];then + echo '' > $DIR/logs/panel_task.log fi - python3 task.py >> $DIR/logs/task.log 2>&1 & - port=7200 + python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & + port=7200 if [ -f /www/server/mdserver-web/data/port.pl ];then port=$(cat /www/server/mdserver-web/data/port.pl) fi + + if [ -f ${DIR}/data/port.pl ];then + port=$(cat ${DIR}/data/port.pl) + fi # gunicorn -b :${port} -k gevent -w 1 app:app - gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app } -mw_start_debug2(){ - python3 task.py >> $DIR/logs/task.log 2>&1 & - gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app +mw_start_panel(){ + port=7200 + if [ -f ${DIR}/data/port.pl ];then + port=$(cat ${DIR}/data/port.pl) + fi + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app } -mw_start_debug3(){ - gunicorn -c setting.py app:app - python3 task.py +mw_start_bgtask(){ + cd ${DIR}/web && gunicorn -c setting.py app:app + cd ${DIR} && python3 panel_task.py } @@ -78,7 +86,7 @@ mw_stop() kill -9 $i > /dev/null 2>&1 done - pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'` + pids=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'` arr=($pids) for p in ${arr[@]} do @@ -97,12 +105,12 @@ case "$1" in mw_stop mw_start_debug ;; - 'debug2') + 'panel') mw_stop - mw_start_debug2 + mw_start_panel ;; - 'debug3') + 'task') mw_stop - mw_start_debug3 + mw_start_bgtask ;; esac \ No newline at end of file diff --git a/new_cli.sh b/new_cli.sh deleted file mode 100755 index 97b66ee1d..000000000 --- a/new_cli.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin -DIR=$(cd "$(dirname "$0")"; pwd) -MDIR=$(dirname "$DIR") - -export LC_ALL="en_US.UTF-8" - -# echo $DIR - -PATH=$PATH:$DIR/bin -if [ -f ${DIR}/bin/activate ];then - source ${DIR}/bin/activate - - if [ "$?" != "0" ];then - echo "load local python env fail!" - fi -fi - -mw_start_task() -{ - isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') - if [ "$isStart" == '' ];then - echo -e "starting mw-tasks... \c" - cd $DIR && python3 panel_task.py >> ${DIR}/logs/panel_task.log 2>&1 & - sleep 0.3 - isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') - if [ "$isStart" == '' ];then - echo -e "\033[31mfailed\033[0m" - echo '------------------------------------------------------' - tail -n 20 $DIR/logs/panel_task.log - echo '------------------------------------------------------' - echo -e "\033[31mError: mw-tasks service startup failed.\033[0m" - return; - fi - echo -e "\033[32mdone\033[0m" - else - echo "starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running" - fi -} - -mw_start(){ - cd ${DIR}/web && gunicorn -c setting.py app:app - #安全启动 - mw_start_task -} - - -mw_start_debug(){ - if [ ! -f $DIR/logs/panel_task.log ];then - echo '' > $DIR/logs/panel_task.log - fi - - python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & - port=7200 - if [ -f /www/server/mdserver-web/data/port.pl ];then - port=$(cat /www/server/mdserver-web/data/port.pl) - fi - - if [ -f ${DIR}/data/port.pl ];then - port=$(cat ${DIR}/data/port.pl) - fi - # gunicorn -b :${port} -k gevent -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app -} - -mw_start_panel(){ - port=7200 - if [ -f ${DIR}/data/port.pl ];then - port=$(cat ${DIR}/data/port.pl) - fi - # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app -} - -mw_start_bgtask(){ - cd ${DIR}/web && gunicorn -c setting.py app:app - cd ${DIR} && python3 panel_task.py -} - - -mw_stop() -{ - PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` - for i in $PLIST - do - kill -9 $i > /dev/null 2>&1 - done - - pids=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'` - arr=($pids) - for p in ${arr[@]} - do - kill -9 $p > /dev/null 2>&1 - done -} - -case "$1" in - 'start') mw_start;; - 'stop') mw_stop;; - 'restart') - mw_stop - mw_start - ;; - 'debug') - mw_stop - mw_start_debug - ;; - 'panel') - mw_stop - mw_start_panel - ;; - 'task') - mw_stop - mw_start_bgtask - ;; -esac \ No newline at end of file From 29c65c0e7c99d3c0e4b60be63f4fce7b24100cee Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 20:24:57 +0800 Subject: [PATCH 184/965] update --- task.py | 633 ------------------------------------------------------- tools.py | 303 -------------------------- 2 files changed, 936 deletions(-) delete mode 100755 task.py delete mode 100755 tools.py diff --git a/task.py b/task.py deleted file mode 100755 index 71691f088..000000000 --- a/task.py +++ /dev/null @@ -1,633 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 计划任务 -# --------------------------------------------------------------------------------- - -import sys -import os -import json -import time -import threading -import psutil - -if sys.version_info[0] == 2: - reload(sys) - sys.setdefaultencoding('utf-8') - - -sys.path.append(os.getcwd() + "/class/core") -import mw -import db - -# print sys.path - -# 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) - - -global pre, timeoutCount, logPath, isTask, oldEdate, isCheck -pre = 0 -timeoutCount = 0 -isCheck = 0 -oldEdate = None - -logPath = os.getcwd() + '/tmp/panelExec.log' -isTask = os.getcwd() + '/tmp/panelTask.pl' - -if not os.path.exists(os.getcwd() + "/tmp"): - os.system('mkdir -p ' + os.getcwd() + "/tmp") - -if not os.path.exists(logPath): - os.system("touch " + logPath) - - -def service_cmd(method): - cmd = '/etc/init.d/mw' - if os.path.exists(cmd): - execShell(cmd + ' ' + method) - return - - cmd = mw.getRunDir() + '/scripts/init.d/mw' - if os.path.exists(cmd): - execShell(cmd + ' ' + method) - return - - -def openresty_cmd(method = 'reload'): - # 检查是否安装 - odir = mw.getServerDir() + '/openresty' - if not os.path.exists(odir): - return False - - # systemd - systemd = mw.systemdCfgDir()+'/openresty.service' - if os.path.exists(systemd): - execShell('systemctl ' + method + ' openresty') - return True - - sys_initd = '/etc/init.d/openresty' - if os.path.exists(sys_initd): - os.system(sys_initd + ' ' + method) - return True - - install_initd = mw.getServerDir()+'/openresty/init.d/openresty' - if os.path.exists(install_initd): - os.system(install_initd + ' ' + method) - return True - return False - -def mw_async(f): - def wrapper(*args, **kwargs): - thr = threading.Thread(target=f, args=args, kwargs=kwargs) - thr.start() - return wrapper - - -@mw_async -def restartMw(): - time.sleep(1) - cmd = mw.getRunDir() + '/scripts/init.d/mw reload &' - mw.execShell(cmd) - - -def execShell(cmdstring, cwd=None, timeout=None, shell=True): - try: - global logPath - import shlex - import datetime - import subprocess - - if timeout: - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) - - cmd = cmdstring + ' > ' + logPath + ' 2>&1' - sub = subprocess.Popen( - cmd, cwd=cwd, stdin=subprocess.PIPE, shell=shell, bufsize=4096) - while sub.poll() is None: - time.sleep(0.1) - - data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) - except Exception as e: - return (None, None) - - -def downloadFile(url, filename): - # 下载文件 - try: - import urllib - import socket - socket.setdefaulttimeout(300) - - headers = ( - 'User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36') - opener = urllib.request.build_opener() - opener.addheaders = [headers] - urllib.request.install_opener(opener) - - urllib.request.urlretrieve( - url, filename=filename, reporthook=downloadHook) - - if not mw.isAppleSystem(): - os.system('chown www.www ' + filename) - - writeLogs('done') - except Exception as e: - writeLogs(str(e)) - - -def downloadHook(count, blockSize, totalSize): - # 下载文件进度回调 - global pre - used = count * blockSize - pre1 = int((100.0 * used / totalSize)) - if pre == (100 - pre1): - return - speed = {'total': totalSize, 'used': used, 'pre': pre1} - writeLogs(json.dumps(speed)) - - -def writeLogs(logMsg): - # 写输出日志 - try: - global logPath - fp = open(logPath, 'w+') - fp.write(logMsg) - fp.close() - except: - pass - - -def runTask(): - global isTask - try: - if os.path.exists(isTask): - sql = db.Sql() - sql.table('tasks').where( - "status=?", ('-1',)).setField('status', '0') - taskArr = sql.table('tasks').where("status=?", ('0',)).field( - 'id,type,execstr').order("id asc").select() - for value in taskArr: - start = int(time.time()) - if not sql.table('tasks').where("id=?", (value['id'],)).count(): - continue - sql.table('tasks').where("id=?", (value['id'],)).save( - 'status,start', ('-1', start)) - if value['type'] == 'download': - argv = value['execstr'].split('|mw|') - downloadFile(argv[0], argv[1]) - elif value['type'] == 'execshell': - execShell(value['execstr']) - end = int(time.time()) - sql.table('tasks').where("id=?", (value['id'],)).save( - 'status,end', ('1', end)) - - if(sql.table('tasks').where("status=?", ('0')).count() < 1): - os.system('rm -f ' + isTask) - - sql.close() - except Exception as e: - print(str(e)) - - # 站点过期检查 - siteEdate() - - -def startTask(): - # 任务队列 - try: - while True: - runTask() - time.sleep(2) - except Exception as e: - time.sleep(60) - startTask() - - -def siteEdate(): - # 网站到期处理 - global oldEdate - try: - if not oldEdate: - oldEdate = mw.readFile('data/edate.pl') - if not oldEdate: - oldEdate = '0000-00-00' - mEdate = time.strftime('%Y-%m-%d', time.localtime()) - if oldEdate == mEdate: - return False - edateSites = mw.M('sites').where('edate>? AND edate 80: - panel_title = mw.getConfig('title') - ip = mw.getHostAddr() - now_time = mw.getDateFromNow() - msg = now_time + '|节点[' + panel_title + ':' + ip + \ - ']处于高负载[' + str(tmp['used']) + '],请排查原因!' - mw.notifyMessage(msg, '面板监控', 600) - - if not cpuInfo: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - if cpuInfo['used'] < tmp['used']: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - # 取当前网络Io - networkIo = sm.psutilNetIoCounters() - if not network_up: - network_up = networkIo[0] - network_down = networkIo[1] - tmp = {} - tmp['upTotal'] = networkIo[0] - tmp['downTotal'] = networkIo[1] - tmp['up'] = round(float((networkIo[0] - network_up) / 1024), 2) - tmp['down'] = round(float((networkIo[1] - network_down) / 1024), 2) - tmp['downPackets'] = networkIo[3] - tmp['upPackets'] = networkIo[2] - - network_up = networkIo[0] - network_down = networkIo[1] - - if not networkInfo: - networkInfo = tmp - if (tmp['up'] + tmp['down']) > (networkInfo['up'] + networkInfo['down']): - networkInfo = tmp - # 取磁盘Io - # if os.path.exists('/proc/diskstats'): - diskio_2 = psutil.disk_io_counters() - if not diskio_1: - diskio_1 = diskio_2 - tmp = {} - tmp['read_count'] = diskio_2.read_count - diskio_1.read_count - tmp['write_count'] = diskio_2.write_count - diskio_1.write_count - tmp['read_bytes'] = diskio_2.read_bytes - diskio_1.read_bytes - tmp['write_bytes'] = diskio_2.write_bytes - diskio_1.write_bytes - tmp['read_time'] = diskio_2.read_time - diskio_1.read_time - tmp['write_time'] = diskio_2.write_time - diskio_1.write_time - - if not diskInfo: - diskInfo = tmp - else: - diskInfo['read_count'] += tmp['read_count'] - diskInfo['write_count'] += tmp['write_count'] - diskInfo['read_bytes'] += tmp['read_bytes'] - diskInfo['write_bytes'] += tmp['write_bytes'] - diskInfo['read_time'] += tmp['read_time'] - diskInfo['write_time'] += tmp['write_time'] - diskio_1 = diskio_2 - - # print diskInfo - if count >= 12: - try: - addtime = int(time.time()) - deltime = addtime - (day * 86400) - - data = (cpuInfo['used'], cpuInfo['mem'], addtime) - sql.table('cpuio').add('pro,mem,addtime', data) - sql.table('cpuio').where("addtime 100: - lpro = 100 - sql.table('load_average').add('pro,one,five,fifteen,addtime', (lpro, load_average[ - 'one'], load_average['five'], load_average['fifteen'], addtime)) - - lpro = None - load_average = None - cpuInfo = None - networkInfo = None - diskInfo = None - count = 0 - reloadNum += 1 - if reloadNum > 1440: - reloadNum = 0 - mw.writeFile('logs/sys_interrupt.pl', - "reload num:" + str(reloadNum)) - restartMw() - except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - del(tmp) - time.sleep(5) - count += 1 - except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - restartMw() - - time.sleep(30) - systemTask() - - -# -------------------------------------- PHP监控 start --------------------------------------------- # -# 502错误检查线程 -def check502Task(): - try: - while True: - if os.path.exists(mw.getRunDir() + '/data/502Task.pl'): - check502() - time.sleep(30) - except: - time.sleep(30) - check502Task() - - -def check502(): - try: - verlist = [ - '52', '53', '54', '55', '56', '70', - '71', '72', '73', '74', '80', '81', - '82', '83' - ] - for ver in verlist: - sdir = mw.getServerDir() - php_path = sdir + '/php/' + ver + '/sbin/php-fpm' - if not os.path.exists(php_path): - continue - if checkPHPVersion(ver): - continue - if startPHPVersion(ver): - print('检测到PHP-' + ver + '处理异常,已自动修复!') - mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!') - except Exception as e: - print(str(e)) - - -# 处理指定PHP版本 -def startPHPVersion(version): - sdir = mw.getServerDir() - try: - # system - phpService = mw.systemdCfgDir() + '/php' + version + '.service' - if os.path.exists(phpService): - mw.execShell("systemctl restart php" + version) - if checkPHPVersion(version): - return True - - # initd - fpm = sdir + '/php/init.d/php' + version - php_path = sdir + '/php/' + version + '/sbin/php-fpm' - if not os.path.exists(php_path): - if os.path.exists(fpm): - os.remove(fpm) - return False - - if not os.path.exists(fpm): - return False - - # 尝试重载服务 - os.system(fpm + ' reload') - if checkPHPVersion(version): - return True - - # 尝试重启服务 - cgi = '/tmp/php-cgi-' + version + '.sock' - pid = sdir + '/php/' + version + '/var/run/php-fpm.pid' - data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'") - if data[0] != '': - os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ") - time.sleep(0.5) - if not os.path.exists(cgi): - os.system('rm -f ' + cgi) - if not os.path.exists(pid): - os.system('rm -f ' + pid) - os.system(fpm + ' start') - if checkPHPVersion(version): - return True - - # 检查是否正确启动 - if os.path.exists(cgi): - return True - except Exception as e: - print(str(e)) - return True - - -def getFpmConfFile(version): - return mw.getServerDir() + '/php/' + version + '/etc/php-fpm.d/www.conf' - - -def getFpmAddress(version): - fpm_address = '/tmp/php-cgi-{}.sock'.format(version) - php_fpm_file = getFpmConfFile(version) - try: - content = readFile(php_fpm_file) - tmp = re.findall(r"listen\s*=\s*(.+)", content) - if not tmp: - return fpm_address - if tmp[0].find('sock') != -1: - return fpm_address - if tmp[0].find(':') != -1: - listen_tmp = tmp[0].split(':') - if bind: - fpm_address = (listen_tmp[0], int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(tmp[0])) - return fpm_address - except: - return fpm_address - - -def checkPHPVersion(version): - # 检查指定PHP版本 - try: - sock = getFpmAddress(version) - data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json') - result = str(data, encoding='utf-8') - except Exception as e: - result = 'Bad Gateway' - - # print(version,result) - # 检查openresty - if result.find('Bad Gateway') != -1: - return False - if result.find('HTTP Error 404: Not Found') != -1: - return False - - # 检查Web服务是否启动 - if result.find('Connection refused') != -1: - return False - # global isTask - # if os.path.exists(isTask): - # isStatus = mw.readFile(isTask) - # if isStatus == 'True': - # return True - - # # systemd - # systemd = mw.systemdCfgDir() + '/openresty.service' - # if os.path.exists(systemd): - # execShell('systemctl reload openresty') - # return True - # # initd - # initd = '/etc/init.d/openresty' - # if os.path.exists(initd): - # os.system(initd + ' reload') - return True - -# --------------------------------------PHP监控 end--------------------------------------------- # - - -# --------------------------------------OpenResty Auto Restart Start --------------------------------------------- # -# 解决acme.sh续签后,未起效。 -def openrestyAutoRestart(): - try: - while True: - # 检查是否安装 - odir = mw.getServerDir() + '/openresty' - if not os.path.exists(odir): - time.sleep(86400) - continue - - openresty_cmd('reload') - time.sleep(86400) - except Exception as e: - print(str(e)) - time.sleep(86400) - -# --------------------------------------OpenResty Auto Restart End --------------------------------------------- # - -# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ # - - -def openrestyRestartAtOnce(): - restart_nginx_tip = 'data/restart_nginx.pl' - while True: - if os.path.exists(restart_nginx_tip): - os.remove(restart_nginx_tip) - openresty_cmd('reload') - time.sleep(1) -# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ # - - -# --------------------------------------Panel Restart Start --------------------------------------------- # -def restartPanelService(): - restartTip = 'data/restart.pl' - while True: - if os.path.exists(restartTip): - os.remove(restartTip) - service_cmd('restart_panel') - time.sleep(1) -# --------------------------------------Panel Restart End --------------------------------------------- # - - -def setDaemon(t): - if sys.version_info.major == 3 and sys.version_info.minor >= 10: - t.daemon = True - else: - t.setDaemon(True) - return t - -if __name__ == "__main__": - - # 系统监控 - sysTask = threading.Thread(target=systemTask) - sysTask = setDaemon(sysTask) - sysTask.start() - - # PHP 502错误检查线程 - php502 = threading.Thread(target=check502Task) - php502 = setDaemon(php502) - php502.start() - - # OpenResty Restart At Once Start - oraos = threading.Thread(target=openrestyRestartAtOnce) - oraos = setDaemon(oraos) - oraos.start() - - - # OpenResty Auto Restart Start - oar = threading.Thread(target=openrestyAutoRestart) - oar = setDaemon(oar) - oar.start() - - - # Panel Restart Start - rps = threading.Thread(target=restartPanelService) - rps = setDaemon(rps) - rps.start() - - startTask() diff --git a/tools.py b/tools.py deleted file mode 100755 index 6a050f9d9..000000000 --- a/tools.py +++ /dev/null @@ -1,303 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 工具箱 -# --------------------------------------------------------------------------------- - - -import sys -import os -import json -import time -import re - -sys.path.append(os.getcwd() + "/class/core") -import mw -import db - -# 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) - -INIT_DIR = "/etc/rc.d/init.d" -if mw.isAppleSystem(): - INIT_DIR = mw.getRunDir() + "/scripts/init.d" - -INIT_CMD = INIT_DIR + "/mw" - - -def mw_input_cmd(msg): - if sys.version_info[0] == 2: - in_val = raw_input(msg) - else: - in_val = input(msg) - return in_val - - -def mwcli(mw_input=0): - raw_tip = "======================================================" - if not mw_input: - print("===============mdserver-web cli tools=================") - print("(1) 重启面板服务") - print("(2) 停止面板服务") - print("(3) 启动面板服务") - print("(4) 重载面板服务") - print("(5) 修改面板端口") - print("(10) 查看面板默认信息") - print("(11) 修改面板密码") - print("(12) 修改面板用户名") - print("(13) 显示面板错误日志") - print("(20) 关闭BasicAuth认证") - print("(21) 解除域名绑定") - print("(22) 解除面板SSL绑定") - print("(23) 开启IPV6支持") - print("(24) 关闭IPV6支持") - print("(25) 开启防火墙SSH端口") - print("(26) 关闭二次验证") - print("(27) 查看防火墙信息") - print("(100) 开启PHP52显示") - print("(101) 关闭PHP52显示") - print("(200) 切换Linux系统软件源") - print("(201) 简单速度测试") - print("(0) 取消") - print(raw_tip) - try: - mw_input = input("请输入命令编号:") - if sys.version_info[0] == 3: - mw_input = int(mw_input) - except: - mw_input = 0 - - nums = [ - 1, 2, 3, 4, 5, 10, 11, 12, 13, - 20, 21, 22, 23, 24, 25, 26, 27, - 100, 101, - 200, 201 - ] - if not mw_input in nums: - print(raw_tip) - print("已取消!") - exit() - - if mw_input == 1: - os.system(INIT_CMD + " restart") - elif mw_input == 2: - os.system(INIT_CMD + " stop") - elif mw_input == 3: - os.system(INIT_CMD + " start") - elif mw_input == 4: - os.system(INIT_CMD + " reload") - elif mw_input == 5: - in_port = mw_input_cmd("请输入新的面板端口:") - in_port_int = int(in_port.strip()) - if in_port_int < 65536 and in_port_int > 0: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs( - in_port, 'WEB面板[TOOLS修改]', 'port') - mw.writeFile('data/port.pl', in_port) - os.system(INIT_CMD + " restart_panel") - os.system(INIT_CMD + " default") - else: - print("|-端口范围在0-65536之间") - return - elif mw_input == 10: - os.system(INIT_CMD + " default") - elif mw_input == 11: - input_pwd = mw_input_cmd("请输入新的面板密码:") - if len(input_pwd.strip()) < 5: - print("|-错误,密码长度不能小于5位") - return - set_panel_pwd(input_pwd.strip(), True) - elif mw_input == 12: - input_user = mw_input_cmd("请输入新的面板用户名(>=5位):") - set_panel_username(input_user.strip()) - elif mw_input == 13: - os.system('tail -100 ' + mw.getRunDir() + '/logs/error.log') - elif mw_input == 20: - basic_auth = 'data/basic_auth.json' - if os.path.exists(basic_auth): - os.remove(basic_auth) - os.system(INIT_CMD + " restart") - print("|-关闭basic_auth成功") - elif mw_input == 21: - bind_domain = 'data/bind_domain.pl' - if os.path.exists(bind_domain): - os.remove(bind_domain) - os.system(INIT_CMD + " unbind_domain") - print("|-解除域名绑定成功") - elif mw_input == 22: - ssl_choose = 'ssl/choose.pl' - if os.path.exists(ssl_choose): - os.remove(ssl_choose) - os.system(INIT_CMD + " unbind_ssl") - print("|-解除面板SSL绑定成功") - elif mw_input == 23: - listen_ipv6 = 'data/ipv6.pl' - if not os.path.exists(listen_ipv6): - mw.writeFile(listen_ipv6,'True') - os.system(INIT_CMD + " restart") - print("|-开启IPv6支持了") - else: - print("|-已开启IPv6支持!") - elif mw_input == 24: - listen_ipv6 = 'data/ipv6.pl' - if not os.path.exists(listen_ipv6): - print("|-已关闭IPv6支持!") - else: - os.remove(listen_ipv6) - os.system(INIT_CMD + " restart") - print("|-关闭IPv6支持了") - elif mw_input == 25: - open_ssh_port() - print("|-已开启!") - elif mw_input == 26: - auth_secret = 'data/auth_secret.pl' - if os.path.exists(auth_secret): - os.remove(auth_secret) - print("|-关闭二次验证成功!") - else: - print("|-二次验证已关闭!") - elif mw_input == 27: - cmd = 'which ufw' - run_cmd = False - find_cmd = mw.execShell(cmd) - if find_cmd[0].strip() != '': - run_cmd = True - os.system('ufw status') - - cmd = 'which firewall-cmd' - find_cmd = mw.execShell(cmd) - if find_cmd[0].strip() != '': - run_cmd = True - os.system('firewall-cmd --list-all') - if not run_cmd: - mw.echoInfo("未检测到防火墙!") - elif mw_input == 100: - php_conf = 'plugins/php/info.json' - if os.path.exists(php_conf): - cont = mw.readFile(php_conf) - cont = re.sub("\"53\"", "\"52\",\"53\"", cont) - cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont) - mw.writeFile(php_conf, cont) - print("|-执行PHP52显示成功!") - elif mw_input == 101: - php_conf = 'plugins/php/info.json' - if os.path.exists(php_conf): - cont = mw.readFile(php_conf) - cont = re.sub("\"52\",", "", cont) - cont = re.sub("\"5.2.17\",", cont) - mw.writeFile(php_conf, cont) - print("|-执行PHP52隐藏成功!") - elif mw_input == 200: - os.system(INIT_CMD + " mirror") - elif mw_input == 201: - os.system('curl -Lso- bench.sh | bash') - - -def open_ssh_port(): - import firewall_api - find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1" - cmd_data = mw.execShell(find_ssh_port_cmd) - ssh_port = cmd_data[0].replace("Port ", '').strip() - if ssh_port == '': - ssh_port = '22' - - print("|-SSH端口: "+ str(ssh_port)) - firewall_api.firewall_api().addAcceptPortArgs(ssh_port, 'SSH远程管理服务', 'port') - return True - - -def set_panel_pwd(password, ncli=False): - # 设置面板密码 - import db - sql = db.Sql() - result = sql.table('users').where('id=?', (1,)).setField( - 'password', mw.md5(password)) - username = sql.table('users').where('id=?', (1,)).getField('username') - if ncli: - print("|-用户名: " + username) - print("|-新密码: " + password) - else: - print(username) - - -def show_panel_pwd(): - # 设置面板密码 - import db - sql = db.Sql() - password = sql.table('users').where('id=?', (1,)).getField('password') - - file_pwd = '' - if os.path.exists('data/default.pl'): - file_pwd = mw.readFile('data/default.pl').strip() - - if mw.md5(file_pwd) == password: - print('password: ' + file_pwd) - return - print("password has been changed!") - - -def set_panel_username(username=None): - # 随机面板用户名 - import db - sql = db.Sql() - if username: - if len(username) < 5: - print("|-错误,用户名长度不能少于5位") - return - if username in ['admin', 'root']: - print("|-错误,不能使用过于简单的用户名") - return - - sql.table('users').where('id=?', (1,)).setField('username', username) - print("|-新用户名: %s" % username) - return - - username = sql.table('users').where('id=?', (1,)).getField('username') - if username == 'admin': - username = mw.getRandomString(8).lower() - sql.table('users').where('id=?', (1,)).setField('username', username) - print('username: ' + username) - - -def getServerIp(): - version = sys.argv[2] - # ip = mw.execShell( - # "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version)) - ip = mw.execShell( - "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version)) - print(ip[0]) - - -if __name__ == "__main__": - method = sys.argv[1] - if method == 'panel': - set_panel_pwd(sys.argv[2]) - elif method == 'username': - if len(sys.argv) > 2: - set_panel_username(sys.argv[2]) - else: - set_panel_username() - elif method == 'password': - show_panel_pwd() - elif method == 'getServerIp': - getServerIp() - elif method == "cli": - clinum = 0 - try: - if len(sys.argv) > 2: - clinum = int(sys.argv[2]) if sys.argv[2][:6] else sys.argv[2] - except: - clinum = sys.argv[2] - mwcli(clinum) - else: - print('ERROR: Parameter error') From b9371e9b947fa040a4af63cde7918c559e4dc8e9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 22:40:15 +0800 Subject: [PATCH 185/965] Update firewall.py --- web/utils/firewall.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 0cdc41b12..38b61d83f 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -54,9 +54,31 @@ class Firewall(object): def AIF_Firewalld(self): # firewall-cmd --list-all | grep ' ports' - data = mw.execShell("firewall-cmd --list-all | grep ' ports'") - all_port = data[0].strip() - print(all_port) + t = mw.execShell("firewall-cmd --list-all | grep ' ports'") + all_port = t[0].strip() + data = all_port.split(":") + ports_str = data[1] + ports_list = ports_str.strip().split(' ') + + ports_all = [] + for pinfo in ports_list: + info = pinfo.split('/') + + is_same = False + for i in range(len(ports_all)): + if ports_all[i]['port'] == info[0] and ports_all[i]['protocol'] != info[1]: + ports_all[i]['protocol'] = ports_all[i]['protocol']+'/'+info[1] + is_same = True + + if not is_same: + t = {} + t['port'] = info[0] + t['protocol'] = info[1] + ports_all.append(t) + + for add_info in ports_all: + if thisdb.getFirewallCountByPort(add_info['port']) == 0: + thisdb.addFirewall(add_info['port'], ps='自动识别',protocol=add_info['protocol']) def getList(self, page=1,size=10): info = thisdb.getFirewallList(page=page, size=size) From 28def91c7d04016c5fc5b21da51a5fc2aad01b1f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 12 Nov 2024 22:56:40 +0800 Subject: [PATCH 186/965] Update __init__.py --- web/admin/__init__.py | 51 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 776c95b9d..a33c569ff 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -66,32 +66,6 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True # 初始化db setup.init() -# webssh -socketio = SocketIO() -socketio.init_app(app) - -@socketio.on('webssh_websocketio') -def webssh_websocketio(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return - import utils.ssh.ssh_terminal as ssh_terminal - shell_client = ssh_terminal.ssh_terminal.instance() - shell_client.run(request.sid, data) - return - - -@socketio.on('webssh') -def webssh(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return None - - import utils.ssh.ssh_local as ssh_local - shell = ssh_local.ssh_local.instance() - shell.run(data) - return - # BASIC AUTH app.config['BASIC_AUTH_OPEN'] = False try: @@ -171,6 +145,31 @@ def inject_global_variables(): } return dict(config=g_config, data=data) +# webssh +socketio = SocketIO() +socketio.init_app(app) + +@socketio.on('webssh_websocketio') +def webssh_websocketio(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return + import utils.ssh.ssh_terminal as ssh_terminal + shell_client = ssh_terminal.ssh_terminal.instance() + shell_client.run(request.sid, data) + return + + +@socketio.on('webssh') +def webssh(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return None + + import utils.ssh.ssh_local as ssh_local + shell = ssh_local.ssh_local.instance() + shell.run(data) + return # Log the startup app.logger.info('########################################################') From 289f7361600df4049f8f41381c409132552a4e1a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 13 Nov 2024 14:25:22 +0800 Subject: [PATCH 187/965] Update setting.py --- web/setting.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/setting.py b/web/setting.py index 332e17387..7d4fa956f 100755 --- a/web/setting.py +++ b/web/setting.py @@ -40,10 +40,8 @@ if os.path.exists(default_port_file): panel_port.strip() else: from utils.firewall import Firewall as MwFirewall - import common - common.initDB() mw_port = str(random.randint(10000, 65530)) - MwFirewall.instance().addAcceptPort(panel_port, 'WEB面板', 'port') + MwFirewall.instance().addAcceptPort(panel_port, 'Panel端口', 'port') mw.writeFile('data/port.pl', panel_port) bind = [] From c8b9dd2ae99ba6e87aeb4e302e1f56d58ad60829 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 13 Nov 2024 14:25:29 +0800 Subject: [PATCH 188/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index 7d4fa956f..c56853617 100755 --- a/web/setting.py +++ b/web/setting.py @@ -41,7 +41,7 @@ if os.path.exists(default_port_file): else: from utils.firewall import Firewall as MwFirewall mw_port = str(random.randint(10000, 65530)) - MwFirewall.instance().addAcceptPort(panel_port, 'Panel端口', 'port') + MwFirewall.instance().addAcceptPort(panel_port, 'PANEL端口', 'port') mw.writeFile('data/port.pl', panel_port) bind = [] From bfc1974043c93acb04489d10146dc9a634f85593 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 13 Nov 2024 14:27:27 +0800 Subject: [PATCH 189/965] Update setting.py --- web/setting.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index c56853617..5afb73d0a 100755 --- a/web/setting.py +++ b/web/setting.py @@ -27,7 +27,6 @@ workers = cpu_info[1] panel_dir = mw.getPanelDir() - log_dir = mw.getMWLogs() if not os.path.exists(log_dir): os.mkdir(log_dir) From f685054a9a5a3c18aba21496d21a417d8ca1b932 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 13 Nov 2024 14:45:53 +0800 Subject: [PATCH 190/965] update --- web/admin/setting/app.py | 13 +++++++++++++ web/core/mw.py | 3 +++ web/setting.py | 2 +- web/static/app/config.js | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/web/admin/setting/app.py b/web/admin/setting/app.py index 441f068bf..5b025a3e4 100644 --- a/web/admin/setting/app.py +++ b/web/admin/setting/app.py @@ -25,6 +25,17 @@ import utils.config as utils_config from .setting import blueprint import thisdb +# 设置API +@blueprint.route('/set_panel_api', endpoint='set_panel_api', methods=['POST']) +@panel_login_required +def set_panel_api(): + admin_api = thisdb.getOption('api', default='no') + if admin_api == 'no': + thisdb.setOption('api', 'yes') + return mw.returnData(True, '开启API成功!') + thisdb.setOption('api', 'no') + return mw.returnData(True, '开启API成功!') + # 获取APP列表 @blueprint.route('/get_app_list', endpoint='get_app_list', methods=['POST']) @@ -48,6 +59,8 @@ def add_app(): app_id = request.form.get('app_id', '').strip() app_secret = request.form.get('app_secret', '1').strip() limit_addr = request.form.get('limit_addr', '').strip() + if limit_addr == '': + return mw.returnData(False, 'IP限制不能为空!') rid = thisdb.addApp(app_id,app_secret,limit_addr) if rid > 0: diff --git a/web/core/mw.py b/web/core/mw.py index f3310d32c..0d7a8f6a2 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -84,6 +84,9 @@ def getPanelDataDir(): def getMWLogs(): return getPanelDir() + '/logs' +def getPanelLogs(): + return getPanelDir() + '/logs' + def getPanelTmp(): return getPanelDir() + '/tmp' diff --git a/web/setting.py b/web/setting.py index 5afb73d0a..929e1e547 100755 --- a/web/setting.py +++ b/web/setting.py @@ -41,7 +41,7 @@ else: from utils.firewall import Firewall as MwFirewall mw_port = str(random.randint(10000, 65530)) MwFirewall.instance().addAcceptPort(panel_port, 'PANEL端口', 'port') - mw.writeFile('data/port.pl', panel_port) + mw.writeFile(default_port_file panel_port) bind = [] default_ipv6_file = panel_dir+'/data/ipv6.pl' diff --git a/web/static/app/config.js b/web/static/app/config.js index 4c3783e6b..a56a25a10 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -1464,7 +1464,7 @@ function showPanelApi(){ function setPanelApi(){ var cfg_panel_api = $('#cfg_panel_api').prop("checked"); - $.post('/setting/set_panel_token', {'op_type':"2"},function(rdata){ + $.post('/setting/set_panel_api', {},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ addApp(); From ee64e55ecaf6b3d3137d0e61d1596debe41b1b3b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 13 Nov 2024 19:28:56 +0800 Subject: [PATCH 191/965] update --- web/admin/__init__.py | 18 +++++++++ web/config.py | 7 +++- web/utils/email.py | 11 +++++- web/utils/enhanced_log_rotation.py | 61 ++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 web/utils/enhanced_log_rotation.py diff --git a/web/admin/__init__.py b/web/admin/__init__.py index a33c569ff..dc2770ff0 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -171,6 +171,24 @@ def webssh(data): shell.run(data) return +# File logging +logger = logging.getLogger('werkzeug') +logger.setLevel(config.CONSOLE_LOG_LEVEL) + +from utils.enhanced_log_rotation import EnhancedRotatingFileHandler +fh = EnhancedRotatingFileHandler(config.LOG_FILE, + config.LOG_ROTATION_SIZE, + config.LOG_ROTATION_AGE, + config.LOG_ROTATION_MAX_LOG_FILES) +fh.setLevel(config.FILE_LOG_LEVEL) +app.logger.addHandler(fh) +logger.addHandler(fh) + +# Console logging +ch = logging.StreamHandler() +ch.setLevel(config.CONSOLE_LOG_LEVEL) +ch.setFormatter(logging.Formatter(config.CONSOLE_LOG_FORMAT)) + # Log the startup app.logger.info('########################################################') app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) diff --git a/web/config.py b/web/config.py index 82aa42513..60d4b98f2 100644 --- a/web/config.py +++ b/web/config.py @@ -46,11 +46,14 @@ DATA_DIR = mw.getPanelDataDir() # 日志文件名。这将进入数据目录,服务器模式下的非Windows平台除外。 LOG_FILE = os.path.join(mw.getMWLogs(), APP_LOG_NAME + '.log') +CONSOLE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s' +FILE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s' + # 日志旋转设置日志文件将根据LOG_ROTATION_SIZE和LOG_ROTATION_AGE的值进行切换。 # 旋转的文件将以格式命名Y-m-d_H-M-S -LOG_ROTATION_SIZE = 10 # MBs +LOG_ROTATION_SIZE = 1 # MBs LOG_ROTATION_AGE = 1440 # minutes -LOG_ROTATION_MAX_LOG_FILES = 90 # 要保留的最大备份数 +LOG_ROTATION_MAX_LOG_FILES = 5 # 要保留的最大备份数 # 用于存储用户帐户和设置的SQLite数据库的默认路径。 # 此默认设置将文件放置在与此相同的目录中 配置文件,但会生成一个在整个应用程序中使用的绝对路径。 diff --git a/web/utils/email.py b/web/utils/email.py index 6ba7b62d5..9112622cb 100644 --- a/web/utils/email.py +++ b/web/utils/email.py @@ -1,4 +1,13 @@ -# coding: utf-8 +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + import smtplib diff --git a/web/utils/enhanced_log_rotation.py b/web/utils/enhanced_log_rotation.py new file mode 100644 index 000000000..1dfa449c0 --- /dev/null +++ b/web/utils/enhanced_log_rotation.py @@ -0,0 +1,61 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +import re +from logging import handlers + + +class EnhancedRotatingFileHandler(handlers.TimedRotatingFileHandler, + handlers.RotatingFileHandler): + """ + Handler for logging to a set of files, which switches from one file + to the next when the current file reaches a certain size, or at certain + timed intervals + @filename - log file name + @max_bytes - file size in bytes to rotate file + @interval - Duration to rotate file + @backup_count - Maximum number of files to retain + @encoding - file encoding + @when - 'when' events supported: + # S - Seconds + # M - Minutes + # H - Hours + # D - Days + # midnight - roll over at midnight + # W{0-6} - roll over on a certain day; 0 - Monday + Here we are defaulting rotation with minutes interval + """ + def __init__(self, filename, max_bytes=1, interval=60, backup_count=0, + encoding=None, when='M'): + max_bytes = max_bytes * 1024 * 1024 + handlers.TimedRotatingFileHandler.__init__(self, filename=filename, + when=when, + interval=interval, + backupCount=backup_count, + encoding=encoding) + + handlers.RotatingFileHandler.__init__(self, filename=filename, + mode='a', + maxBytes=max_bytes, + backupCount=backup_count, + encoding=encoding) + + # Time & Size combined rollover + def shouldRollover(self, record): + return handlers.TimedRotatingFileHandler.shouldRollover(self, record) \ + or handlers.RotatingFileHandler.shouldRollover(self, record) + + # Roll overs current file + def doRollover(self): + self.suffix = "%Y-%m-%d_%H-%M-%S" + self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$" + self.extMatch = re.compile(self.extMatch, re.ASCII) + handlers.TimedRotatingFileHandler.doRollover(self) \ No newline at end of file From bf6aa155050ee206f38f5896b45e79aad13dd813 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 14 Nov 2024 15:00:30 +0800 Subject: [PATCH 192/965] Update crontab.js --- web/static/app/crontab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/static/app/crontab.js b/web/static/app/crontab.js index fe21632c4..87d5e4866 100755 --- a/web/static/app/crontab.js +++ b/web/static/app/crontab.js @@ -85,8 +85,8 @@ function getCronData(page){ $.post("/crontab/list?p="+page,'', function(rdata){ layer.close(load); var cbody = ""; - if(rdata == ""){ - cbody=""+lan.crontab.task_empty+""; + if(rdata.data.length == 0){ + cbody="当前没有计划任务"; } else { for(var i=0;i Date: Thu, 14 Nov 2024 15:14:10 +0800 Subject: [PATCH 193/965] Update crontab.js --- web/static/app/crontab.js | 49 ++------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/web/static/app/crontab.js b/web/static/app/crontab.js index 87d5e4866..cf469fcaa 100755 --- a/web/static/app/crontab.js +++ b/web/static/app/crontab.js @@ -357,51 +357,6 @@ function planAdd(){ },'json'); } -//批量添加任务 -// function allAddCrontab(dataList,successCount,errorMsg){ -// if(dataList.length < 1) { -// layer.msg(lan.get('add_all_task_ok',[successCount]),{icon:1}); -// return; -// } -// var loadT = layer.msg(lan.get('add',[dataList[0]]),{icon:16,time:0,shade: [0.3, '#000']}); -// var sType = $(".planjs").find("b").attr("val"); -// var minute = parseInt($("#cronConfig input[name='minute']").val()); -// var hour = parseInt($("#cronConfig input[name='hour']").val()); -// var sTitle = (sType == 'site')?lan.crontab.backup_site:lan.crontab.backup_database; -// if(sType == 'logs') sTitle = lan.crontab.backup_log; -// minute += 5; -// if(hour !== '' && minute > 59){ -// if(hour >= 23) hour = 0; -// $("#cronConfig input[name='hour']").val(hour+1); -// minute = 5; -// } -// $("#cronConfig input[name='minute']").val(minute); -// $("#cronConfig input[name='name']").val(sTitle + '['+dataList[0]+']'); -// $("#cronConfig input[name='sName']").val(dataList[0]); -// var pdata = $("#cronConfig").serialize() + '&sBody=&urladdress='; -// $.ajax({ -// type:'POST', -// url:'/crontab/add', -// data:pdata, -// async: true, -// success:function(frdata){ -// layer.close(loadT); -// if(frdata.status){ -// successCount++; -// getCronData(1); -// }else{ -// if(!errorMsg){ -// errorMsg = '

    '+lan.crontab.backup_all_err+'

    '; -// } -// errorMsg += '
  • '+dataList[0]+' -> '+frdata.msg+'
  • ' -// } - -// dataList.splice(0,1); -// allAddCrontab(dataList,successCount,errorMsg); -// } -// }); -// } - initDropdownMenu(); function initDropdownMenu(){ $(".dropdown ul li a").click(function(){ @@ -1118,8 +1073,8 @@ function toUrl(){ //释放内存 function rememory(){ $(".planname input[name='name']").removeAttr('readonly style').val(""); - $(".planname input[name='name']").val(lan.crontab.mem); - $("#implement").html(lan.crontab.mem_ps); + $(".planname input[name='name']").val('释放内存'); + $("#implement").html('释放PHP、MYSQL、PURE-FTPD、APACHE、NGINX的内存占用,建议在每天半夜执行!'); return; } //上传 From f6ff18dd8dc73f668401d97f0faaf07ed2618623 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 14 Nov 2024 15:14:53 +0800 Subject: [PATCH 194/965] Update crontab.js --- web/static/app/crontab.js | 170 +++++++++++++++++++------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/web/static/app/crontab.js b/web/static/app/crontab.js index cf469fcaa..24682b4c6 100755 --- a/web/static/app/crontab.js +++ b/web/static/app/crontab.js @@ -736,85 +736,85 @@ function editTaskInfo(id){ shadeClose:false, closeBtn:1, content:'
    \ -
    \ - 任务类型\ - \ +
    \ + 任务类型\ + \ +
    \ +
    \ + 任务名称\ +
    \ +
    \ +
    \ + 执行周期\ + \ +
    \ + \ -
    \ - 任务名称\ -
    \ +
    日
    \ +
    时
    \ +
    分
    \ +
    \ +
    \ + \ +
    \ + 脚本内容\ +
    \ +
    \ +
    \ + 提示\ +
    释放PHP、MYSQL、PURE-FTPD、OpenResty的内存占用,建议在每天半夜执行!
    \ +
    \ +
    \ + URL地址\ +
    \ +
    \ +
    \ +
    保存编辑
    \ +
    \ +
    ', success:function(){ @@ -1024,26 +1024,26 @@ function toWeek(){ //指定1 function toWhere1(ix){ var mBody ='
    \ - \ - '+ix+'\ -
    '; + \ + '+ix+'\ +
    '; $("#ptime").append(mBody); } //小时 function toHour(){ var mBody = '
    \ - \ - 小时\ -
    '; + \ + 小时\ +
    '; $("#ptime").append(mBody); } //分钟 function toMinute(){ var mBody = '
    \ - \ - 分钟\ -
    '; + \ + 分钟\ +
    '; $("#ptime").append(mBody); } From 0eed51452aa4b2f66cf6c222ee0e5cc507ce783a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 14 Nov 2024 16:51:43 +0800 Subject: [PATCH 195/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index 929e1e547..46672561d 100755 --- a/web/setting.py +++ b/web/setting.py @@ -41,7 +41,7 @@ else: from utils.firewall import Firewall as MwFirewall mw_port = str(random.randint(10000, 65530)) MwFirewall.instance().addAcceptPort(panel_port, 'PANEL端口', 'port') - mw.writeFile(default_port_file panel_port) + mw.writeFile(default_port_file, panel_port) bind = [] default_ipv6_file = panel_dir+'/data/ipv6.pl' From 8ae54e30d0257de3746c592bffdd95d9139afc2a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 20 Nov 2024 16:40:06 +0800 Subject: [PATCH 196/965] update --- web/admin/crontab/__init__.py | 5 ++-- web/admin/dashboard/dashboard.py | 3 +- web/admin/files/files.py | 6 ++-- web/admin/firewall/__init__.py | 6 ++-- web/admin/logs/__init__.py | 4 +-- web/admin/monitor/__init__.py | 7 +++-- web/admin/plugins/__init__.py | 5 ++-- web/admin/setting/setting.py | 3 +- web/admin/site/dir.py | 2 +- web/admin/site/logs.py | 2 +- web/admin/site/php.py | 2 +- web/admin/site/proxy.py | 2 +- web/admin/site/redirect.py | 2 +- web/admin/site/site.py | 9 +++++- web/admin/site/site_default.py | 2 +- web/admin/site/site_types.py | 1 - web/admin/site/ssl.py | 9 ++++-- web/admin/soft/__init__.py | 5 +++- web/thisdb/sites.py | 16 +++++++++++ web/utils/site.py | 49 ++++++++++++++++++++++++++++---- 20 files changed, 109 insertions(+), 31 deletions(-) diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index b1b76ba98..17f506fba 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -18,10 +18,11 @@ from utils.crontab import crontab as MwCrontab import core.mw as mw import thisdb -blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folder='../../templates/default') +blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folder='../../templates') @blueprint.route('/index', endpoint='index') def index(): - return render_template('crontab.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/crontab.html' % name) # 计划任务列表 @blueprint.route('/list', endpoint='list', methods=['POST']) diff --git a/web/admin/dashboard/dashboard.py b/web/admin/dashboard/dashboard.py index 21d9e7264..eb5b3d4fc 100644 --- a/web/admin/dashboard/dashboard.py +++ b/web/admin/dashboard/dashboard.py @@ -30,7 +30,8 @@ blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='.. @blueprint.route('/') @panel_login_required def index(): - return render_template('default/index.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/index.html' % name) # 安全路径 @blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) diff --git a/web/admin/files/files.py b/web/admin/files/files.py index 5f87e58a4..cfe8e83c4 100644 --- a/web/admin/files/files.py +++ b/web/admin/files/files.py @@ -21,12 +21,14 @@ from werkzeug.utils import secure_filename from admin.user_login_check import panel_login_required import core.mw as mw import utils.file as file +import thisdb -blueprint = Blueprint('files', __name__, url_prefix='/files', template_folder='../../templates/default') +blueprint = Blueprint('files', __name__, url_prefix='/files', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('files.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/files.html' % name) # 获取文件内容 @blueprint.route('/get_body', endpoint='get_file_body', methods=['POST']) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 331529267..7cb6fdd2d 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -17,12 +17,14 @@ from admin.user_login_check import panel_login_required from utils.firewall import Firewall as MwFirewall import core.mw as mw +import thisdb -blueprint = Blueprint('firewall', __name__, url_prefix='/firewall', template_folder='../../templates/default') +blueprint = Blueprint('firewall', __name__, url_prefix='/firewall', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('firewall.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/firewall.html' % name) # 防火墙列表 diff --git a/web/admin/logs/__init__.py b/web/admin/logs/__init__.py index 67c75234b..22993f2a8 100644 --- a/web/admin/logs/__init__.py +++ b/web/admin/logs/__init__.py @@ -23,8 +23,8 @@ blueprint = Blueprint('logs', __name__, url_prefix='/logs', template_folder='../ @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('default/logs.html') - + name = thisdb.getOption('template', default='default') + return render_template('%s/logs.html' % name) # 日志列表 @blueprint.route('/get_log_list', endpoint='get_log_list', methods=['POST']) diff --git a/web/admin/monitor/__init__.py b/web/admin/monitor/__init__.py index 0a540268a..29fcb5e0e 100644 --- a/web/admin/monitor/__init__.py +++ b/web/admin/monitor/__init__.py @@ -13,8 +13,11 @@ from flask import Blueprint, render_template from admin.user_login_check import panel_login_required -blueprint = Blueprint('monitor', __name__, url_prefix='/monitor', template_folder='../../templates/default') +import thisdb + +blueprint = Blueprint('monitor', __name__, url_prefix='/monitor', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('monitor.html') \ No newline at end of file + name = thisdb.getOption('template', default='default') + return render_template('%s/monitor.html' % name) \ No newline at end of file diff --git a/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py index 8092e015a..e35a9fb9b 100644 --- a/web/admin/plugins/__init__.py +++ b/web/admin/plugins/__init__.py @@ -23,11 +23,12 @@ import utils.config as utils_config import thisdb -blueprint = Blueprint('plugins', __name__, url_prefix='/plugins', template_folder='../../templates/default') +blueprint = Blueprint('plugins', __name__, url_prefix='/plugins', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('plugins.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/plugins.html' % name) # 初始化检查,首页提示选择安装 @blueprint.route('/init', endpoint='init', methods=['POST']) diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 727ef6169..6f07c3e60 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -29,7 +29,8 @@ blueprint = Blueprint('setting', __name__, url_prefix='/setting', template_folde @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('default/setting.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/setting.html' % name) @blueprint.route('/get_panel_list', endpoint='get_panel_list', methods=['POST']) @panel_login_required diff --git a/web/admin/site/dir.py b/web/admin/site/dir.py index 19c153f06..6156e71de 100644 --- a/web/admin/site/dir.py +++ b/web/admin/site/dir.py @@ -18,7 +18,7 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site + import core.mw as mw import thisdb diff --git a/web/admin/site/logs.py b/web/admin/site/logs.py index 4ae249cf5..e916022a8 100644 --- a/web/admin/site/logs.py +++ b/web/admin/site/logs.py @@ -18,7 +18,7 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site + import core.mw as mw import thisdb diff --git a/web/admin/site/php.py b/web/admin/site/php.py index 2425086c9..8934185a0 100644 --- a/web/admin/site/php.py +++ b/web/admin/site/php.py @@ -18,7 +18,7 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site + import core.mw as mw import thisdb diff --git a/web/admin/site/proxy.py b/web/admin/site/proxy.py index 704a9da4a..55e267693 100644 --- a/web/admin/site/proxy.py +++ b/web/admin/site/proxy.py @@ -18,7 +18,7 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site + import core.mw as mw import thisdb diff --git a/web/admin/site/redirect.py b/web/admin/site/redirect.py index ad6bdca19..b1b2e4b72 100644 --- a/web/admin/site/redirect.py +++ b/web/admin/site/redirect.py @@ -18,7 +18,7 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site + import core.mw as mw import thisdb diff --git a/web/admin/site/site.py b/web/admin/site/site.py index 6cbde59e0..2fe533c98 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -18,7 +18,6 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site import core.mw as mw import thisdb @@ -136,6 +135,13 @@ def get_site_php_version(): siteName = request.form.get('siteName', '') return MwSites.instance().getSitePhpVersion(siteName) +# 获取站点PHP版本 +@blueprint.route('/get_site_domains', endpoint='get_site_domains',methods=['POST']) +@panel_login_required +def get_site_domains(): + site_id = request.form.get('id', '') + data = thisdb.getSitesDomainById(site_id) + return mw.returnData(True, 'OK', data) # 设置站点PHP版本 @blueprint.route('/set_php_version', endpoint='set_php_version',methods=['POST']) @@ -287,3 +293,4 @@ def set_default_site(): return MwSites.instance().setDefaultSite(name) + diff --git a/web/admin/site/site_default.py b/web/admin/site/site_default.py index c3cc30ca3..293d128fd 100644 --- a/web/admin/site/site_default.py +++ b/web/admin/site/site_default.py @@ -17,7 +17,7 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site + import core.mw as mw import thisdb diff --git a/web/admin/site/site_types.py b/web/admin/site/site_types.py index 41e3c4396..90d360d60 100644 --- a/web/admin/site/site_types.py +++ b/web/admin/site/site_types.py @@ -18,7 +18,6 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site import core.mw as mw import thisdb diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 23d98e958..10dd57579 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -18,14 +18,13 @@ from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites -import utils.site as site import core.mw as mw import thisdb from .site import blueprint -# 删除代理配置 +# 获取证书信息 @blueprint.route('/get_ssl', endpoint='get_ssl', methods=['POST']) @panel_login_required def get_ssl(): @@ -33,6 +32,12 @@ def get_ssl(): ssl_type = request.form.get('ssl_type', '') return MwSites.instance().getSsl(site_name, ssl_type) +# 获取证书列表 +@blueprint.route('/get_cert_list', endpoint='get_cert_list', methods=['GET','POST']) +@panel_login_required +def get_cert_list(): + return MwSites.instance().getCertList() + diff --git a/web/admin/soft/__init__.py b/web/admin/soft/__init__.py index 09a417826..21d7bb903 100644 --- a/web/admin/soft/__init__.py +++ b/web/admin/soft/__init__.py @@ -13,8 +13,11 @@ from flask import Blueprint, render_template from admin.user_login_check import panel_login_required +import thisdb + blueprint = Blueprint('soft', __name__, url_prefix='/soft', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('default/soft.html') \ No newline at end of file + name = thisdb.getOption('template', default='default') + return render_template('%s/soft.html' % name) \ No newline at end of file diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index f212e1d02..8ea9f7986 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -19,6 +19,22 @@ def getSitesCount(): def getSitesById(site_id): return mw.M('sites').field(__FIELD).where("id=?", (site_id,)).find() +def getSitesByName(site_name): + return mw.M('sites').field(__FIELD).where("name=?", (site_name,)).find() + +def getSitesDomainById(site_id): + data = {} + domains = mw.M('domain').where('pid=?', (site_id,)).field('name,id').select() + binding = mw.M('binding').where('pid=?', (site_id,)).field('domain,id').select() + for b in binding: + t = {} + t['name'] = b['domain'] + t['id'] = b['id'] + domains.append(t) + data['domains'] = domains + data['email'] = mw.M('users').getField('email') + return data + def addSites(name, path): now_time = mw.getDateFromNow() insert_data = { diff --git a/web/utils/site.py b/web/utils/site.py index 8445e960d..3c860e4e4 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1318,8 +1318,8 @@ location ^~ {from} {\n\ 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() + info = thisdb.getSitesByName(site_name) + domains = mw.M('domain').where("pid=?", (info['id'],)).field('name').select() csr_path = path + '/fullchain.pem' # 生成证书路径 key_path = path + '/privkey.pem' # 密钥文件路径 @@ -1330,11 +1330,19 @@ location ^~ {from} {\n\ 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' # 密钥文件路径 + csr_path = acme_dir + '/fullchain.cer' # ACME生成证书路径 + key_path = acme_dir + '/' + site_name + '.key' # ACME密钥文件路径 + + if os.path.exists(key_path): + key = mw.readFile(key_path) + else: + key = '' + + if os.path.exists(csr_path): + csr = mw.readFile(csr_path) + else: + csr = '' - key = mw.readFile(key_path) - csr = mw.readFile(csr_path) cert_data = mw.getCertName(csr_path) data = { 'status': status, @@ -1347,6 +1355,35 @@ location ^~ {from} {\n\ } return mw.returnData(True, 'OK', data) + def getCertList(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.returnData(True, 'OK', data) + except: + return mw.returnData(True, 'OK', []) + def setPhpVersion(self, siteName, version): # nginx From 98fbaa099f0a6d933f8a3d05b13eacae1d3c3882 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 21 Nov 2024 02:54:46 +0800 Subject: [PATCH 197/965] update --- web/admin/setup/option.py | 3 + web/admin/site/__init__.py | 5 +- web/admin/site/ssl.py | 7 + web/admin/site/ssl_acme.py | 52 ++++ web/static/app/site.js | 620 +++++++++++++++++++++---------------- web/thisdb/sites.py | 4 +- web/utils/site.py | 212 ++++++++++++- 7 files changed, 628 insertions(+), 275 deletions(-) create mode 100644 web/admin/site/ssl_acme.py diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 65c7ccf7c..93a02551a 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -20,6 +20,9 @@ def init_option(): thisdb.setOption('recycle_bin', 'open') thisdb.setOption('template', 'default') + # SSL邮件地址 + thisdb.setOption('ssl_email', '') + # 后台面板是否关闭 thisdb.setOption('admin_close', 'no') diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index f7b5c7397..31844ce93 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -11,9 +11,12 @@ from .site import * from .site_types import * from .site_default import * + from .php import * from .logs import * from .dir import * from .redirect import * from .proxy import * -from .ssl import * \ No newline at end of file + +from .ssl import * +from .ssl_acme import * \ No newline at end of file diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 10dd57579..724df7b1e 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -39,6 +39,13 @@ def get_cert_list(): return MwSites.instance().getCertList() +# 获取证书配置 +@blueprint.route('/get_dnsapi', endpoint='get_dnsapi', methods=['GET','POST']) +@panel_login_required +def get_dnsapi(): + return MwSites.instance().getDnsapi() + + diff --git a/web/admin/site/ssl_acme.py b/web/admin/site/ssl_acme.py new file mode 100644 index 000000000..98aa599fc --- /dev/null +++ b/web/admin/site/ssl_acme.py @@ -0,0 +1,52 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取ACME日志 +@blueprint.route('/get_acme_logs', endpoint='get_acme_logs', methods=['POST']) +@panel_login_required +def get_acme_logs(): + log_file = mw.getPanelDir() + '/logs/acme.log' + if not os.path.exists(log_file): + mw.execShell('touch ' + log_file) + return mw.returnData(True, 'OK', log_file) + + +@blueprint.route('/create_acme', endpoint='create_acme', methods=['POST']) +@panel_login_required +def create_acme(): + site_name = request.form.get('siteName', '') + domains = request.form.get('domains', '') + force = request.form.get('force', '') + renew = request.form.get('renew', '') + email = request.form.get('email', '') + return MwSites.instance().createAcme(site_name, domains,force,renew,email) + + + + + + + diff --git a/web/static/app/site.js b/web/static/app/site.js index 798e536d1..c44577a6b 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -2037,16 +2037,222 @@ function renewSSL(type,id,siteName){ } function fileCheck(){ - + $('#dnsapi_option').css('display','none'); } function dnsCheck(){ + $('#dnsapi_option').css('display','block'); +} + +function renderDnsapiHtml(data){ + console.log(data); + layer.open({ + type: 1, + area: '500px', + title: '设置'+data['title']+'接口', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["确定","取消"], + content: "
    \ +
    \ + DNSAPI类型\ +
    \ + \ +
    \ +
    \ +
    \ + CF_Key\ +
    \ + \ +
    \ + CF_Email\ +
    \ + \ +
    \ +
    \ +
    \ +
    \ +
      \ +
    • 使用【"+data['title']+"】的API接口自动解析申请SSL
    • \ +
    \ +
    \ +
    \ +
    ", + success:function(){ + + }, + yes:function(index) { + + } + }); } -//SSL -function opSSL(type, id, siteName, callback){ +function renderDnsapi(){ + $.post('/site/get_dnsapi', {}, function(data){ + var dnsapi_option = ''; + for (var i = 0; i < data.length; i++) { + dnsapi_option+=''; + } + $('#dnsapi_option select').html(dnsapi_option); + + $('#dnsapi_option select').on('change',function(){ + var val = $(this).val(); + var index = $('#dnsapi_option option:selected').attr('index'); + if (val == 'none'){ + $('#dnsapi_option button').css('display','none'); + } else { + $('#dnsapi_option button').css('display','inline-block'); + } + renderDnsapiHtml(data[index]); + }); + + $('#dnsapi_set').on('click', function(){ + var index = $('#dnsapi_option option:selected').attr('index'); + renderDnsapiHtml(data[index]); + }); + },'json'); +} + +function opSSLAcme(type, id, siteName, callback){ + var acme = '
    \ +
    \ +
    \ + 验证方式\ +
    \ + \ + \ + \ + \ +
    \ +
    \ + \ +
    \ +
    \ + \ + \ +
    \ +
    \ +
    \ +
    \ + 邮箱\ + \ +
    \ +
    \ + 域名\ +
      \ +
      \ +
      \ + \ +
      \ +
        \ +
      • 申请之前,请确保域名已解析,如未解析会导致审核失败
      • \ +
      • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
      • \ +
      • 若您的站点使用了CDN或301重定向会导致续签失败
      • \ +
      • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
      \ + \ +
      '; + + $(".tab-con").html(acme); + + renderDnsapi(); + + $.post('/site/get_ssl','site_name='+siteName+'&ssl_type=acme', function(data){ + var rdata = data['data']; + if(rdata.csr == false){ + $.post('/site/get_site_domains','id='+id, function(rdata) { + var data = rdata['data']; + var opt=''; + for(var i=0;i'+data.domains[i].name + +''; + } + } + $("input[name='admin_email']").val(data.email); + $("#ymlist").html(opt); + $("#ymlist li input").click(function(e){ + e.stopPropagation(); + }) + $("#ymlist li").click(function(){ + var o = $(this).find("input"); + if(o.prop("checked")){ + o.prop("checked",false) + } + else{ + o.prop("checked",true); + } + }) + $(".letsApply").click(function(){ + var c = $("#ymlist input[type='checkbox']"); + var str = []; + var domains = ''; + for(var i=0; i
      \ +
      证书(PEM格式)
      \ + \ +
      \ + \ + \ +
      \ + \ +
        \ +
      • 已为您自动生成ACME免费证书
      • \ +
      • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
      • \ +
      • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
      • \ +
      '; + $(".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 = "
      \ +
      证书品牌:"+issuer[0]+"
      \ +
      到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
      \ +
      \ +
      \ +
      认证域名:"+domains+"
      \ +
      "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + },'json'); +} + +function opSSLNow(type, id, siteName, callback){ var now = '
      \ \
      \ @@ -2061,10 +2267,66 @@ function opSSL(type, id, siteName, callback){
    • 粘贴您的*.key以及*.pem内容,然后保存即可。
    • \
    • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
    • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
    • \
    • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
    • \ - '; + '; - // \ - // \ + $(".tab-con").html(now); + var key = ''; + var csr = ''; + var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); + $.post('/site/get_ssl','site_name='+siteName,function(data){ + layer.close(loadT); + var rdata = data['data']; + + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
      \ +
      证书品牌:"+issuer[0]+"
      \ +
      到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
      \ +
      \ +
      \ +
      认证域名:"+domains+"
      \ +
      强制HTTPS:\ + \ +
      \ +
      "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + + if(rdata.key == false){ + rdata.key = ''; + } else { + $(".ssl-btn").append(''); + } + + if(rdata.csr == false){ + rdata.csr = ''; + } + $("#key").val(rdata.key); + $("#csr").val(rdata.csr); + + $("#toHttps").attr('checked',rdata.httpTohttps); + if(rdata.status){ + $('.warning_info').css('display','none'); + + $(".ssl-btn").append(""); + $('#now_ssl').html('当前证书 - [已部署SSL]'); + } else{ + $('.warning_info').css('display','block'); + $('#now_ssl').html('当前证书 - [未部署SSL]'); + } + + + if (typeof (callback) != 'undefined'){ + callback(rdata); + } + },'json'); +} + +function opSSLLet(type, id, siteName, callback){ var lets = '
      \
      \
      \ @@ -2102,274 +2364,96 @@ function opSSL(type, id, siteName, callback){ \
      '; - // \ - // \ - var acme = '
      \ -
      \ -
      \ -
      \ - 验证方式\ -
      \ - \ - \ -
      \ -
      \ -
      \ -
      \ -
      \ - \ - \ -
      \ -
      \ -
      \ -
      \ - 管理员邮箱\ - \ -
      \ -
      \ - 域名\ -
        \ -
        \ -
        \ - \ -
        \ -
          \ -
        • 申请之前,请确保域名已解析,如未解析会导致审核失败
        • \ -
        • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
        • \ -
        • 若您的站点使用了CDN或301重定向会导致续签失败
        • \ -
        • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
        \ - \ -
        '; - - - switch(type){ - case 'lets': - $(".tab-con").html(lets); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name+'' - } + $(".tab-con").html(lets); + $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ + var rdata = data['data']; + if(rdata.csr == false){ + $.post('/site/get_site_domains','id='+id, function(rdata) { + var data = rdata['data']; + var opt=''; + for(var i=0;i'+data.domains[i].name+'' + } + } + $("input[name='admin_email']").val(data.email); + $("#ymlist").html(opt); + $("#ymlist li input").click(function(e){ + e.stopPropagation(); + }) + $("#ymlist li").click(function(){ + var o = $(this).find("input"); + if(o.prop("checked")){ + o.prop("checked",false) + } + else{ + o.prop("checked",true); + } + }) + $(".letsApply").click(function(){ + var c = $("#ymlist input[type='checkbox']"); + var str = []; + var domains = ''; + for(var i=0; i
        \ -
        证书(PEM格式)
        \ -
        \ -
        \ - \ - \ - \ -
        \ -
        \ -
          \ -
        • 已为您自动生成Let\'s Encrypt免费证书
        • \ -
        • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
        • \ -
        • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
        • \ -
        '; - $(".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 = "
        \ -
        证书品牌:"+issuer[0]+"
        \ -
        到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
        \ -
        \ -
        \ -
        认证域名:"+domains+"
        \ -
        "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'acme': - $(".tab-con").html(acme); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=acme', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name - +''; - } - } - $("input[name='admin_email']").val(data.email); - $("#ymlist").html(opt); - $("#ymlist li input").click(function(e){ - e.stopPropagation(); - }) - $("#ymlist li").click(function(){ - var o = $(this).find("input"); - if(o.prop("checked")){ - o.prop("checked",false) - } - else{ - o.prop("checked",true); - } - }) - $(".letsApply").click(function(){ - var c = $("#ymlist input[type='checkbox']"); - var str = []; - var domains = ''; - for(var i=0; i
        \ -
        证书(PEM格式)
        \ - \ -
        \ - \ - \ -
        \ - \ -
          \ -
        • 已为您自动生成ACME免费证书
        • \ -
        • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
        • \ -
        • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
        • \ -
        '; - $(".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 = "
        \ -
        证书品牌:"+issuer[0]+"
        \ -
        到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
        \ -
        \ -
        \ -
        认证域名:"+domains+"
        \ -
        "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'now': - $(".tab-con").html(now); - var key = ''; - var csr = ''; - var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_ssl','site_name='+siteName,function(data){ - layer.close(loadT); - var rdata = data['data']; - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
        \ -
        证书品牌:"+issuer[0]+"
        \ -
        到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
        \ -
        \ -
        \ -
        认证域名:"+domains+"
        \ -
        强制HTTPS:\ - \ -
        \ -
        "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - - if(rdata.key == false){ - rdata.key = ''; - } else { - $(".ssl-btn").append(''); - } - - if(rdata.csr == false){ - rdata.csr = ''; - } - $("#key").val(rdata.key); - $("#csr").val(rdata.csr); - - $("#toHttps").attr('checked',rdata.httpTohttps); - if(rdata.status){ - $('.warning_info').css('display','none'); - - $(".ssl-btn").append(""); - $('#now_ssl').html('当前证书 - [已部署SSL]'); - } else{ - $('.warning_info').css('display','block'); - $('#now_ssl').html('当前证书 - [未部署SSL]'); - } - + } + domains = JSON.stringify(str); + newSSL(siteName, id, domains); + }); if (typeof (callback) != 'undefined'){ callback(rdata); } },'json'); - break; - default: - layer.msg("错误类型", {icon:5}); - break; + return; + } + var lets = '
        \ + \ +
        \ +
        密钥(KEY)
        \ +
        证书(PEM格式)
        \ +
        \ +
        \ + \ + \ + \ +
        \ +
        \ +
          \ +
        • 已为您自动生成Let\'s Encrypt免费证书
        • \ +
        • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
        • \ +
        • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
        • \ +
        '; + $(".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 = "
        \ +
        证书品牌:"+issuer[0]+"
        \ +
        到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
        \ +
        \ +
        \ +
        认证域名:"+domains+"
        \ +
        "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + },'json'); +} + +//SSL +function opSSL(type, id, siteName, callback){ + switch(type){ + case 'lets':opSSLLet(type, id, siteName, callback);break; + case 'acme':opSSLAcme(type, id, siteName, callback);break; + case 'now':opSSLNow(type, id, siteName, callback);break; + default:layer.msg("错误类型", {icon:5});break; } } diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index 8ea9f7986..487b54897 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -10,6 +10,8 @@ import core.mw as mw +from .option import getOption + __FIELD = 'id,name,path,status,ps,edate,type_id,add_time,update_time' def getSitesCount(): @@ -32,7 +34,7 @@ def getSitesDomainById(site_id): t['id'] = b['id'] domains.append(t) data['domains'] = domains - data['email'] = mw.M('users').getField('email') + data['email'] = getOption('ssl_email', default='') return data def addSites(name, path): diff --git a/web/utils/site.py b/web/utils/site.py index 3c860e4e4..dd865fe7c 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -137,6 +137,18 @@ class sites(object): 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 createRootDir(self, path): autoInit = False if not os.path.exists(path): @@ -1319,7 +1331,7 @@ location ^~ {from} {\n\ to_https = self.isToHttps(site_name) info = thisdb.getSitesByName(site_name) - domains = mw.M('domain').where("pid=?", (info['id'],)).field('name').select() + domains = mw.M('domain').field('name').where("pid=?", (info['id'],)).select() csr_path = path + '/fullchain.pem' # 生成证书路径 key_path = path + '/privkey.pem' # 密钥文件路径 @@ -1333,15 +1345,13 @@ location ^~ {from} {\n\ csr_path = acme_dir + '/fullchain.cer' # ACME生成证书路径 key_path = acme_dir + '/' + site_name + '.key' # ACME密钥文件路径 + key = '' if os.path.exists(key_path): key = mw.readFile(key_path) - else: - key = '' + csr = '' if os.path.exists(csr_path): csr = mw.readFile(csr_path) - else: - csr = '' cert_data = mw.getCertName(csr_path) data = { @@ -1472,6 +1482,198 @@ location ^~ {from} {\n\ thisdb.addSiteTypes(name) return mw.returnData(True, '添加成功!') + def getDnsapi(self): + dnsapi_data = thisdb.getOptionByJson('dnsapi', default={}) + dnsapi_option = [ + {"name":"none", "title":'手动解析', 'key':''}, + {"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret'}, + {"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'}, + {"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'}, + {"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'}, + {"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'}, + {"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'}, + {"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'}, + {"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'}, + {"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'}, + {"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'}, + {"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'}, + {"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'}, + {"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'}, + {"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'}, + {"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'}, + {"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'}, + {"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'}, + {"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'}, + {"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'}, + {"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'}, + {"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'}, + {"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'}, + {"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'}, + {"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'}, + {"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'}, + {"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'}, + {"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'}, + {"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'}, + {"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'}, + {"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'}, + {"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'}, + {"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'}, + ]; + + for i in range(len(dnsapi_option)): + dval = dnsapi_option[i]['key'] + dname = dnsapi_option[i]['name'] + if dname == 'none': + continue + + keys = dval.split(':') + data = {} + if dname in dnsapi_data: + dnsapi_option[i]['data'] = dnsapi_data[dname] + dnsapi_option[i]['title'] = dnsapi_option[i]['title'] + '[已配置]' + else: + t = {} + for field in keys: + t[field] = '' + dnsapi_option[i]['data'] = t + return dnsapi_option + + def createAcme(self, site_name, domains, force, renew, input_email): + domains = json.loads(domains) + + print(site_name,domains,input_email,renew,force) + email = thisdb.getOption('ssl_email', default='') + if input_email.strip() != '': + thisdb.setOption('ssl_email', input_email) + email = input_email + + if not len(domains): + return mw.returnData(False, '请选择域名') + + file = self.getHostConf(site_name) + if os.path.exists(file): + siteConf = mw.readFile(file) + if siteConf.find('301-END') != -1: + return mw.returnData(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') + + # 检测存在反向代理 + data_path = self.getProxyDataPath(site_name) + 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, site_name) + proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' + if os.path.exists(proxy_dir_file): + return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') + + siteInfo = thisdb.getSitesByName(site_name) + path = self.getSitePath(site_name) + if path == '': + return mw.returnData(False, '【'+site_name+'】配置文件,异常!') + + srcPath = siteInfo['path'] + + # 检测acme是否安装 + acme_dir = mw.getAcmeDir() + if not os.path.exists(acme_dir): + try: + mw.execShell("curl -sS curl https://get.acme.sh | sh") + except: + pass + if not os.path.exists(acme_dir): + return mw.returnData(False, '尝试自动安装ACME失败,请通过以下命令尝试手动安装

        安装命令: curl https://get.acme.sh | sh

        ') + + # 避免频繁执行 + checkAcmeRun = mw.execShell('ps -ef|grep acme.sh |grep -v grep') + if checkAcmeRun[0] != '': + return mw.returnData(False, '正在申请或更新SSL中...') + + if force == 'true': + force_bool = True + + if renew == 'true': + execStr = acme_dir + "/acme.sh --renew --yes-I-know-dns-manual-mode-enough-go-ahead-please" + else: + execStr = acme_dir + "/acme.sh --issue --force" + + # 确定主域名顺序 + domainsTmp = [] + if site_name in domains: + domainsTmp.append(site_name) + for domainTmp in domains: + if domainTmp == site_name: + continue + domainsTmp.append(domainTmp) + domains = domainsTmp + + domainCount = 0 + for domain in domains: + if mw.checkIp(domain): + continue + if domain.find('*.') != -1: + return mw.returnData(False, '泛域名不能使用【文件验证】的方式申请证书!') + execStr += ' -w ' + path + execStr += ' -d ' + domain + domainCount += 1 + if domainCount == 0: + return mw.returnData(False, '请选择域名(不包括IP地址与泛域名)!') + + log_file = mw.getPanelDir() + '/logs/acme.log' + mw.writeFile(log_file, "开始ACME申请...\n", "wb+") + cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + execStr + ' >> ' + log_file + # print(domains) + # print(cmd) + result = mw.execShell(cmd) + + src_path = mw.getAcmeDomainDir(domains[0]) + src_cert = src_path + '/fullchain.cer' + src_key = src_path + '/' + domains[0] + '.key' + src_cert.replace("\\*", "*") + + msg = '签发失败,您尝试申请证书的失败次数已达上限!

        1、检查域名是否绑定到对应站点

        \ +

        2、检查域名是否正确解析到本服务器,或解析还未完全生效

        \ +

        3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭

        \ +

        4、如果您的站点设置了301重定向,请先将其关闭

        \ +

        5、如果以上检查都确认没有问题,请尝试更换DNS服务商

        ' + if not os.path.exists(src_cert): + data = {} + data['err'] = result + data['out'] = result[0] + data['msg'] = msg + data['result'] = {} + if result[1].find('new-authz error:') != -1: + data['result'] = json.loads( + re.search("{.+}", result[1]).group()) + if data['result']['status'] == 429: + data['msg'] = msg + data['status'] = False + return mw.getJson(data) + + dst_path = self.sslDir + '/' + site_name + dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 + dst_key = dst_path + "/privkey.pem" # 密钥文件路径 + + if not os.path.exists(dst_path): + mw.execShell("mkdir -p " + dst_path) + + mw.buildSoftLink(src_cert, dst_cert, True) + mw.buildSoftLink(src_key, dst_key, True) + mw.execShell('echo "acme" > "' + dst_path + '/README"') + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return mw.getJson(result) + result['csr'] = mw.readFile(src_cert) + result['key'] = mw.readFile(src_key) + + mw.restartWeb() + return mw.returnData(True, '证书已更新!', result) + def getPhpVersion(self): phpVersions = ('00', '52', '53', '54', '55', From 4c1e2a206d08959157f2b800a963ccf05ebac2af Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 22 Nov 2024 01:09:42 +0800 Subject: [PATCH 198/965] up --- web/admin/site/ssl.py | 10 +++- web/admin/site/ssl_acme.py | 4 +- web/static/app/site.js | 118 +++++++++++++++++++++++++------------ web/utils/site.py | 105 ++++++++++++++++++++------------- 4 files changed, 155 insertions(+), 82 deletions(-) diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 724df7b1e..15b6a129d 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -39,12 +39,20 @@ def get_cert_list(): return MwSites.instance().getCertList() -# 获取证书配置 +# 获取DNSAPI @blueprint.route('/get_dnsapi', endpoint='get_dnsapi', methods=['GET','POST']) @panel_login_required def get_dnsapi(): return MwSites.instance().getDnsapi() +# 设置DNSAPI +@blueprint.route('/set_dnsapi', endpoint='set_dnsapi', methods=['GET','POST']) +@panel_login_required +def set_dnsapi(): + type = request.form.get('type', '') + data = request.form.get('data') + return MwSites.instance().setDnsapi(type,data) + diff --git a/web/admin/site/ssl_acme.py b/web/admin/site/ssl_acme.py index 98aa599fc..81938bde1 100644 --- a/web/admin/site/ssl_acme.py +++ b/web/admin/site/ssl_acme.py @@ -42,7 +42,9 @@ def create_acme(): force = request.form.get('force', '') renew = request.form.get('renew', '') email = request.form.get('email', '') - return MwSites.instance().createAcme(site_name, domains,force,renew,email) + apply_type = request.form.get('apply_type', 'file') + dnspai = request.form.get('dnspai','') + return MwSites.instance().createAcme(site_name, domains,force,renew,apply_type,dnspai, email) diff --git a/web/static/app/site.js b/web/static/app/site.js index c44577a6b..11e432136 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -2036,16 +2036,20 @@ function renewSSL(type,id,siteName){ }); } -function fileCheck(){ - $('#dnsapi_option').css('display','none'); -} -function dnsCheck(){ - $('#dnsapi_option').css('display','block'); -} function renderDnsapiHtml(data){ - console.log(data); + + var fields = data.data; + var fields_html = ''; + + for (var d in fields) { + fields_html += ""+d+"\ +
        \ + \ +
        "; + } + layer.open({ type: 1, area: '500px', @@ -2058,20 +2062,13 @@ function renderDnsapiHtml(data){
        \ DNSAPI类型\
        \ - \ \ \
        \
        \
        \ - CF_Key\ -
        \ - \ -
        \ - CF_Email\ -
        \ - \ -
        \ + "+fields_html+"\
        \
        \
        \ @@ -2081,17 +2078,29 @@ function renderDnsapiHtml(data){
        \
        \ ", - success:function(){ - - }, - yes:function(index) { - + yes:function(index,l) { + var type_name = $('select[name="type_name"]').val(); + var data_field = {}; + for (var d in fields) { + data_field[d] = $('input[name="'+d+'"]').val(); + } + + $.post('/site/set_dnsapi', {'type':type_name,'data':JSON.stringify(data_field)}, function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){ + layer.close(index); + renderDnsapi(); + } + },{icon:rdata.status?1:2}); + },'json'); } }); } function renderDnsapi(){ + $('#dnsapi_set').css('display', 'none'); + $.post('/site/get_dnsapi', {}, function(data){ var dnsapi_option = ''; for (var i = 0; i < data.length; i++) { @@ -2099,7 +2108,6 @@ function renderDnsapi(){ } $('#dnsapi_option select').html(dnsapi_option); - $('#dnsapi_option select').on('change',function(){ var val = $(this).val(); var index = $('#dnsapi_option option:selected').attr('index'); @@ -2107,8 +2115,10 @@ function renderDnsapi(){ $('#dnsapi_option button').css('display','none'); } else { $('#dnsapi_option button').css('display','inline-block'); + if (!(data[index]['title'].indexOf('[')>0)){ + renderDnsapiHtml(data[index]); + } } - renderDnsapiHtml(data[index]); }); $('#dnsapi_set').on('click', function(){ @@ -2124,21 +2134,27 @@ function opSSLAcme(type, id, siteName, callback){
        \ 验证方式\
        \ - \ + \ \ - \ + \ \
        \
        \ \ + \
        \
        \ \ @@ -2167,6 +2183,18 @@ function opSSLAcme(type, id, siteName, callback){ $(".tab-con").html(acme); + $('input[name="apply_type"]').on('change', function(){ + var val = $(this).val(); + if (val == 'file'){ + $('#dnsapi_option').css('display','none'); + $('#wildcard_domain_block').css('display','none'); + } else { + $('#dnsapi_option').css('display','block'); + $('#wildcard_domain_block').css('display','block'); + } + }); + + renderDnsapi(); $.post('/site/get_ssl','site_name='+siteName+'&ssl_type=acme', function(data){ @@ -2346,7 +2374,7 @@ function opSSLLet(type, id, siteName, callback){
        \
        \
        \ - 管理员邮箱\ + 邮箱\ \
        \
        \ @@ -2512,19 +2540,31 @@ function newSSL(siteName, id, domains){ function newAcmeSSL(siteName, id, domains){ showSpeedWindow('正在由ACME申请...', 'site.get_acme_logs', function(layers,index){ var force = ''; - if($("#checkDomain").prop("checked")){ - force = '&force=true'; - } var email = $("input[name='admin_email']").val(); - $.post('/site/create_acme','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ + var apply_type = $('input[name="apply_type"]:checked').val(); + + var pdata = {}; + pdata['siteName'] = siteName; + pdata['domains'] = domains; + pdata['email'] = email; + pdata['apply_type'] = apply_type; + if($("#checkDomain").prop("checked")){ + pdata['force'] = 'true'; + } + + if (apply_type == 'dns'){ + pdata['dnspai'] = $('#dnsapi_option option:selected').val(); + } + + console.log(pdata); + $.post('/site/create_acme',pdata,function(rdata){ + console.log(rdata); + showMsg(rdata.msg, function(){ + layer.close(index); + if(rdata.status){ $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); + } + },{icon:rdata.status?1:2}, 3000); },'json'); }); } diff --git a/web/utils/site.py b/web/utils/site.py index dd865fe7c..54f20ed48 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1490,34 +1490,34 @@ location ^~ {from} {\n\ {"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'}, {"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'}, {"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'}, - {"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'}, - {"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'}, - {"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'}, - {"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'}, - {"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'}, - {"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'}, - {"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'}, - {"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'}, - {"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'}, - {"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'}, - {"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'}, - {"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'}, - {"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'}, - {"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'}, - {"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'}, - {"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'}, - {"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'}, - {"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'}, - {"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'}, - {"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'}, - {"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'}, - {"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'}, - {"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'}, - {"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'}, - {"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'}, - {"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'}, - {"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'}, - {"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'}, + # {"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'}, + # {"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'}, + # {"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'}, + # {"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'}, + # {"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'}, + # {"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'}, + # {"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'}, + # {"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'}, + # {"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'}, + # {"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'}, + # {"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'}, + # {"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'}, + # {"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'}, + # {"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'}, + # {"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'}, + # {"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'}, + # {"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'}, + # {"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'}, + # {"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'}, + # {"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'}, + # {"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'}, + # {"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'}, + # {"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'}, + # {"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'}, + # {"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'}, + # {"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'}, + # {"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'}, + # {"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'}, ]; for i in range(len(dnsapi_option)): @@ -1538,17 +1538,39 @@ location ^~ {from} {\n\ dnsapi_option[i]['data'] = t return dnsapi_option - def createAcme(self, site_name, domains, force, renew, input_email): + def setDnsapi(self, type, data): + dnsapi_data = thisdb.getOptionByJson('dnsapi', default={}) + dnsapi_data[type] = json.loads(data) + thisdb.setOption('dnsapi',json.dumps(dnsapi_data)) + return mw.returnData(True, '设置成功!') + + + def acmeLogFile(self): + return mw.getPanelDir() + '/logs/acme.log' + + def writeAcmeLog(self,msg): + log_file = self.acmeLogFile() + mw.writeFile(log_file, msg+"\n", "wb+") + return True + + def createAcmeMultiDomin(self): + pass + + def createAcmeFile(self): + pass + + def createAcmeDns(self): + pass + + def createAcme(self, site_name, domains,force,renew, apply_type, dnspai, email): domains = json.loads(domains) - - print(site_name,domains,input_email,renew,force) - email = thisdb.getOption('ssl_email', default='') - if input_email.strip() != '': - thisdb.setOption('ssl_email', input_email) - email = input_email - - if not len(domains): + if len(domains) < 1: return mw.returnData(False, '请选择域名') + if email.strip() != '': + thisdb.setOption('ssl_email', email) + + print(site_name, domains,force,renew,apply_type, dnspai, email) + file = self.getHostConf(site_name) if os.path.exists(file): @@ -1622,11 +1644,12 @@ location ^~ {from} {\n\ if domainCount == 0: return mw.returnData(False, '请选择域名(不包括IP地址与泛域名)!') - log_file = mw.getPanelDir() + '/logs/acme.log' - mw.writeFile(log_file, "开始ACME申请...\n", "wb+") + self.writeAcmeLog('开始ACME申请...') + log_file = self.acmeLogFile() cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + execStr + ' >> ' + log_file - # print(domains) - # print(cmd) + print(domains) + print(cmd) + return mw.returnData(False, '测试中!') result = mw.execShell(cmd) src_path = mw.getAcmeDomainDir(domains[0]) From 0862df318291725b8886ce248dd2f2de83966c73 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 22 Nov 2024 01:39:37 +0800 Subject: [PATCH 199/965] Update site.py --- web/utils/site.py | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index 54f20ed48..2fb0df438 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1556,18 +1556,8 @@ location ^~ {from} {\n\ def createAcmeMultiDomin(self): pass - def createAcmeFile(self): - pass - - def createAcmeDns(self): - pass - - def createAcme(self, site_name, domains,force,renew, apply_type, dnspai, email): - domains = json.loads(domains) - if len(domains) < 1: - return mw.returnData(False, '请选择域名') - if email.strip() != '': - thisdb.setOption('ssl_email', email) + def createAcmeFile(self, site_name, domains,force,renew, apply_type, dnspai, email): + print(site_name, domains,force,renew,apply_type, dnspai, email) @@ -1697,6 +1687,34 @@ location ^~ {from} {\n\ mw.restartWeb() return mw.returnData(True, '证书已更新!', result) + def createAcmeDns(self, site_name, domains, force, renew, dnspai): + dnsapi_option = thisdb.getOptionByJson('dnsapi', default={}) + if not dnspai in dnsapi_option: + return mw.returnData(False, dnspai+'未设置') + + dnsapi_data = dnsapi_option[dnspai] + for k in dnsapi_data: + if dnsapi_data[k] == '': + return mw.returnData(False, k+'为空!') + + + print(dnsapi_data) + print(domains) + return mw.returnData(False, '测试中!') + + def createAcme(self, site_name, domains,force,renew, apply_type, dnspai, email): + domains = json.loads(domains) + if len(domains) < 1: + return mw.returnData(False, '请选择域名') + if email.strip() != '': + thisdb.setOption('ssl_email', email) + + if apply_type == 'file': + return self.createAcmeFile(site_name, domains,force,renew, apply_type, dnspai, email) + elif apply_type == 'dns': + return self.createAcmeDns(site_name, domains, force, renew, dnspai) + return mw.returnData(False, '异常请求') + def getPhpVersion(self): phpVersions = ('00', '52', '53', '54', '55', From 4a16502269a23ffbe2e1f76cc84b5a97c5d9705b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 22 Nov 2024 21:47:33 +0800 Subject: [PATCH 200/965] up --- web/admin/site/dir.py | 9 --- web/admin/site/site.py | 19 ++++++ web/static/app/site.js | 12 ++-- web/thisdb/domain.py | 4 ++ web/thisdb/sites.py | 11 +++- web/utils/site.py | 127 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 17 deletions(-) diff --git a/web/admin/site/dir.py b/web/admin/site/dir.py index 6156e71de..5cc7d15a7 100644 --- a/web/admin/site/dir.py +++ b/web/admin/site/dir.py @@ -15,8 +15,6 @@ from flask import Blueprint, render_template from flask import request from admin.user_login_check import panel_login_required - -from utils.plugin import plugin as MwPlugin from utils.site import sites as MwSites import core.mw as mw @@ -74,10 +72,3 @@ def del_dir_bind(): return MwSites.instance().delDirBinding(binding_id) - - - - - - - diff --git a/web/admin/site/site.py b/web/admin/site/site.py index 2fe533c98..051d0f9bd 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -53,6 +53,25 @@ def add(): port = request.form.get('port', '') return MwSites.instance().add(webname, port, ps, path, version) +# 添加站点 - 域名 +@blueprint.route('/add_domain', endpoint='add_domain',methods=['POST']) +@panel_login_required +def add_domain(): + domain = request.form.get('domain', '') + site_name = request.form.get('site_name', '') + site_id = request.form.get('id', '') + return MwSites.instance().addDomain(site_id, site_name, domain) + +# 删除站点 - 域名 +@blueprint.route('/del_domain', endpoint='del_domain',methods=['POST']) +@panel_login_required +def del_domain(): + site_name = request.form.get('site_name', '') + site_id = request.form.get('id', '') + domain = request.form.get('domain', '') + port = request.form.get('port', '') + return MwSites.instance().delDomain(site_id, site_name, domain, port) + # 站点删除 @blueprint.route('/delete', endpoint='delete',methods=['POST']) @panel_login_required diff --git a/web/static/app/site.js b/web/static/app/site.js index 11e432136..50c8eeb13 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -691,7 +691,7 @@ function checkDomain() { * @param {Int} id 网站ID * @param {String} webname 主域名 */ -function domainAdd(id, webname,type) { +function domainAdd(id, webname, type) { var Domain = $("#newdomain").val().split("\n"); var domainlist = ''; @@ -706,7 +706,7 @@ function domainAdd(id, webname,type) { domainlist = domainlist.substring(0,domainlist.length-1); var loadT = layer.load(); - var data = "domain=" + domainlist + "&webname=" + webname + "&id=" + id; + var data = "domain=" + domainlist + "&site_name=" + webname + "&id=" + id; $.post('/site/add_domain', data, function(retuls) { layer.close(loadT); domainEdit(id, webname, retuls.msg, retuls.status); @@ -726,10 +726,9 @@ function delDomain(wid, wname, domain, port,type) { layer.msg(lan.site.domain_last_cannot); } layer.confirm(lan.site.domain_del_confirm,{icon:3,closeBtn:2}, function(index) { - var url = "/site/del_domain" - var data = "id=" + wid + "&webname=" + wname + "&domain=" + domain + "&port=" + port; - var loadT = layer.msg(lan.public.the_del,{time:0,icon:16}); - $.post(url,data, function(ret) { + var data = "id=" + wid + "&site_name=" + wname + "&domain=" + domain + "&port=" + port; + var loadT = layer.msg('您真的要从站点中删除这个域名吗?',{time:0,icon:16}); + $.post('/site/del_domain', data, function(ret) { layer.close(loadT); layer.msg(ret.msg,{icon:ret.status?1:2}) if(type == 1){ @@ -2039,7 +2038,6 @@ function renewSSL(type,id,siteName){ function renderDnsapiHtml(data){ - var fields = data.data; var fields_html = ''; diff --git a/web/thisdb/domain.py b/web/thisdb/domain.py index bdf3e47d2..e78995cfb 100644 --- a/web/thisdb/domain.py +++ b/web/thisdb/domain.py @@ -16,6 +16,10 @@ def getDomainCountByName(name): # .debug(True) return mw.M('domain').where("name=?", (name,)).count() +def getDomainCountBySiteId(site_id): + # .debug(True) + return mw.M('domain').where("pid=?", (site_id,)).count() + def addDomain(pid, name, port): now_time = mw.getDateFromNow() insert_data = { diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index 487b54897..20c316012 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -14,10 +14,19 @@ from .option import getOption __FIELD = 'id,name,path,status,ps,edate,type_id,add_time,update_time' +def checkSitesDomainIsExist(domain, port): + nums = mw.M('domain').where("name=? AND port=?", (domain, port,)).count() + if nums>0: + return True + + nums = mw.M('binding').where("name=? AND port=?", (domain, port,)).count() + if nums>0: + return True + return False + def getSitesCount(): return mw.M('sites').count() - def getSitesById(site_id): return mw.M('sites').field(__FIELD).where("id=?", (site_id,)).find() diff --git a/web/utils/site.py b/web/utils/site.py index 2fb0df438..501a28f66 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -62,6 +62,25 @@ class sites(object): if not os.path.exists(self.sslLetsDir): mw.execShell("mkdir -p " + self.sslLetsDir +" && chmod -R 755 " + self.sslLetsDir) + + def runHook(self, hook_name, func_name): + # 站点操作Hook + hook_cfg = thisdb.getOptionByJson('hook_site_cb',type='hook',default=[]) + hook_num = len(hook_cfg) + if hook_num == 0: + return + + from utils.plugin import plugin as MwPlugin + pa = MwPlugin.instance() + + for x in range(hook_num): + hook_data = hook_cfg[x] + if func_name in hook_data: + app_name = hook_data["name"] + run_func = hook_data[func_name]['func'] + pa.run(app_name, run_func) + return True + # 域名编码转换 def toPunycode(self, domain): if sys.version_info[0] == 2: @@ -198,6 +217,99 @@ class sites(object): # return mw.returnData(False, '开发中!') return mw.returnData(True, '添加成功') + def nginxAddDomain(self, site_name, domain, port): + file = self.getHostConf(site_name) + 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 addDomain(self, site_id, site_name, domain): + isError = mw.checkWebConfig() + if isError != True: + msg = 'ERROR: 检测到配置文件有错误,请先排除后再操作

        ' + isError.replace("\n", '
        ') + '
        ' + return mw.returnData(False, msg) + + domains = domain.split(',') + for d in domains: + if d == '': + continue + d = d.split(':') + name = self.toPunycode(d[0]) + port = '80' + if len(d) == 2: + port = d[1] + + if not mw.checkPort(port): + return mw.returnData(False, '端口范围不合法!') + + reg = r"^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" + if not re.match(reg, name): + return mw.returnData(False, '域名格式不正确!') + + if thisdb.checkSitesDomainIsExist(name, port): + return mw.returnData(False, '您添加的域名[{}:{}],已使用。请仔细检查!'.format(name, port)) + + self.nginxAddDomain(site_name, name, port) + + msg = mw.getInfo('网站[{1}]添加域名[{2}]成功!', (site_name, name)) + mw.writeLog('网站管理', msg) + thisdb.addDomain(site_id, name, port) + + mw.restartWeb() + self.runHook('site_cb', 'add') + return mw.returnData(True, '域名添加成功!') + + def delDomain(self, site_id, site_name, domain, port): + domain_nums = thisdb.getDomainCountBySiteId(site_id) + if domain_nums == 1: + return mw.returnData(False, '最后一个域名不能删除!') + + info = mw.M('domain').field('id,name').where("pid=? AND name=? AND port=?",(site_id, domain, port)).find() + + file = self.getHostConf(site_name) + 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_nums = mw.M('domain').where('pid=? AND port=?', (site_id, port)).count() + if mw.inArray(tmp, port) == True and port_nums < 2: + rep = r"\n*\s+listen\s+" + port + ";" + conf = re.sub(rep, '', conf) + # 保存配置 + mw.writeFile(file, conf) + + thisdb.deleteDomainId(info['id']) + msg = mw.getInfo('网站[{1}]删除域名[{2}:{3}]成功!', (site_name, domain, port)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, '站点删除成功!') def deleteALlLogs(self, webname): assLogPath = mw.getLogsDir() + '/' + webname + '.log' @@ -1687,6 +1799,15 @@ location ^~ {from} {\n\ mw.restartWeb() return mw.returnData(True, '证书已更新!', result) + def getDnsapiExportVar(self, data): + def_var = '' + for k in data: + def_var += 'export '+k+'="'+data[k]+'"\n' + return def_var + + def getDomainRootName(self, domain): + pass + def createAcmeDns(self, site_name, domains, force, renew, dnspai): dnsapi_option = thisdb.getOptionByJson('dnsapi', default={}) if not dnspai in dnsapi_option: @@ -1697,9 +1818,15 @@ location ^~ {from} {\n\ if dnsapi_data[k] == '': return mw.returnData(False, k+'为空!') + cmd = self.getDnsapiExportVar(dnsapi_data) + + for d in domains: + print(d) + cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+d+' -d "*.'+d+'" --force' print(dnsapi_data) print(domains) + print(cmd) return mw.returnData(False, '测试中!') def createAcme(self, site_name, domains,force,renew, apply_type, dnspai, email): From ed375271d2481d0004fb3ccd4823466822efefc5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 23 Nov 2024 17:42:03 +0800 Subject: [PATCH 201/965] Update site.py --- web/utils/site.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index 501a28f66..3cfa99822 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1597,11 +1597,12 @@ location ^~ {from} {\n\ def getDnsapi(self): dnsapi_data = thisdb.getOptionByJson('dnsapi', default={}) dnsapi_option = [ - {"name":"none", "title":'手动解析', 'key':''}, - {"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret'}, - {"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'}, - {"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'}, - {"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'}, + {"name":"none", "title":'手动解析', 'key':'', 'help':''}, + {"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret', 'help':'阿里云控制台》用户头像》accesskeys按指引获取AccessKey/SecretKey'}, + {"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email', 'help':'CloudFlare后台获取Global API Key'}, + {"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key','help':'DnsPod后台》用户中心》安全设置,开启API Token'}, + {"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key','help':'DnsPod后台》用户中心》安全设置,开启API Token'}, + {"name":"dns_tencent", "title":"腾讯云DNS", 'key':'Tencent_SecretId:Tencent_SecretKey', 'help':'腾讯云后台获取通行证'}, # {"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'}, # {"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'}, # {"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'}, @@ -1806,9 +1807,13 @@ location ^~ {from} {\n\ return def_var def getDomainRootName(self, domain): - pass + s = domain.split('.') + count = len(s) + last_index = count - 1 + top_domain = s[last_index-1]+'.'+s[last_index] + return top_domain - def createAcmeDns(self, site_name, domains, force, renew, dnspai): + def createAcmeDns(self, site_name, domains, dnspai, force, renew): dnsapi_option = thisdb.getOptionByJson('dnsapi', default={}) if not dnspai in dnsapi_option: return mw.returnData(False, dnspai+'未设置') @@ -1821,8 +1826,8 @@ location ^~ {from} {\n\ cmd = self.getDnsapiExportVar(dnsapi_data) for d in domains: - print(d) - cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+d+' -d "*.'+d+'" --force' + top_domain = self.getDomainRootName(d) + cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+top_domain+' -d "*.'+top_domain+'" --force' print(dnsapi_data) print(domains) @@ -1839,7 +1844,7 @@ location ^~ {from} {\n\ if apply_type == 'file': return self.createAcmeFile(site_name, domains,force,renew, apply_type, dnspai, email) elif apply_type == 'dns': - return self.createAcmeDns(site_name, domains, force, renew, dnspai) + return self.createAcmeDns(site_name, domains, dnspai,force, renew) return mw.returnData(False, '异常请求') From 5b2e40404dcde570569077c55d4b3fabe8227ef3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 23 Nov 2024 23:15:25 +0800 Subject: [PATCH 202/965] update --- web/admin/site/ssl_acme.py | 3 +- web/admin/site/ssl_let.py | 55 +++++++ web/core/mw.py | 178 +++++++++++++++++++++- web/static/app/site.js | 246 +++++++++++++++++------------- web/utils/site.py | 297 +++++++++++++++++++++++++++++++++---- 5 files changed, 640 insertions(+), 139 deletions(-) create mode 100644 web/admin/site/ssl_let.py diff --git a/web/admin/site/ssl_acme.py b/web/admin/site/ssl_acme.py index 81938bde1..fea23b068 100644 --- a/web/admin/site/ssl_acme.py +++ b/web/admin/site/ssl_acme.py @@ -42,9 +42,10 @@ def create_acme(): force = request.form.get('force', '') renew = request.form.get('renew', '') email = request.form.get('email', '') + wildcard_domain = request.form.get('wildcard_domain','') apply_type = request.form.get('apply_type', 'file') dnspai = request.form.get('dnspai','') - return MwSites.instance().createAcme(site_name, domains,force,renew,apply_type,dnspai, email) + return MwSites.instance().createAcme(site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain) diff --git a/web/admin/site/ssl_let.py b/web/admin/site/ssl_let.py new file mode 100644 index 000000000..dda93e6fa --- /dev/null +++ b/web/admin/site/ssl_let.py @@ -0,0 +1,55 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取ACME日志 +@blueprint.route('/get_let_logs', endpoint='get_let_logs', methods=['POST']) +@panel_login_required +def get_let_logs(): + log_file = MwSites.instance().letLogFile() + if not os.path.exists(log_file): + mw.execShell('touch ' + log_file) + return mw.returnData(True, 'OK', log_file) + + +@blueprint.route('/create_let', endpoint='create_let', methods=['POST']) +@panel_login_required +def create_let(): + site_name = request.form.get('siteName', '') + domains = request.form.get('domains', '') + force = request.form.get('force', '') + renew = request.form.get('renew', '') + email = request.form.get('email', '') + wildcard_domain = request.form.get('wildcard_domain','') + apply_type = request.form.get('apply_type', 'file') + dnspai = request.form.get('dnspai','') + return MwSites.instance().createLet(site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain) + + + + + + + diff --git a/web/core/mw.py b/web/core/mw.py index 0d7a8f6a2..fc9efa08d 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -836,6 +836,166 @@ def deDoubleCrypt(key, strings): writeFileLog(getTracebackInfo()) return strings +def aesEncrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): + # aes加密 + # @param data 被加密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from cryptography.hazmat.primitives import padding + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + + if not isinstance(data, bytes): + data = data.encode() + + # AES_CBC_KEY = os.urandom(32) + # AES_CBC_IV = os.urandom(16) + + AES_CBC_KEY = key.encode() + AES_CBC_IV = vi.encode() + + # print("AES_CBC_KEY:", AES_CBC_KEY) + # print("AES_CBC_IV:", AES_CBC_IV) + + padder = padding.PKCS7(algorithms.AES.block_size).padder() + padded_data = padder.update(data) + padder.finalize() + + cipher = Cipher(algorithms.AES(AES_CBC_KEY), + modes.CBC(AES_CBC_IV), + backend=default_backend()) + encryptor = cipher.encryptor() + + edata = encryptor.update(padded_data) + + # print(edata) + # print(str(edata)) + # print(edata.decode()) + return edata + + +def aesDecrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): + # aes加密 + # @param data 被解密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from cryptography.hazmat.primitives import padding + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + + from cryptography.hazmat.primitives import padding + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + + if not isinstance(data, bytes): + data = data.encode() + + AES_CBC_KEY = key.encode() + AES_CBC_IV = vi.encode() + + cipher = Cipher(algorithms.AES(AES_CBC_KEY), + modes.CBC(AES_CBC_IV), + backend=default_backend()) + decryptor = cipher.decryptor() + + ddata = decryptor.update(data) + + unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() + data = unpadder.update(ddata) + + try: + uppadded_data = data + unpadder.finalize() + except ValueError: + raise Exception('无效的加密信息!') + + return uppadded_data + +def aesEncrypt_Crypto(data, key, vi): + # 该方法保留,暂时不使用 + # aes加密 + # @param data 被加密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from Crypto.Cipher import AES + cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) + # 判断是否含有中文 + zhmodel = re.compile(u'[\u4e00-\u9fff]') + match = zhmodel.search(data) + if match == None: + # 无中文时 + add = 16 - len(data) % 16 + pad = lambda s: s + add * chr(add) + data = pad(data) + enctext = cryptor.encrypt(data.encode('utf8')) + else: + # 含有中文时 + data = data.encode() + add = 16 - len(data) % 16 + data = data + add * (chr(add)).encode() + enctext = cryptor.encrypt(data) + encodestrs = base64.b64encode(enctext).decode('utf8') + return encodestrs + + +def aesDecrypt_Crypto(data, key, vi): + # 该方法保留,暂时不使用 + # aes加密 + # @param data 被加密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from crypto.Cipher import AES + data = data.encode('utf8') + encodebytes = base64.urlsafe_b64decode(data) + cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) + text_decrypted = cipher.decrypt(encodebytes) + # 判断是否含有中文 + zhmodel = re.compile(u'[\u4e00-\u9fff]') + match = zhmodel.search(text_decrypted) + if match == False: + # 无中文时补位 + unpad = lambda s: s[0:-s[-1]] + text_decrypted = unpad(text_decrypted) + text_decrypted = text_decrypted.decode('utf8').rstrip() # 去掉补位的右侧空格 + return text_decrypted + +def getDefault(data,val,def_val=''): + if val in data: + return data[val] + return def_val + +def encodeImage(imgsrc, newsrc): + # 图片加密 + import struct + old_fp = open(imgsrc, 'rb') + imgFile = old_fp.read() + old_fp.close() + + new_fp = open(newsrc,"wb") + for x in imgFile: + value = x ^ 86 + value = hex(value) + s = struct.pack('B',int(value,16)) + new_fp.write(s) + new_fp.close() + return True + +def buildSoftLink(src, dst, force=False): + ''' + 建立软连接 + ''' + if not os.path.exists(src): + return False + + if os.path.exists(dst) and force: + os.remove(dst) + + if not os.path.exists(dst): + execShell('ln -sf "' + src + '" "' + dst + '"') + return True + return False # ------------------------------ network start ----------------------------- def HttpGet(url, timeout=10): @@ -1009,6 +1169,14 @@ def panelCmd(method): # ------------------------------ openresty start ----------------------------- +def getOpVer(): + version = '' + version_file_pl = getServerDir() + '/openresty/version.pl' + if os.path.exists(version_file_pl): + version = readFile(version_file_pl) + version = version.strip() + return version + def checkWebConfig(): op_dir = getServerDir() + '/openresty/nginx' # "ulimit -n 10240 && " + @@ -1179,6 +1347,10 @@ def getMyORM(): # --------------------------------------------------------------------------------- ##################### ssl start ######################################### + +def strfDate(sdate): + return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) + # 获取证书名称 def getCertName(certPath): if not os.path.exists(certPath): @@ -1205,11 +1377,9 @@ def getCertName(certPath): if hasattr(issuer, 'O'): result['issuer'] = issuer.O # 取到期时间 - result['notAfter'] = strfDate( - bytes.decode(x509.get_notAfter())[:-1]) + result['notAfter'] = strfDate(bytes.decode(x509.get_notAfter())[:-1]) # 取申请时间 - result['notBefore'] = strfDate( - bytes.decode(x509.get_notBefore())[:-1]) + result['notBefore'] = strfDate(bytes.decode(x509.get_notBefore())[:-1]) # 取可选名称 result['dns'] = [] for i in range(x509.get_extension_count()): diff --git a/web/static/app/site.js b/web/static/app/site.js index 50c8eeb13..30abea07c 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -2119,13 +2119,87 @@ function renderDnsapi(){ } }); - $('#dnsapi_set').on('click', function(){ + $('#dnsapi_set').unbind('click').on('click', function(){ var index = $('#dnsapi_option option:selected').attr('index'); renderDnsapiHtml(data[index]); }); },'json'); } +function opSSLNow(type, id, siteName, callback){ + var now = '
        \ + \ +
        \ +
        密钥(KEY)
        \ +
        证书(PEM格式)
        \ +
        \ +
        \ + \ +
        \ +
        \ +
          \ +
        • 粘贴您的*.key以及*.pem内容,然后保存即可。
        • \ +
        • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
        • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
        • \ +
        • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
        • \ +
        '; + + $(".tab-con").html(now); + var key = ''; + var csr = ''; + var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); + $.post('/site/get_ssl','site_name='+siteName,function(data){ + layer.close(loadT); + var rdata = data['data']; + + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
        \ +
        证书品牌:"+issuer[0]+"
        \ +
        到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
        \ +
        \ +
        \ +
        认证域名:"+domains+"
        \ +
        强制HTTPS:\ + \ +
        \ +
        "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + + if(rdata.key == false){ + rdata.key = ''; + } else { + $(".ssl-btn").append(''); + } + + if(rdata.csr == false){ + rdata.csr = ''; + } + $("#key").val(rdata.key); + $("#csr").val(rdata.csr); + + $("#toHttps").attr('checked',rdata.httpTohttps); + if(rdata.status){ + $('.warning_info').css('display','none'); + + $(".ssl-btn").append(""); + $('#now_ssl').html('当前证书 - [已部署SSL]'); + } else{ + $('.warning_info').css('display','block'); + $('#now_ssl').html('当前证书 - [未部署SSL]'); + } + + + if (typeof (callback) != 'undefined'){ + callback(rdata); + } + },'json'); +} + function opSSLAcme(type, id, siteName, callback){ var acme = '
        \
        \ @@ -2192,7 +2266,6 @@ function opSSLAcme(type, id, siteName, callback){ } }); - renderDnsapi(); $.post('/site/get_ssl','site_name='+siteName+'&ssl_type=acme', function(data){ @@ -2278,92 +2351,33 @@ function opSSLAcme(type, id, siteName, callback){ },'json'); } -function opSSLNow(type, id, siteName, callback){ - var now = '
        \ - \ -
        \ -
        密钥(KEY)
        \ -
        证书(PEM格式)
        \ -
        \ -
        \ - \ -
        \ -
        \ -
          \ -
        • 粘贴您的*.key以及*.pem内容,然后保存即可。
        • \ -
        • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
        • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
        • \ -
        • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
        • \ -
        '; - - $(".tab-con").html(now); - var key = ''; - var csr = ''; - var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_ssl','site_name='+siteName,function(data){ - layer.close(loadT); - var rdata = data['data']; - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
        \ -
        证书品牌:"+issuer[0]+"
        \ -
        到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
        \ -
        \ -
        \ -
        认证域名:"+domains+"
        \ -
        强制HTTPS:\ - \ -
        \ -
        "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - - if(rdata.key == false){ - rdata.key = ''; - } else { - $(".ssl-btn").append(''); - } - - if(rdata.csr == false){ - rdata.csr = ''; - } - $("#key").val(rdata.key); - $("#csr").val(rdata.csr); - - $("#toHttps").attr('checked',rdata.httpTohttps); - if(rdata.status){ - $('.warning_info').css('display','none'); - - $(".ssl-btn").append(""); - $('#now_ssl').html('当前证书 - [已部署SSL]'); - } else{ - $('.warning_info').css('display','block'); - $('#now_ssl').html('当前证书 - [未部署SSL]'); - } - - - if (typeof (callback) != 'undefined'){ - callback(rdata); - } - },'json'); -} - function opSSLLet(type, id, siteName, callback){ var lets = '
        \
        \
        \ -
        \ - 验证方式\ -
        \ - \ - \ -
        \ -
        \ + 验证方式\ +
        \ + \ + \ + \ + \ +
        \
        \ + \ + \
        \
        \ \ @@ -2391,6 +2405,20 @@ function opSSLLet(type, id, siteName, callback){
        '; $(".tab-con").html(lets); + + $('input[name="apply_type"]').on('change', function(){ + var val = $(this).val(); + if (val == 'file'){ + $('#dnsapi_option').css('display','none'); + $('#wildcard_domain_block').css('display','none'); + } else { + $('#dnsapi_option').css('display','block'); + $('#wildcard_domain_block').css('display','block'); + } + }); + + renderDnsapi(); + $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ var rdata = data['data']; if(rdata.csr == false){ @@ -2517,46 +2545,58 @@ function ocSSL(action,siteName){ //生成SSL function newSSL(siteName, id, domains){ showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){ - var force = ''; - if ($("#checkDomain").prop("checked")){ - force = '&force=true'; + var pdata = {}; + pdata['siteName'] = siteName; + pdata['domains'] = domains; + pdata['email'] = $("input[name='admin_email']").val(); + + if($("#checkDomain").prop("checked")){ + pdata['force'] = 'true'; } - var email = $("input[name='admin_email']").val(); - $.post('/site/create_let','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ + + if($("#wildcard_domain").prop("checked")){ + pdata['wildcard_domain'] = 'true'; + } + + var apply_type = $('input[name="apply_type"]:checked').val(); + pdata['apply_type'] = apply_type; + if (apply_type == 'dns'){ + pdata['dnspai'] = $('#dnsapi_option option:selected').val(); + } + + $.post('/site/create_let',pdata,function(rdata){ + showMsg(rdata.msg, function(){ + layer.close(index); + if(rdata.status){ $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); + } + },{icon:rdata.status?1:2}, 3000); },'json'); }); } function newAcmeSSL(siteName, id, domains){ showSpeedWindow('正在由ACME申请...', 'site.get_acme_logs', function(layers,index){ - var force = ''; - var email = $("input[name='admin_email']").val(); - var apply_type = $('input[name="apply_type"]:checked').val(); - var pdata = {}; pdata['siteName'] = siteName; pdata['domains'] = domains; - pdata['email'] = email; - pdata['apply_type'] = apply_type; + pdata['email'] = $("input[name='admin_email']").val(); + if($("#checkDomain").prop("checked")){ pdata['force'] = 'true'; } + if($("#wildcard_domain").prop("checked")){ + pdata['wildcard_domain'] = 'true'; + } + + var apply_type = $('input[name="apply_type"]:checked').val(); + pdata['apply_type'] = apply_type; if (apply_type == 'dns'){ pdata['dnspai'] = $('#dnsapi_option option:selected').val(); } - console.log(pdata); $.post('/site/create_acme',pdata,function(rdata){ - console.log(rdata); showMsg(rdata.msg, function(){ layer.close(index); if(rdata.status){ diff --git a/web/utils/site.py b/web/utils/site.py index 3cfa99822..cf88d95ef 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -419,6 +419,74 @@ class sites(object): mw.writeLog('网站管理', '设置成功,站点【{1}】到期【{2}】后将自动停止!', (info['name'], end_date,)) return mw.returnData(True, '设置成功,站点到期后将自动停止!') + # ssl相关方法 start + def setSslConf(self, site_name): + file = self.getHostConf(site_name) + conf = mw.readFile(file) + + version = mw.getOpVer() + + keyPath = self.sslDir + '/' + site_name + '/privkey.pem' + certPath = self.sslDir + '/' + site_name + '/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成功!', (site_name,)) + mw.writeLog('网站管理', msg) + + mw.restartWeb() + return mw.returnData(True, 'SSL开启成功!') + # 设置网站备注 def setPs(self, site_id, ps): if thisdb.setSitesData(site_id, ps=ps): @@ -1466,6 +1534,7 @@ location ^~ {from} {\n\ csr = mw.readFile(csr_path) cert_data = mw.getCertName(csr_path) + # print(csr_path,cert_data) data = { 'status': status, 'domain': domains, @@ -1477,6 +1546,21 @@ location ^~ {from} {\n\ } return mw.returnData(True, 'OK', data) + 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, '证书保存失败!') + def getCertList(self): try: vpath = self.sslDir @@ -1657,7 +1741,6 @@ location ^~ {from} {\n\ thisdb.setOption('dnsapi',json.dumps(dnsapi_data)) return mw.returnData(True, '设置成功!') - def acmeLogFile(self): return mw.getPanelDir() + '/logs/acme.log' @@ -1666,13 +1749,20 @@ location ^~ {from} {\n\ mw.writeFile(log_file, msg+"\n", "wb+") return True + def letLogFile(self): + return mw.getPanelDir() + '/logs/letsencrypt.log' + + def writeLetLog(self,msg): + log_file = self.letLogFile() + mw.writeFile(log_file, msg+"\n", "wb+") + return True + def createAcmeMultiDomin(self): pass - def createAcmeFile(self, site_name, domains,force,renew, apply_type, dnspai, email): - + def createAcmeFile(self, site_name, domains, email, force, renew): - print(site_name, domains,force,renew,apply_type, dnspai, email) + print(site_name, domains,force,renew, email) file = self.getHostConf(site_name) @@ -1702,20 +1792,7 @@ location ^~ {from} {\n\ srcPath = siteInfo['path'] - # 检测acme是否安装 - acme_dir = mw.getAcmeDir() - if not os.path.exists(acme_dir): - try: - mw.execShell("curl -sS curl https://get.acme.sh | sh") - except: - pass - if not os.path.exists(acme_dir): - return mw.returnData(False, '尝试自动安装ACME失败,请通过以下命令尝试手动安装

        安装命令: curl https://get.acme.sh | sh

        ') - - # 避免频繁执行 - checkAcmeRun = mw.execShell('ps -ef|grep acme.sh |grep -v grep') - if checkAcmeRun[0] != '': - return mw.returnData(False, '正在申请或更新SSL中...') + if force == 'true': force_bool = True @@ -1813,7 +1890,7 @@ location ^~ {from} {\n\ top_domain = s[last_index-1]+'.'+s[last_index] return top_domain - def createAcmeDns(self, site_name, domains, dnspai, force, renew): + def createAcmeDns(self, site_name, domains, dnspai, wildcard_domain, force, renew): dnsapi_option = thisdb.getOptionByJson('dnsapi', default={}) if not dnspai in dnsapi_option: return mw.returnData(False, dnspai+'未设置') @@ -1823,29 +1900,187 @@ location ^~ {from} {\n\ if dnsapi_data[k] == '': return mw.returnData(False, k+'为空!') - cmd = self.getDnsapiExportVar(dnsapi_data) - + acme_dir = mw.getAcmeDir() for d in domains: - top_domain = self.getDomainRootName(d) - cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+top_domain+' -d "*.'+top_domain+'" --force' + cmd = ''' +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin:%s +export PATH +''' % (acme_dir,) - print(dnsapi_data) - print(domains) - print(cmd) - return mw.returnData(False, '测试中!') + cmd += self.getDnsapiExportVar(dnsapi_data) + if wildcard_domain == 'true': + top_domain = self.getDomainRootName(d) + cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+top_domain+' -d "*.'+top_domain+'"' + d = top_domain + else: + cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+d - def createAcme(self, site_name, domains,force,renew, apply_type, dnspai, email): + log_file = self.acmeLogFile() + cmd += ' >> ' + log_file + print(cmd) + result = mw.execShell(cmd) + print(result) + + # acme源的ssl证书 + src_path = mw.getAcmeDomainDir(d) + src_cert = src_path + '/fullchain.cer' + src_key = src_path + '/' + d + '.key' + src_cert.replace("\\*", "*") + + msg = '签发失败,您尝试申请证书的失败次数已达上限!\ +

        1、检查域名是否正确解析到本服务器,或解析还未完全生效

        \ +

        2、如果以上检查都确认没有问题,请尝试更换DNS服务商

        ' + if not os.path.exists(src_cert): + data = {} + data['err'] = result + data['out'] = result[0] + data['msg'] = msg + data['result'] = {} + if result[1].find('new-authz error:') != -1: + data['result'] = json.loads(re.search("{.+}", result[1]).group()) + if data['result']['status'] == 429: + data['msg'] = msg + data['status'] = False + return data + + # acme源建立软链接(目标) + dst_path = self.sslDir + '/' + site_name + dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 + dst_key = dst_path + "/privkey.pem" # 密钥文件路径 + + if not os.path.exists(dst_path): + mw.execShell("mkdir -p " + dst_path) + + mw.buildSoftLink(src_cert, dst_cert, True) + mw.buildSoftLink(src_key, dst_key, True) + mw.execShell('echo "acme" > "' + dst_path + '/README"') + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return result + result['csr'] = mw.readFile(src_cert) + result['key'] = mw.readFile(src_key) + + mw.restartWeb() + return mw.returnData(True, '证书已更新!', result) + + def createAcme(self, site_name, domains,force,renew, apply_type, dnspai, email, wildcard_domain): domains = json.loads(domains) if len(domains) < 1: return mw.returnData(False, '请选择域名') if email.strip() != '': thisdb.setOption('ssl_email', email) + # 检测acme是否安装 + acme_dir = mw.getAcmeDir() + if not os.path.exists(acme_dir): + try: + mw.execShell("curl -sS curl https://get.acme.sh | sh") + except: + pass + if not os.path.exists(acme_dir): + return mw.returnData(False, '尝试自动安装ACME失败,请通过以下命令尝试手动安装

        安装命令: curl https://get.acme.sh | sh

        ') + + # 避免频繁执行 + checkAcmeRun = mw.execShell('ps -ef|grep acme.sh |grep -v grep') + if checkAcmeRun[0] != '': + return mw.returnData(False, '正在申请或更新SSL中...') + if apply_type == 'file': - return self.createAcmeFile(site_name, domains,force,renew, apply_type, dnspai, email) + return self.createAcmeFile(site_name, domains, email,force,renew) elif apply_type == 'dns': - return self.createAcmeDns(site_name, domains, dnspai,force, renew) - return mw.returnData(False, '异常请求') + return self.createAcmeDns(site_name, domains, dnspai, wildcard_domain,force, renew) + return mw.returnData(False, '异常请求') + + def createLet(self, site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain): + 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 + '

        响应状态:' + str(emsg['status']) + '

        错误类型:' + emsg[ + 'type'] + '

        错误代码:' + emsg['detail'] + '

        ' + return mw.returnJson(data['status'], msg, data['msg']) + + mw.execShell('mkdir -p ' + dst_letpath) + mw.buildSoftLink(src_csrpath, dst_csrpath, True) + mw.buildSoftLink(src_keypath, dst_keypath, True) + mw.execShell('echo "lets" > "' + dst_letpath + '/README"') + + # 写入配置文件 + result = self.setSslConf(siteName) + if not result['status']: + return mw.getJson(result) + + result['csr'] = mw.readFile(src_csrpath) + result['key'] = mw.readFile(src_keypath) + + mw.restartWeb() + return mw.returnData(data['status'], data['msg'], result) def getPhpVersion(self): From f510d23c9266873401f9bfc290059b507e893568 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 23 Nov 2024 23:18:03 +0800 Subject: [PATCH 203/965] Update site.py --- web/utils/site.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index cf88d95ef..2b81081ab 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1995,29 +1995,21 @@ export PATH return mw.returnData(False, '异常请求') def createLet(self, site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain): - 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 len(domains) < 1: + return mw.returnData(False, '请选择域名') + if email.strip() != '': + thisdb.setOption('ssl_email', email) - if not len(domains): - return mw.returnJson(False, '请选择域名') - host_conf_file = self.getHostConf(siteName) + host_conf_file = self.getHostConf(site_name) 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_path = self.getProxyDataPath(site_name) data_content = mw.readFile(data_path) if data_content != False: try: @@ -2025,14 +2017,14 @@ export PATH except: pass for proxy in data: - proxy_dir = "{}/{}".format(self.proxyPath, siteName) + proxy_dir = "{}/{}".format(self.proxyPath, site_name) 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) + auth_to = self.getSitePath(site_name) rep = r"\s*root\s*(.+);" replace_root = "\n\troot " + auth_to + ";" siteConf = re.sub(rep, replace_root, siteConf) @@ -2045,11 +2037,11 @@ export PATH 'auth_to': auth_to, } - src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName + src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + site_name src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径 src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径 - dst_letpath = self.sslDir + '/' + siteName + dst_letpath = self.sslDir + '/' + site_name dst_csrpath = dst_letpath + '/fullchain.pem' dst_keypath = dst_letpath + '/privkey.pem' @@ -2064,7 +2056,7 @@ export PATH emsg = data['msg'][1]['challenges'][0]['error'] msg = msg + '

        响应状态:' + str(emsg['status']) + '

        错误类型:' + emsg[ 'type'] + '

        错误代码:' + emsg['detail'] + '

        ' - return mw.returnJson(data['status'], msg, data['msg']) + return mw.returnData(data['status'], msg, data['msg']) mw.execShell('mkdir -p ' + dst_letpath) mw.buildSoftLink(src_csrpath, dst_csrpath, True) @@ -2072,9 +2064,9 @@ export PATH mw.execShell('echo "lets" > "' + dst_letpath + '/README"') # 写入配置文件 - result = self.setSslConf(siteName) + result = self.setSslConf(site_name) if not result['status']: - return mw.getJson(result) + return result result['csr'] = mw.readFile(src_csrpath) result['key'] = mw.readFile(src_keypath) From 33c67f90db9a4e054f4f6c54ad2c8006850436b5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 23 Nov 2024 23:48:13 +0800 Subject: [PATCH 204/965] Create cert.py --- web/utils/cert/cert.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 web/utils/cert/cert.py diff --git a/web/utils/cert/cert.py b/web/utils/cert/cert.py new file mode 100644 index 000000000..b69881868 --- /dev/null +++ b/web/utils/cert/cert.py @@ -0,0 +1,10 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + From 9765cb99ea503f15eede96ea9f4e5012c3861dc7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 00:19:18 +0800 Subject: [PATCH 205/965] Update site.js --- web/static/app/site.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/static/app/site.js b/web/static/app/site.js index 30abea07c..79fdd3441 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1967,12 +1967,13 @@ function setCertSsl(certName,siteName){ //ssl function setSSL(id,siteName){ + // Let\'s Encrypt var sslHtml = '\
        \ 当前证书 - [未部署SSL]\ - Let\'s Encrypt\ + \ ACME\ 证书夹' + '
        \ From cf7323cf1299e7c2e1fdf23fd39081ce2e7fe2fb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 00:19:45 +0800 Subject: [PATCH 206/965] Update site.js --- web/static/app/site.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/static/app/site.js b/web/static/app/site.js index 79fdd3441..eeb3cdd3b 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1968,6 +1968,7 @@ function setCertSsl(certName,siteName){ //ssl function setSSL(id,siteName){ // Let\'s Encrypt + // 暂时关闭 Let 申请模式 var sslHtml = '\ From c1607752b29677ec982463c486f43fc76a40967e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 00:44:39 +0800 Subject: [PATCH 207/965] update --- web/admin/files/files.py | 40 ++++++++++++++++++++++++++++++++++++++-- web/utils/file.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/web/admin/files/files.py b/web/admin/files/files.py index cfe8e83c4..326ff7e9c 100644 --- a/web/admin/files/files.py +++ b/web/admin/files/files.py @@ -19,6 +19,8 @@ from flask import send_from_directory from werkzeug.utils import secure_filename from admin.user_login_check import panel_login_required +from admin import session + import core.mw as mw import utils.file as file import thisdb @@ -31,9 +33,43 @@ def index(): return render_template('%s/files.html' % name) # 获取文件内容 -@blueprint.route('/get_body', endpoint='get_file_body', methods=['POST']) +@blueprint.route('/check_exists_files', endpoint='check_exists_files', methods=['POST']) @panel_login_required -def get_file_body(): +def check_exists_files(): + dfile = request.form.get('dfile', '') + filename = request.form.get('filename', '') + data = [] + filesx = [] + if filename == '': + filesx = json.loads(session['selected']['data']) + else: + filesx.append(filename) + + for fn in filesx: + if fn == '.': + continue + filename = dfile + '/' + fn + if os.path.exists(filename): + tmp = {} + stat = os.stat(filename) + tmp['filename'] = fn + tmp['size'] = os.path.getsize(filename) + tmp['mtime'] = str(int(stat.st_mtime)) + data.append(tmp) + return mw.returnData(True, 'ok', data) + +# 获取文件内容 +@blueprint.route('/copy_file', endpoint='copy_file', methods=['POST']) +@panel_login_required +def copy_file(): + sfile = request.form.get('sfile', '') + dfile = request.form.get('dfile', '') + return file.copyFile(sfile, dfile) + +# 获取文件内容 +@blueprint.route('/get_body', endpoint='get_body', methods=['POST']) +@panel_login_required +def get_body(): path = request.form.get('path', '') return file.getFileBody(path) diff --git a/web/utils/file.py b/web/utils/file.py index 02adcaac4..a9adf31ea 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -16,6 +16,45 @@ import shutil import core.mw as mw import thisdb +def copyDir(src_file, dst_file): + if not os.path.exists(src_file): + return mw.returnData(False, '指定目录不存在!') + + if os.path.exists(dst_file): + return mw.returnData(False, '指定目录已存在!') + + import shutil + try: + shutil.copytree(src_file, dst_file) + stat = os.stat(src_file) + os.chown(dst_file, stat.st_uid, stat.st_gid) + msg = mw.getInfo('复制目录[{1}]到[{2}]成功!', (src_file, dst_file)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '目录复制成功!') + except: + return mw.returnData(False, '目录复制失败!') + +def copyFile(src_file, dst_file): + if src_file == dst_file: + return mw.returnJson(False, '源与目的一致!') + + if not os.path.exists(src_file): + return mw.returnJson(False, '指定文件不存在!') + + if os.path.isdir(src_file): + return copyDir(src_file, dst_file) + + try: + import shutil + shutil.copyfile(src_file, dst_file) + msg = mw.getInfo('复制文件[{1}]到[{2}]成功!', (src_file, dst_file,)) + mw.writeLog('文件管理', msg) + stat = os.stat(src_file) + os.chown(dst_file, stat.st_uid, stat.st_gid) + return mw.returnData(True, '文件复制成功!') + except: + return mw.returnData(False, '文件复制失败!') + def setFileAccept(filename): auth = 'www:www' if mw.getOs() == 'darwin': From b0568926764757c255789d586366de86ea382b6f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 02:57:18 +0800 Subject: [PATCH 208/965] update --- data/tpl/nginx.conf | 11 ++++++----- web/admin/site/site.py | 4 ++-- web/static/app/site.js | 3 +-- web/utils/site.py | 23 ++++++++--------------- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/data/tpl/nginx.conf b/data/tpl/nginx.conf index 1e4655be1..5da306f9d 100755 --- a/data/tpl/nginx.conf +++ b/data/tpl/nginx.conf @@ -28,7 +28,6 @@ server include {$OR_REWRITE}/{$SERVER_NAME}.conf; #REWRITE-END - #禁止访问的文件或目录 location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) { @@ -39,12 +38,14 @@ server location ~ \.well-known{ allow all; } - - location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2)$ + + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2)$ { - expires 30d; - error_log /dev/null; + expires 30d; access_log /dev/null; + if ($invalid_referer){ + return 404; + } } access_log {$LOGPATH}/{$SERVER_NAME}.log main; diff --git a/web/admin/site/site.py b/web/admin/site/site.py index 051d0f9bd..0650bda16 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -46,12 +46,12 @@ def list(): @blueprint.route('/add', endpoint='add',methods=['POST']) @panel_login_required def add(): - webname = request.form.get('webinfo', '') + webinfo = 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 MwSites.instance().add(webname, port, ps, path, version) + return MwSites.instance().add(webinfo, port, ps, path, version) # 添加站点 - 域名 @blueprint.route('/add_domain', endpoint='add_domain',methods=['POST']) diff --git a/web/static/app/site.js b/web/static/app/site.js index eeb3cdd3b..cd19d997f 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1117,9 +1117,8 @@ function getSiteErrorLogs(siteName){ //防盗链 function security(id,name){ - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); + var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_security',{id:id,name:name},function(rdata){ - console.log(rdata); layer.close(loadT); var mbody = '
        ' +'

        URL后缀

        ' diff --git a/web/utils/site.py b/web/utils/site.py index 2b81081ab..345cc5b1f 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -180,15 +180,14 @@ class sites(object): mw.writeFile(path + '/index.html', 'Work has started!!!') mw.execShell('chmod -R 755 ' + path) - def add(self, webname, port, ps, path, version): + def add(self, site_info, port, ps, path, version): site_root_dir = mw.getWwwDir() if site_root_dir == path.rstrip('/'): return mw.returnData(False, '不要以网站根目录创建站点!') - print(webname, port, ps, path, version) - siteMenu = json.loads(webname) + site_info = json.loads(site_info) - self.siteName = self.toPunycode(siteMenu['domain'].strip().split(':')[0]).strip() + self.siteName = self.toPunycode(site_info['domain'].strip().split(':')[0]).strip() self.sitePath = self.toPunycodePath(self.getPath(path.replace(' ', ''))) self.sitePort = port.strip().replace(' ', '') self.phpVersion = version @@ -196,25 +195,20 @@ class sites(object): if thisdb.isSitesExist(self.siteName): return mw.returnData(False, '您添加的站点[%s]已存在!' % self.siteName) - pid = thisdb.addSites(self.siteName, self.sitePath) - if pid < 1: + site_id = thisdb.addSites(self.siteName, self.sitePath) + if site_id < 1: return mw.returnData(False, '添加失败!') - self.createRootDir(self.sitePath) self.nginxAddConf() # 主域名配置 - thisdb.addDomain(pid, self.siteName, self.sitePort) - - # 绑定域名配置 - + thisdb.addDomain(site_id, self.siteName, self.sitePort) # 添加更多域名 - # for domain in siteMenu['domainlist']: - # thisdb.addDomain(pid, self.siteName, self.sitePort) + for domain in site_info['domainlist']: + self.addDomain(site_id, self.siteName, domain) mw.restartWeb() - # return mw.returnData(False, '开发中!') return mw.returnData(True, '添加成功') def nginxAddDomain(self, site_name, domain, port): @@ -957,7 +951,6 @@ class sites(object): def setSecurity(self, site_id, fix, domains, status, none=''): info = thisdb.getSitesById(site_id) name = info['name'] - if len(fix) < 2: return mw.returnData(False, 'URL后缀不能为空!') From 6246cc84d51ab02007a8dca7bf6da6d9edd75202 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 03:20:34 +0800 Subject: [PATCH 209/965] Update notify_email.py --- web/admin/setting/notify_email.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/web/admin/setting/notify_email.py b/web/admin/setting/notify_email.py index ce38e1cb4..9bd8b3f2b 100644 --- a/web/admin/setting/notify_email.py +++ b/web/admin/setting/notify_email.py @@ -31,8 +31,12 @@ import thisdb def get_notify_email(): notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') - decrypt_data = mw.deDoubleCrypt('email', notify_email['cfg']) - notify_email['email'] = json.loads(decrypt_data) + if 'cfg' in notify_email: + decrypt_data = mw.deDoubleCrypt('email', notify_email['cfg']) + notify_email['email'] = json.loads(decrypt_data) + else: + notify_email['email'] = {'smtp_host':'','smtp_port':'','smtp_ssl':'','to_mail_addr':'','username':'','password':''} + return mw.returnData(True,'ok',notify_email) From 08ac6ea08c4e011d0e7ed123d318f37ef76f02de Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 03:49:53 +0800 Subject: [PATCH 210/965] update --- cli.sh | 9 +++++---- web/admin/__init__.py | 8 ++++++-- web/app.py | 12 ++++++++++++ web/setting.py | 4 ++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/cli.sh b/cli.sh index 97b66ee1d..6b848c6c3 100755 --- a/cli.sh +++ b/cli.sh @@ -59,8 +59,8 @@ mw_start_debug(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - # gunicorn -b :${port} -k gevent -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } mw_start_panel(){ @@ -68,8 +68,9 @@ mw_start_panel(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + } mw_start_bgtask(){ diff --git a/web/admin/__init__.py b/web/admin/__init__.py index dc2770ff0..6bb30f1a0 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -38,7 +38,9 @@ import config import utils.config as utils_config import thisdb - +# from threading import Lock +# thread = None +# thread_lock = Lock() app = Flask(__name__, template_folder='templates/default') @@ -51,7 +53,9 @@ from whitenoise import WhiteNoise app.wsgi_app = WhiteNoise(app.wsgi_app, root="../web/static/", prefix="static/", max_age=604800) # session配置 -app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] +# app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] +app.config['SECRET_KEY'] = uuid.UUID(int=uuid.getnode()).hex[-12:] + # app.config['sessions'] = dict() app.config['SESSION_PERMANENT'] = True app.config['SESSION_USE_SIGNER'] = True diff --git a/web/app.py b/web/app.py index cc166c571..a6f5f7610 100644 --- a/web/app.py +++ b/web/app.py @@ -14,6 +14,13 @@ import os from admin import app, socketio import config +from gevent.pywsgi import WSGIServer +from geventwebsocket.handler import WebSocketHandler + +# from gevent import monkey +# monkey.patch_all() + + if sys.version_info < (3, 6): raise RuntimeError('This application must be run under Python 3.6 or later.') @@ -24,6 +31,11 @@ if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): # print(config.DEFAULT_SERVER,config.DEFAULT_SERVER_PORT) # app = create_app() def main(): + # http_server = WSGIServer( + # (HOST, PORT), app, handler_class=WebSocketHandler) + # http_server.serve_forever() + # socketio.run(app, host=HOST, port=PORT) + socketio.run( app, debug=config.DEBUG, diff --git a/web/setting.py b/web/setting.py index 46672561d..31a634367 100755 --- a/web/setting.py +++ b/web/setting.py @@ -57,8 +57,8 @@ threads = workers * 1 backlog = 512 reload = False daemon = True -# worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -worker_class = 'eventlet' +worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' +# worker_class = 'eventlet' timeout = 600 keepalive = 60 preload_app = True From 2cf2a893e6f3aed56b3fec08162469bde4d831c9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 03:53:23 +0800 Subject: [PATCH 211/965] Update mw.tpl --- scripts/init.d/mw.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 722179492..4ba5b9339 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -345,7 +345,7 @@ mw_debug(){ if [ -d ${PANEL_DIR}/web ];then cd ${PANEL_DIR}/web fi - gunicorn -b :$port -w 1 app:app + gunicorn -b :$port -k eventlet -w 1 app:app } From d8bf989f835d63e1b55a2410eb11e190e5597707 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:06:53 +0800 Subject: [PATCH 212/965] update --- web/admin/firewall/__init__.py | 11 +++++++++++ web/utils/firewall.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 7cb6fdd2d..deafbf12f 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -93,5 +93,16 @@ def set_fw(): status = request.form.get('status', '1') return MwFirewall.instance().setFw(status) +@blueprint.route('/set_fw', endpoint='set_fw', methods=['POST']) +@panel_login_required +def set_fw(): + if mw.isAppleSystem(): + return mw.returnData(True, '开发机不能设置!') + status = request.form.get('status', '1') + return MwFirewall.instance().setSshPassStatus(status) + + + + diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 38b61d83f..6b9fb15f4 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -357,5 +357,33 @@ class Firewall(object): self.reload() return True + def setSshPassStatus(self, status): + msg = '禁止密码登陆成功' + if status == "1": + msg = '开启密码登陆成功' + + file = '/etc/ssh/sshd_config' + if not os.path.exists(file): + return mw.returnJson(False, '无法设置!') + + conf = mw.readFile(file) + + pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" + pass_status = re.search(pass_rep, conf) + if not pass_status: + rep = r"(#)?PasswordAuthentication\s+(\w*)\s*\n" + conf = re.sub(rep, "PasswordAuthentication yes\n", conf) + + if status == '1': + rep = r"PasswordAuthentication\s+(\w*)\s*\n" + conf = re.sub(rep, "PasswordAuthentication yes\n", conf) + else: + rep = r"PasswordAuthentication\s+(\w*)\s*\n" + conf = re.sub(rep, "PasswordAuthentication no\n", conf) + mw.writeFile(file, conf) + mw.execShell("systemctl restart sshd.service") + mw.writeLog("SSH管理", msg) + return mw.returnJson(True, msg) + From 9f6cd7589369b29120a568f48171a66548821754 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:13:22 +0800 Subject: [PATCH 213/965] update --- web/admin/firewall/__init__.py | 4 +-- web/static/app/firewall.js | 62 ++++++++++++++++++++-------------- web/utils/firewall.py | 12 ++++++- 3 files changed, 49 insertions(+), 29 deletions(-) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index deafbf12f..68b898855 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -93,9 +93,9 @@ def set_fw(): status = request.form.get('status', '1') return MwFirewall.instance().setFw(status) -@blueprint.route('/set_fw', endpoint='set_fw', methods=['POST']) +@blueprint.route('/set_ssh_pass_status', endpoint='set_ssh_pass_status', methods=['POST']) @panel_login_required -def set_fw(): +def set_ssh_pass_status(): if mw.isAppleSystem(): return mw.returnData(True, '开发机不能设置!') status = request.form.get('status', '1') diff --git a/web/static/app/firewall.js b/web/static/app/firewall.js index 2f5b2437f..3936a7b45 100755 --- a/web/static/app/firewall.js +++ b/web/static/app/firewall.js @@ -53,33 +53,43 @@ function sshMgr(){ $.post('/firewall/get_ssh_info', '', function(rdata){ var ssh_status = rdata.status ? 'checked':''; var pass_prohibit_status = rdata.pass_prohibit_status ? 'checked':''; + var pubkey_prohibit_status = rdata.pubkey_prohibit_status ? 'checked':''; var con = '
        \ -
        \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
        名称状态
        启动SSH\ -
        \ - \ - \ -
        \ -
        禁止密码登陆\ -
        \ - \ - \ -
        \ -
        \ -
        \ -
        '; +
        \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
        名称状态
        启动SSH\ +
        \ + \ + \ +
        \ +
        禁止密码登陆\ +
        \ + \ + \ +
        \ +
        禁止密钥登陆\ +
        \ + \ + \ +
        \ +
        \ +
        \ +
        '; layer.open({ type: 1, title: "SSH管理", diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 6b9fb15f4..983dbe155 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -157,8 +157,8 @@ class Firewall(object): if ssh_status[0] != '': status = False - data['pass_prohibit_status'] = False # 密码登陆配置检查 + data['pass_prohibit_status'] = False pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" pass_status = re.search(pass_rep, conf) if pass_status: @@ -167,6 +167,16 @@ class Firewall(object): else: data['pass_prohibit_status'] = True + # 密钥登陆配置检查 + data['pubkey_prohibit_status'] = False + pass_rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + pass_status = re.search(pass_rep, conf) + if pass_status: + if pass_status and pass_status.groups(0)[0].strip() == 'no': + data['pubkey_prohibit_status'] = True + else: + data['pubkey_prohibit_status'] = True + data['port'] = port data['status'] = status data['ping'] = isPing From 92f9a2b9a29adc4f9f07beed85c8acff425b535e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:17:05 +0800 Subject: [PATCH 214/965] update --- web/admin/firewall/__init__.py | 9 +++++++++ web/static/app/firewall.js | 37 ++++++++++++++++++++++++++++++---- web/utils/firewall.py | 30 ++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 68b898855..0e5fd77ea 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -101,6 +101,15 @@ def set_ssh_pass_status(): status = request.form.get('status', '1') return MwFirewall.instance().setSshPassStatus(status) +@blueprint.route('/set_ssh_pubkey_status', endpoint='set_ssh_pubkey_status', methods=['POST']) +@panel_login_required +def set_ssh_pubkey_status(): + if mw.isAppleSystem(): + return mw.returnData(True, '开发机不能设置!') + status = request.form.get('status', '1') + return MwFirewall.instance().setSshPubkeyStatus(status) + + diff --git a/web/static/app/firewall.js b/web/static/app/firewall.js index 3936a7b45..fe9d1a22f 100755 --- a/web/static/app/firewall.js +++ b/web/static/app/firewall.js @@ -49,7 +49,6 @@ $("#firewalldType").change(function(){ function sshMgr(){ - $.post('/firewall/get_ssh_info', '', function(rdata){ var ssh_status = rdata.status ? 'checked':''; var pass_prohibit_status = rdata.pass_prohibit_status ? 'checked':''; @@ -81,8 +80,8 @@ function sshMgr(){ 禁止密钥登陆\ \
        \ - \ - \ + \ + \
        \ \ \ @@ -100,7 +99,6 @@ function sshMgr(){ success:function(){ }, }); - },'json'); } @@ -283,6 +281,37 @@ function setSshPassStatus(){ }); } +/** + * 设置远程服务状态 + * @param {Int} state 0.启用 1.关闭 + */ +function setSshPubkeyStatus(){ + status = $("#pubkey_status").prop("checked")==true?1:0; + var msg = status==1?'开启密码登陆,继续吗?':'确定禁止密码登陆吗?'; + layer.confirm(msg,{title:'警告',closeBtn:2,cancel:function(){ + if(status == 0){ + $("#pubkey_status").prop("checked",false); + } else { + $("#pubkey_status").prop("checked",true); + } + }},function(index){ + if(index > 0){ + layer.msg('正在处理,请稍候...',{icon:16,time:20000}); + $.post('/firewall/set_ssh_pubkey_status','status='+status,function(rdata){ + layer.msg(rdata.msg,{icon:rdata.status?1:2}); + },'json'); + } + },function(){ + if(status == 0){ + $("#pubkey_status").prop("checked",false); + } else { + $("#pubkey_status").prop("checked",true); + } + }); +} + + + /** * 取回数据 * @param {Int} page 分页号 diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 983dbe155..7a1906a75 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -393,7 +393,35 @@ class Firewall(object): mw.writeFile(file, conf) mw.execShell("systemctl restart sshd.service") mw.writeLog("SSH管理", msg) - return mw.returnJson(True, msg) + return mw.returnData(True, msg) + + def setSshPubkeyStatus(self, status): + msg = '禁止密钥登陆成功' + if status == "1": + msg = '开启密钥登陆成功' + + file = '/etc/ssh/sshd_config' + if not os.path.exists(file): + return mw.returnJson(False, '无法设置!') + + content = mw.readFile(file) + + pubkey_rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + pubkey_status = re.search(pubkey_rep, content) + if not pubkey_status: + rep = r"(#)?PubkeyAuthentication\s+(\w*)\s*\n" + content = re.sub(rep, "PubkeyAuthentication yes\n", content) + + if status == '1': + rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + content = re.sub(rep, "PubkeyAuthentication yes\n", content) + else: + rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + content = re.sub(rep, "PubkeyAuthentication no\n", content) + mw.writeFile(file, content) + mw.execShell("systemctl restart sshd.service") + mw.writeLog("SSH管理", msg) + return mw.returnData(True, msg) From ba96b38a82c91e8cb99dfa59f53b82930e61ab76 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:18:02 +0800 Subject: [PATCH 215/965] Update firewall.js --- web/static/app/firewall.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/static/app/firewall.js b/web/static/app/firewall.js index fe9d1a22f..cb5ad5fa6 100755 --- a/web/static/app/firewall.js +++ b/web/static/app/firewall.js @@ -92,7 +92,7 @@ function sshMgr(){ layer.open({ type: 1, title: "SSH管理", - area: ['300px', '230px'], + area: ['300px', '260px'], closeBtn: 1, shadeClose: false, content: '
        '+con+'
        ', From 7ef0b5a9ae0c39e73cf7579da488d682ac107dcf Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:19:22 +0800 Subject: [PATCH 216/965] Update version.py --- web/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/version.py b/web/version.py index 31ccc059f..ffe400f04 100644 --- a/web/version.py +++ b/web/version.py @@ -13,7 +13,7 @@ # 应用程序版本号组件 APP_RELEASE = 0 APP_REVISION = 18 -APP_SMALL_VERSION = 0 +APP_SMALL_VERSION = 0.1 # 应用程序版本后缀,例如“beta1”、“dev”。通常为空字符串GA发布。 APP_SUFFIX = '' From 21af7b2c1cc1a0dc6b23bc9dc406efee096f188e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:21:44 +0800 Subject: [PATCH 217/965] Update setting.py --- web/setting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/setting.py b/web/setting.py index 31a634367..46672561d 100755 --- a/web/setting.py +++ b/web/setting.py @@ -57,8 +57,8 @@ threads = workers * 1 backlog = 512 reload = False daemon = True -worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -# worker_class = 'eventlet' +# worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' +worker_class = 'eventlet' timeout = 600 keepalive = 60 preload_app = True From 339d9e829e599ea79770cac9fc6e1db8fa6a2471 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:24:12 +0800 Subject: [PATCH 218/965] Update cli.sh --- cli.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli.sh b/cli.sh index 6b848c6c3..d4a3f95dc 100755 --- a/cli.sh +++ b/cli.sh @@ -68,8 +68,8 @@ mw_start_panel(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - # cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } From c639d2736e2cdcd19b80bc20e75a9563b473b813 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:24:46 +0800 Subject: [PATCH 219/965] update --- route/__init__.py | 628 -- route/static/app/config.js | 1457 ---- route/static/app/control.js | 867 --- route/static/app/crontab.js | 1131 --- route/static/app/files.js | 1864 ----- route/static/app/firewall.js | 370 - route/static/app/index.js | 1463 ---- route/static/app/logs.js | 205 - route/static/app/public.js | 2784 -------- route/static/app/site.js | 2923 -------- route/static/app/soft.js | 517 -- route/static/app/upload.js | 238 - .../css/bootstrap-theme.min.css | 5 - .../bootstrap-3.3.5/css/bootstrap.min.css | 5 - route/static/bootstrap-3.3.5/css/index.html | 13 - .../fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes .../fonts/glyphicons-halflings-regular.svg | 1 - .../fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes route/static/bootstrap-3.3.5/fonts/index.html | 13 - route/static/bootstrap-3.3.5/index.html | 13 - route/static/build/addons/attach/attach.js | 141 - route/static/build/addons/attach/index.html | 93 - route/static/build/addons/attach/package.json | 5 - route/static/build/addons/fit/fit.js | 81 - route/static/build/addons/fit/package.json | 5 - .../build/addons/fullscreen/fullscreen.css | 10 - .../build/addons/fullscreen/fullscreen.js | 50 - .../build/addons/fullscreen/package.json | 5 - route/static/build/addons/search/search.js | 116 - .../static/build/addons/search/search.js.map | 1 - .../build/addons/terminado/package.json | 5 - .../build/addons/terminado/terminado.js | 134 - .../build/addons/winptyCompat/winptyCompat.js | 29 - .../addons/winptyCompat/winptyCompat.js.map | 1 - route/static/build/addons/zmodem/demo/app.js | 87 - .../build/addons/zmodem/demo/index.html | 128 - route/static/build/addons/zmodem/demo/main.js | 383 - route/static/build/addons/zmodem/zmodem.js | 108 - route/static/build/xterm.css | 124 - route/static/build/xterm.js | 6241 ----------------- route/static/build/xterm.js.map | 1 - .../static/codemirror/addon/dialog/dialog.css | 1 - .../static/codemirror/addon/dialog/dialog.js | 1 - .../static/codemirror/addon/dialog/index.html | 13 - .../codemirror/addon/display/autorefresh.js | 1 - .../codemirror/addon/display/fullscreen.css | 1 - .../codemirror/addon/display/fullscreen.js | 1 - .../codemirror/addon/display/index.html | 13 - .../static/codemirror/addon/display/panel.js | 1 - .../codemirror/addon/display/placeholder.js | 1 - .../static/codemirror/addon/display/rulers.js | 1 - .../codemirror/addon/edit/closebrackets.js | 1 - .../static/codemirror/addon/edit/closetag.js | 1 - .../codemirror/addon/edit/continuelist.js | 1 - route/static/codemirror/addon/edit/editAll.js | 1 - route/static/codemirror/addon/edit/index.html | 13 - .../codemirror/addon/edit/matchbrackets.js | 1 - .../static/codemirror/addon/edit/matchtags.js | 1 - .../codemirror/addon/edit/trailingspace.js | 1 - .../codemirror/addon/hint/anyword-hint.js | 1 - .../static/codemirror/addon/hint/css-hint.js | 1 - .../static/codemirror/addon/hint/html-hint.js | 1 - route/static/codemirror/addon/hint/index.html | 13 - .../codemirror/addon/hint/javascript-hint.js | 1 - .../codemirror/addon/hint/show-hint.css | 1 - .../static/codemirror/addon/hint/show-hint.js | 1 - .../static/codemirror/addon/hint/sql-hint.js | 1 - .../static/codemirror/addon/hint/xml-hint.js | 1 - route/static/codemirror/addon/index.html | 13 - .../addon/scroll/annotatescrollbar.js | 1 - .../static/codemirror/addon/scroll/index.html | 13 - .../codemirror/addon/scroll/scrollpastend.js | 1 - .../addon/scroll/simplescrollbars.css | 1 - .../addon/scroll/simplescrollbars.js | 1 - .../static/codemirror/addon/search/index.html | 13 - .../codemirror/addon/search/jump-to-line.js | 1 - .../addon/search/match-highlighter.js | 1 - .../addon/search/matchesonscrollbar.css | 1 - .../addon/search/matchesonscrollbar.js | 1 - .../static/codemirror/addon/search/search.js | 1 - .../codemirror/addon/search/searchcursor.js | 1 - route/static/codemirror/index.html | 13 - route/static/codemirror/lib/codemirror.css | 4 - route/static/codemirror/lib/codemirror.js | 1 - route/static/codemirror/lib/index.html | 13 - route/static/codemirror/mode/index.html | 13 - route/static/codemirror/mode/modeAll.js | 1 - route/static/css/ensite.css | 3703 ---------- route/static/css/index.html | 0 route/static/css/install.css | 181 - route/static/css/login.css | 539 -- route/static/css/site.css | 5211 -------------- route/static/favicon.ico | Bin 9662 -> 0 bytes route/static/images/ico-ts-cpu-active.png | Bin 780 -> 0 bytes route/static/images/ico-ts-cpu.png | Bin 645 -> 0 bytes route/static/images/ico-ts-disk-active.png | Bin 1072 -> 0 bytes route/static/images/ico-ts-disk.png | Bin 758 -> 0 bytes route/static/images/ico-ts-mem-active.png | Bin 503 -> 0 bytes route/static/images/ico-ts-mem.png | Bin 359 -> 0 bytes route/static/images/ico-ts-refresh.png | Bin 1185 -> 0 bytes route/static/images/ico-ts-score.gif | Bin 9773 -> 0 bytes route/static/images/ico-ts-score.jpg | Bin 3961 -> 0 bytes route/static/images/ico-ts-score1.png | Bin 2061 -> 0 bytes route/static/images/ico-ts-score2.png | Bin 2191 -> 0 bytes route/static/images/ico-ts-score3.png | Bin 2119 -> 0 bytes route/static/images/index.html | 13 - route/static/images/logo.png | Bin 1939 -> 0 bytes route/static/images/move.png | Bin 105 -> 0 bytes route/static/images/right.png | Bin 396 -> 0 bytes route/static/images/weixin.png | Bin 2139 -> 0 bytes route/static/img/Detailsbg.png | Bin 110 -> 0 bytes route/static/img/DrawRecordord.png | Bin 95 -> 0 bytes route/static/img/account.png | Bin 414 -> 0 bytes route/static/img/alipay_zz.png | Bin 7307 -> 0 bytes route/static/img/ico-close.png | Bin 260 -> 0 bytes route/static/img/ico-computer.png | Bin 246 -> 0 bytes route/static/img/ico-copy.png | Bin 281 -> 0 bytes route/static/img/ico-home.png | Bin 243 -> 0 bytes route/static/img/ico-success.png | Bin 585 -> 0 bytes route/static/img/ico/ico-access.png | Bin 1331 -> 0 bytes route/static/img/ico/ico-apk.png | Bin 1010 -> 0 bytes route/static/img/ico/ico-avi.png | Bin 1139 -> 0 bytes route/static/img/ico/ico-bmp.png | Bin 1027 -> 0 bytes route/static/img/ico/ico-bt.png | Bin 1166 -> 0 bytes route/static/img/ico/ico-c.png | Bin 1037 -> 0 bytes route/static/img/ico/ico-cdr.png | Bin 1035 -> 0 bytes route/static/img/ico/ico-cpp.png | Bin 1057 -> 0 bytes route/static/img/ico/ico-cs.png | Bin 1085 -> 0 bytes route/static/img/ico/ico-doc.png | Bin 1158 -> 0 bytes route/static/img/ico/ico-docx.png | Bin 1195 -> 0 bytes route/static/img/ico/ico-flv.png | Bin 1116 -> 0 bytes route/static/img/ico/ico-gif.png | Bin 973 -> 0 bytes route/static/img/ico/ico-htm.png | Bin 1066 -> 0 bytes route/static/img/ico/ico-html.png | Bin 1072 -> 0 bytes route/static/img/ico/ico-java.png | Bin 1417 -> 0 bytes route/static/img/ico/ico-jpeg.png | Bin 1018 -> 0 bytes route/static/img/ico/ico-jpg.png | Bin 1007 -> 0 bytes route/static/img/ico/ico-js.png | Bin 1107 -> 0 bytes route/static/img/ico/ico-ltr.png | Bin 233 -> 0 bytes route/static/img/ico/ico-mht.png | Bin 1061 -> 0 bytes route/static/img/ico/ico-mkv.png | Bin 1247 -> 0 bytes route/static/img/ico/ico-mov.png | Bin 1175 -> 0 bytes route/static/img/ico/ico-mp4.png | Bin 1147 -> 0 bytes route/static/img/ico/ico-mpeg.png | Bin 1177 -> 0 bytes route/static/img/ico/ico-mpg.png | Bin 1168 -> 0 bytes route/static/img/ico/ico-pdf.png | Bin 1086 -> 0 bytes route/static/img/ico/ico-php.png | Bin 1318 -> 0 bytes route/static/img/ico/ico-png.png | Bin 1023 -> 0 bytes route/static/img/ico/ico-pptx.png | Bin 1127 -> 0 bytes route/static/img/ico/ico-psd.png | Bin 1119 -> 0 bytes route/static/img/ico/ico-rm.png | Bin 1129 -> 0 bytes route/static/img/ico/ico-rmvb.png | Bin 1209 -> 0 bytes route/static/img/ico/ico-rocket.gif | Bin 1079 -> 0 bytes route/static/img/ico/ico-swf.png | Bin 964 -> 0 bytes route/static/img/ico/ico-url.png | Bin 1152 -> 0 bytes route/static/img/ico/ico-webm.png | Bin 1214 -> 0 bytes route/static/img/ico/ico-webp.png | Bin 1060 -> 0 bytes route/static/img/ico/ico-wma.png | Bin 978 -> 0 bytes route/static/img/ico/ico-wmv.png | Bin 1211 -> 0 bytes route/static/img/ico/ico-xls.png | Bin 1121 -> 0 bytes route/static/img/ico/ico-xlsx.png | Bin 1163 -> 0 bytes route/static/img/ico/ico-xml.png | Bin 1095 -> 0 bytes route/static/img/ico/index.html | 13 - route/static/img/ico/menu_icon_control.png | Bin 327 -> 0 bytes .../img/ico/menu_icon_control_active.gif | Bin 805 -> 0 bytes .../img/ico/menu_icon_control_active.png | Bin 316 -> 0 bytes route/static/img/ico/menu_icon_data.png | Bin 347 -> 0 bytes .../static/img/ico/menu_icon_data_active.gif | Bin 936 -> 0 bytes .../static/img/ico/menu_icon_data_active.png | Bin 330 -> 0 bytes route/static/img/ico/menu_icon_day.png | Bin 291 -> 0 bytes route/static/img/ico/menu_icon_day_active.gif | Bin 568 -> 0 bytes route/static/img/ico/menu_icon_day_active.png | Bin 271 -> 0 bytes route/static/img/ico/menu_icon_exit.png | Bin 240 -> 0 bytes .../static/img/ico/menu_icon_exit_active.gif | Bin 545 -> 0 bytes .../static/img/ico/menu_icon_exit_active.png | Bin 232 -> 0 bytes route/static/img/ico/menu_icon_firewall.png | Bin 324 -> 0 bytes .../img/ico/menu_icon_firewall_active.gif | Bin 550 -> 0 bytes .../img/ico/menu_icon_firewall_active.png | Bin 310 -> 0 bytes route/static/img/ico/menu_icon_folder.png | Bin 274 -> 0 bytes .../img/ico/menu_icon_folder_active.gif | Bin 719 -> 0 bytes .../img/ico/menu_icon_folder_active.png | Bin 239 -> 0 bytes route/static/img/ico/menu_icon_ftp.png | Bin 384 -> 0 bytes route/static/img/ico/menu_icon_ftp_active.gif | Bin 1054 -> 0 bytes route/static/img/ico/menu_icon_ftp_active.png | Bin 365 -> 0 bytes route/static/img/ico/menu_icon_home.png | Bin 271 -> 0 bytes .../static/img/ico/menu_icon_home_active.gif | Bin 976 -> 0 bytes .../static/img/ico/menu_icon_home_active.png | Bin 308 -> 0 bytes route/static/img/ico/menu_icon_log.png | Bin 188 -> 0 bytes route/static/img/ico/menu_icon_log_active.png | Bin 180 -> 0 bytes route/static/img/ico/menu_icon_set.png | Bin 319 -> 0 bytes route/static/img/ico/menu_icon_set_active.gif | Bin 1923 -> 0 bytes route/static/img/ico/menu_icon_set_active.png | Bin 308 -> 0 bytes route/static/img/ico/menu_icon_soft.png | Bin 303 -> 0 bytes .../static/img/ico/menu_icon_soft_active.gif | Bin 955 -> 0 bytes .../static/img/ico/menu_icon_soft_active.png | Bin 311 -> 0 bytes route/static/img/ico/menu_icon_web.png | Bin 363 -> 0 bytes route/static/img/ico/menu_icon_web_active.gif | Bin 766 -> 0 bytes route/static/img/ico/menu_icon_web_active.png | Bin 343 -> 0 bytes route/static/img/ico/rocket_min.png | Bin 435 -> 0 bytes route/static/img/ico_line.png | Bin 145 -> 0 bytes route/static/img/icon-item.png | Bin 224 -> 0 bytes route/static/img/icon3.png | Bin 330 -> 0 bytes route/static/img/icon4.png | Bin 358 -> 0 bytes route/static/img/index.html | 0 route/static/img/ing.gif | Bin 131 -> 0 bytes route/static/img/ings.gif | Bin 131 -> 0 bytes route/static/img/label-icon.png | Bin 305 -> 0 bytes route/static/img/loading.gif | Bin 1785 -> 0 bytes route/static/img/morebtn.png | Bin 530 -> 0 bytes route/static/img/ns-loading.gif | Bin 1513 -> 0 bytes route/static/img/ref-icon.png | Bin 349 -> 0 bytes route/static/img/return-icon.png | Bin 363 -> 0 bytes route/static/img/safety_ico.png | Bin 410 -> 0 bytes route/static/img/ser-icon.png | Bin 275 -> 0 bytes route/static/img/soft_ico/ico-app.png | Bin 581 -> 0 bytes route/static/img/soft_ico/ico-beta.png | Bin 398 -> 0 bytes route/static/img/soft_ico/ico-cmd-hover.png | Bin 178 -> 0 bytes route/static/img/soft_ico/ico-cmd.png | Bin 181 -> 0 bytes route/static/img/soft_ico/ico-deployment.png | Bin 422 -> 0 bytes .../static/img/soft_ico/ico-load_leveling.png | Bin 477 -> 0 bytes route/static/img/soft_ico/ico-log.png | Bin 308 -> 0 bytes route/static/img/soft_ico/ico-logs.png | Bin 753 -> 0 bytes route/static/img/soft_ico/ico-mongodb.png | Bin 808 -> 0 bytes route/static/img/soft_ico/ico-node.png | Bin 1423 -> 0 bytes route/static/img/soft_ico/ico-phpsafe.png | Bin 1169 -> 0 bytes route/static/img/soft_ico/ico-pm2.png | Bin 870 -> 0 bytes route/static/img/soft_ico/ico-psync.png | Bin 280 -> 0 bytes route/static/img/soft_ico/ico-rsync.png | Bin 835 -> 0 bytes route/static/img/soft_ico/ico-safelogin.png | Bin 603 -> 0 bytes route/static/img/soft_ico/ico-score.png | Bin 457 -> 0 bytes .../static/img/soft_ico/ico-task_manager.png | Bin 320 -> 0 bytes route/static/img/soft_ico/ico-test.png | Bin 654 -> 0 bytes route/static/img/soft_ico/ico-tomcat.png | Bin 2286 -> 0 bytes route/static/img/soft_ico/ico-txcos.png | Bin 3136 -> 0 bytes route/static/img/soft_ico/ico-webhook.png | Bin 653 -> 0 bytes route/static/img/soft_ico/index.html | 13 - route/static/img/success-pic.png | Bin 3726 -> 0 bytes route/static/img/tip_suu.png | Bin 2862 -> 0 bytes route/static/img/weixin_zz.jpg | Bin 60575 -> 0 bytes route/static/index.html | 13 - route/static/js/Validform_v5.3.2_min.js | 8 - route/static/js/bootstrap.min.js | 7 - route/static/js/clipboard.min.js | 7 - route/static/js/echarts.min.js | 45 - route/static/js/jquery-1.10.2.min.js | 5 - route/static/js/jquery-qrcode-0.18.0.min.js | 2 - route/static/js/jquery-ui.min.js | 13 - route/static/js/jquery.contextify.min.js | 6 - route/static/js/jquery.contextify.min.js.map | 1 - route/static/js/jquery.cookie-1.4.1.min.js | 2 - route/static/js/jquery.dragsort-0.5.2.min.js | 3 - route/static/js/jquery.fly.min.js | 4 - route/static/js/requestAnimationFrame.js | 27 - route/static/js/socket.io.min.js | 7 - route/static/js/socket.io.min.js.map | 1 - route/static/js/term-websocketio.js | 136 - route/static/js/term.js | 158 - route/static/js/xm-select.js | 8 - .../static/language/Simplified_Chinese/lan.js | 1016 --- .../language/Simplified_Chinese/log.json | 123 - .../language/Simplified_Chinese/public.json | 303 - .../language/Simplified_Chinese/template.json | 299 - route/static/language/index.html | 0 route/static/language/list.json | 4 - route/static/language/zh-cn.js | 19 - route/static/laydate/index.html | 13 - route/static/laydate/laydate.js | 1 - .../laydate/theme/default/font/iconfont.eot | Bin 2456 -> 0 bytes .../laydate/theme/default/font/iconfont.svg | 1 - .../laydate/theme/default/font/iconfont.ttf | Bin 2272 -> 0 bytes .../laydate/theme/default/font/iconfont.woff | Bin 1492 -> 0 bytes .../laydate/theme/default/font/index.html | 13 - route/static/laydate/theme/default/index.html | 13 - .../static/laydate/theme/default/laydate.css | 2 - route/static/laydate/theme/index.html | 13 - route/static/layer/extend/index.html | 13 - route/static/layer/extend/layer.ext.js | 2 - route/static/layer/index.html | 13 - route/static/layer/layer.js | 2 - route/static/layer/mobile/index.html | 13 - route/static/layer/mobile/layer.js | 2 - route/static/layer/mobile/need/index.html | 13 - route/static/layer/mobile/need/layer.css | 1 - route/static/layer/skin/default/close.png | Bin 581 -> 0 bytes .../static/layer/skin/default/close_hover.png | Bin 767 -> 0 bytes route/static/layer/skin/default/icon-ext.png | Bin 2713 -> 0 bytes route/static/layer/skin/default/icon.png | Bin 6864 -> 0 bytes route/static/layer/skin/default/index.html | 13 - route/static/layer/skin/default/layer.css | 1 - route/static/layer/skin/default/loading-0.gif | Bin 5310 -> 0 bytes route/static/layer/skin/default/loading-1.gif | Bin 699 -> 0 bytes route/static/layer/skin/default/loading-2.gif | Bin 1785 -> 0 bytes route/static/layer/skin/index.html | 13 - route/static/layer/skin/layer.css | 7 - route/static/layer/skin/layer.ext.css | 8 - route/static/logo.png | Bin 22782 -> 0 bytes route/static/mdw.jpg | Bin 66905 -> 0 bytes route/templates/default/close.html | 13 - route/templates/default/config.html | 196 - route/templates/default/control.html | 176 - route/templates/default/crontab.html | 155 - route/templates/default/files.html | 113 - route/templates/default/firewall.html | 117 - route/templates/default/index.html | 171 - route/templates/default/layout.html | 208 - route/templates/default/login.html | 257 - route/templates/default/logs.html | 189 - route/templates/default/path.html | 16 - route/templates/default/plugin_menu.html | 4 - route/templates/default/site.html | 75 - route/templates/default/soft.html | 58 - route/templates/index.html | 1 - 314 files changed, 36210 deletions(-) delete mode 100755 route/__init__.py delete mode 100755 route/static/app/config.js delete mode 100755 route/static/app/control.js delete mode 100755 route/static/app/crontab.js delete mode 100755 route/static/app/files.js delete mode 100755 route/static/app/firewall.js delete mode 100755 route/static/app/index.js delete mode 100644 route/static/app/logs.js delete mode 100755 route/static/app/public.js delete mode 100755 route/static/app/site.js delete mode 100755 route/static/app/soft.js delete mode 100755 route/static/app/upload.js delete mode 100755 route/static/bootstrap-3.3.5/css/bootstrap-theme.min.css delete mode 100755 route/static/bootstrap-3.3.5/css/bootstrap.min.css delete mode 100755 route/static/bootstrap-3.3.5/css/index.html delete mode 100755 route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot delete mode 100755 route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.svg delete mode 100755 route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf delete mode 100755 route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff delete mode 100755 route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 delete mode 100755 route/static/bootstrap-3.3.5/fonts/index.html delete mode 100755 route/static/bootstrap-3.3.5/index.html delete mode 100755 route/static/build/addons/attach/attach.js delete mode 100755 route/static/build/addons/attach/index.html delete mode 100755 route/static/build/addons/attach/package.json delete mode 100755 route/static/build/addons/fit/fit.js delete mode 100755 route/static/build/addons/fit/package.json delete mode 100755 route/static/build/addons/fullscreen/fullscreen.css delete mode 100755 route/static/build/addons/fullscreen/fullscreen.js delete mode 100755 route/static/build/addons/fullscreen/package.json delete mode 100755 route/static/build/addons/search/search.js delete mode 100755 route/static/build/addons/search/search.js.map delete mode 100755 route/static/build/addons/terminado/package.json delete mode 100755 route/static/build/addons/terminado/terminado.js delete mode 100755 route/static/build/addons/winptyCompat/winptyCompat.js delete mode 100755 route/static/build/addons/winptyCompat/winptyCompat.js.map delete mode 100755 route/static/build/addons/zmodem/demo/app.js delete mode 100755 route/static/build/addons/zmodem/demo/index.html delete mode 100755 route/static/build/addons/zmodem/demo/main.js delete mode 100755 route/static/build/addons/zmodem/zmodem.js delete mode 100755 route/static/build/xterm.css delete mode 100755 route/static/build/xterm.js delete mode 100755 route/static/build/xterm.js.map delete mode 100755 route/static/codemirror/addon/dialog/dialog.css delete mode 100755 route/static/codemirror/addon/dialog/dialog.js delete mode 100755 route/static/codemirror/addon/dialog/index.html delete mode 100755 route/static/codemirror/addon/display/autorefresh.js delete mode 100755 route/static/codemirror/addon/display/fullscreen.css delete mode 100755 route/static/codemirror/addon/display/fullscreen.js delete mode 100755 route/static/codemirror/addon/display/index.html delete mode 100755 route/static/codemirror/addon/display/panel.js delete mode 100755 route/static/codemirror/addon/display/placeholder.js delete mode 100755 route/static/codemirror/addon/display/rulers.js delete mode 100755 route/static/codemirror/addon/edit/closebrackets.js delete mode 100755 route/static/codemirror/addon/edit/closetag.js delete mode 100755 route/static/codemirror/addon/edit/continuelist.js delete mode 100755 route/static/codemirror/addon/edit/editAll.js delete mode 100755 route/static/codemirror/addon/edit/index.html delete mode 100755 route/static/codemirror/addon/edit/matchbrackets.js delete mode 100755 route/static/codemirror/addon/edit/matchtags.js delete mode 100755 route/static/codemirror/addon/edit/trailingspace.js delete mode 100755 route/static/codemirror/addon/hint/anyword-hint.js delete mode 100755 route/static/codemirror/addon/hint/css-hint.js delete mode 100755 route/static/codemirror/addon/hint/html-hint.js delete mode 100755 route/static/codemirror/addon/hint/index.html delete mode 100755 route/static/codemirror/addon/hint/javascript-hint.js delete mode 100755 route/static/codemirror/addon/hint/show-hint.css delete mode 100755 route/static/codemirror/addon/hint/show-hint.js delete mode 100755 route/static/codemirror/addon/hint/sql-hint.js delete mode 100755 route/static/codemirror/addon/hint/xml-hint.js delete mode 100755 route/static/codemirror/addon/index.html delete mode 100755 route/static/codemirror/addon/scroll/annotatescrollbar.js delete mode 100755 route/static/codemirror/addon/scroll/index.html delete mode 100755 route/static/codemirror/addon/scroll/scrollpastend.js delete mode 100755 route/static/codemirror/addon/scroll/simplescrollbars.css delete mode 100755 route/static/codemirror/addon/scroll/simplescrollbars.js delete mode 100755 route/static/codemirror/addon/search/index.html delete mode 100755 route/static/codemirror/addon/search/jump-to-line.js delete mode 100755 route/static/codemirror/addon/search/match-highlighter.js delete mode 100755 route/static/codemirror/addon/search/matchesonscrollbar.css delete mode 100755 route/static/codemirror/addon/search/matchesonscrollbar.js delete mode 100755 route/static/codemirror/addon/search/search.js delete mode 100755 route/static/codemirror/addon/search/searchcursor.js delete mode 100755 route/static/codemirror/index.html delete mode 100755 route/static/codemirror/lib/codemirror.css delete mode 100755 route/static/codemirror/lib/codemirror.js delete mode 100755 route/static/codemirror/lib/index.html delete mode 100755 route/static/codemirror/mode/index.html delete mode 100755 route/static/codemirror/mode/modeAll.js delete mode 100755 route/static/css/ensite.css delete mode 100755 route/static/css/index.html delete mode 100755 route/static/css/install.css delete mode 100755 route/static/css/login.css delete mode 100755 route/static/css/site.css delete mode 100644 route/static/favicon.ico delete mode 100755 route/static/images/ico-ts-cpu-active.png delete mode 100755 route/static/images/ico-ts-cpu.png delete mode 100755 route/static/images/ico-ts-disk-active.png delete mode 100755 route/static/images/ico-ts-disk.png delete mode 100755 route/static/images/ico-ts-mem-active.png delete mode 100755 route/static/images/ico-ts-mem.png delete mode 100755 route/static/images/ico-ts-refresh.png delete mode 100755 route/static/images/ico-ts-score.gif delete mode 100755 route/static/images/ico-ts-score.jpg delete mode 100755 route/static/images/ico-ts-score1.png delete mode 100755 route/static/images/ico-ts-score2.png delete mode 100755 route/static/images/ico-ts-score3.png delete mode 100755 route/static/images/index.html delete mode 100755 route/static/images/logo.png delete mode 100755 route/static/images/move.png delete mode 100755 route/static/images/right.png delete mode 100755 route/static/images/weixin.png delete mode 100755 route/static/img/Detailsbg.png delete mode 100755 route/static/img/DrawRecordord.png delete mode 100755 route/static/img/account.png delete mode 100644 route/static/img/alipay_zz.png delete mode 100755 route/static/img/ico-close.png delete mode 100755 route/static/img/ico-computer.png delete mode 100755 route/static/img/ico-copy.png delete mode 100755 route/static/img/ico-home.png delete mode 100755 route/static/img/ico-success.png delete mode 100755 route/static/img/ico/ico-access.png delete mode 100755 route/static/img/ico/ico-apk.png delete mode 100755 route/static/img/ico/ico-avi.png delete mode 100755 route/static/img/ico/ico-bmp.png delete mode 100755 route/static/img/ico/ico-bt.png delete mode 100755 route/static/img/ico/ico-c.png delete mode 100755 route/static/img/ico/ico-cdr.png delete mode 100755 route/static/img/ico/ico-cpp.png delete mode 100755 route/static/img/ico/ico-cs.png delete mode 100755 route/static/img/ico/ico-doc.png delete mode 100755 route/static/img/ico/ico-docx.png delete mode 100755 route/static/img/ico/ico-flv.png delete mode 100755 route/static/img/ico/ico-gif.png delete mode 100755 route/static/img/ico/ico-htm.png delete mode 100755 route/static/img/ico/ico-html.png delete mode 100755 route/static/img/ico/ico-java.png delete mode 100755 route/static/img/ico/ico-jpeg.png delete mode 100755 route/static/img/ico/ico-jpg.png delete mode 100755 route/static/img/ico/ico-js.png delete mode 100755 route/static/img/ico/ico-ltr.png delete mode 100755 route/static/img/ico/ico-mht.png delete mode 100755 route/static/img/ico/ico-mkv.png delete mode 100755 route/static/img/ico/ico-mov.png delete mode 100755 route/static/img/ico/ico-mp4.png delete mode 100755 route/static/img/ico/ico-mpeg.png delete mode 100755 route/static/img/ico/ico-mpg.png delete mode 100755 route/static/img/ico/ico-pdf.png delete mode 100755 route/static/img/ico/ico-php.png delete mode 100755 route/static/img/ico/ico-png.png delete mode 100755 route/static/img/ico/ico-pptx.png delete mode 100755 route/static/img/ico/ico-psd.png delete mode 100755 route/static/img/ico/ico-rm.png delete mode 100755 route/static/img/ico/ico-rmvb.png delete mode 100755 route/static/img/ico/ico-rocket.gif delete mode 100755 route/static/img/ico/ico-swf.png delete mode 100755 route/static/img/ico/ico-url.png delete mode 100755 route/static/img/ico/ico-webm.png delete mode 100755 route/static/img/ico/ico-webp.png delete mode 100755 route/static/img/ico/ico-wma.png delete mode 100755 route/static/img/ico/ico-wmv.png delete mode 100755 route/static/img/ico/ico-xls.png delete mode 100755 route/static/img/ico/ico-xlsx.png delete mode 100755 route/static/img/ico/ico-xml.png delete mode 100755 route/static/img/ico/index.html delete mode 100755 route/static/img/ico/menu_icon_control.png delete mode 100755 route/static/img/ico/menu_icon_control_active.gif delete mode 100755 route/static/img/ico/menu_icon_control_active.png delete mode 100755 route/static/img/ico/menu_icon_data.png delete mode 100755 route/static/img/ico/menu_icon_data_active.gif delete mode 100755 route/static/img/ico/menu_icon_data_active.png delete mode 100755 route/static/img/ico/menu_icon_day.png delete mode 100755 route/static/img/ico/menu_icon_day_active.gif delete mode 100755 route/static/img/ico/menu_icon_day_active.png delete mode 100755 route/static/img/ico/menu_icon_exit.png delete mode 100755 route/static/img/ico/menu_icon_exit_active.gif delete mode 100755 route/static/img/ico/menu_icon_exit_active.png delete mode 100755 route/static/img/ico/menu_icon_firewall.png delete mode 100755 route/static/img/ico/menu_icon_firewall_active.gif delete mode 100755 route/static/img/ico/menu_icon_firewall_active.png delete mode 100755 route/static/img/ico/menu_icon_folder.png delete mode 100755 route/static/img/ico/menu_icon_folder_active.gif delete mode 100755 route/static/img/ico/menu_icon_folder_active.png delete mode 100755 route/static/img/ico/menu_icon_ftp.png delete mode 100755 route/static/img/ico/menu_icon_ftp_active.gif delete mode 100755 route/static/img/ico/menu_icon_ftp_active.png delete mode 100755 route/static/img/ico/menu_icon_home.png delete mode 100755 route/static/img/ico/menu_icon_home_active.gif delete mode 100755 route/static/img/ico/menu_icon_home_active.png delete mode 100755 route/static/img/ico/menu_icon_log.png delete mode 100755 route/static/img/ico/menu_icon_log_active.png delete mode 100755 route/static/img/ico/menu_icon_set.png delete mode 100755 route/static/img/ico/menu_icon_set_active.gif delete mode 100755 route/static/img/ico/menu_icon_set_active.png delete mode 100755 route/static/img/ico/menu_icon_soft.png delete mode 100755 route/static/img/ico/menu_icon_soft_active.gif delete mode 100755 route/static/img/ico/menu_icon_soft_active.png delete mode 100755 route/static/img/ico/menu_icon_web.png delete mode 100755 route/static/img/ico/menu_icon_web_active.gif delete mode 100755 route/static/img/ico/menu_icon_web_active.png delete mode 100755 route/static/img/ico/rocket_min.png delete mode 100755 route/static/img/ico_line.png delete mode 100755 route/static/img/icon-item.png delete mode 100755 route/static/img/icon3.png delete mode 100755 route/static/img/icon4.png delete mode 100755 route/static/img/index.html delete mode 100755 route/static/img/ing.gif delete mode 100755 route/static/img/ings.gif delete mode 100755 route/static/img/label-icon.png delete mode 100755 route/static/img/loading.gif delete mode 100755 route/static/img/morebtn.png delete mode 100755 route/static/img/ns-loading.gif delete mode 100755 route/static/img/ref-icon.png delete mode 100755 route/static/img/return-icon.png delete mode 100755 route/static/img/safety_ico.png delete mode 100755 route/static/img/ser-icon.png delete mode 100755 route/static/img/soft_ico/ico-app.png delete mode 100755 route/static/img/soft_ico/ico-beta.png delete mode 100755 route/static/img/soft_ico/ico-cmd-hover.png delete mode 100755 route/static/img/soft_ico/ico-cmd.png delete mode 100755 route/static/img/soft_ico/ico-deployment.png delete mode 100755 route/static/img/soft_ico/ico-load_leveling.png delete mode 100755 route/static/img/soft_ico/ico-log.png delete mode 100755 route/static/img/soft_ico/ico-logs.png delete mode 100755 route/static/img/soft_ico/ico-mongodb.png delete mode 100755 route/static/img/soft_ico/ico-node.png delete mode 100755 route/static/img/soft_ico/ico-phpsafe.png delete mode 100755 route/static/img/soft_ico/ico-pm2.png delete mode 100755 route/static/img/soft_ico/ico-psync.png delete mode 100755 route/static/img/soft_ico/ico-rsync.png delete mode 100755 route/static/img/soft_ico/ico-safelogin.png delete mode 100755 route/static/img/soft_ico/ico-score.png delete mode 100755 route/static/img/soft_ico/ico-task_manager.png delete mode 100755 route/static/img/soft_ico/ico-test.png delete mode 100755 route/static/img/soft_ico/ico-tomcat.png delete mode 100755 route/static/img/soft_ico/ico-txcos.png delete mode 100755 route/static/img/soft_ico/ico-webhook.png delete mode 100755 route/static/img/soft_ico/index.html delete mode 100755 route/static/img/success-pic.png delete mode 100755 route/static/img/tip_suu.png delete mode 100644 route/static/img/weixin_zz.jpg delete mode 100755 route/static/index.html delete mode 100755 route/static/js/Validform_v5.3.2_min.js delete mode 100755 route/static/js/bootstrap.min.js delete mode 100755 route/static/js/clipboard.min.js delete mode 100644 route/static/js/echarts.min.js delete mode 100755 route/static/js/jquery-1.10.2.min.js delete mode 100755 route/static/js/jquery-qrcode-0.18.0.min.js delete mode 100755 route/static/js/jquery-ui.min.js delete mode 100755 route/static/js/jquery.contextify.min.js delete mode 100755 route/static/js/jquery.contextify.min.js.map delete mode 100644 route/static/js/jquery.cookie-1.4.1.min.js delete mode 100755 route/static/js/jquery.dragsort-0.5.2.min.js delete mode 100755 route/static/js/jquery.fly.min.js delete mode 100755 route/static/js/requestAnimationFrame.js delete mode 100644 route/static/js/socket.io.min.js delete mode 100644 route/static/js/socket.io.min.js.map delete mode 100644 route/static/js/term-websocketio.js delete mode 100644 route/static/js/term.js delete mode 100755 route/static/js/xm-select.js delete mode 100755 route/static/language/Simplified_Chinese/lan.js delete mode 100755 route/static/language/Simplified_Chinese/log.json delete mode 100755 route/static/language/Simplified_Chinese/public.json delete mode 100755 route/static/language/Simplified_Chinese/template.json delete mode 100755 route/static/language/index.html delete mode 100755 route/static/language/list.json delete mode 100755 route/static/language/zh-cn.js delete mode 100755 route/static/laydate/index.html delete mode 100755 route/static/laydate/laydate.js delete mode 100755 route/static/laydate/theme/default/font/iconfont.eot delete mode 100755 route/static/laydate/theme/default/font/iconfont.svg delete mode 100755 route/static/laydate/theme/default/font/iconfont.ttf delete mode 100755 route/static/laydate/theme/default/font/iconfont.woff delete mode 100755 route/static/laydate/theme/default/font/index.html delete mode 100755 route/static/laydate/theme/default/index.html delete mode 100755 route/static/laydate/theme/default/laydate.css delete mode 100755 route/static/laydate/theme/index.html delete mode 100755 route/static/layer/extend/index.html delete mode 100755 route/static/layer/extend/layer.ext.js delete mode 100755 route/static/layer/index.html delete mode 100755 route/static/layer/layer.js delete mode 100755 route/static/layer/mobile/index.html delete mode 100755 route/static/layer/mobile/layer.js delete mode 100755 route/static/layer/mobile/need/index.html delete mode 100755 route/static/layer/mobile/need/layer.css delete mode 100755 route/static/layer/skin/default/close.png delete mode 100755 route/static/layer/skin/default/close_hover.png delete mode 100755 route/static/layer/skin/default/icon-ext.png delete mode 100755 route/static/layer/skin/default/icon.png delete mode 100755 route/static/layer/skin/default/index.html delete mode 100755 route/static/layer/skin/default/layer.css delete mode 100755 route/static/layer/skin/default/loading-0.gif delete mode 100755 route/static/layer/skin/default/loading-1.gif delete mode 100755 route/static/layer/skin/default/loading-2.gif delete mode 100755 route/static/layer/skin/index.html delete mode 100755 route/static/layer/skin/layer.css delete mode 100755 route/static/layer/skin/layer.ext.css delete mode 100644 route/static/logo.png delete mode 100644 route/static/mdw.jpg delete mode 100755 route/templates/default/close.html delete mode 100755 route/templates/default/config.html delete mode 100755 route/templates/default/control.html delete mode 100755 route/templates/default/crontab.html delete mode 100755 route/templates/default/files.html delete mode 100755 route/templates/default/firewall.html delete mode 100755 route/templates/default/index.html delete mode 100755 route/templates/default/layout.html delete mode 100755 route/templates/default/login.html delete mode 100644 route/templates/default/logs.html delete mode 100755 route/templates/default/path.html delete mode 100644 route/templates/default/plugin_menu.html delete mode 100755 route/templates/default/site.html delete mode 100755 route/templates/default/soft.html delete mode 100644 route/templates/index.html diff --git a/route/__init__.py b/route/__init__.py deleted file mode 100755 index bffc16e33..000000000 --- a/route/__init__.py +++ /dev/null @@ -1,628 +0,0 @@ -# coding:utf-8 - -import sys -import io -import os -import time -import shutil -import uuid -import json -import traceback -import socket - -# reload(sys) -# sys.setdefaultencoding('utf-8') -import paramiko -from datetime import timedelta - -from flask import Flask -from flask import render_template -from flask import make_response -from flask import Response -from flask import session -from flask import request -from flask import redirect -from flask import url_for -from flask import render_template_string, abort -from flask_caching import Cache -from flask_session import Session -# from flask_compress import Compress - -from whitenoise import WhiteNoise - -sys.path.append(os.getcwd() + "/class/core") - -import db -import mw -import config_api - -import common -common.init() - -app = Flask(__name__, template_folder='templates/default') -app.config.version = config_api.config_api().getVersion() -# Compress(app) - -app.wsgi_app = WhiteNoise( - app.wsgi_app, root="route/static/", prefix="static/", max_age=604800) - -cache = Cache(config={'CACHE_TYPE': 'simple'}) -cache.init_app(app, config={'CACHE_TYPE': 'simple'}) - -try: - from flask_sqlalchemy import SQLAlchemy - app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/py_mw_session.db' - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True - app.config['SESSION_TYPE'] = 'sqlalchemy' - app.config['SESSION_SQLALCHEMY'] = sdb - app.config['SESSION_SQLALCHEMY_TABLE'] = 'session' - sdb = SQLAlchemy(app) - sdb.create_all() -except: - app.config['SESSION_TYPE'] = 'filesystem' - app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + str(sys.version_info[0]) - app.config['SESSION_FILE_THRESHOLD'] = 1024 - app.config['SESSION_FILE_MODE'] = 384 - -app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] -app.config['SESSION_PERMANENT'] = True -app.config['SESSION_USE_SIGNER'] = True -app.config['SESSION_KEY_PREFIX'] = 'MW_:' -app.config['SESSION_COOKIE_NAME'] = "MW_VER_1" -app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) - -if mw.isAppleSystem(): - app.config['DEBUG'] = True - -# Session(app) - -# 设置BasicAuth -basic_auth_conf = 'data/basic_auth.json' -app.config['BASIC_AUTH_OPEN'] = False -if os.path.exists(basic_auth_conf): - try: - ba_conf = json.loads(mw.readFile(basic_auth_conf)) - # print(ba_conf) - app.config['BASIC_AUTH_USERNAME'] = ba_conf['basic_user'] - app.config['BASIC_AUTH_PASSWORD'] = ba_conf['basic_pwd'] - app.config['BASIC_AUTH_OPEN'] = ba_conf['open'] - app.config['BASIC_AUTH_FORCE'] = True - except Exception as e: - print(e) - -# socketio -from flask_socketio import SocketIO, emit, send -socketio = SocketIO() -socketio.init_app(app) - -# sockets -from flask_sockets import Sockets -sockets = Sockets(app) - -# from gevent.pywsgi import WSGIServer -# from geventwebsocket.handler import WebSocketHandler -# http_server = WSGIServer(('0.0.0.0', '7200'), app, -# handler_class=WebSocketHandler) -# http_server.serve_forever() - -# debug macosx dev -if mw.isDebugMode(): - app.debug = True - app.config.version = app.config.version + str(time.time()) - -# ---------- error function start ----------------- - - -def getErrorNum(key, limit=None): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - if not limit: - return num - if limit > num: - return True - return False - - -def setErrorNum(key, empty=False, expire=3600): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - else: - if empty: - cache.delete(key) - return True - cache.set(key, num + 1, expire) - return True -# ---------- error function end ----------------- - - -def funConvert(fun): - block = fun.split('_') - func = block[0] - for x in range(len(block) - 1): - suf = block[x + 1].title() - func += suf - return func - - -def sendAuthenticated(): - # 发送http认证信息 - request_host = mw.getHostAddr() - result = Response( - '', 401, {'WWW-Authenticate': 'Basic realm="%s"' % request_host.strip()}) - if not 'login' in session and not 'admin_auth' in session: - session.clear() - return result - - -@app.before_request -def requestCheck(): - # Flask请求勾子 - if app.config['BASIC_AUTH_OPEN']: - auth = request.authorization - if request.path in ['/download', '/hook', '/down']: - return - - if not auth: - return sendAuthenticated() - salt = '_md_salt' - if mw.md5(auth.username.strip() + salt) != app.config['BASIC_AUTH_USERNAME'] \ - or mw.md5(auth.password.strip() + salt) != app.config['BASIC_AUTH_PASSWORD']: - return sendAuthenticated() - - domain_check = mw.checkDomainPanel() - if domain_check: - return domain_check - - -@app.after_request -def requestAfter(response): - response.headers['soft'] = 'mdserver-web' - response.headers['mw-version'] = app.config.version - return response - - -def isLogined(): - if 'login' in session and 'username' in session and session['login'] == True: - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - # print(userInfo) - if userInfo['username'] != session['username']: - return False - - now_time = int(time.time()) - - if 'overdue' in session and now_time > session['overdue']: - # 自动续期 - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - return False - - if 'tmp_login_expire' in session and now_time > int(session['tmp_login_expire']): - session.clear() - return False - - return True - - # if os.path.exists('data/api_login.txt'): - # content = mw.readFile('data/api_login.txt') - # session['login'] = True - # session['username'] = content - # os.remove('data/api_login.txt') - return False - - -def publicObject(toObject, func, action=None, get=None): - name = funConvert(func) + 'Api' - try: - if hasattr(toObject, name): - efunc = 'toObject.' + name + '()' - data = eval(efunc) - return data - data = {'msg': '404,not find api[' + name + ']', "status": False} - return mw.getJson(data) - except Exception as e: - # API发生错误记录 - if mw.isDebugMode(): - print(traceback.print_exc()) - data = {'msg': '访问异常:' + str(mw.getTracebackInfo()) + '!', "status": False} - return mw.getJson(data) - - -# @app.route("/debug") -# def debug(): -# print(sys.version_info) -# print(session) -# os = mw.getOs() -# print(os) -# return mw.getLocalIp() - -@app.route("/.well-known/acme-challenge/") -def wellknow(val=None): - # 申请面板ssl使用 - f = mw.getRunDir() + "/tmp/.well-known/acme-challenge/" + val - if os.path.exists(f): - return mw.readFile(f) - return '' - - -@app.route("/hook", methods=['POST', 'GET']) -def webhook(): - # 仅针对webhook插件 - - # 兼容获取关键数据 - access_key = request.args.get('access_key', '').strip() - if access_key == '': - access_key = request.form.get('access_key', '').strip() - - params = request.args.get('params', '').strip() - if params == '': - params = request.form.get('params', '').strip() - - input_args = { - 'access_key': access_key, - 'params': params, - } - - wh_install_path = mw.getServerDir() + '/webhook' - if not os.path.exists(wh_install_path): - return mw.returnJson(False, '请先安装WebHook插件!') - - package = os.getcwd() + "/plugins/webhook" - if not package in sys.path: - sys.path.append(package) - - try: - import webhook_index - return webhook_index.runShellArgs(input_args) - except Exception as e: - return str(e) - - -@app.route('/close') -def close(): - if not os.path.exists('data/close.pl'): - return redirect('/') - data = {} - data['cmd'] = 'rm -rf ' + mw.getRunDir() + '/data/close.pl' - return render_template('close.html', data=data) - - -@app.route("/code") -def code(): - import vilidate - vie = vilidate.vieCode() - codeImage = vie.GetCodeImage(80, 4) - # try: - # from cStringIO import StringIO - # except: - # from StringIO import StringIO - - out = io.BytesIO() - codeImage[0].save(out, "png") - session['code'] = mw.md5(''.join(codeImage[1]).lower()) - - img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) - return make_response(img) - - -@app.route("/check_login", methods=['POST']) -def checkLogin(): - if isLogined(): - return "true" - return "false" - - -@app.route("/verify_login", methods=['POST']) -def verifyLogin(): - username = request.form.get('username', '').strip() - password = request.form.get('password', '').strip() - password = mw.md5(password) - - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - if userInfo['username'] != username or userInfo['password'] != password: - return mw.returnJson(-1, "密码错误?") - - auth = request.form.get('auth', '').strip() - - import pyotp - auth_file = 'data/auth_secret.pl' - if os.path.exists(auth_file): - content = mw.readFile(auth_file) - sec = mw.deDoubleCrypt('mdserver-web', content) - totp = pyotp.TOTP(sec) - if totp.verify(auth): - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - session['login'] = True - session['username'] = userInfo['username'] - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - return mw.returnJson(1, '二次验证成功!') - - return mw.returnJson(-1, '二次验证失败!') - - -@app.route("/do_login", methods=['POST']) -def doLogin(): - login_cache_count = 5 - login_cache_limit = cache.get('login_cache_limit') - - filename = 'data/close.pl' - if os.path.exists(filename): - return mw.returnJson(False, '面板已经关闭!') - - username = request.form.get('username', '').strip() - password = request.form.get('password', '').strip() - code = request.form.get('code', '').strip() - # print(session) - if 'code' in session: - if session['code'] != mw.md5(code): - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - mw.writeFile(filename, 'True') - return mw.returnJson(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - code_msg = mw.getInfo("验证码错误,您还可以尝试[{1}]次!", (str( - login_cache_count - login_cache_limit))) - mw.writeLog('用户登录', code_msg) - return mw.returnJson(False, code_msg) - - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - password = mw.md5(password) - - if userInfo['username'] != username or userInfo['password'] != password: - msg = "密码错误,帐号:{1},密码:{2},登录IP:{3}", (( - '****', '******', request.remote_addr)) - - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - mw.writeFile(filename, 'True') - return mw.returnJson(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - mw.writeLog('用户登录', mw.getInfo(msg)) - return mw.returnJson(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))) - - - - cache.delete('login_cache_limit') - # 二次验证密钥 - auth_secret = 'data/auth_secret.pl' - if os.path.exists(auth_secret): - return mw.returnJson(2, '绑定二次验证了...') - - session['login'] = True - session['username'] = userInfo['username'] - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - - # fix 跳转时,数据消失,可能是跨域问题 - # mw.writeFile('data/api_login.txt', userInfo['username']) - return mw.returnJson(1, '登录成功,正在跳转...') - - -@app.errorhandler(404) -def page_unauthorized(error): - return render_template_string('404 not found', error_info=error), 404 - - -def get_admin_safe(): - path = 'data/admin_path.pl' - if os.path.exists(path): - cont = mw.readFile(path) - cont = cont.strip().strip('/') - return (True, cont) - return (False, '') - - -def admin_safe_path(path, req, data, pageFile): - if path != req and not isLogined(): - if data['status_code'] == '0': - return render_template('path.html') - else: - return Response(status=int(data['status_code'])) - - if not isLogined(): - return render_template('login.html', data=data) - - if not req in pageFile: - return redirect('/') - - return render_template(req + '.html', data=data) - - -def login_temp_user(token): - if len(token) != 48: - return '错误的参数!' - - skey = mw.getClientIp() + '_temp_login' - if not getErrorNum(skey, 10): - return '连续10次验证失败,禁止1小时' - - stime = int(time.time()) - data = mw.M('temp_login').where('state=? and expire>?',(0, stime)).field('id,token,salt,expire,addtime').find() - if not data: - setErrorNum(skey) - return '验证失败!' - - if stime > int(data['expire']): - setErrorNum(skey) - return "过期" - - r_token = mw.md5(token + data['salt']) - if r_token != data['token']: - setErrorNum(skey) - return '验证失败!' - - userInfo = mw.M('users').where("id=?", (1,)).field('id,username').find() - session['login'] = True - session['username'] = userInfo['username'] - session['tmp_login'] = True - session['tmp_login_id'] = str(data['id']) - session['tmp_login_expire'] = int(data['expire']) - session['uid'] = data['id'] - - login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT')) - mw.writeLog('用户登录', "登录成功,帐号:{1},登录IP:{2}", - (userInfo['username'], login_addr)) - mw.M('temp_login').where('id=?', (data['id'],)).update( - {"login_time": stime, 'state': 1, 'login_addr': login_addr}) - - # print(session) - return redirect('/') - - -@app.route('/api//', methods=['POST', 'GET']) -def api(reqClass=None, reqAction=None, reqData=None): - comReturn = common.local() - if comReturn: - return comReturn - - import config_api - isOk, data = config_api.config_api().checkPanelToken() - if not isOk: - return mw.returnJson(False, '未开启API') - - request_time = request.form.get('request_time', '') - request_token = request.form.get('request_token', '') - request_ip = request.remote_addr - request_ip = request_ip.replace('::ffff:', '') - - # print(request_time, request_token) - if not mw.inArray(data['limit_addr'], request_ip): - return mw.returnJson(False, 'IP校验失败,您的访问IP为[' + request_ip + ']') - - local_token = mw.deCrypt(data['token'], data['token_crypt']) - token_md5 = mw.md5(str(request_time) + mw.md5(local_token)) - - if not (token_md5 == request_token): - return mw.returnJson(False, '密钥错误') - - if reqClass == None: - return mw.returnJson(False, '请指定请求方法类') - - if reqAction == None: - return mw.returnJson(False, '请指定请求方法') - - classFile = ('config_api', 'crontab_api', 'files_api', 'firewall_api', - 'plugins_api', 'system_api', 'site_api', 'task_api', - 'logs_api') - className = reqClass + '_api' - if not className in classFile: - return mw.returnJson(False, 'external api request error') - - eval_str = "__import__('" + className + "')." + className + '()' - newInstance = eval(eval_str) - - try: - return publicObject(newInstance, reqAction) - except Exception as e: - return mw.getTracebackInfo() - - -@app.route('//', methods=['POST', 'GET']) -@app.route('//', methods=['POST', 'GET']) -@app.route('/', methods=['POST', 'GET']) -@app.route('/', methods=['POST', 'GET']) -def index(reqClass=None, reqAction=None, reqData=None): - - comReturn = common.local() - if comReturn: - return comReturn - - # 页面请求 - if reqAction == None: - import config_api - data = config_api.config_api().get() - - if reqClass == None: - reqClass = 'index' - - pageFile = ('config', 'control', 'crontab', 'files', 'logs', 'firewall', - 'index', 'plugins', 'login', 'system', 'site', 'cert', 'ssl', 'task', 'soft') - - if reqClass == 'login': - token = request.args.get('tmp_token', '').strip() - if token != '': - return login_temp_user(token) - - # 设置了安全路径 - ainfo = get_admin_safe() - - # 登录页 - if reqClass == 'login': - - signout = request.args.get('signout', '') - if signout == 'True': - session.clear() - session['login'] = False - session['overdue'] = 0 - - if ainfo[0]: - return admin_safe_path(ainfo[1], reqClass, data, pageFile) - - return render_template('login.html', data=data) - - if ainfo[0]: - return admin_safe_path(ainfo[1], reqClass, data, pageFile) - - if not reqClass in pageFile: - return redirect('/') - - if not isLogined(): - return redirect('/login') - - return render_template(reqClass + '.html', data=data) - - if not isLogined(): - return 'error request!' - - # API请求 - classFile = ('config_api', 'crontab_api', 'files_api', 'logs_api', 'firewall_api', - 'plugins_api', 'system_api', 'site_api', 'task_api', 'vip_api') - className = reqClass + '_api' - if not className in classFile: - return "api error request" - - eval_str = "__import__('" + className + "')." + className + '()' - newInstance = eval(eval_str) - - return publicObject(newInstance, reqAction) - - -##################### ssh start ########################### - -@socketio.on('webssh_websocketio') -def webssh_websocketio(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return - - import ssh_terminal - shell_client = ssh_terminal.ssh_terminal.instance() - shell_client.run(request.sid, data) - return - - -@socketio.on('webssh') -def webssh(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return None - - import ssh_local - shell = ssh_local.ssh_local.instance() - shell.run(data) - return - -##################### ssh end ########################### diff --git a/route/static/app/config.js b/route/static/app/config.js deleted file mode 100755 index 31f8b2077..000000000 --- a/route/static/app/config.js +++ /dev/null @@ -1,1457 +0,0 @@ -/** op **/ -// $(".set-submit").click(function(){ -// var data = $("#set_config").serialize(); -// layer.msg('正在保存配置...',{icon:16,time:0,shade: [0.3, '#000']}); -// $.post('/config/set',data,function(rdata){ -// layer.closeAll(); -// layer.msg(rdata.msg,{icon:rdata.status?1:2}); -// if(rdata.status){ -// setTimeout(function(){ -// window.location.href = ((window.location.protocol.indexOf('https') != -1)?'https://':'http://') + rdata.data.host + window.location.pathname; -// },2500); -// } -// },'json'); -// }); - -$('input[name="webname"]').change(function(){ - var webname = $(this).val(); - $('.btn_webname').removeAttr('disabled'); - $('.btn_webname').unbind().click(function(){ - $.post('/config/set_webname','webname='+webname, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - - -$('input[name="host_ip"]').change(function(){ - var host_ip = $(this).val(); - $('.btn_host_ip').removeAttr('disabled'); - $('.btn_host_ip').unbind().click(function(){ - $.post('/config/set_ip','host_ip='+host_ip, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - -$('input[name="port"]').change(function(){ - var port = $(this).val(); - var old_port = $(this).data('port'); - $('.btn_port').removeAttr('disabled'); - $('.btn_port').unbind().click(function(){ - $.post('/config/set_port','port='+port, function(rdata){ - showMsg(rdata.msg,function(){ - window.location.href = window.location.href.replace(old_port,port); - // window.location.reload(); - },{icon:rdata.status?1:2},5000); - },'json'); - }); -}); - -$('input[name="sites_path"]').change(function(){ - var sites_path = $(this).val(); - $('.btn_sites_path').removeAttr('disabled'); - $('.btn_sites_path').unbind().click(function(){ - $.post('/config/set_www_dir','sites_path='+sites_path, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - - -$('input[name="backup_path"]').change(function(){ - var backup_path = $(this).val(); - $('.btn_backup_path').removeAttr('disabled'); - $('.btn_backup_path').unbind().click(function(){ - $.post('/config/set_backup_dir','backup_path='+backup_path, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - - -$('input[name="bind_domain"]').change(function(){ - var domain = $(this).val(); - $('.btn_bind_domain').removeAttr('disabled'); - $('.btn_bind_domain').unbind().click(function(){ - $.post('/config/set_panel_domain','domain='+domain, function(rdata){ - showMsg(rdata.msg,function(){ - window.location.href = rdata.data; - },{icon:rdata.status?1:2},5000); - },'json'); - }); -}); - -$('input[name="bind_ssl"]').click(function(){ - var panel_ssl = $(this).prop("checked"); - $(this).prop("checked",!panel_ssl); - - //开启证书 - if (panel_ssl){ - // - layer.open({ - type:1, - closeBtn: 1, - title:"开启SSL证书", - area: ['600px','440px'], - btn: ["开启SSL证书访问"], - maxmin:false, - shadeClose: true, - content: '
        \ -
        \ -

        【开启SSL证书】保护面板访问安全

        \ -
        \ -
          \ -
        • 自签证书访问步骤:
        • \ -
        • 1. 部署SSL证书
        • \ -
        • 2. 浏览器地址栏修改为https://访问
        • \ -
        • 3. 如提醒风险(正常现象)点击【高级】或【详情】
        • \ -
        • 4.【继续访问】或【接收风险并继续】
        • \ -
        \ -
        \ -
        \ - 类型\ -
        \ - \ -
        \ -
        \ -
          \ -
        • 开启后导致面板不能访问,可以点击查看
        • \ -
        • 自签证书不被浏览器信任,显示不安全是正常现象
        • \ -
        \ -
        \ -
        ', - yes: function(){ - - var cert_type = $('select[name=cert_type]').val(); - $.post('/config/set_panel_local_ssl',{'cert_type':cert_type}, function(rdata){ - // console.log(rdata); - var to_https = window.location.href.replace('http','https'); - showMsg(rdata.msg,function(){ - if (rdata.status){ - window.location.href = to_https; - } - },{icon:rdata.status?1:2},5000); - },'json'); - - } - }); - } else { - //关闭SSL - layer.open({ - type:1, - closeBtn: 1, - title:"关闭SSL证书", - area: ['480px','280px'], - btn: ["确定","取消"], - shadeClose: true, - content: '
        \ -
        \ -
        关闭SSL极易被抓包攻击导致账号密码泄露,请勿关闭
        \ -
        \ -
        \ -
        请手动输入【我要关闭】,完成验证
        \ - \ -
        \ -
        ', - yes: function(index){ - var val = $('#prompt_input_box').val(); - if (val != '我要关闭'){ - layer.msg("关闭SSL失败!"); - return; - } - - $.post('/config/close_panel_ssl',{}, function(rdata){ - var to_http = window.location.href.replace('https','http'); - showMsg(rdata.msg,function(){ - if (rdata.status){ - window.location.href = to_http; - } - },{icon:rdata.status?1:2},5000); - },'json'); - } - }); - } - -}); - -/** op **/ - - -// VIP -- start -function setVipInfo(){ - layer.open({ - type: 1, - area: "400px", - title: 'VIP登录', - closeBtn: 1, - shift: 5, - btn:["登录","关闭"], - shadeClose: false, - content: "
        \ -
        \ - 用户名\ -
        \ -
        \ -
        \ - 密码\ -
        \ -
        \ -
        ", - yes:function(index){ - var pdata = {}; - - pdata['username'] = $('input[name="username"]').val(); - pdata['password'] = $('input[name="password"]').val(); - - if (pdata['username'] == ''){ - layer.msg('用户名不能为空!', {icon:2}); - return false; - } - - if (pdata['password'] == ''){ - layer.msg('密码不能为空!', {icon:2}); - return false; - } - - $.post('/vip/login',{'username':pdata['username'], 'password':pdata['password']},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - },'json'); - }, - }); -} -// VIP -- end - - -//关闭面板 -function closePanel(){ - layer.confirm('关闭面板会导致您无法访问面板 ,您真的要关闭Linux面板吗?',{title:'关闭面板',closeBtn:2,icon:13,cancel:function(){ - $("#closePl").prop("checked",false); - }}, function() { - $.post('/config/close_panel','',function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - setTimeout(function(){ - window.location.reload(); - },1000); - },'json'); - },function(){ - $("#closePl").prop("checked",false); - }); -} - -//开发模式 -function debugMode(){ - var loadT = layer.msg('正在发送请求,请稍候...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/open_debug', {}, function (rdata) { - layer.close(loadT); - showMsg(rdata.msg, function(){ - window.location.reload(); - } ,{icon:rdata.status?1:2}, 1000); - },'json'); -} - - -function modifyAuthPath() { - var auth_path = $("#admin_path").val(); - layer.open({ - type: 1, - area: "500px", - title: "修改安全入口", - closeBtn: 1, - shift: 5, - btn:['提交','关闭'], - shadeClose: false, - content: '
        \ -
        \ - 入口地址\ -
        \ - \ -
        \ -
        \ -
        ', - yes:function(index){ - var auth_path = $("input[name='auth_path_set']").val(); - if (auth_path == '/' || auth_path == ''){ - layer.confirm('警告,关闭安全入口等于直接暴露你的后台地址在外网,十分危险, 您真的要这样更改吗?',{title:'安全入口修改',closeBtn:1,icon:13, - cancel:function(){ - }}, function() { - var loadT = layer.msg(lan.config.config_save, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/set_admin_path', { admin_path: auth_path }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - $("#admin_path").val(auth_path); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - }); - return; - } else { - var loadT = layer.msg(lan.config.config_save, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/set_admin_path', { admin_path: auth_path }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - $("#admin_path").val(auth_path); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - } - } - }); -} - -function setPassword(a) { - if(a == 1) { - p1 = $("#p1").val(); - p2 = $("#p2").val(); - if(p1 == "" || p1.length < 8) { - layer.msg('面板密码不能少于8位!', {icon: 2}); - return - } - - //准备弱口令匹配元素 - var checks = ['admin888','123123123','12345678','45678910','87654321','asdfghjkl','password','qwerqwer']; - pchecks = 'abcdefghijklmnopqrstuvwxyz1234567890'; - for(var i=0;i\ -
        \ - 密码\ -
        \ -
        \ -
        \ - 重复\ -
        \ -
        \ -
        \ - 随机\ - \ - \ -
        \ -
        " - }); -} - - -function randPwd(){ - var pwd = randomStrPwd(12); - $("#p1").val(pwd); - $("#p2").val(pwd); - layer.msg(lan.bt.pass_rep_ps,{time:2000}) -} - -function setUserName(a) { - if(a == 1) { - p1 = $("#p1").val(); - p2 = $("#p2").val(); - if(p1 == "" || p1.length < 3) { - layer.msg('用户名长度不能少于3位', {icon: 2}); - return; - } - if(p1 != p2) { - layer.msg('两次输入的用户名不一致', {icon: 2}); - return; - } - $.post("/config/set_name", "name1=" + encodeURIComponent(p1) + "&name2=" + encodeURIComponent(p2), function(b) { - if(b.status) { - layer.closeAll(); - layer.msg(b.msg, {icon: 1}); - $("input[name='username_']").val(p1) - } else { - layer.msg(b.msg, {icon: 2}); - } - },'json'); - return - } - layer.open({ - type: 1, - area: "290px", - title: '修改面板用户名', - closeBtn: 1, - shift: 5, - shadeClose: false, - content: "
        \ -
        用户名\ -
        \ -
        \ -
        \ - 重复\ -
        \ -
        \ -
        \ - \ - \ -
        \ -
        " - }) -} - -function setTimezone(){ - layer.open({ - type: 1, - area: ["400px","200px"], - title: '设置服务器时区', - closeBtn: 1, - shift: 5, - shadeClose: false, - btn:["确定","取消","同步"], - content: "
        \ -
        \ - 时区\ -
        \ - \ -
        \ -
        \ -
        ", - success:function(){ - var tbody = ''; - $.post('/config/get_timezone_list', {}, function (rdata) { - for (var i = 0; i < rdata.length; i++) { - - if (rdata[i] == 'Asia/Shanghai'){ - tbody += ''; - } else { - tbody += ''; - } - - } - $('select[name="timezone"]').append(tbody); - },'json'); - }, - yes:function(index){ - var loadT = layer.msg("正在设置时区...", { icon: 16, time: 0, shade: [0.3, '#000'] }); - var timezone = $('select[name="timezone"]').val(); - $.post('/config/set_timezone', { timezone: timezone }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - location.reload(); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - }, - btn3:function(){ - var loadT = layer.msg('正在同步时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/sync_date','',function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - setTimeout(function(){window.location.reload();},1500); - },'json'); - } - }) -} - - -function setIPv6() { - var loadT = layer.msg('正在配置,请稍候...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/set_ipv6_status', {}, function (rdata) { - layer.close(loadT); - layer.msg(rdata.msg, {icon:rdata.status?1:2}); - setTimeout(function(){window.location.reload();},5000); - },'json'); -} - - -//设置面板SSL -function setPanelSSL(){ - var status = $("#sshswitch").prop("checked")==true?1:0; - var msg = $("#panelSSL").attr('checked')?'关闭SSL后,必需使用http协议访问面板,继续吗?':'危险!此功能不懂别开启!\ -
      • 必须要用到且了解此功能才决定自己是否要开启!
      • \ -
      • 面板SSL是自签证书,不被浏览器信任,显示不安全是正常现象
      • \ -
      • 开启后导致面板不能访问,可以点击下面链接了解解决方法
      • \ -

        \ - \ -

        '; - layer.confirm(msg,{title:'设置面板SSL',closeBtn:1,icon:3,area:'550px',cancel:function(){ - $("#panelSSL").prop("checked", status == 0?false:true); - }},function(){ - if(window.location.protocol.indexOf('https') == -1){ - if(!$("#checkSSL").prop('checked')){ - layer.msg(lan.config.ssl_ps,{icon:2}); - return false; - } - } - var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/set_panel_ssl','',function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - if(rdata.status === true){ - $.post('/system/restart','',function (rdata) { - layer.close(loadT); - layer.msg(rdata.msg); - setTimeout(function(){ - window.location.href = ((window.location.protocol.indexOf('https') != -1)?'http://':'https://') + window.location.host + window.location.pathname; - },3000); - },'json'); - } - },'json'); - },function(){ - if(status == 0){ - $("#panelSSL").prop("checked",false); - }else{ - $("#panelSSL").prop("checked",true); - } - }); -} - -function setNotifyApi(tag, obj){ - var enable = $(obj).prop("checked"); - // console.log(tag,obj,enable); - $.post('/config/set_notify_enable', {'tag':tag, 'enable':enable},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){} - } ,{icon:rdata.status?1:2}, 1000); - },'json'); -} - -function getTgbot(){ - var loadT = layer.msg('正在获取TgBot信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/get_notify',{},function(data){ - layer.close(loadT); - - var app_token = ''; - var chat_id = ''; - - if (data.status){ - if (typeof(data['data']['tgbot']) !='undefined'){ - app_token = data['data']['tgbot']['data']['app_token']; - chat_id = data['data']['tgbot']['data']['chat_id']; - } - } - - layer.open({ - type: 1, - area: "500px", - title: 'TgBot配置', - closeBtn: 1, - shift: 5, - btn:["确定","关闭","验证"], - shadeClose: false, - content: "
        \ -
        \ - APP_TOKEN\ -
        \ -
        \ -
        \ - CHAT_ID\ -
        \ -
        \ -
        ", - yes:function(index){ - var pdata = {}; - pdata['app_token'] = $('input[name="app_token"]').val(); - pdata['chat_id'] = $('input[name="chat_id"]').val(); - - if (pdata['app_token'] == ''){ - layer.msg('app_token不能为空!', {icon:2}); - return false; - } - - if (pdata['chat_id'] == ''){ - layer.msg('chat_id不能为空!', {icon:2}); - return false; - } - - $.post('/config/set_notify',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - }, - - btn3:function(index){ - var pdata = {}; - pdata['app_token'] = $('input[name="app_token"]').val(); - pdata['chat_id'] = $('input[name="chat_id"]').val(); - - if (pdata['app_token'] == ''){ - layer.msg('app_token不能为空!', {icon:2}); - return false; - } - - if (pdata['chat_id'] == ''){ - layer.msg('chat_id不能为空!', {icon:2}); - return false; - } - - $.post('/config/set_notify_test',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - return false; - } - }); - }); -} - -function getEmailCfg(){ - var loadT = layer.msg('正在获取邮件配置信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/get_notify',{},function(data){ - layer.close(loadT); - - var smtp_host = 'smtp.163.com'; - var smtp_port = '25'; - var username = 'admin'; - var password = ''; - var to_mail_addr = ''; - - var smtp_ssl_no = 'checked'; - var smtp_ssl_yes = ''; - - if (data.status){ - if (typeof(data['data']['email']) !='undefined'){ - smtp_host = data['data']['email']['data']['smtp_host']; - smtp_port = data['data']['email']['data']['smtp_port']; - username = data['data']['email']['data']['username']; - password = data['data']['email']['data']['password']; - to_mail_addr = data['data']['email']['data']['to_mail_addr']; - - - var smtp_ssl = data['data']['email']['data']['smtp_ssl']; - if (smtp_ssl == 'ssl'){ - smtp_ssl_no = ''; - smtp_ssl_yes = 'checked'; - } - } - } - - layer.open({ - type: 1, - area: "500px", - title: '邮件配置', - closeBtn: 1, - shift: 5, - btn:["确定","关闭","验证"], - shadeClose: false, - content: "
        \ -
        \ - SMTP服务器\ -
        \ -
        \ -
        \ - SMTP安全\ -
        \ - \ - \ -
        \ -
        \ -
        \ - SMTP端口\ -
        \ -
        \ -
        \ - 用户名\ -
        \ -
        \ -
        \ - 授权码\ -
        \ -
        \ -
        \ - 发送地址\ -
        \ -
        \ -
        \ - 验证测试\ -
        \ -
        \ -
        \ -
        ", - yes:function(index){ - var pdata = {}; - pdata['smtp_host'] = $('input[name="smtp_host"]').val(); - pdata['smtp_port'] = $('input[name="smtp_port"]').val(); - pdata['smtp_ssl'] = $('input[name="smtp_ssl"]:checked').val(); - pdata['username'] = $('input[name="username"]').val(); - pdata['password'] = $('input[name="password"]').val(); - pdata['to_mail_addr'] = $('input[name="to_mail_addr"]').val(); - - if (pdata['smtp_host'] == ''){ - layer.msg('SMTP服务器不能为空!', {icon:2}); - return false; - } - - if (pdata['smtp_port'] == ''){ - layer.msg('SMTP端口不能为空!', {icon:2}); - return false; - } - - if (pdata['username'] == ''){ - layer.msg('用户名不能为空!', {icon:2}); - return false; - } - - if (pdata['password'] == ''){ - layer.msg('授权码不能为空!', {icon:2}); - return false; - } - - if (pdata['to_mail_addr'] == ''){ - layer.msg('发送地址不能为空!', {icon:2}); - return false; - } - - $.post('/config/set_notify',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - }, - - btn3:function(index){ - var pdata = {}; - pdata['smtp_host'] = $('input[name="smtp_host"]').val(); - pdata['smtp_port'] = $('input[name="smtp_port"]').val(); - pdata['smtp_ssl'] = $('input[name="smtp_ssl"]:checked').val(); - pdata['username'] = $('input[name="username"]').val(); - pdata['password'] = $('input[name="password"]').val(); - pdata['to_mail_addr'] = $('input[name="to_mail_addr"]').val(); - pdata['mail_test'] = $('textarea[name="mail_test"]').val(); - - - if (pdata['smtp_host'] == ''){ - layer.msg('SMTP服务器不能为空!', {icon:2}); - return false; - } - - if (pdata['smtp_port'] == ''){ - layer.msg('SMTP端口不能为空!', {icon:2}); - return false; - } - - if (pdata['username'] == ''){ - layer.msg('用户名不能为空!', {icon:2}); - return false; - } - - if (pdata['password'] == ''){ - layer.msg('授权码不能为空!', {icon:2}); - return false; - } - - if (pdata['to_mail_addr'] == ''){ - layer.msg('发送地址不能为空!', {icon:2}); - return false; - } - - if (pdata['mail_test'] == ''){ - layer.msg('验证测试不能为空!', {icon:2}); - return false; - } - $.post('/config/set_notify_test',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - return false; - } - }); - }); -} - -function getPanelSSL(){ - var loadT = layer.msg('正在获取证书信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/get_panel_ssl',{},function(cert){ - layer.close(loadT); - - // console.log(cert); - var choose = cert['choose']; - var choose_local = ''; - var choose_nginx = ''; - - if (choose == 'local'){ - cert = cert['local']; - choose_local = 'selected="selected"'; - } else if (choose == 'nginx') { - cert = cert['nginx']; - choose_nginx = 'selected="selected"'; - } else { - cert = cert['local']; - } - - var cert_data = ''; - - //
        \ - // 强制HTTPS:\ - // \ - // \ - // \ - // \ - //
        \ - if (cert['info']){ - cert_data = "
        \ -
        证书品牌:\ - "+cert['info']['issuer']+"
        \ -
        到期时间:\ - 剩余"+cert['info']['endtime']+"天到期
        \ -
        \ -
        \ -
        认证域名:\ - "+cert['info']['subject']+"
        \ -
        "; - } - - // \ - // \ - var certBody = '
        \ -
        \ - '+cert_data+'\ -
        \ -
        密钥(KEY)
        \ - \ -
        \ -
        证书(PEM格式)
        \ - \ -
        \ -
        \ -
        \ - \ - \ - \ -
        \ -
        \ -
          \ -
        • 粘贴您的*.key以及*.pem内容,然后保存即可。
        • \ -
        • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
        • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
        • \ -
        \ -
        ' - layer.open({ - type: 1, - area: "600px", - title: '自定义面板证书', - closeBtn: 1, - shift: 5, - shadeClose: false, - content:certBody, - success:function(layero, layer_id){ - - - //保存SSL - $('.save-panel-ssl').click(function(){ - var data = { - privateKey:$("#key").val(), - certPem:$("#csr").val() - } - - layer.confirm('选择保存面板SSL方式?', - { - title:'提示', - shade:0.001, - btn: ['本地SSL', '取消'],//'OpenResty' - btn3:function(){ - data['choose'] = 'nginx'; - var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.closeAll(); - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }, - }, - function(index) { - data['choose'] = 'local'; - var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.closeAll(); - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }, - function(index) { - layer.close(index); - }); - }); - - //删除SSL - $('.del-panel-ssl').click(function(){ - - layer.confirm('选择删除面板SSL方式?', - { - title:'提示', - shade:0.001, - btn: ['本地SSL', '取消'],//, 'OpenResty' - btn3:function(){ - var data = {}; - data['choose'] = 'nginx'; - var loadT = layer.msg('正在删除面板SSL【nginx】...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/del_panel_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.closeAll(); - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }, - }, - function(index) { - var data = {}; - data['choose'] = 'local'; - var loadT = layer.msg('正在删除面板SSL【本地】...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/del_panel_ssl',data,function(rdata){ - console.log(rdata); - layer.close(loadT); - showMsg(rdata.msg, function(){ - if(rdata.status){ - location.href = rdata.data; - } - },{icon:rdata.status?1:2},3000); - },'json'); - }, - function(index) { - layer.close(index); - }); - - - }); - - // // 设置面板SSL的Http - // $('.set_panel_http_to_https').click(function(){ - // var https = $('#toHttps').prop('checked'); - // $.post('/config/set_panel_http_to_https',{'https':https},function(rdata){ - // layer.close(loadT); - // if(rdata.status){ - // layer.closeAll(); - // } - // layer.msg(rdata.msg,{icon:rdata.status?1:2}); - // },'json'); - // }); - - // //申请Lets证书 - // $('.apply-lets-ssl').click(function(){ - // showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){ - // $.post('/config/apply_panel_acme_ssl',{},function(rdata){ - // layer.close(loadT); - // if(rdata.status){ - // layer.close(index); - // var tdata = rdata['data']; - // $('.ssl_issue').text(tdata['info']['issuer']); - // $('.ssl_endtime').text("剩余"+tdata['info']['endtime']+"天到期"); - // $('.ssl_subject').text(tdata['info']['subject']); - - // $('textarea[name="key"]').val(tdata['info']['privateKey']); - // $('textarea[name="csr"]').val(tdata['info']['certPem']); - // } - // layer.msg(rdata.msg,{icon:rdata.status?1:2}); - // },'json'); - // }); - // }); - } - }); - },'json'); -} - - -function removeTempAccess(id){ - $.post('/config/remove_temp_login', {id:id}, function(rdata){ - showMsg(rdata.msg, function(){ - setTempAccessReq(); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); -} - -function getTempAccessLogsReq(id){ - $.post('/config/get_temp_login_logs', {id:id}, function(rdata){ - var tbody = ''; - for (var i = 0; i < rdata.data.length; i++) { - - tbody += ''; - tbody += '' + (rdata.data[i]['type']) +''; - tbody += '' + rdata.data[i]['addtime'] +''; - tbody += ''+ rdata.data[i]['log'] +''; - tbody += ''; - } - $('#logs_list').html(tbody); - - },'json'); -} - -function getTempAccessLogs(id){ - - layer.open({ - area: ['700px', '250px'], - title: '临时授权管理', - closeBtn:1, - shift: 0, - type: 1, - content: "
        \ - \ -
        \ - \ - \ - \ - \ - \ -
        操作类型操作时间日志
        \ -
        \ -
        ", - success:function(){ - getTempAccessLogsReq(id); - $('.refresh_log').click(function(){ - getTempAccessLogsReq(id); - }); - } - }); -} - -function setTempAccessReq(page){ - if (typeof(page) == 'undefined'){ - page = 1; - } - - $.post('/config/get_temp_login', {page:page}, function(rdata){ - if ( typeof(rdata.status) !='undefined' && !rdata.status){ - showMsg(rdata.msg,function(){ - layer.closeAll(); - },{icon:2}, 2000); - return; - } - - var tbody = ''; - for (var i = 0; i < rdata.data.length; i++) { - - tbody += ''; - tbody += '' + (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 += ''; - } - - $('#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: '
        \ -
        \ - 相应状态\ -
        \ - \ -
        \ -
        \ -
        • 用于未登录且未正确输入安全入口时的响应,用于隐藏面板特征
        \ -
        ', - success:function(){ - var msg_list = [ - {'code':'0','msg':'默认-安全入口错误提示'}, - {'code':'403','msg':'403-拒绝访问'}, - {'code':'404','msg':'404-页面不存在'}, - {'code':'416','msg':'416-无效的请求'}, - {'code':'408','msg':'408-客户端超时'}, - {'code':'400','msg':'400-客户端请求错误'}, - {'code':'401','msg':'401-未授权访问'}, - ]; - - var tbody = ''; - for(i in msg_list){ - if (msg_list[i]['code'] == code){ - tbody += ''; - } else{ - tbody += ''; - } - - } - $('select[name="status_code"]').append(tbody); - }, - yes:function(index){ - var loadT = layer.msg("正在设置未认证时的响应状态", { icon: 16, time: 0, shade: [0.3, '#000'] }); - var status_code = $('select[name="status_code"]').val(); - $.post('/config/set_status_code', { status_code: status_code }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - location.reload(); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - } - }); -} - -function setTempAccess(){ - layer.open({ - area: ['700px', '250px'], - title: '临时授权管理', - closeBtn:1, - shift: 0, - type: 1, - content: "
        \ - \ -
        \ - \ - \ - \ - \ - \ -
        登录IP状态登录时间过期时间操作
        \ - \ -
        \ -
        ", - success:function(){ - setTempAccessReq(); - - $('.create_temp_login').click(function(){ - layer.confirm('注意1:滥用临时授权可能导致安全风险。
        注意2:请勿在公共场合发布临时授权连接

        即将创建临时授权连接,继续吗?', - { - title:'风险提示', - closeBtn:1, - icon:13, - }, function(create_temp_login_layer) { - $.post('/config/set_temp_login', {}, function(rdata){ - layer.close(create_temp_login_layer); - setTempAccessReq(); - layer.open({ - area: '570px', - title: '创建临时授权', - shift: 0, - type: 1, - content: "
        \ -
        \ - 临时授权地址\ -
        \ - \ -
        \ -
        \ -
        \ -
          \ -
        • 临时授权生成后1小时内使用有效,为一次性授权,使用后立即失效
        • \ -
        • 使用临时授权登录面板后1小时内拥有面板所有权限,请勿在公共场合发布临时授权连接
        • \ -
        • 授权连接信息仅在此处显示一次,若在使用前忘记,请重新生成
        • \ -
        \ -
        ", - success:function(){ - var temp_link = "".concat(location.origin, "/login?tmp_token=").concat(rdata.token); - $('#temp_link').val(temp_link); - - copyText(temp_link); - $('.btn-copy-temp-link').click(function(){ - copyText(temp_link); - }); - } - }); - },'json'); - }); - }); - } - }); -} - -//二次验证 -function setAuthBind(){ - $.post('/config/get_auth_secret', {}, function(rdata){ - console.log(rdata); - var tip = layer.open({ - area: ['500px', '355px'], - title: '二次验证设置', - closeBtn:1, - shift: 0, - type: 1, - content: '
        \ -
        \ - 绑定密钥\ -
        \ - \ - \ -
        \ -
        \ -
        \ - 二维码\ -
        \ -
        \ -
          \ -
        \ -
        ', - success:function(layero,index){ - - $('input[name="secret"]').val(rdata.data['secret']); - $('.qrcode').qrcode({ text: rdata.data['url']}); - - $('.reset_secret').click(function(){ - layer.confirm('您确定要重置当前密钥吗?
        重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。',{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: '
        \ -
        \ -

        风险操作!此功能不懂请勿开启!

        \ -
        \ -
          \ -
        • 必须要用到且了解此功能才决定自己是否要开启!
        • \ -
        • 开启后,以任何方式访问面板,将先要求输入BasicAuth用户名和密码
        • \ -
        • 开启后,能有效防止面板被扫描发现,但并不能代替面板本身的帐号密码
        • \ -
        • 请牢记BasicAuth密码,一但忘记将无法访问面板
        • \ -
        • 如忘记密码,可在SSH通过mw命令来关闭BasicAuth验证
        • \ -
        \ -
        \ - \ - \ -
        \ -
        ', - btn:["确定","取消"], - yes:function(l,index){ - is_agree = $('#agreement_more').prop("checked"); - if (is_agree){ - layer.close(tip); - callback(); - } - return is_agree; - }, - btn2: function(index, layero){ - $('#cfg_basic_auth').prop("checked", false); - } - }); -} - -function setBasicAuth(){ - - var basic_auth = $('#cfg_basic_auth').prop("checked"); - if (!basic_auth){ - setBasicAuthTip(function(){ - var tip = layer.open({ - area: ['500px', '385px'], - title: '配置BasicAuth认证', - closeBtn:1, - shift: 0, - type: 1, - content: '
        \ -
        \ - 用户名\ -
        \ -
        \ -
        \ - 密码\ -
        \ -
        \ -
        \ - \ -
        \ -
        \ -
          \ -
        • 注意:请不要在这里使用您的常用密码,这可能导致密码泄漏!
        • \ -
        • 开启后,以任何方式访问面板,将先要求输入BasicAuth用户名和密码
        • \ -
        • 开启后,能有效防止面板被扫描发现,但并不能代替面板本身的帐号密码
        • \ -
        • 请牢记BasicAuth密码,一但忘记将无法访问面板
        • 如忘记密码,可在SSH通过mw命令来关闭BasicAuth验证
        • \ -
        \ -
        ', - success:function(){ - $('.save_auth_cfg').click(function(){ - var basic_user = $('input[name="basic_user"]').val(); - var basic_pwd = $('input[name="basic_pwd"]').val(); - $.post('/config/set_basic_auth', {'basic_user':basic_user,'basic_pwd':basic_pwd},function(rdata){ - showMsg(rdata.msg, function(){ - window.location.reload(); - } ,{icon:rdata.status?1:2}, 2000); - },'json'); - }); - }, - cancel:function(){ - $('#cfg_basic_auth').prop("checked", false); - }, - }); - }); - } else { - layer.confirm('关闭BasicAuth认证后,面板登录将不再验证BasicAuth基础认证,这将会导致面板安全性下降,继续操作!', - {btn: ['确定', '取消'], title: "是否关闭BasicAuth认证?", icon:13}, function (index) { - var basic_user = ''; - var basic_pwd = ''; - $.post('/config/set_basic_auth', {'is_open':'false'},function(rdata){ - showMsg(rdata.msg, function(){ - layer.close(index); - window.location.reload(); - } ,{icon:rdata.status?1:2}, 2000); - },'json'); - },function(){ - $('#cfg_basic_auth').prop("checked", true); - }); - } -} - -function showPanelApi(){ - $.post('/config/get_panel_token', '', function(rdata){ - var tip = layer.open({ - area: ['500px', '355px'], - title: '配置面板API', - closeBtn:1, - shift: 0, - type: 1, - content: '
        \ -
        \ - 接口密钥\ -
        \ - \ - \ -
        \ -
        \ -
        \ - IP白名单
        (每行1个)
        \ -
        \ -
        \ -
        \ - \ -
        \ -
        \ -
          \ -
        • 开启API后,必需在IP白名单列表中的IP才能访问面板API接口
        • \ -
        • 请不要在生产环境开启,这可能增加服务器安全风险;
        • \ -
        \ -
        ', - success:function(layero,index){ - - $('input[name="token"]').val(rdata.data.token); - $('textarea[name="api_limit_addr"]').val(rdata.data.limit_addr); - - - $('.reset_token').click(function(){ - layer.confirm('您确定要重置当前密钥吗?
        重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。',{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:'
        ' - +'
        '
        -			// +'
        ' - // +'' - // +'' - // +'
        ' - +'
        ', - success:function(index,layer_index){ - requestLogs(layer_index); - reqTimer = setInterval(function(){ - requestLogs(layer_index); - },3000); - }, - - yes:function(index){ - clearInterval(reqTimer); - closeLogs(id); - layer.close(index); - }, - }); -} - - -function getBackupName(hook_data, name){ - for (var i = 0; i < hook_data.length; i++) { - if (hook_data[i]['name'] == name){ - return hook_data[i]['title']; - } - } - return name; -} - -function getCronData(page){ - var load = layer.msg(lan.public.the,{icon:16,time:0,shade: [0.3, '#000']}); - $.post("/crontab/list?p="+page,'', function(rdata){ - layer.close(load); - var cbody = ""; - if(rdata == ""){ - cbody=""+lan.crontab.task_empty+""; - } else { - for(var i=0;i正常' - :'停用'; - - - var cron_save = '--'; - if (rdata.data[i]['save'] != ''){ - cron_save = rdata.data[i]['save']+'份'; - } - - var cron_backupto = '-'; - if (rdata.data[i]['stype'] == 'site' || rdata.data[i]['stype']=='path' || rdata.data[i]['stype']=='database' || rdata.data[i]['stype'].indexOf('database_')>-1 ){ - cron_backupto = '本地磁盘'; - if (rdata.data[i]['backup_to'] != 'localhost'){ - cron_backupto = getBackupName(rdata['backup_hook'],rdata.data[i]['backup_to']); - } - } - - cbody += "\ - "+rdata.data[i].name+"\ - "+status+"\ - "+rdata.data[i].type+"\ - "+rdata.data[i].cycle+"\ - "+cron_save +"\ - "+cron_backupto+"\ - "+rdata.data[i].addtime+"\ - \ - 执行 | \ - 编辑 | \ - 日志 | \ - 删除\ - \ - "; - } - } - $('#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 59){ -// if(hour >= 23) hour = 0; -// $("#cronConfig input[name='hour']").val(hour+1); -// minute = 5; -// } -// $("#cronConfig input[name='minute']").val(minute); -// $("#cronConfig input[name='name']").val(sTitle + '['+dataList[0]+']'); -// $("#cronConfig input[name='sName']").val(dataList[0]); -// var pdata = $("#cronConfig").serialize() + '&sBody=&urladdress='; -// $.ajax({ -// type:'POST', -// url:'/crontab/add', -// data:pdata, -// async: true, -// success:function(frdata){ -// layer.close(loadT); -// if(frdata.status){ -// successCount++; -// getCronData(1); -// }else{ -// if(!errorMsg){ -// errorMsg = '

        '+lan.crontab.backup_all_err+'

        '; -// } -// errorMsg += '
      • '+dataList[0]+' -> '+frdata.msg+'
      • ' -// } - -// dataList.splice(0,1); -// allAddCrontab(dataList,successCount,errorMsg); -// } -// }); -// } - -initDropdownMenu(); -function initDropdownMenu(){ - $(".dropdown ul li a").click(function(){ - var txt = $(this).text(); - var type = $(this).attr("value"); - $(this).parents(".dropdown").find("button b").text(txt).attr("val",type); - switch(type){ - case 'day': - closeOpt(); - toHour(); - toMinute(); - break; - case 'day-n': - closeOpt(); - toWhere1('天'); - toHour(); - toMinute(); - break; - case 'hour': - closeOpt(); - toMinute(); - break; - case 'hour-n': - closeOpt(); - toWhere1('小时'); - toMinute(); - break; - case 'minute-n': - closeOpt(); - toWhere1('分钟'); - break; - case 'week': - closeOpt(); - toWeek(); - toHour(); - toMinute(); - break; - case 'month': - closeOpt(); - toWhere1('日'); - toHour(); - toMinute(); - break; - case 'toFile': - toFile(); - break; - case 'toShell': - toShell(); - $(".controls").html('脚本内容'); - break; - case 'rememory': - rememory(); - $(".controls").html('提示'); - break; - case 'site': - toBackup('sites'); - $(".controls").html('备份网站'); - break; - case 'database_mariadb': - case 'database_mongodb': - case 'database_postgresql': - case 'database_mysql-apt': - case 'database_mysql-yum': - case 'database': - toBackup(type); - $(".controls").html('备份数据库'); - break; - case 'path': - toBackup('path'); - $(".controls").html('备份目录'); - break; - case 'logs': - toLogsHtml('logs'); - $(".controls").html('切割网站'); - break; - case 'toUrl': - toUrl(); - $(".controls").html('URL地址'); - break; - } - }); -} - - -//备份 -function toLogsHtml(type){ - var sMsg = ""; - switch(type){ - case 'sites': - sMsg = '备份网站'; - sType = "sites"; - break; - case 'database_mariadb': - case 'database_mongodb': - case 'database_postgresql': - case 'database_mysql-apt': - case 'database_mysql-yum': - case 'database': - sMsg = '备份数据库'; - suffix = type.replace('database','') - if (suffix != ''){ - suffix = suffix.replace('_','') - sMsg = '备份数据库['+suffix+']'; - } - sType = type; - break; - case 'logs': - sMsg = '切割日志'; - sType = "logs"; - break; - case 'path': - sMsg = '备份目录'; - sType = "path"; - break; - } - var data = 'type='+sType; - - $.post('/crontab/get_data_list',data,function(rdata){ - $(".planname input[name='name']").attr('readonly','true').css({"background-color":"#f6f6f6","color":"#666"}); - var sOpt = ""; - if(rdata.data.length == 0){ - layer.msg(lan.public.list_empty,{icon:2}) - return; - } - - for(var i=0;i'+rdata.data[i].name+'['+rdata.data[i].ps+']'; - } - - - if (sType != 'path'){ - sOpt = '
      • 所有
      • ' + sOpt; - } - - var orderOpt = ''; - for (var i=0;i'+rdata.orderOpt[i].title+'' - } - - - var changeDir = ''; - if (sType == 'path'){ - changeDir = ''; - } - - var sBody = '\ - '+ changeDir +'\ -
        \ -
        保留最新
        \ - \ - 份\ -
        '; - $("#implement").html(sBody); - getselectname(); - - $('.changePathDir').click(function(){ - changePathCallback($('#sName').val(),function(select_dir){ - $(".planname input[name='name']").val('备份目录['+select_dir+']'); - $('#implement .sname b').attr('val',select_dir).text(select_dir); - }); - }); - - - $(".dropdown ul li a").click(function(){ - var sName = $("#sName").attr("val"); - if(!sName) return; - $(".planname input[name='name']").val(sMsg+'['+sName+']'); - }); - },'json'); - -} - -//备份 -function toBackup(type){ - var sMsg = ""; - switch(type){ - case 'sites': - sMsg = '备份网站'; - sType = "sites"; - break; - case 'database_mariadb': - case 'database_mongodb': - case 'database_postgresql': - case 'database_mysql-apt': - case 'database_mysql-yum': - case 'database': - sMsg = '备份数据库'; - suffix = type.replace('database','') - if (suffix != ''){ - suffix = suffix.replace('_','') - sMsg = '备份数据库['+suffix+']'; - } - sType = type; - break; - case 'logs': - sMsg = '切割日志'; - sType = "logs"; - break; - case 'path': - sMsg = '备份目录'; - sType = "path"; - break; - } - var data = 'type='+sType; - - $.post('/crontab/get_data_list',data,function(rdata){ - $(".planname input[name='name']").attr('readonly','true').css({"background-color":"#f6f6f6","color":"#666"}); - var sOpt = ""; - if(rdata.data.length == 0){ - layer.msg(lan.public.list_empty,{icon:2}) - return; - } - - for(var i=0;i'+rdata.data[i].name+'['+rdata.data[i].ps+']'; - } - - - if (sType != 'path'){ - sOpt = '
      • 所有
      • ' + sOpt; - } - - var orderOpt = ''; - for (var i=0;i'+rdata.orderOpt[i].title+'' - } - - - var changeDir = ''; - if (sType == 'path'){ - changeDir = ''; - } - - var sBody = '\ - '+ changeDir +'\ -
        备份到
        \ - \ -
        保留最新
        \ - \ - 份\ -
        '; - $("#implement").html(sBody); - getselectname(); - - $('.changePathDir').click(function(){ - changePathCallback($('#sName').val(),function(select_dir){ - $(".planname input[name='name']").val('备份目录['+select_dir+']'); - $('#implement .sname b').attr('val',select_dir).text(select_dir); - }); - }); - - - $(".dropdown ul li a").click(function(){ - var sName = $("#sName").attr("val"); - if(!sName) return; - $(".planname input[name='name']").val(sMsg+'['+sName+']'); - }); - },'json'); - -} - - -// 编辑计划任务 -function editTaskInfo(id){ - layer.msg('正在获取,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/crontab/get_crond_find',{id:id},function(rdata){ - layer.closeAll(); - // console.log('get_crond_find:', rdata); - var sTypeName = '',sTypeDom = '',cycleName = '',cycleDom = '',weekName = '',weekDom = '',sNameName ='',sNameDom = '',backupsName = '',backupsDom =''; - obj = { - from:{ - id:rdata.id, - name: rdata.name, - type: rdata['type'], - stype: rdata.stype, - where1: rdata.where1, - hour: rdata.where_hour, - minute: rdata.where_minute, - week: rdata.where1, - sbody: rdata.sbody, - sname: rdata.sname, - backup_to: rdata.backup_to, - save: rdata.save, - urladdress: rdata.urladdress, - }, - sTypeArray:[['toShell','Shell脚本'],['site','备份网站'],['database','备份数据库'],['logs','日志切割'],['path','备份目录'],['rememory','释放内存'],['toUrl','访问URL']], - cycleArray:[['day','每天'],['day-n','N天'],['hour','每小时'],['hour-n','N小时'],['minute-n','N分钟'],['week','每星期'],['month','每月']], - weekArray:[[1,'周一'],[2,'周二'],[3,'周三'],[4,'周四'],[5,'周五'],[6,'周六'],[7,'周日']], - sNameArray:[], - backupsArray:[], - create:function(callback){ - if (obj.from['stype'].indexOf('database_')>-1){ - name = obj.from['stype'].replace('database_',''); - sTypeName = '备份数据库['+name+']'; - sTypeDom += '
      • '+ sTypeName +'
      • '; - } else { - for(var i = 0; i '+ obj['sTypeArray'][i][1] +''; - } - } - - for(var i = 0; i '+ obj['cycleArray'][i][1] +''; - } - - for(var i = 0; i '+ obj['weekArray'][i][1] +''; - } - - if(obj.from.stype == 'site' || obj.from.stype == 'database' || obj.from.stype == 'path' || obj.from.stype == 'logs' || obj.from['stype'].indexOf('database_')>-1){ - $.post('/crontab/get_data_list',{type:obj.from.stype},function(rdata){ - // console.log(rdata); - obj.sNameArray = rdata.data; - obj.sNameArray.unshift({name:'ALL',ps:'所有'}); - obj.backupsArray = rdata.orderOpt; - obj.backupsArray.unshift({title:'服务器磁盘',name:'localhost'}); - for(var i = 0; i '+ obj['sNameArray'][i]['ps'] +''; - } - for(var i = 0; i '+ obj['backupsArray'][i]['title'] +''; - } - callback(); - },'json'); - }else{ - callback(); - } - } - }; - obj.create(function(){ - - var changeDir = ''; - if (obj.from.stype == 'path'){ - changeDir = ''; - } - - layer.open({ - type:1, - title:'编辑计划任务-['+rdata.name+']', - area: ['850px','440px'], - skin:'layer-create-content', - shadeClose:false, - closeBtn:1, - content:'
        \ -
        \ - 任务类型\ - \ -
        \ -
        \ - 任务名称\ -
        \ -
        \ -
        \ - 执行周期\ - \ -
        \ - \ -
        日
        \ -
        时
        \ -
        分
        \ -
        \ -
        \ - \ -
        \ - 脚本内容\ -
        \ -
        \ -
        \ - 提示\ -
        释放PHP、MYSQL、PURE-FTPD、OpenResty的内存占用,建议在每天半夜执行!
        \ -
        \ -
        \ - URL地址\ -
        \ -
        \ -
        \ -
        保存编辑
        \ -
        \ -
        ', - - success:function(){ - - $('.changePathDir').click(function(){ - changePathCallback($('#sName').val(),function(select_dir){ - $('input[name="name"]').val('备份目录['+select_dir+']'); - $('.sName_btn .sname b').attr('val',select_dir).text(select_dir); - obj.from.sname = select_dir; - }); - }); - - if(obj.from.stype == 'toShell'){ - $('.site_list').hide(); - } else if (obj.from.stype == 'rememory') { - $('.site_list').hide(); - } else if ( obj.from.stype == 'toUrl'){ - $('.site_list').hide(); - } else { - $('.site_list').show(); - } - - - - obj.from.minute = $('.minute_create').val(); - obj.from.hour = $('.hour_create').val(); - obj.from.where1 = $('.where1_create').val(); - - $('.sName_create').blur(function () { - obj.from.name = $(this).val(); - }); - $('.where1_create').blur(function () { - obj.from.where1 = $(this).val(); - }); - - $('.hour_create').blur(function () { - obj.from.hour = $(this).val(); - }); - - $('.minute_create').blur(function () { - obj.from.minute = $(this).val(); - }); - - $('.save_create').blur(function () { - obj.from.save = $(this).val(); - }); - - $('.sBody_create').blur(function () { - obj.from.sbody = $(this).val(); - }); - $('.url_create').blur(function () { - obj.from.urladdress = $(this).val(); - }); - - $('[aria-labelledby="cycle"] a').unbind().click(function () { - $('.cycle_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - var type = $(this).attr('value'); - switch(type){ - case 'day': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.type = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'day-n': - $('.week_btn').hide(); - $('.where1_input').show().find('input').val('1'); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = 1; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'hour': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').hide(); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = ''; - obj.from.minute = 30; - break; - case 'hour-n': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'minute-n': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').hide(); - $('.minute_input').show(); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = ''; - obj.from.minute = 30; - console.log(obj.from); - break; - case 'week': - $('.week_btn').show(); - $('.where1_input').hide(); - $('.hour_input').show(); - $('.minute_input').show(); - obj.from.week = 1; - obj.from.where1 = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'month': - $('.week_btn').hide(); - $('.where1_input').show(); - $('.hour_input').show(); - $('.minute_input').show(); - obj.from.week = ''; - obj.from.where1 = 1; - obj.from.hour = 1; - obj.from.minute = 30; - break; - } - obj.from.type = $(this).attr('value'); - }); - - $('[aria-labelledby="week"] a').unbind().click(function () { - $('.week_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - obj.from.week = $(this).attr('value'); - }); - - $('[aria-labelledby="backupTo"] a').unbind().click(function () { - $('.backup_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - obj.from.backup_to = $(this).attr('value'); - }); - $('.plan-submits').unbind().click(function(){ - if(obj.from.type == 'hour-n'){ - obj.from.where1 = obj.from.hour; - obj.from.hour = ''; - } else if(obj.from.type == 'minute-n') { - obj.from.where1 = obj.from.minute; - obj.from.minute = ''; - } - var loadT = layer.msg('正在保存编辑内容,请稍后...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/crontab/modify_crond',obj.from,function(rdata){ - - if (!rdata.status){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - return; - } - - showMsg(rdata.msg, function(){ - layer.closeAll(); - getCronData(1); - initDropdownMenu(); - },{icon:rdata.status?1:2}, 2000); - - },'json'); - }); - } - ,cancel: function(){ - initDropdownMenu(); - } - }); - }); - },'json'); -} - - -//下拉菜单名称 -function getselectname(){ - $(".dropdown ul li a").click(function(){ - var txt = $(this).text(); - var type = $(this).attr("value"); - $(this).parents(".dropdown").find("button b").text(txt).attr("val",type); - }); -} -//清理 -function closeOpt(){ - $("#ptime").html(''); -} -//星期 -function toWeek(){ - var mBody = ''; - $("#ptime").html(mBody); - getselectname(); -} -//指定1 -function toWhere1(ix){ - var mBody ='
        \ - \ - '+ix+'\ -
        '; - $("#ptime").append(mBody); -} -//小时 -function toHour(){ - var mBody = '
        \ - \ - 小时\ -
        '; - $("#ptime").append(mBody); -} - -//分钟 -function toMinute(){ - var mBody = '
        \ - \ - 分钟\ -
        '; - $("#ptime").append(mBody); - -} - -//从文件 -function toFile(){ - var tBody = '\ - '; - $("#implement").html(tBody); - $(".planname input[name='name']").removeAttr('readonly style').val(""); -} - -//从脚本 -function toShell(){ - var tBody = ""; - $("#implement").html(tBody); - $(".planname input[name='name']").removeAttr('readonly style').val(""); -} - -//从脚本 -function toUrl(){ - var tBody = ""; - $("#implement").html(tBody); - $(".planname input[name='name']").removeAttr('readonly style').val(""); -} - -//释放内存 -function rememory(){ - $(".planname input[name='name']").removeAttr('readonly style').val(""); - $(".planname input[name='name']").val(lan.crontab.mem); - $("#implement").html(lan.crontab.mem_ps); - return; -} -//上传 -function fileupload(){ - $("#sFile").change(function(){ - $("#viewfile").val($("#sFile").val()); - }); - $("#sFile").click(); -} \ No newline at end of file diff --git a/route/static/app/files.js b/route/static/app/files.js deleted file mode 100755 index 5fad6c602..000000000 --- a/route/static/app/files.js +++ /dev/null @@ -1,1864 +0,0 @@ -// console.log(lan); - -//判断磁盘数量超出宽度 -function isDiskWidth(){ - var comlistWidth = $("#comlist").width(); - var bodyWidth = $(".file-box").width(); - if(comlistWidth + 530 > bodyWidth){ - $("#comlist").css({"width":bodyWidth-530+"px","height":"34px","overflow":"auto"}); - } - else{ - $("#comlist").removeAttr("style"); - } -} - -//打开回收站 -function recycleBin(type){ - $.post('/files/get_recycle_bin','',function(data){ - // console.log(rdata); - var rdata = data['data']; - var body = '' - switch(type){ - case 1: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.dirs[i].size)+'\ - '+getLocalTime(rdata.dirs[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - '; - } - for(var i=0;i 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+'\ - \ - '; - - 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+'\ - '+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 2: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.dirs[i].size)+'\ - '+getLocalTime(rdata.dirs[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - ' - } - $("#RecycleBody").html(body); - return; - break; - case 3: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.files[i].size)+'\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - '; - } - $("#RecycleBody").html(body); - return; - break; - case 4: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.files[i].size)+'\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - '; - } - } - $("#RecycleBody").html(body); - return; - break; - case 5: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.files[i].size)+'\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - ' - } - } - $("#RecycleBody").html(body); - return; - } - - - var tablehtml = '
        \ -
        \ - 文件回收站\ -
        \ - \ - \ -
        \ -
        \ - 注意:关闭回收站,删除的文件无法恢复!\ - \ -
        \ -
        \ -
        \ -

        全部

        \ -

        文件夹

        \ -

        文件

        \ -

        图片

        \ -

        文档

        \ -
        \ -
        \ -
        \ - \ - \ - \ - \ - \ - \ - \ - \ - '+body+'\ -
        文件名原位置大小删除时间操作
        '; - if(type == 'open'){ - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: ['80%','606px'], - title: lan.files.recycle_bin_title, - content: tablehtml - }); - - if(window.location.href.indexOf("database") != -1){ - recycleBin(6); - $(".re-con-menu p:last-child").addClass("on").siblings().removeClass("on"); - }else{ - recycleBin(1); - } - } - $(".re-con-menu p").click(function(){ - $(this).addClass("on").siblings().removeClass("on"); - }) - },'json'); -} - -//去扩展名不处理 -function getFileName(name){ - var text = name.split("/"); - var n = text.length-1; - text = text[n]; - return text; -} - -//判断图片文件 -function reisImage(fileName){ - var exts = ['jpg','jpeg','png','bmp','gif','tiff','ico']; - for(var i=0; i正在删除,请稍候...
        ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){ - getSpeed('.myspeed'); - },1000); - $.post('/files/close_recycle_bin', '', function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - $("#RecycleBody").html(''); - },'json'); - }); -} - - -//回收站开关 -function setRecycleBin(db){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - var data = {} - if(db == 1){ - data = {db:db}; - } - $.post('/files/recycle_bin',data,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - },'json'); -} - -function openFilename(obj){ - var path = $(obj).attr('data-path'); - var ext = getSuffixName(path); - - // console.log(path,ext); - if (inArray(ext,['html','htm','php','txt','md','js','css','scss','json','c','h','pl','py','java','log','conf','sh','json','ini'])){ - onlineEditFile(0, path); - } - - if (inArray(ext,['png','jpeg','jpg','gif','webp','bmp','ico'])){ - getImage(path); - } - - if (inArray(ext,['svg'])){ - var url = '/files/download?filename='+path; - layer.open({ - type:1, - closeBtn: 1, - title:"SVG预览", - area: ['600px','500px'], - maxmin:true, - shadeClose: true, - content: '' - }); - } -} - -function searchFile(p){ - getFiles(p); -} - -//处理排序 -function listFileOrder(skey, obj){ - var or = getCookie('file_order'); - var orderType = 'desc'; - if(or){ - var or_arr = or.split('|'); - if(or.split('|')[1] == 'desc'){ - orderType = 'asc'; - } else if (or_arr[1] == 'asc'){ - orderType = 'none'; - } else { - orderType = 'desc'; - } - } - setCookie('file_order',skey + '|' + orderType); - getFiles(1); - // console.log(obj,orderType); - // if(orderType == 'desc'){ - // $(obj).find(".glyphicon-triangle-top").remove(); - // $(obj).append(""); - // } else { - // $(obj).find(".glyphicon-triangle-bottom").remove(); - // $(obj).append(""); - // } -} - -function makeFilePage(showRow, page = ''){ - var rows = ['10','50','100','200','500','1000','2000']; - var rowOption = ''; - for(var i=0;i'+rows[i]+''; - } - - //分页 - $("#filePage").html(page); - $("#filePage div").append("每页条"); - $("#filePage .Pcount").css("left","16px"); -} - -//取数据 -function getFiles(Path) { - if(isNaN(Path)){ - var p = 1; - } else { - var p = Path; - Path = getCookie('open_dir_path'); - } - - var args = {}; - args['p'] = p; - var post = {}; - post['path'] = Path; - - var file_row = $.cookie('file_row'); - if(!file_row) { - file_row = '100'; - } - args['row'] = file_row; - - var body = ''; - var totalSize = 0; - - var search = ''; - var search_file = $("#search_file").val(); - - if(search_file.length > 0){ - args['search'] = search_file; - } - - var search_all = ''; - var all = $('#search_all').hasClass('active'); - if(all){ - args['all'] = 'yes'; - } - - var file_order = $.cookie('file_order'); - if (file_order){ - post['order'] = file_order.replace('|',' '); - } - - var query_str = toUrlParam(args); - - var loadT = layer.load(); - $.post('/files/get_dir?' + query_str, post, function(rdata) { - layer.close(loadT); - - //构建分页 - makeFilePage(file_row,rdata.PAGE); - - if(rdata.DIR == null) { - rdata.DIR = []; - } - - for (var i = 0; i < rdata.DIR.length; i++) { - - var fmp = rdata.DIR[i].split(";"); - var cnametext =fmp[0] + fmp[5]; - - fmp[0] = fmp[0].replace(/'/, "\\'"); - if(cnametext.length>20){ - cnametext = cnametext.substring(0,20) + '...'; - } - - if(isChineseChar(cnametext)){ - if(cnametext.length>10){ - cnametext = cnametext.substring(0,10) + '...'; - } - } - - var timetext ='--'; - if(getCookie('rank') == 'a'){ - //列表展示 - $("#set_list").addClass("active"); - $("#set_icon").removeClass("active"); - body += "\ - \ - \ - " + cnametext + "\ - "+toSize(fmp[1])+"\ - "+getLocalTime(fmp[2])+"\ - "+fmp[3]+"\ - "+fmp[4]+"\ - \ - 复制 | \ - 剪切 | \ - 重命名 | \ - 权限 | \ - 压缩 | \ - 删除\ - \ - "; - } 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])) + "\ - " + ((fmp[2].length > 11)?fmp[2]:getLocalTime(fmp[2])) + "\ - "+fmp[3]+"\ - "+fmp[4]+"\ - \ - 复制 | \ - 剪切 | \ - 重命名 | \ - 权限 | \ - 压缩 | " - + bodyZip - + download - + "删除\ - \ - "; - } - 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 = '\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - '+body+'\ -
        文件名'+fname_icon+'大小'+size_icon+'修改时间'+mtime_icon+'权限所有者操作
        '; - $("#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 = '
        \ - \ - \ -
        '; - if (rdata.PATH != '/') { - BarTools += ' '; - } - setCookie('open_dir_path',rdata.PATH); - BarTools += ' \ - '; - var copyName = getCookie('copyFileName'); - var cutName = getCookie('cutFileName'); - var isPaste = (copyName == 'null') ? cutName : copyName; - // console.log('isPaste:',isPaste); - //--- - if (isPaste != 'null' && isPaste != undefined) { - BarTools += ' '; - } - - $("#Batch").html(''); - var batchTools = ''; - var isBatch = getCookie('BatchSelected'); - if (isBatch == 1 || isBatch == '1') { - batchTools += ' '; - } - $("#Batch").html(batchTools); - - $("#setBox").prop("checked", false); - - $("#BarTools").html(BarTools); - - $("input[name=id]").click(function(){ - if($(this).prop("checked")) { - $(this).prop("checked", true); - $(this).parents("tr").addClass("ui-selected"); - } - else{ - $(this).prop("checked", false); - $(this).parents("tr").removeClass("ui-selected"); - } - showSeclect(); - }); - - $("#setBox").click(function() { - if ($(this).prop("checked")) { - $("input[name=id]").prop("checked", true); - $("#filesBody > tr").addClass("ui-selected"); - - } else { - $("input[name=id]").prop("checked", false); - $("#filesBody > tr").removeClass("ui-selected"); - } - showSeclect(); - }); - //阻止冒泡 - $("#filesBody .btlink").click(function(e){ - e.stopPropagation(); - }); - $("input[name=id]").dblclick(function(e){ - e.stopPropagation(); - }); - - - //禁用右键 - $("#fileCon").bind("contextmenu",function(e){ - return false; - }); - bindselect(); - - // //绑定右键 - $("#fileCon").mousedown(function(e){ - var count = totalFile(); - if(e.which == 3) { - if(count>1){ - rightMenuClickAll(e); - } else { - return; - } - } - }); - - $(".folderBox,.folderBoxTr").mousedown(function(e){ - // console.log(e); - var count = totalFile(); - if(e.which == 3) { - if(count <= 1){ - var a = $(this); - var option = rightMenuClick(a.attr("filetype"),a.attr("data-path"),a.find("input").val()); - a.contextify(option); - } else{ - rightMenuClickAll(e); - } - } - }); - - //每页行数 - $(".showRow").change(function(){ - setCookie('file_row',$(this).val()); - getFiles(p); - }); - pathPlaceBtn(rdata.PATH); - },'json'); - // setTimeout(function(){getCookie('open_dir_path');},200); -} - - -//统计选择数量 -function totalFile(){ - var el = $("input[name='id']"); - var len = el.length; - var count = 0; - for(var i=0;i 1){ - batchTools = '\ - \ - \ - \ - '; - }else{ - //setCookie('BatchSelected', null); - } - $("#Batch").html(batchTools); -} - -//滚动条事件 -$(window).scroll(function () { - if($(window).scrollTop() > 16){ - $("#tipTools").css({"position":"fixed","top":"0","left":"195px","box-shadow":"0 1px 10px 3px #ccc"}); - }else{ - $("#tipTools").css({"position":"absolute","top":"0","left":"0","box-shadow":"none"}); - } -}); -$("#tipTools").width($(".file-box").width()); -$("#PathPlaceBtn").width($(".file-box").width()-700); -$("#DirPathPlace input").width($(".file-box").width()-700); -if($(window).width()<1160){ - $("#PathPlaceBtn").width(290); -} -window.onresize = function(){ - $("#tipTools").width($(".file-box").width()-30); - $("#PathPlaceBtn").width($(".file-box").width()-700); - $("#DirPathPlace input").width($(".file-box").width()-700); - if($(window).width()<1160){ - $("#PathPlaceBtn,#DirPathPlace input").width(290); - } - pathLeft(); - isDiskWidth(); -} - -//批量操作 -function batch(type,access){ - var path = $("#DirPathPlace input").val(); - var el = document.getElementsByTagName('input'); - var len = el.length; - var data='path='+path+'&type='+type; - var name = 'data'; - var datas = []; - - var oldType = getCookie('BatchPaste'); - - for(var i=0;i正在处理,请稍候...
        ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){getSpeed('.myspeed');},1000); - // console.log(data); - $.post('/files/set_batch_data',data,function(rdata){ - layer.close(myloadT); - getFiles(path); - layer.msg(rdata.msg,{icon:1}); - },'json'); -} - -//批量粘贴 -function batchPaste(){ - var path = $("#DirPathPlace input").val(); - var type = getCookie('BatchPaste'); - var data = 'type='+type+'&path='+path; - - $.post('/files/check_exists_files',{dfile:path},function(rdata){ - var result = rdata['data']; - if(result.length > 0){ - var tbody = ''; - for(var i=0;i'+toSize(result[i].size)+''+getLocalTime(result[i].mtime)+''; - } - var mbody = '
        \ - '+tbody+'\ -
        文件名大小最后修改时间
        '; - safeMessage('即将覆盖以下文件',mbody,function(){ - batchPasteTo(data,path); - }); - $(".layui-layer-page").css("width","500px"); - }else{ - batchPasteTo(data,path); - } - },'json'); -} - -function batchPasteTo(data,path){ - myloadT = layer.msg("
        正在处理,请稍候...
        ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){getSpeed('.myspeed');},1000); - $.post('/files/batch_paste',data,function(rdata){ - layer.close(myloadT); - setCookie('BatchSelected', null); - getFiles(path); - layer.msg(rdata.msg,{icon:1}); - },'json'); -} - - -function getSuffixName(fileName){ - var extArr = fileName.split("."); - var exts = ['folder-unempty','sql','c','cpp','cs','flv','css','js', - 'htm','html','java','log','mht','url','xml','ai','bmp','cdr','gif','ico', - 'jpeg','jpg','JPG','png','psd','webp','ape','avi','mkv','mov','mp3','mp4', - 'mpeg','mpg','rm','rmvb','swf','wav','webm','wma','wmv','rtf','docx','fdf','potm', - 'pptx','txt','xlsb','xlsx','7z','cab','iso','rar','zip','gz','bt','file','apk','bookfolder', - 'folder','folder-empty','fromchromefolder','documentfolder','fromphonefolder', - 'mix','musicfolder','picturefolder','videofolder','sefolder','access','mdb','accdb', - 'fla','doc','docm','dotx','dotm','dot','pdf', - 'ppt','pptm','pot','xls','csv','xlsm','scss','svg','pl','py','php','md','json','sh','conf']; - var extLastName = extArr[extArr.length - 1]; - for(var i=0; i tr").hover(function(){ - $(this).addClass("hover"); - },function(){ - $(this).removeClass("hover"); - }).click(function(){ - $(this).addClass("on").siblings().removeClass("on"); - }); -} - -//取磁盘 -function getDisk() { - var LBody = ''; - $.get('/system/disk_info', function(rdata) { - for (var i = 0; i < rdata.length; i++) { - LBody += "\ -  " + (rdata[i].path=='/'?lan.files.path_root:rdata[i].path) + "(" + rdata[i].size[2] + ")"; - } - var trash = '\ -  回收站'; - $("#comlist").html(LBody+trash); - isDiskWidth(); - },'json'); -} - -//返回上一级 -function backDir() { - var str = $("#DirPathPlace input").val().replace('//','/'); - if(str.substr(str.length-1,1) == '/'){ - str = str.substr(0,str.length-1); - } - var Path = str.split("/"); - var back = '/'; - if (Path.length > 2) { - var count = Path.length - 1; - for (var i = 0; i < count; i++) { - back += Path[i] + '/'; - } - if(back.substr(back.length-1,1) == '/'){ - back = back.substr(0,back.length-1); - } - getFiles(back); - } else { - back += Path[0]; - getFiles(back); - } - setTimeout('pathPlaceBtn(getCookie("open_dir_path"));',200); -} -//新建文件 -function createFile(type, path) { - if (type == 1) { - var fileName = $("#newFileName").val(); - layer.msg(lan.public.the, { - icon: 16, - time: 10000 - }); - $.post('/files/create_file', 'path=' + encodeURIComponent(path + '/' + fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - if(rdata.status){ - getFiles($("#DirPathPlace input").val()); - onlineEditFile(0,path + '/' + fileName); - } - },'json'); - return; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', - title: '新建空白文件', - content: '
        \ -
        \ - \ -
        \ -
        \ - \ - \ -
        \ -
        ', - success:function(){ - $("#newFileName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#createFileBtn").click(); - }); - } - }); - -} -//新建目录 -function createDir(type, path) { - if (type == 1) { - var dirName = $("#newDirName").val(); - layer.msg('正在处理,请稍候...', { - icon: 16, - time: 10000 - }); - $.post('/files/create_dir', 'path=' + encodeURIComponent(path + '/' + dirName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles($("#DirPathPlace input").val()); - },'json'); - return; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', - title: '新建目录', - content: '
        \ -
        \ - \ -
        \ -
        \ - \ - \ -
        \ -
        ', - success:function(){ - $("#newDirName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#createDirBtn").click(); - }); - } - }); - -} - -//删除文件 -function deleteFile(fileName){ - layer.confirm(lan.get('recycle_bin_confirm',[fileName]),{title:'删除文件',closeBtn:2,icon:3},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/files/delete', 'path=' + encodeURIComponent(fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2, - }); - getFiles($("#DirPathPlace input").val()); - },'json'); - }); -} - -//删除目录 -function deleteDir(dirName){ - layer.confirm(lan.get('recycle_bin_confirm_dir',[dirName]),{title:'删除目录',closeBtn:2,icon:3},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/files/delete_dir', 'path=' + encodeURIComponent(dirName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles($("#DirPathPlace input").val()); - },'json'); - }); -} -//批量删除文件 -function allDeleteFileSub(data,path){ - layer.confirm('您确实要把这些文件放入回收站吗?',{title:'批量删除文件',closeBtn:2,icon:3},function(){ - layer.msg("
        正在处理,请稍候...
        ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){getSpeed('.myspeed');},1000); - $.post('/files/set_batch_data',data,function(rdata){ - layer.closeAll(); - getFiles(path); - layer.msg(rdata.msg,{icon:1}); - },'json'); - }); -} - -//重载文件列表 -function reloadFiles(){ - setInterval(function(){ - var path = $("#DirPathPlace input").val(); - getFiles(path); - },3000); -} - -//下载文件 -function downloadFile(action){ - - if(action == 1){ - var fUrl = $("#mUrl").val(); - fUrl = encodeURIComponent(fUrl); - - var fpath = $("#dpath").val(); - fname = encodeURIComponent($("#dfilename").val()); - - if (fUrl == "" ){ - layer.msg("URL地址不能为空!",{icon:2}); - return; - } - - layer.closeAll(); - layer.msg(lan.files.down_task,{time:0,icon:16,shade: [0.3, '#000']}); - - $.post('/files/download_file','path='+fpath+'&url='+fUrl+'&filename='+fname,function(rdata){ - layer.closeAll(); - getFiles(fpath); - getTaskCount(); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - return; - } - - var path = $("#DirPathPlace input").val(); - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '500px', - btn:["确定","关闭"], - title: lan.files.down_title, - content: '
        \ -
        \ - URL地址:\ - \ -
        \ -
        \ - 下载到:\ - \ -
        \ -
        \ - 文件名:\ - \ -
        \ -
        ', - success:function(){ - $("#mUrl").keyup(function(){ - durl = $(this).val(); - tmp = durl.split('/'); - $("#dfilename").val(tmp[tmp.length-1]); - }); - }, - yes:function(){ - downloadFile(1); - } - }); -} - -//重命名 -function reName(type, fileName) { - if (type == 1) { - var path = $("#DirPathPlace input").val(); - var newFileName = encodeURIComponent(path + '/' + $("#newFileName").val()); - var oldFileName = encodeURIComponent(path + '/' + fileName); - layer.msg(lan.public.the, { - icon: 16, - time: 10000 - }); - $.post('/files/mv_file', 'sfile=' + oldFileName + '&dfile=' + newFileName, function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles(path); - },'json'); - return; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', - title: '重命名', - btn:["确定","取消"], - content: '
        \ -
        \ - \ -
        \ -
        ', - success:function(){ - $("#newFileName").focus().keyup(function(e){ - if(e.keyCode == 13) $(".layui-layer-btn0").click(); - }); - }, - yes:function(){ - reName(1,fileName.replace(/'/,"\\'")); - } - }); - -} -//剪切 -function cutFile(fileName) { - var path = $("#DirPathPlace input").val(); - setCookie('cutFileName', fileName); - setCookie('copyFileName', null); - layer.msg('已剪切', { - icon: 1, - time: 1000, - }); - setTimeout(function(){ - getFiles(path); - },1000); -} -//复制 -function copyFile(fileName) { - var path = $("#DirPathPlace input").val(); - setCookie('copyFileName', fileName); - setCookie('cutFileName', null); - layer.msg('已复制', { - icon: 1, - time: 1000, - }); - - setTimeout(function(){ - getFiles(path); - },1000); -} -//粘贴 -function pasteFile(fileName) { - var path = $("#DirPathPlace input").val(); - var copyName = getCookie('copyFileName'); - var cutName = getCookie('cutFileName'); - var filename = copyName; - if(cutName != 'null' && cutName != undefined) filename=cutName; - filename = filename.split('/').pop(); - $.post('/files/check_exists_files',{dfile:path,filename:filename},function(result){ - if(result.length > 0){ - var tbody = ''; - for(var i=0;i'+toSize(result[i].size)+''+getLocalTime(result[i].mtime)+''; - } - var mbody = '
        \ - '+tbody+'\ -
        文件名大小最后修改时间
        '; - safeMessage('即将覆盖以下文件',mbody,function(){ - pasteTo(path,copyName,cutName,fileName); - }); - } else { - pasteTo(path,copyName,cutName,fileName); - } - },'json'); -} - - -function pasteTo(path,copyName,cutName,fileName){ - if (copyName != 'null' && copyName != undefined) { - layer.msg(lan.files.copy_the, { - icon: 16, - time: 0,shade: [0.3, '#000'] - }); - - //同目录下 - if (copyName == path +'/'+ fileName){ - fileName = '__copy__'+ fileName; - } - - $.post('/files/copy_file', 'sfile=' + encodeURIComponent(copyName) + '&dfile=' + encodeURIComponent(path +'/'+ fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles(path); - },'json'); - setCookie('copyFileName', null); - setCookie('cutFileName', null); - return; - } - - if (cutName != 'null' && cutName != undefined) { - layer.msg(lan.files.mv_the, { - icon: 16, - time: 0,shade: [0.3, '#000'] - }); - $.post('/files/mv_file', 'sfile=' + encodeURIComponent(cutName) + '&dfile=' + encodeURIComponent(path + '/'+fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); - getFiles(path); - },'json'); - setCookie('copyFileName', null); - setCookie('cutFileName', null); - } -} - - -//压缩目录 -function zip(dirName,submits) { - var path = $("#DirPathPlace input").val(); - if(submits != undefined){ - if(dirName.indexOf(',') == -1){ - tmp = $("#sfile").val().split('/'); - sfile = encodeURIComponent(tmp[tmp.length-1]); - }else{ - sfile = encodeURIComponent(dirName); - } - - dfile = encodeURIComponent($("#dfile").val()); - layer.closeAll(); - layer.msg(lan.files.zip_the, {icon: 16,time: 0,shade: [0.3, '#000']}); - $.post('/files/zip', 'sfile=' + sfile + '&dfile=' + dfile + '&type=tar&path='+encodeURIComponent(path), function(rdata) { - layer.closeAll(); - if(rdata == null || rdata == undefined){ - layer.msg(lan.files.zip_ok,{icon:1}); - getFiles(path) - reloadFiles(); - return; - } - layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); - if(rdata.status) getFiles(path); - },'json'); - return - } - - param = dirName; - if(dirName.indexOf(',') != -1){ - tmp = path.split('/'); - dirName = path + '/' + tmp[tmp.length-1]; - } - - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '650px', - title: lan.files.zip_title, - content: '
        ' - +'
        ' - +'' - +''+lan.files.zip_to+'\ - \ - ' - +'
        ' - +'
        ' - +'' - +'' - +'
        ' - +'
        ', - success:function(){ - $("#dfile").change(function(){ - var dfile = $(this).val() - tmp = dfile.split('.'); - if(tmp[tmp.length-1] != 'gz'){ - var path = $("#DirPathPlace input").val(); - tmp = path.split('/'); - dfile += '/' + tmp[tmp.length-1] + '.tar.gz' - $(this).val(dfile.replace(/\/\//g,'/')) - } - }); - } - }); - -} - -//解压目录 -function unZip(fileName, type) { - var path = $("#DirPathPlace input").val(); - if(type.length ==3){ - var sfile = encodeURIComponent($("#sfile").val()); - var dfile = encodeURIComponent($("#dfile").val()); - var coding = $("select[name='coding']").val(); - var tip = layer.msg(lan.files.unzip_the, {icon: 16,time: 0,shade: [0.3, '#000']}); - $.post('/files/unzip', 'sfile=' + sfile + '&dfile=' + dfile +'&type=' + type + '&path='+encodeURIComponent(path), function(rdata) { - layer.close(tip); - showMsg(rdata.msg, function(){ - getFiles(path); - },{icon: rdata.status ? 1 : 2},2000); - },'json'); - return; - } - - type = (type == 1) ? 'tar':'zip'; - var umpass = ''; - if(type == 'zip'){ - umpass = '
        \ - '+lan.files.zip_pass_title+'\ - \ -
        '; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '490px', - title: '解压文件', - content: '
        ' - +'
        ' - +''+lan.files.unzip_name+'
        ' - +'
        '+lan.files.unzip_to+'
        ' - + umpass +'
        '+lan.files.unzip_coding+'' - +'
        ' - +'
        ' - +'' - +'' - +'
        ' - +'
        ' - }); -} - -function isCompressFile(fileName){ - var ext = fileName.split('.'); - var extName = ext[ext.length-1].toLowerCase(); - var support = ['zip','gz','tgz','rar']; - for (x in support) { - if (support[x]==extName){ - return true; - } - } - return false; -} - -function unCompressFile(fileName, type = 0){ - // 解压文件 - var path = $("#DirPathPlace input").val(); - if(type == 3){ - var sfile = encodeURIComponent($("#sfile").val()); - var dfile = encodeURIComponent($("#dfile").val()); - var coding = $("select[name='coding']").val(); - var tip = layer.msg('正在解压,请稍候...', {icon: 16,time: 0,shade: [0.3, '#000']}); - $.post('/files/uncompress', 'sfile=' + sfile + '&dfile=' + dfile +'&type=' + type + '&path='+encodeURIComponent(path), function(rdata) { - layer.close(tip); - showMsg(rdata.msg, function(){ - layer.closeAll(); - getFiles(path); - },{icon: rdata.status ? 1 : 2},2000); - },'json'); - return; - } - - // var umpass = '
        \ - // 解压密码\ - // \ - //
        '; - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '490px', - title: '解压文件', - content: '
        \ -
        \ - 文件名\ - \ -
        \ -
        \ - 解压到\ - \ -
        \ -
        \ - 编码\ - \ -
        \ -
        \ - \ - \ -
        \ -
        ' - }); -} - -//是否压缩文件 -function isZip(fileName){ - var ext = fileName.split('.'); - var extName = ext[ext.length-1].toLowerCase(); - if( extName == 'zip') return 0; - if( extName == 'gz' || extName == 'tgz') return 1; - return -1; -} - -//是否文本文件 -function isText(fileName){ - var exts = ['rar','zip','tar.gz','gz','iso','xsl','doc','xdoc','jpeg', - 'jpg','png','gif','bmp','tiff','exe','so','7z','bz']; - return isExts(fileName,exts)?false:true; -} - -//是否图片文件 -function isImage(fileName){ - var exts = ['jpg','jpeg','png','bmp','gif','tiff','ico']; - return isExts(fileName,exts); -} - -//是否为指定扩展名 -function isExts(fileName,exts){ - var ext = fileName.split('.'); - if(ext.length < 2) return false; - var extName = ext[ext.length-1].toLowerCase(); - for(var i=0;i
        ' - }); -} - -//获取文件数据 -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:'
        \ - \ - \ - \ - \ - \ - \ - 文件编码:\ - \ - \ - \ -
          \ -
          ' - }); - uploadStart(); -} - -//设置权限 -function setChmod(action,fileName){ - if(action == 1){ - var chmod = $("#access").val(); - var chown = $("#chown").val(); - var data = 'filename='+ encodeURIComponent(fileName)+'&user='+chown+'&access='+chmod; - var loadT = layer.msg('正在设置...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/files/set_file_access',data,function(rdata){ - layer.close(loadT); - if(rdata.status) layer.closeAll(); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - var path = $("#DirPathPlace input").val(); - getFiles(path) - },'json'); - return; - } - - var toExec = fileName == lan.files.all?'batch(3,1)':'setChmod(1,\''+fileName+'\')'; - $.post('/files/file_access','filename='+encodeURIComponent(fileName),function(rdata){ - // console.log(rdata); - var sys_users = rdata.sys_users; - var own_html = ''; - var is_find_own_option = false; - for (var i = 0; i < sys_users.length; i++) { - var own = sys_users[i]; - if (rdata.chown==own){ - is_find_own_option = true; - own_html += ''; - } else { - own_html += ''; - } - } - if (!is_find_own_option){ - own_html += ''; - } - - layer.open({ - type:1, - closeBtn: 1, - title: '设置权限['+fileName+']', - area: '400px', - shadeClose:false, - content:'
          \ -
          \ - 所有者\ -

          读取

          \ -

          写入

          \ -

          执行

          \ -
          \ -
          \ - 用户组\ -

          读取

          \ -

          写入

          \ -

          执行

          \ -
          \ -
          \ - 公共\ -

          读取

          \ -

          写入

          \ -

          执行

          \ -
          \ -
          权限,\ - 所有者\ -
          \ -
          \ - \ - \ -
          \ -
          ' - }); - - onAccess(); - $("#access").keyup(function(){ - onAccess(); - }); - - $("input[type=checkbox]").change(function(){ - var idName = ['owner','group','public']; - var onacc = ''; - for(var n=0;n idName.length) continue; - if(onacc > 7) $("#access").val(access.substr(0,access.length-1)); - switch(onacc){ - case '1': - $("#"+idName[i]+"_x").prop('checked',true); - break; - case '2': - $("#"+idName[i]+"_w").prop('checked',true); - break; - case '3': - $("#"+idName[i]+"_x").prop('checked',true); - $("#"+idName[i]+"_w").prop('checked',true); - break; - case '4': - $("#"+idName[i]+"_r").prop('checked',true); - break; - case '5': - $("#"+idName[i]+"_r").prop('checked',true); - $("#"+idName[i]+"_x").prop('checked',true); - break; - case '6': - $("#"+idName[i]+"_r").prop('checked',true); - $("#"+idName[i]+"_w").prop('checked',true); - break; - case '7': - $("#"+idName[i]+"_r").prop('checked',true); - $("#"+idName[i]+"_w").prop('checked',true); - $("#"+idName[i]+"_x").prop('checked',true); - break; - } - } -} - - -//右键菜单 -function rightMenuClick(type,path,name){ - // console.log(type,path,name); - var displayZip = isZip(type); - var options = {items:[ - {text: "复制", onclick: function() {copyFile(path)}}, - {text: "剪切", onclick: function() {cutFile(path)}}, - {text: "重命名", onclick: function() {reName(0,name)}}, - {text: lan.files.file_menu_auth, onclick: function() {setChmod(0,path)}}, - {text: lan.files.file_menu_zip, onclick: function() {zip(path)}}, - ]}; - if(type == "dir"){ - options.items.push({text: lan.files.file_menu_del, onclick: function() { - deleteDir(path)} - }); - } - else if(isText(type)){ - options.items.push({text: lan.files.file_menu_edit, onclick: function() { - onlineEditFile(0,path); - }},{text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{ text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - else if(displayZip != -1){ - options.items.push({text: lan.files.file_menu_unzip, onclick: function() { - unZip(path,displayZip); - }},{text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - else if(isImage(type)){ - options.items.push({text: lan.files.file_menu_img, onclick: function() { - getImage(path); - }},{text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - else{ - options.items.push({text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - return options; -} - -//右键批量操作 -function rightMenuClickAll(e){ - var menu = $("#rmenu"); - var windowWidth = $(window).width(), - windowHeight = $(window).height(), - menuWidth = menu.outerWidth(), - menuHeight = menu.outerHeight(), - x = (menuWidth + e.clientX < windowWidth) ? e.clientX : windowWidth - menuWidth, - y = (menuHeight + e.clientY < windowHeight) ? e.clientY : windowHeight - menuHeight; - - menu.css('top', y) - .css('left', x) - .css('position', 'fixed') - .css("z-index","1") - .show(); -} -//取目录大小 -function getPathSize(){ - var path = encodeURIComponent($("#DirPathPlace input").val()); - layer.msg("正在计算,请稍候...",{icon:16,time:0,shade: [0.3, '#000']}) - $.post("/files/get_dir_size","path="+path,function(rdata){ - layer.closeAll(); - $("#pathSize").text(rdata.msg); - },'json'); -} - -$("body").not(".def-log").click(function(){ - $("#rmenu").hide() -}); - -//指定路径 -$("#DirPathPlace input").keyup(function(e){ - if(e.keyCode == 13) { - var fpath = $(this).val(); - fpath = filterPath(fpath); - getFiles(fpath); - } -}); - -function pathPlaceBtn(path){ - var html = ''; - var title = ''; - if(path == '/'){ - html = '
        • '+lan.files.path_root+'
        • '; - } else { - var dst_path = path.split("/"); - for(var i = 0; i'+dst_path[i]+''; - } - } - - html = '
            '+html+'
          '; - $("#PathPlaceBtn").html(html); - $("#PathPlaceBtn ul li a").click(function(e){ - var go_path = $(this).attr("title"); - if(go_path.length>1){ - if(go_path.substr(go_path.length-1,go_path.length) =='/'){ - go_path = go_path.substr(0,go_path.length-1); - } - } - getFiles(go_path); - e.stopPropagation(); - }); - pathLeft(); -} -//计算当前目录偏移 -function pathLeft(){ - var UlWidth = $("#PathPlaceBtn ul").width(); - var SpanPathWidth = $("#PathPlaceBtn").width() - 50; - var Ml = UlWidth - SpanPathWidth; - if(UlWidth > SpanPathWidth ){ - $("#PathPlaceBtn ul").css("left",-Ml); - } - else{ - $("#PathPlaceBtn ul").css("left",0); - } -} - -//路径快捷点击 -$("#PathPlaceBtn").on("click", function(e){ - if($("#DirPathPlace").is(":hidden")){ - $("#DirPathPlace").css("display","inline"); - $("#DirPathPlace input").focus(); - $(this).hide(); - }else{ - $("#DirPathPlace").hide(); - $(this).css("display","inline"); - } - $(document).one("click", function(){ - $("#DirPathPlace").hide(); - $("#PathPlaceBtn").css("display","inline"); - }); - e.stopPropagation(); -}); -$("#DirPathPlace").on("click", function(e){ - e.stopPropagation(); -}); diff --git a/route/static/app/firewall.js b/route/static/app/firewall.js deleted file mode 100755 index 19f7d6875..000000000 --- a/route/static/app/firewall.js +++ /dev/null @@ -1,370 +0,0 @@ -setTimeout(function(){ - getSshInfo(); -},500); - -setTimeout(function(){ - showAccept(1); -},1000); - - -$(function(){ - // start - $.post('/firewall/get_www_path',function(data){ - var html ='日志目录\ - 0KB\ - '; - $('#firewall_weblog').html(html); - - $.post('/files/get_dir_size','path='+data['path'], function(rdata){ - $("#logSize").html(rdata.msg); - },'json'); - },'json'); - // end -}); - -function closeLogs(){ - $.post('/files/close_logs','',function(rdata){ - $("#logSize").html(rdata.msg); - layer.msg('已清理!',{icon:1}); - },'json'); -} - -$("#firewalldType").change(function(){ - var type = $(this).val(); - var w = '120px'; - var p = '端口'; - var t = '放行'; - var m = '说明: 支持放行端口范围,如: 3000:3500'; - if(type == 'address'){ - w = '150px'; - p = '欲屏蔽的IP地址'; - t = '屏蔽'; - m = '说明: 支持屏蔽IP段,如: 192.168.0.0/24'; - } - $("#AcceptPort").css("width",w); - $("#AcceptPort").attr('placeholder',p); - $("#toAccept").html(t); - $("#f-ps").html(m); -}); - - -function sshMgr(){ - - $.post('/firewall/get_ssh_info', '', function(rdata){ - var ssh_status = rdata.status ? 'checked':''; - var pass_prohibit_status = rdata.pass_prohibit_status ? 'checked':''; - var con = '
          \ -
          \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
          名称状态
          启动SSH\ -
          \ - \ - \ -
          \ -
          禁止密码登陆\ -
          \ - \ - \ -
          \ -
          \ -
          \ -
          '; - layer.open({ - type: 1, - title: "SSH管理", - area: ['300px', '230px'], - closeBtn: 1, - shadeClose: false, - content: '
          '+con+'
          ', - success:function(){ - }, - }); - - },'json'); -} - - -function getSshInfo(){ - $.post('/firewall/get_ssh_info', '', function(rdata){ - if(!rdata.status){ - $("#mstscPort").attr('disabled','disabled'); - } - - $("#mstscPort").val(rdata.port); - var isPint = ''; - if(rdata.ping){ - isPing = ""; - }else{ - isPing = ""; - } - $("#is_ping").html(isPing); - - // console.log(rdata.firewall_status); - var fStatus = ''; - if (rdata.firewall_status){ - fStatus = ""; - }else{ - fStatus = ""; - } - $("#firewall_status").html(fStatus); - - showAccept(1); - - },'json'); -} - - -/** - * 修改远程端口 - */ - -function mstsc(port) { - layer.confirm('更改远程端口时,将会注消所有已登录帐户,您真的要更改远程端口吗?', {title: '远程端口'}, function(index) { - var data = "port=" + port; - var loadT = layer.load({ - shade: true, - shadeClose: false - }); - $.post('/firewall/set_ssh_port', data, function(ret) { - layer.msg(ret.msg,{icon:ret.status?1:2}) - layer.close(loadT); - getSshInfo(); - },'json'); - }); -} - -/** - * 更改禁ping状态 - * @param {Int} state 0.禁ping 1.可ping - */ -function ping(status){ - var msg = status == 1 ? '禁PING后不影响服务器正常使用,但无法ping通服务器,您真的要禁PING吗?' : '解除禁PING状态可能会被黑客发现您的服务器,您真的要解禁吗?'; - layer.confirm(msg,{title:'是否禁ping',closeBtn:2,cancel:function(){ - if(status == 1){ - $("#noping").prop("checked",true); - } else { - $("#noping").prop("checked",false); - } - }},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_ping','status='+status, function(data) { - layer.closeAll(); - if (data['status'] == true) { - if(status == 1){ - layer.msg(data['msg'], {icon: 1}); - } else { - layer.msg('已解除禁PING', {icon: 1}); - } - setTimeout(function(){window.location.reload();},3000); - } else { - layer.msg('连接服务器失败', {icon: 2}); - } - },'json'); - },function(){ - if(status == 1){ - $("#noping").prop("checked",true); - } else { - $("#noping").prop("checked",false); - } - }); -} - - - -/** - * 更改防火墙状态 - * @param {Int} state 0,开启 1.禁用 - */ -function firewall(status){ - var msg = status == 1 ? '禁用防火墙会增加服务器不安全性,您真的要禁用防火墙吗?' : '开启防火墙,增加服务器安全!'; - layer.confirm(msg,{title:'是否开启防火墙!',closeBtn:2,cancel:function(){ - if(status == 1){ - $("#firewall_status").prop("checked",true); - } else { - $("#firewall_status").prop("checked",false); - } - }},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_fw','status='+status, function(data) { - layer.closeAll(); - if (data['status'] == true) { - layer.msg(data['msg'], {icon: 1}); - setTimeout(function(){window.location.reload();},3000); - } else { - layer.msg('连接服务器失败', {icon: 2}); - } - },'json'); - },function(){ - if(status == 1){ - $("#firewall_status").prop("checked",true); - } else { - $("#firewall_status").prop("checked",false); - } - }); -} - - -/** - * 设置远程服务状态 - * @param {Int} state 0.启用 1.关闭 - */ -function setMstscStatus(){ - status = $("#sshswitch").prop("checked")==true?1:0; - var msg = status==1?'停用SSH服务的同时也将注销所有已登录用户,继续吗?':'确定启用SSH服务吗?'; - layer.confirm(msg,{title:'警告',closeBtn:2,cancel:function(){ - if(status == 0){ - $("#sshswitch").prop("checked",false); - } else { - $("#sshswitch").prop("checked",true); - } - }},function(index){ - if(index > 0){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_ssh_status','status='+status,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - } - },function(){ - if(status == 0){ - $("#sshswitch").prop("checked",false); - } else { - $("#sshswitch").prop("checked",true); - } - }); -} - -/** - * 设置远程服务状态 - * @param {Int} state 0.启用 1.关闭 - */ -function setSshPassStatus(){ - status = $("#pass_status").prop("checked")==true?1:0; - var msg = status==1?'开启密码登陆,继续吗?':'确定禁止密码登陆吗?'; - layer.confirm(msg,{title:'警告',closeBtn:2,cancel:function(){ - if(status == 0){ - $("#pass_status").prop("checked",false); - } else { - $("#pass_status").prop("checked",true); - } - }},function(index){ - if(index > 0){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_ssh_pass_status','status='+status,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - } - },function(){ - if(status == 0){ - $("#pass_status").prop("checked",false); - } else { - $("#pass_status").prop("checked",true); - } - }); -} - -/** - * 取回数据 - * @param {Int} page 分页号 - */ -function showAccept(page,search) { - search = search == undefined ? '':search; - var loadT = layer.load(); - $.post('/firewall/get_list','limit=10&p=' + page+"&search="+search, function(data) { - layer.close(loadT); - var body = ''; - for (var i = 0; i < data.data.length; i++) { - var status = ''; - switch(data.data[i].status){ - case 0: - status = '未使用'; - break; - case 1: - status = '外网不通'; - break; - default: - status = '正常'; - break; - } - body += "\ - " + 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 + "\ - 删除\ - "; - } - $("#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 65535 ){ - layer.msg('端口范围不合法!',{icon:5}); - return; - } - } - action = "add_accept_port"; - } - - - if(ps.length < 1){ - layer.msg('备注/说明 不能为空!',{icon:2}); - $("#Ps").focus(); - return; - } - var loadT = layer.msg('正在添加,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}) - $.post('/firewall/'+action,'port='+port+"&ps="+ps+'&type='+type+'&protocol='+protocol,function(rdata){ - layer.close(loadT); - if(rdata.status == true || rdata.status == 'true'){ - layer.msg(rdata.msg,{icon:1}); - showAccept(1); - $("#AcceptPort").val(''); - $("#Ps").val(''); - }else{ - layer.msg(rdata.msg,{icon:2}); - } - - $("#AcceptPort").attr('value',''); - $("#Ps").attr('value',''); - },'json'); -} - -//删除放行 -function delAcceptPort(id, port,protocol) { - var action = "del_drop_address"; - if(port.indexOf('.') == -1){ - action = "del_accept_port"; - } - - layer.confirm(lan.get('confirm_del',[port]), {title: '删除防火墙规则',closeBtn:2}, function(index) { - var loadT = layer.msg('正在删除,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}) - $.post("/firewall/"+action, "id=" + id + "&port=" + port+'&protocol='+protocol, function(ret) { - layer.close(loadT); - layer.msg(ret.msg,{icon:ret.status?1:2}) - showAccept(1); - },'json'); - }); -} diff --git a/route/static/app/index.js b/route/static/app/index.js deleted file mode 100755 index c623ad35e..000000000 --- a/route/static/app/index.js +++ /dev/null @@ -1,1463 +0,0 @@ -//获取负载 -function getLoad(data) { - $("#LoadList .mask").html("获取中.."); - setCookie('one', data.one); - setCookie('five', data.five); - setCookie('fifteen', data.fifteen); - var transformLeft, transformRight, LoadColor, Average, Occupy, AverageText, conterError = ''; - var index = $("#LoadList"); - if (Average == undefined) Average = data.one; - setCookie('conterError', conterError); - Occupy = Math.round((Average / data.max) * 100); - if (Occupy > 100) Occupy = 100; - if (Occupy <= 30) { - LoadColor = '#20a53a'; - AverageText = '运行流畅'; - } else if (Occupy <= 70) { - LoadColor = '#6ea520'; - AverageText = '运行正常'; - } else if (Occupy <= 90) { - LoadColor = '#ff9900'; - AverageText = '运行缓慢'; - } else { - LoadColor = '#dd2f00'; - AverageText = '运行堵塞'; - } - index.find('.circle').css("background", LoadColor); - index.find('.mask').css({ "color": LoadColor }); - $("#LoadList .mask").html("%"); - $('#Load').html(Occupy); - $('#LoadState').html('' + AverageText + ''); - setImg(); -} - -$('#LoadList .circle').click(function() { - // getNet(); -}); - -$('#LoadList .mask').hover(function() { - var one, five, fifteen; - var that = this; - one = getCookie('one'); - five = getCookie('five'); - fifteen = getCookie('fifteen'); - var text = '最近1分钟平均负载:' + one + '
          最近5分钟平均负载:' + five + '
          最近15分钟平均负载:' + fifteen + ''; - layer.tips(text, that, { time: 0, tips: [1, '#999'] }); -}, function() { - layer.closeAll('tips'); -}); - - -function showCpuTips(rdata){ - $('#cpuChart .mask').unbind().hover(function() { - var cpuText = ''; - if (rdata.cpu[2].length == 1){ - var cpuUse = parseFloat(rdata.cpu[2][0] == 0 ? 0 : rdata.cpu[2][0]).toFixed(1); - cpuText += 'CPU-1:' + cpuUse + '%'; - } else { - for (var i = 1; i < rdata.cpu[2].length + 1; i++) { - var cpuUse = parseFloat(rdata.cpu[2][i - 1] == 0 ? 0 : rdata.cpu[2][i - 1]).toFixed(1); - if (i % 2 != 0) { - cpuText += 'CPU-' + i + ':' + cpuUse + '% | '; - } else { - cpuText += 'CPU-' + i + ':' + cpuUse + '%'; - cpuText += '\n'; - } - } - } - layer.tips(rdata.cpu[3] + "
          " + rdata.cpu[5] + "个物理CPU," + (rdata.cpu[4]) + "个物理核心," + rdata.cpu[1] + "个逻辑核心
          " + cpuText, this, { time: 0, tips: [1, '#999'] }); - }, function() { - layer.closeAll('tips'); - }); -} - - -function rocket(sum, m) { - var n = sum - m; - $(".mem-release").find(".mask span").text(n); -} - -//释放内存 -function reMemory() { - setTimeout(function() { - $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px' }).html('1' + lan.index.memre_ok_0 + ' '); - $.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("" + percent + "" + 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("" + percent + "" + lan.index.memre_ok_1 + "
          " + memNull + "MB"); - } else { - $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px' }).html("" + percent + "" + 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].size[3].replace('%', '') + '%
          '; - - var ipre = parseInt(rdata[i].inodes[3].replace('%', '')); - if (ipre > 95) { - $("#messageError").show(); - $("#messageError").append('

          分区[' + rdata[i].path + ']当前Inode使用率超过' + ipre + '%,当使用率满100%时将无法在此分区创建文件,请及时清理![清理垃圾]

          '); - } - } - - if (rdata[i].path == '/' || rdata[i].path == '/www') { - if (rdata[i].size[2].indexOf('M') != -1) { - $("#messageError").show(); - $("#messageError").append('

          ' + lan.get('diskinfo_span_1', [rdata[i].path]) + '[清理垃圾]

          '); - } - } - - dBody = '
        • ' + - '

          ' + rdata[i].path + '

          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          ' + - '
          '+ inodes +'
          ' + - '

          ' + rdata[i].size[1] + '/' + rdata[i].size[0] + '

          ' + - '
        • ' - $("#systemInfoList").append(dBody); - setImg(); - } - },'json'); -} - -//清理垃圾 -function clearSystem() { - var loadT = layer.msg('正在清理系统垃圾 ', { 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('

          当前可用物理内存小于64M,这可能导致MySQL自动停止,站点502等错误,请尝试释放内存!

          ') - } -} - -function getInfo() { - $.get("/system/system_total", function(info) { - - setMemImg(info); - - $("#info").html(info.system); - $("#running").html(info.time); - var _system = info.system; - if(_system.indexOf("Windows") != -1){ - $(".ico-system").addClass("ico-windows"); - } else if(_system.indexOf("CentOS") != -1) { - $(".ico-system").addClass("ico-centos"); - } else if(_system.indexOf("Ubuntu") != -1) { - $(".ico-system").addClass("ico-ubuntu"); - } else if(_system.indexOf("Debian") != -1) { - $(".ico-system").addClass("ico-debian"); - } else if(_system.indexOf("Fedora") != -1) { - $(".ico-system").addClass("ico-fedora"); - } else if(_system.indexOf("Mac") != -1){ - $(".ico-system").addClass("ico-mac"); - } else { - $(".ico-system").addClass("ico-linux"); - } - $("#core").html(info.cpuNum + ' 核心'); - $("#state").html(info.cpuRealUsed); - setcolor(info.cpuRealUsed, "#state", 30, 70, 90); - - - // if (info.isuser > 0) { - // $("#messageError").show(); - // $("#messageError").append('

          ' + lan.index.user_warning + ' [不可忽略] [立即修改]

          ') - // } - setImg(); - },'json'); -} - - -function setcolor(pre, s, s1, s2, s3) { - var LoadColor; - if (pre <= s1) { - LoadColor = '#20a53a'; - } else if (pre <= s2) { - LoadColor = '#6ea520'; - } else if (pre <= s3) { - LoadColor = '#ff9900'; - } else { - LoadColor = '#dd2f00'; - } - if (s == false) { - return LoadColor; - } - var co = $(s).parent('.mask'); - co.css("color", LoadColor); - co.parent('.circle').css("background", LoadColor); -} - - -function getNet() { - var up, down; - $.get("/system/network", function(net) { - - console.log(net); - - $("#InterfaceSpeed").html(lan.index.interfacespeed + ": 1.0Gbps"); - $("#upSpeed").html(toSize(net.up)); - $("#downSpeed").html(toSize(net.down)); - $("#downAll").html(toSize(net.downTotal)); - $("#downAll").attr('title', lan.index.package + ':' + net.downPackets) - $("#upAll").html(toSize(net.upTotal)); - $("#upAll").attr('title', lan.index.package + ':' + net.upPackets) - $("#core").html(net.cpu[1] + " " + lan.index.cpu_core); - $("#state").html(net.cpu[0]); - setcolor(net.cpu[0], "#state", 30, 70, 90); - setCookie("upNet", net.up); - setCookie("downNet", net.down); - - //负载 - getLoad(net.load); - - //内存 - setMemImg(net.mem); - - //绑定hover事件 - setImg(); - showCpuTips(net); - - },'json'); -} - -//网络IO -function netImg() { - - var xData = []; - var yData = []; - var zData = []; - - function getTime() { - var now = new Date(); - var hour = now.getHours(); - var minute = now.getMinutes(); - var second = now.getSeconds(); - if (minute < 10) { - minute = "0" + minute; - } - if (second < 10) { - second = "0" + second; - } - var nowdate = hour + ":" + minute + ":" + second; - return nowdate; - } - - function ts(m) { return m < 10 ? '0' + m : m } - - function format(sjc) { - var time = new Date(sjc); - var h = time.getHours(); - var mm = time.getMinutes(); - var s = time.getSeconds(); - return ts(h) + ':' + ts(mm) + ':' + ts(s); - } - - var default_unit = 'KB/s'; - function addData(shift) { - xData.push(getTime()); - - if (getCookie("upNet") > getCookie("downNet") ){ - tmp = getCookie("upNet"); - } else { - tmp = getCookie("downNet"); - } - var tmpSize = toSize(tmp); - default_unit = tmpSize.split(' ')[1] + '/s'; - - - var upNetTmp = toSize(getCookie("upNet")); - var downNetTmp = toSize(getCookie("downNet")); - - var upNetTmpSize = upNetTmp.split(' ')[0]; - var downNetTmp = downNetTmp.split(' ')[0]; - - yData.push(upNetTmpSize); - zData.push(downNetTmp); - if (shift) { - xData.shift(); - yData.shift(); - zData.shift(); - } - } - for (var i = 8; i >= 0; i--) { - var time = (new Date()).getTime(); - xData.push(format(time - (i * 3 * 1000))); - yData.push(0); - zData.push(0); - } - // 指定图表的配置项和数据 - var option = { - title: { - text: "接口流量实时", - left: 'center', - textStyle: { - color: '#888888', - fontStyle: 'normal', - fontFamily: "宋体", - fontSize: 16, - } - }, - tooltip: { - trigger: 'axis' - }, - legend: { - data: [lan.index.net_up, lan.index.net_down], - bottom: '2%' - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine: { - lineStyle: { - color: "#666" - } - } - }, - yAxis: { - name: '单位 '+ default_unit, - splitLine: { - lineStyle: { color: "#eee" } - }, - axisLine: { - lineStyle: { color: "#666" } - } - }, - series: [{ - name: '上行', - type: 'line', - 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: 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, - } - } - }] - }; - - var echartsNetImg = echarts.init(document.getElementById('netImg')); - setInterval(function() { - getNet(); - addData(true); - echartsNetImg.setOption({ - yAxis: { - name: '单位 '+ default_unit, - splitLine: { lineStyle: { color: "#eee" } }, - axisLine: { lineStyle: { color: "#666" } } - }, - xAxis: { - data: xData - }, - series: [{ - name: lan.index.net_up, - data: yData - }, { - name: lan.index.net_down, - data: zData - }] - }); - }, 3000); - - // 使用刚指定的配置项和数据显示图表。 - echartsNetImg.setOption(option); - window.addEventListener("resize", function() { - echartsNetImg.resize(); - }); -} - - -function setImg() { - $('.circle').each(function(index, el) { - var num = $(this).find('span').text() * 3.6; - if (num <= 180) { - $(this).find('.left').css('transform', "rotate(0deg)"); - $(this).find('.right').css('transform', "rotate(" + num + "deg)"); - } else { - $(this).find('.right').css('transform', "rotate(180deg)"); - $(this).find('.left').css('transform', "rotate(" + (num - 180) + "deg)"); - }; - }); - - $('.diskbox .mask').unbind().hover(function() { - layer.closeAll('tips'); - var that = this; - var conterError = $(this).attr("data"); - layer.tips(conterError, that, { time: 0, tips: [1, '#999'] }); - }, function() { - layer.closeAll('tips'); - }); -} - -// 检查更新 -setTimeout(function() { - $.get('/system/update_server?type=check', function(rdata) { - if (rdata.status == false) return; - if (rdata.data != undefined) { - $("#toUpdate").html('更新'); - $('#toUpdate a').html('更新.'); - $('#toUpdate a').css("position","relative"); - return; - } - },'json').error(function() { - }); -}, 3000); - - -//检查更新 -function checkUpdate() { - var loadT = layer.msg(lan.index.update_get, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/system/update_server?type=check', function(rdata) { - layer.close(loadT); - - if (rdata.data == 'download'){ - updateStatus();return; - } - - if (rdata.status === false) { - layer.confirm(rdata.msg, { title: lan.index.update_check, icon: 1, closeBtn: 1, btn: [lan.public.know, lan.public.close] }); - return; - } - layer.msg(rdata.msg, { icon: 1 }); - if (rdata.data != undefined) updateMsg(); - },'json'); -} - -function updateMsg(){ - $.get('/system/update_server?type=info',function(rdata){ - - if (rdata.data == 'download'){ - updateStatus();return; - } - - var v = rdata.data.version; - var v_info = ''; - if (v.split('.').length>3){ - v_info = "测试版本"; - } else { - v_info = "正式版本"; - } - - layer.open({ - type:1, - title:v_info + '升级到['+rdata.data.version+']', - area: '400px', - shadeClose:false, - closeBtn:2, - content:'
          ' - +'

          '+rdata.data.content+'

          ' - +'
          ' - +'' - +'' - +'
          ' - +'
          ' - }); - },'json'); -} - - -//开始升级 -function updateVersion(version) { - var loadT = layer.msg('正在升级面板..', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/system/update_server?type=update&version='+version, function(rdata) { - - layer.closeAll(); - if (rdata.status === false) { - layer.msg(rdata.msg, { icon: 5, time: 5000 }); - return; - } - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - if (rdata.status) { - $("#btversion").html(version); - $("#toUpdate").html(''); - } - },'json').error(function() { - layer.msg('更新失败,请重试!', { icon: 2 }); - setTimeout(function() { - window.location.reload(); - }, 3000); - }); -} - -function pluginIndexService(pname,pfunc, callback){ - $.post('/plugins/run', {name:'openresty', func:pfunc}, function(data) { - if (!data.status){ - layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - - if(typeof(callback) == 'function'){ - callback(data); - } - },'json'); -} - -//重启服务器 -function reBoot() { - layer.open({ - type: 1, - title: '重启服务器或者面板', - area: '330px', - closeBtn: 1, - shadeClose: false, - content: '' - }); - - $('.rebt-con a').click(function () { - var type = $(this).attr('data-id'); - switch (type) { - case 'panel': - layer.confirm('即将重启面板服务,继续吗?', { title: '重启面板服务', closeBtn: 1, icon: 3 }, function () { - var loadT = layer.load(); - $.post('/system/restart','',function (rdata) { - layer.close(loadT); - layer.msg(rdata.msg); - setTimeout(function () { window.location.reload(); }, 3000); - },'json'); - }); - break; - case 'server': - var rebootbox = layer.open({ - type: 1, - title: '安全重启服务器', - area: ['500px', '280px'], - closeBtn: 1, - shadeClose: false, - content: "
          \ -
          \ -

          注意,若您的服务器是一个容器,请取消。

          \ -
          \ -

          安全重启有利于保障文件安全,将执行以下操作:

          \ -

          1.停止Web服务

          \ -

          2.停止MySQL服务

          \ -

          3.开始重启服务器

          \ -

          4.等待服务器启动

          \ -
          \ -
          \ -
          \ - \ - \ -
          \ -
          " - }); - setTimeout(function () { - $(".btn-reboot").click(function () { - rebootbox.close(); - }) - $(".WSafeRestart").click(function () { - var body = '
          '; - $(".bt-window-restart").html(body); - $(".SafeRestartCode").append("

          正在停止Web服务

          "); - pluginIndexService('openresty', 'stop', function (r1) { - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

          正在停止MySQL服务...

          "); - pluginIndexService('mysql','stop', function (r2) { - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

          开始重启服务器...

          "); - $.post('/system/restart_server', '',function (rdata) { - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

          等待服务器启动...

          "); - var sEver = setInterval(function () { - $.get("/system/system_total", function(info) { - clearInterval(sEver); - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

          服务器重启成功!...

          "); - setTimeout(function () { - layer.closeAll(); - }, 3000); - }) - }, 3000); - }) - }) - }) - }) - }, 100); - break; - } - }); -} - -//修复面板 -function repPanel() { - layer.confirm(lan.index.rep_panel_msg, { title: lan.index.rep_panel_title, closeBtn: 1, icon: 3 }, function() { - var loadT = layer.msg(lan.index.rep_panel_the, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/system?action=RepPanel', function(rdata) { - layer.close(loadT); - layer.msg(lan.index.rep_panel_ok, { icon: 1 }); - }).error(function() { - layer.close(loadT); - layer.msg(lan.index.rep_panel_ok, { icon: 1 }); - }); - }); -} - -//获取警告信息 -function getWarning() { - $.get('/ajax?action=GetWarning', function(wlist) { - var num = 0; - for (var i = 0; i < wlist.data.length; i++) { - if (wlist.data[i].ignore_count >= wlist.data[i].ignore_limit) continue; - if (wlist.data[i].ignore_time != 0 && (wlist.time - wlist.data[i].ignore_time) < wlist.data[i].ignore_timeout) continue; - var btns = ''; - for (var n = 0; n < wlist.data[i].btns.length; n++) { - if (wlist.data[i].btns[n].type == 'javascript') { - btns += ' ' + wlist.data[i].btns[n].title + '' - } else { - btns += ' ' + wlist.data[i].btns[n].title + '' - } - } - $("#messageError").append('

          ' + wlist.data[i].body + btns + '

          '); - num++; - } - if (num > 0) $("#messageError").show(); - }); -} - -//按钮调用 -function warningTo(to_url, def) { - var loadT = layer.msg(lan.public.the_get, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post(to_url, {}, function(rdata) { - layer.close(loadT); - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - if (rdata.status && def) setTimeout(function() { location.reload(); }, 1000); - },'json'); -} - -function setSafeHide() { - setCookie('safeMsg', '1'); - $("#safeMsg").remove(); -} - -//查看报告 -function showDanger(num, port) { - var atxt = "因未使用安全隔离登录,所有IP都可以尝试连接,存在较高风险,请立即处理。"; - if (port == "22") { - atxt = "因未修改SSH默认22端口,且未使用安全隔离登录,所有IP都可以尝试连接,存在较高风险,请立即处理。"; - } - layer.open({ - type: 1, - area: ['720px', '410px'], - title: '安全提醒(如你想放弃任何安全提醒通知,请删除宝塔安全登录插件)', - closeBtn: 1, - shift: 5, - content: '
          \ - \ - \ - \ - \ - \ - \ - \ - \ -
          风险类型:暴力破解 说明
          累计遭遇攻击总数:' + num + ' 详细(数据直接来源本服务器日志)
          风险等级:较高风险
          风险描述:' + atxt + '
          可参考解决方案:

          方案一:修改SSH默认端口,修改SSH验证方式为数字证书,清除近期登陆日志。

          方案二:购买宝塔企业运维版,一键部署安全隔离服务,高效且方便。

          \ -
          ' - }); - $(".showDanger td").css("padding", "8px"); -} - -function pluginInit(){ - $.post('/plugins/init', function(data){ - if (!data.status){ - return false; - } - - var rdata = data.data; - var plugin_list = ''; - - for (var i = 0; i < rdata.length; i++) { - var ver = rdata[i]['versions']; - var select_list = ''; - if (typeof(ver)=='string'){ - select_list = ''; - } else { - for (var vi = 0; vi < ver.length; vi++) { - - if (ver[vi] == rdata[i]['default_ver']){ - select_list += ''; - } else { - select_list += ''; - } - } - } - - var pn_checked = ''; - if (rdata[i]['name'] == 'swap'){ - var pn_checked = ''; - } - - plugin_list += '
        • \ - \ - \ - \ - '+pn_checked+'
        • '; - } - - layer.open({ - type: 1, - title: '推荐安装', - area: ["320px", "400px"], - closeBtn: 2, - shadeClose: false, - content:"\ -
          \ -
          \ -

          推荐以下一键套件,或在软件管理按需选择。

          \ - \ -
          \ -
          \ -

          经典LNMP

          \ -
          \ -
            " + plugin_list + "
          \ -
          一键安装
          \ -
          \ -
          \ -
          ", - success:function(l,index){ - $('.rec-box-con .onekey').click(function(){ - var post_data = []; - for (var i = 0; i < rdata.length; i++) { - var key_ver = '#select_'+rdata[i]['name']; - var key_checked = '#data_'+rdata[i]['name']; - - var val_checked = $(key_checked).prop("checked"); - if (val_checked){ - - var tmp = {}; - var val_key = $(key_ver).val(); - - tmp['version'] = val_key; - tmp['name'] = rdata[i]['name']; - post_data.push(tmp); - } - } - - $.post('/plugins/init_install', 'list='+JSON.stringify(post_data), function(data){ - showMsg(data.msg, function(){ - if (data.status){ - layer.closeAll(); - messageBox(); - } - },{ icon: data.status ? 1 : 2 },2000); - },'json'); - }); - }, - cancel:function(){ - layer.confirm('是否不再显示推荐安装套件?', {btn : ['确定', '取消'],title: "不再显示推荐?"}, function() { - $.post('/files/create_dir', 'path=/www/server/php', function(rdata) { - layer.closeAll(); - },'json'); - }); - } - }); - },'json'); -} - -function loadKeyDataCount(){ - var plist = ['mysql', 'gogs', 'gitea']; - for (var i = 0; i < plist.length; i++) { - pname = plist[i]; - function call(pname){ - $.post('/plugins/run', {name:pname, func:'get_total_statistics'}, function(data) { - try { - var rdata = $.parseJSON(data['data']); - } catch(e){ - return; - } - if (!rdata['status']){ - return; - } - var html = '
        • \ -

          '+pname+'

          \ - \ -
        • '; - $('#index_overview').append(html); - },'json'); - } - call(pname); - } -} - -$(function() { - $(".mem-release").hover(function() { - $(this).addClass("shine_green"); - if (!($(this).hasClass("mem-action"))) { - $(this).find(".mem-re-min").hide(); - $(this).find(".mask").css({ "color": "#d2edd8" }); - $(this).find(".mem-re-con").css({ "display": "block" }); - $(this).find(".mem-re-con").animate({ "top": "0", opacity: 1 }); - $("#memory").text('内存释放'); - } - }, function() { - $(this).removeClass("shine_green"); - $(this).find(".mask").css({ "color": "#20a53a" }); - $(this).find(".mem-re-con").css({ "top": "15px", opacity: 1, "display": "none" }); - $("#memory").text(getCookie("mem-before")); - $(this).find(".mem-re-min").hide(); - }).click(function() { - $(this).find(".mem-re-min").hide(); - if (!($(this).hasClass("mem-action"))) { - reMemory(); - var btlen = $(".mem-release").find(".mask span").text(); - $(this).addClass("mem-action"); - $(this).find(".mask").css({ "color": "#20a53a" }); - $(this).find(".mem-re-con").animate({ "top": "-400px", opacity: 0 }); - $(this).find(".pie_right .right").css({ "transform": "rotate(3deg)" }); - for (var i = 0; i < btlen; i++) { - setTimeout("rocket(" + btlen + "," + i + ")", i * 30); - } - } - }); - - $("select[name='network-io'],select[name='disk-io']").change(function () { - var key = $(this).val(), type = $(this).attr('name'); - if (type == 'network-io') { - if (key == 'ALL') { - key = ''; - } - setCookie('network_io_key', key); - } else { - if (key == 'ALL') { - key = ''; - } - setCookie('disk_io_key', key); - } - }); - - $('.tabs-nav span').click(function () { - var indexs = $(this).index(); - $(this).addClass('active').siblings().removeClass('active'); - $('.tabs-content .tabs-item:eq(' + indexs + ')').addClass('tabs-active').siblings().removeClass('tabs-active'); - $('.tabs-down select:eq(' + indexs + ')').removeClass('hide').siblings().addClass('hide'); - switch (indexs) { - case 0: - index.net.table.resize(); - break; - case 1: - index.iostat.table.resize(); - break; - } - }) -}); - -var index = { - common:{ - ts:function (m) { return m < 10 ? '0' + m : m }, - format:function (sjc) { - var time = new Date(sjc); - var h = time.getHours(); - var mm = time.getMinutes(); - var s = time.getSeconds(); - return h+ ':' + mm + ':' +s; - }, - getTime:function () { - var now = new Date(); - var hour = now.getHours(); - var minute = now.getMinutes(); - var second = now.getSeconds(); - if (minute < 10) { - minute = "0" + minute; - } - if (second < 10) { - second = "0" + second; - } - var nowdate = hour + ":" + minute + ":" + second; - return nowdate; - }, - }, - net: { - table: null, - data: { - xData: [], - yData: [], - zData: [] - }, - default_unit : 'KB/s', - init_select : false, - init: function(){ - for (var i = 8; i >= 0; i--) { - var time = (new Date()).getTime(); - index.net.data.xData.push(index.common.format(time - (i * 3 * 1000))); - index.net.data.yData.push(0); - index.net.data.zData.push(0); - } - - index.net.table = echarts.init(document.getElementById('netImg')); - var option = index.net.defaultOption(); - index.net.table.setOption(option); - - window.addEventListener("resize", function () { - index.net.table.resize(); - }); - }, - render:function(){ - index.net.table.setOption({ - yAxis: { - name: '单位 '+ index.net.default_unit, - splitLine: { lineStyle: { color: "#eee" } }, - axisLine: { lineStyle: { color: "#666" } } - }, - xAxis: { - data: index.net.data.xData - }, - series: [{ - name: lan.index.net_up, - data: index.net.data.yData - }, { - name: lan.index.net_down, - data: index.net.data.zData - }] - }); - }, - defaultOption:function(){ - var option = { - title: { - text: "", - left: 'center', - textStyle: { - color: '#888888', - fontStyle: 'normal', - fontFamily: "宋体", - fontSize: 16, - } - }, - tooltip: { - trigger: 'axis', - formatter :function (config) { - var _config = config, _tips = "时间:" + _config[0].axisValue + "
          "; - 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 += '
          '+tip+'
          '; - } else { - option += '
          '+tip+'
          '; - } - } - $("#logAudit .logAuditTab").html(option); - - getAuditFile(data[0]['name']); - $('#logAudit .logAuditItem').click(function(){ - $('#logAudit .logAuditItem').removeClass('active'); - $(this).addClass('active'); - getAuditFile($(this).data('file')); - }); - - },'json'); -} - - -function getAuditFile(log_name){ - var loadT = layer.msg('正在获取日志审计内容...', { icon: 16, time: 0, shade: 0.3 }); - $.post('/logs/get_audit_file',{log_name:log_name}, function(data) { - layer.close(loadT); - // console.log(data); - try{ - if (typeof(data) == 'object'){ - var plist = data.data; - - var pre_html ='
          \ -
          \ -
          \ - \ -
          \ - \ -
          \ -
          \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
          用户来源端口时间
          root117.139.193.29pts/02023-08-25 13:27 still logged in
          \ -
          \ -
          \ -
          \ -
          \ -
          \ -
          \ -
          ' - - // var pre_html = '\ - // \ - // \ - //
          时间角色事件
          '; - $('#logAudit .logAuditContent').html(pre_html); - - if (plist.length>0){ - var tmp = plist[0]; - var thead = ''; - tbody += '' - for (var i in tmp) { - tbody+=''+ i + ''; - } - tbody += ''; - $('#logAudit .logAuditContent thead').html(tbody); - } - - - var tbody = ''; - for (var i = 0; i < plist.length; i++) { - tbody += ''; - for (var vv in plist[i]) { - tbody+= ''+ plist[i][vv] + '' - } - tbody += ''; - } - $('#logAudit .logAuditContent tbody').html(tbody); - - $('#logAudit .refresh').click(function(){ - getAuditFile(log_name); - }); - } - - if (typeof(data) == 'string'){ - var cc = '
          \ -
          '+data+'
          \ -
          '; - $('#logAudit .logAuditContent').html(cc); - } - } catch (e) { - layer.msg(str(e),{icon:2,time:10000,shade: [0.3, '#000']}); - } - }); -} - -function getLogs(page,search) { - search = search == undefined ? '':search; - var loadT = layer.load(); - $.post('/logs/get_log_list','limit=10&p=' + page+"&search="+search, function(data) { - layer.close(loadT); - var body = ''; - for (var i = 0; i < data.data.length; i++) { - body += "\ - " + data.data[i].id + "\ - " + data.data[i].type + "\ - " + data.data[i].log + "\ - " + data.data[i].addtime + "\ - "; - } - $("#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: "
          \ -
          \ - \ -
          当前路径:
          \ -
          \ -
          \ -
          \ -
          计算机
          \ -
          \ -
          \ -
            \ -
            \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
            文件名修改时间权限所有者
            \ -
            \ -
            \ -
            \ -
            \ -
            \ - \ - \ - \ -
            ", - success:function(layero,layer_index){ - $('.btn-close').click(function(){ - layer.close(layer_index); - }); - - $('.btn-choose').click(function(){ - 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); - } - callback(a); - layer.close(layer_index); - }); - } - }); - getDiskList(default_dir); - activeDisk(); -} - -function changePath(d) { - setCookie('SetId', d); - setCookie('SetName', ''); - var c = layer.open({ - type: 1, - area: "650px", - title: '选择目录', - closeBtn: 1, - shift: 5, - shadeClose: false, - content: "\ -
            \ -
            \ - \ -
            当前路径:
            \ -
            \ -
            \ -
            计算机
            \ -
            \ -
              \ -
              \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
              文件名修改时间权限所有者
              \ -
              \ -
              \ -
              \ -
              \ -
              \ - \ - \ - \ -
              " - }); - setCookie("changePath", c); - var b = $("#" + d).val(); - tmp = b.split("."); - if(tmp[tmp.length - 1] == "gz") { - tmp = b.split("/"); - b = ""; - for(var a = 0; a < tmp.length - 1; a++) { - b += "/" + tmp[a] - } - setCookie("SetName", tmp[tmp.length - 1]) - } - b = b.replace(/\/\//g, "/"); - getDiskList(b); - activeDisk(); -} - -function getDiskList(b) { - var d = ""; - var a = ""; - var c = "path=" + b + "&disk=True"; - $.post("/files/get_dir", c, function(h) { - if(h.DISK != undefined) { - for(var f = 0; f < h.DISK.length; f++) { - a += "
               " + h.DISK[f].path + "
              " - } - $("#changecomlist").html(a) - } - for(var f = 0; f < h.DIR.length; f++) { - var g = h.DIR[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\ - "; - } - 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] + "\ - \ - "; - } - } - $(".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 = "\ - \ - \ -   \ - "; - 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: '
              \ -
              \ -

              提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!\ - \ -

              \ - '; - $(".taskcon").html(lbody); - var ob = document.getElementById('exec_log'); - ob.scrollTop = ob.scrollHeight; - }); -} - -/** - * 获取时分秒 - * @param {Number} seconds 总秒数 - * @param {String} dateFormat 返回的日期格式,默认为'H:i:s' - */ -function getSFM(seconds, dateFormat = 'H:i:s') { - - var obj = {}; - obj.H = Number.parseInt(seconds / 3600); - obj.i = Number.parseInt((seconds - obj.H * 3600) / 60); - obj.s = Number.parseInt(seconds - obj.H * 3600 - obj.i * 60); - if (obj.H < 10) { - obj.H = '0' + obj.H; - } - if (obj.i < 10) { - obj.i = '0' + obj.i; - } - if (obj.s < 10) { - obj.s = '0' + obj.s; - } - - // 3.解析 - var rs = dateFormat.replace('H', obj.H).replace('i', obj.i).replace('s', obj.s); - return rs; -} - -function remind(a){ - a = a == undefined ? 1 : a; - $.post("/task/list", "table=tasks&result=2,4,6,8&limit=10&p=" + a, function(g) { - var e = ''; - var f = false; - var task_count = 0; - for(var d = 0; d < g.data.length; d++) { - var status = g.data[d].status; - var status_text = '已经完成'; - var cos_text = ''; - if (status == '1'){ - status_text = '完成'; - cos_text = '耗时['+getSFM(g.data[d].end - g.data[d].start)+']' - } else if (status == '0'){ - status_text = '正在处理'; - cos_text = '等待中..'; - } else if (status == '-1'){ - status_text = '安装中'; - cos_text = '..'; - } - - e += '\ - \ - \ -
              '+g.data[d].name+'\ - 【'+status_text+'】\ - '+cos_text+'\ -
              \ - \ - '+g.data[d].addtime+'\ - '; - } - var con = '
              \ - \ - \ - \ - \ - \ - \ - '+e+'\ -
              '+lan.bt.task_name+''+lan.bt.task_time+'
              \ -
              \ -
              \ - \ -
              \ -
              '; - - $(".task_count").text(task_count); - $(".msg_count").text(g.count); - $(".taskcon").html(con); - $("#taskPage").html(g.page); - - $("#Rs-checkAll").click(function(){ - if($(this).prop("checked")){ - $("#remind").find("input").prop("checked",true); - } else { - $("#remind").find("input").prop("checked",false); - } - }); - },'json'); -} - -function getReloads() { - var a = 0; - var mm = $(".bt-w-menu .bgw").html() - if(mm == undefined || mm.indexOf(lan.bt.task_list) == -1) { - clearInterval(speed); - a = 0; - speed = null; - return - } - if(speed) {return;} - speed = setInterval(function() { - var mm = $(".bt-w-menu .bgw").html() - if(mm == undefined || mm.indexOf(lan.bt.task_list) == -1) { - clearInterval(speed); - speed = null; - a = 0; - return - } - a++; - $.post('/task/get_task_speed', '', function(h) { - if(h.task == undefined) { - $(".cmdlist").html(lan.bt.task_not_list); - return; - } - var b = ""; - var d = ""; - $("#task").text(h.task.length); - $(".task_count").text(h.task.length); - for(var g = 0; g < h.task.length; g++) { - if(h.task[g].status == "-1") { - if(h.task[g].type != "download") { - var c = ""; - var f = h.msg.split("\n"); - for(var e = 0; e < f.length; e++) { - c += f[e] + "
              " - } - if(h.task[g].name.indexOf("扫描") != -1) { - b = "
            • \ - " + h.task[g].name + "\ - 正在扫描 | 关闭\ - \ -
              " + c + "
              \ -
            • "; - } else { - b = "
            • \ - " + h.task[g].name + "\ - 正在安装 | 关闭\ -
              " + c + "
              \ -
            • "; - } - } else { - b = "
            • \ -
              \ - " + h.task[g].name + "" + (toSize(h.msg.used) + "/" + toSize(h.msg.total)) + "\ - " + h.msg.pre + "%\ - "+lan.bt.task_downloading+" | "+lan.public.close+"\ -
            • " - } - } else { - d += "
            • " + h.task[g].name + "等待 | 删除
            • ' - } - } - $(".cmdlist").html(b + d); - $(".cmd").html(c); - try{ - if($(".cmd")[0].scrollHeight) $(".cmd").scrollTop($(".cmd")[0].scrollHeight); - }catch(e){ - return; - } - },'json').error(function(){}); - }, 1000); -} - -//检查选中项 -function RscheckSelect(){ - setTimeout(function(){ - var checkList = $("#remind").find("input"); - var count = 0; - for(var i=0;i 0){ - $(".buttongroup .btn").removeAttr("disabled"); - }else{ - $(".rs-del,.rs-read").attr("disabled","disabled"); - } - },5); -} - - -function tasklist(a){ - var con='
                \ - 若任务长时间未执行,请尝试在首页点【重启面板】来重置任务队列'; - $(".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 = "
              • \ - " + g.data[d].name + "\ - 正在安装 | 关闭\ -
                \
                -						
              • "; - } else { - b = "
              • \ -
                " + g.data[d].name + "0.0M/12.5M\ - 0%下载中 | 关闭\ -
              • "; - } - task_count++; - break; - case "0": - c += "
              • \ - " + g.data[d].name + "\ - 等待 | "+lan.public.del+"\ -
              • "; - task_count++; - break; - } - } - - - $(".task_count").text(task_count); - $(".cmdlist").html(b + c); - getReloads(); - return f - },'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 check_login(){ - $.post('/check_login',{},function(rdata){ - if(rdata === true) return; - }); -} - -//登陆跳转 -function to_login(){ - layer.confirm('您的登陆状态已过期,请重新登陆!',{title:'会话已过期',icon:2,closeBtn: 1,shift: 5},function(){ - location.reload(); - }); -} -//表格头固定 -function table_fixed(name){ - var tableName = document.querySelector('#'+name); - tableName.addEventListener('scroll',scroll_handle); -} -function scroll_handle(e){ - var scrollTop = this.scrollTop; - $(this).find("thead").css({"transform":"translateY("+scrollTop+"px)","position":"relative","z-index":"1"}); -} - - -$(function(){ -/// -setInterval(function(){check_login();},6000); -/// -}); - -function asyncLoadImage(obj, url){ - - if (typeof(url) == 'undefined'){ - return; - } - - function loadImage(obj,url,callback){ - var img = new Image(); - img.src = url; - - if(img.complete){ - callback.call(img,obj); - return; - } - img.onload = function(){ - callback.call(img,obj); - } - } - - function showImage(obj){ - obj.src = this.src; - } - loadImage(obj, url, showImage); -} - -function loadImage(){ - $('img').each(function(i){ - // console.log($(this).attr('data-src')); - if ($(this).attr('data-src') != ''){ - asyncLoadImage(this, $(this).attr('data-src')); - } - }); -} - -var socket, gterm; -function webShell() { - var termCols = 83; - var termRows = 21; - var sendTotal = 0; - if(!socket)socket = io.connect(); - var term = new Terminal({ cols: termCols, rows: termRows, screenKeys: true, useStyle: true}); - - term.open(); - term.setOption('cursorBlink', true); - term.setOption('fontSize', 14); - gterm = term; - - socket.on('server_response', function (data) { - term.write(data.data); - if (data.data == '\r\n登出\r\n' || - data.data == '登出\r\n' || - data.data == '\r\nlogout\r\n' || - data.data == 'logout\r\n') { - setTimeout(function () { - layer.closeAll(); - term.destroy(); - clearInterval(interval); - }, 500); - } - }); - - $(window).unload(function(){ -   term.destroy(); - clearInterval(interval); - }); - - if (socket) { - socket.emit('webssh', ''); - interval = setInterval(function () { - socket.emit('webssh', ''); - }, 500); - } - - term.on('data', function (data) { - socket.emit('webssh', data); - }); - - - var term_box = layer.open({ - type: 1, - title: "本地终端", - area: ['685px','463px'], - closeBtn: 1, - shadeClose: false, - content: '
                \ -
                \ - \ - \ -
                  \ -
                • 此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改。
                • \ -
                '; - - - 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 = '

                提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!

                \ - \ - \ - \ -
                  \ -
                • 此处为【'+ _name + version +'】主配置文件,若您不了解配置规则,请勿随意修改。
                • \ -
                '; - $(".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 = '

                提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!

                \ - \ - \ - \ -
                  \ -
                • 此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改。
                • \ -
                '; - $(".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 ='

                当前状态:'+(status ? '已加载' : '未加载' )+ - '

                \ - \ -
                '; - $(".soft-man-con").html(serviceCon); -} - -function pluginOpInitD(a, _version, b, _suffix_name='') { - var c = "name=" + a + "&func=" + b + "&version="+_version; - var d = ""; - b = b.split('_'+_suffix_name)[0]; - switch(b) { - case "initd_install":d = '加载';break; - case "initd_uninstall":d = '卸载';break; - } - - _ver = _version; - if(_version != ''){ - _ver = '【' + _version + '】'; - } - - layer.confirm( msgTpl('您真的要{1}{2}{3}服务吗?', [d,a,_ver]), {icon:3,closeBtn: 1}, function() { - var e = layer.msg(msgTpl('正在{1}{2}{3}服务,请稍候...',[d,a,_ver]), {icon: 16,time: 0}); - $.post("/plugins/run", c, function(g) { - layer.close(e); - var f = g.data == 'ok' ? msgTpl('{1}{3}服务已{2}',[a,d,_ver]) : msgTpl('{1}{3}服务{2}失败!',[a,d,_ver]); - layer.msg(f, {icon: g.data == 'ok' ? 1 : 2}); - - if ( b == 'initd_install' && g.data == 'ok' ) { - pluginSetInitD(a, _version, true); - }else{ - pluginSetInitD(a, _version, false); - } - if(g.data != 'ok') { - layer.msg(g.data, {icon: 2,time: 0,shade: 0.3,shadeClose: true}); - } - },'json').error(function() { - layer.close(e); - layer.msg('系统异常!', {icon: 0}); - }); - }) -} - -function pluginLogs(_name, version, func, line){ - - var _this = this; - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var func_name = 'error_log'; - if ( typeof(func) != 'undefined' ){ - func_name = func; - } - - var file_line = 100; - if ( typeof(line) != 'undefined' ){ - file_line = line; - } - - - 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);*/} - - - var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - var fileName = data.data; - $.post('/files/get_last_body', 'path=' + fileName+'&line='+file_line, function(rdata) { - layer.close(loadT2); - if (!rdata.status){ - layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - - if(rdata.data == '') { - rdata.data = '当前没有日志!'; - } - - - var h = parseInt($('.bt-w-menu').css('height')) - 40; - var ebody = ''; - $(".soft-man-con").html(ebody); - var ob = document.getElementById('info_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); - },'json'); -} - - -function pluginRollingLogs(_name, version, func, _args, line){ - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var func_name = 'error_log'; - if ( typeof(func) != 'undefined' ){ - func_name = func; - } - - var file_line = 100; - if ( typeof(line) != 'undefined' ){ - file_line = line; - } - - var reqTimer = null; - - function requestLogs(fileName){ - $.post('/files/get_last_body', 'path=' + fileName+'&line='+file_line, function(rdata) { - if (!rdata.status){ - return; - } - - if(rdata.data == '') { - rdata.data = '当前没有日志!'; - } - var ebody = ''; - $("#plugins_rolling_logs").html(ebody); - var ob = document.getElementById('roll_info_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); - } - - - layer.open({ - type: 1, - title: _name + '日志', - area: '640px', - end: function(){ - if (reqTimer){ - clearInterval(reqTimer); - } - }, - content:'
                \ - \ -
                ', - success:function(){ - $.post('/plugins/run', {name:_name, func:func_name, version:version, args:_args},function (data) { - var fileName = data.data; - requestLogs(fileName); - reqTimer = setInterval(function(){ - requestLogs(fileName); - },1000); - },'json'); - } - }); -} - - -function pluginStandAloneLogs(_name, version, func, _args, line){ - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var func_name = 'error_log'; - if ( typeof(func) != 'undefined' ){ - func_name = func; - } - - var file_line = 100; - if ( typeof(line) != 'undefined' ){ - file_line = line; - } - - - layer.open({ - type: 1, - title: _name + '日志', - area: '640px', - content:'
                \ - \ -
                ' - }); - - $.post('/plugins/run', {name:_name, func:func_name, version:version,args:_args},function (data) { - var fileName = data.data; - $.post('/files/get_last_body', 'path=' + fileName+'&line='+file_line, function(rdata) { - if (!rdata.status){ - return; - } - - if(rdata.data == '') { - rdata.data = '当前没有日志!'; - } - var ebody = ''; - $("#plugins_stand_alone_logs").html(ebody); - var ob = document.getElementById('plugins_stand_alone_logs'); - ob.scrollTop = ob.scrollHeight; - },'json'); - },'json'); -} -/*** 其中功能,针对插件通过库使用 end ***/ - - -$(function() { - autoHeight(); -}); -$(window).resize(function() { - autoHeight(); -}); \ No newline at end of file diff --git a/route/static/app/site.js b/route/static/app/site.js deleted file mode 100755 index 7a03629a0..000000000 --- a/route/static/app/site.js +++ /dev/null @@ -1,2923 +0,0 @@ - - -$("#site_search_input").keyup(function(event){ - if(event.keyCode == 13) { - getWeb(1, -1, $(this).val()); - } -}); - -$('#site_search').click(function(){ - getWeb(1, -1, $('#site_search_input').val()); -}); - -/** - * 取回网站数据列表 - * @param {Number} page 当前页 - * @param {String} search 搜索条件 - */ - function getWeb(page, type_id, search) { - if ( typeof(search) == 'undefined' ){ - search = $('#site_search_input').val(); - } - - var page = page == undefined ? '1':page; - var order = getCookie('order'); - if(order){ - order = '&order=' + order; - } else { - order = ''; - } - - var type = ''; - if ( typeof(type_id) == 'undefined' ){ - type = '&type_id=-1'; - } else { - type = '&type_id='+type_id; - } - - var sUrl = '/site/list'; - var pdata = 'limit=10&p=' + page + '&search=' + search + order + type; - var loadT = layer.load(); - //取回数据 - $.post(sUrl, pdata, function(data) { - layer.close(loadT); - //构造数据列表 - var body = ''; - $("#webBody").html(body); - for (var i = 0; i < data.data.length; i++) { - //当前站点状态 - if (data.data[i].status == '正在运行' || data.data[i].status == '1') { - var status = "运行中"; - } else { - var status = "已停止"; - } - - //是否有备份 - if (data.data[i].backup_count > 0) { - var backup = "有备份"; - } else { - var backup = "无备份"; - } - //是否设置有效期 - var web_end_time = (data.data[i].edate == "0000-00-00") ? '永久': data.data[i].edate; - //表格主体 - var shortwebname = data.data[i].name; - var shortpath = data.data[i].path; - if(data.data[i].name.length > 30) { - shortwebname = data.data[i].name.substring(0, 30) + "..."; - } - if(data.data[i].path.length > 30){ - shortpath = data.data[i].path.substring(0, 30) + "..."; - } - var idname = data.data[i].name.replace(/\./g,'_'); - - body = "\ - " + shortwebname + "\ - " + status + "\ - " + backup + "\ - " + shortpath + "\ - " + web_end_time + "\ - " + data.data[i].ps + "\ - \ - 设置\ - | 删除\ - " - - $("#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 = "当前没有站点数据"; - // $(".dataTables_paginate").hide(); - $("#webBody").html(body); - } - //输出数据列表 - $(".btn-more").hover(function(){ - $(this).addClass("open"); - },function(){ - $(this).removeClass("open"); - }); - - //输出分页 - $("#webPage").html(data.page); - // $("#webPage").html('
                站点分类:
                '); - - $(".btlinkbed").click(function(){ - var dataid = $(this).attr("data-id"); - var databak = $(this).text(); - if(databak == null){ - databak = ''; - } - $(this).hide().after(""); - $(".baktext").focus(); - }); - - readerTableChecked(); - },'json'); -} - - -function getBakPost(b) { - $(".baktext").hide().prev().show(); - var id = $(".baktext").attr("data-id"); - var page = $(".baktext").attr("data-page"); - var a = $(".baktext").val(); - if(a == "") { - a = '空'; - } - setWebPs(b, id, a,page); - $("a[data-id='" + id + "']").html(a); - $(".baktext").remove(); -} - -function setWebPs(b, id, ps,page) { - var d = layer.load({shade: true,shadeClose: false}); - var ps = 'ps=' + ps; - $.post('/site/set_ps', 'id=' + id + "&" + ps, function(data) { - if(data['status']) { - getWeb(page); - layer.closeAll(); - layer.msg('修改成功!', {icon: 1}); - } else { - layer.closeAll(); - layer.msg('修改失败!', {icon: 2}); - } - },'json'); -} - -//创建站点前,检查服务是否开启 -function webAdd(type){ - loading = layer.msg('正在检查是否开启OpenResty服务!',{icon:16,time:0,shade: [0.3, "#000"]}) - $.post('/site/check_web_status', function(data){ - layer.close(loading); - if (data.status){ - webAddPage(type) - } else { - layer.msg(data.msg,{icon:0,time:3000,shade: [0.3, "#000"]}) - } - },'json'); -} - -//添加站点 -function webAddPage(type) { - - if (type == 1) { - var array; - var str=""; - var domainlist=''; - var domain = array = $("#mainDomain").val().replace('http://','').replace('https://','').split("\n"); - var webport=[]; - var checkDomain = domain[0].split('.'); - if(checkDomain.length < 1){ - layer.msg(lan.site.domain_err_txt,{icon:2}); - return; - } - for(var i=1; i
                "; - layer.open({ - type: 1, - skin: 'demo-class', - area: '640px', - title: '添加网站', - closeBtn: 1, - shift: 0, - shadeClose: false, - content: "\ -
                \ - "+lan.site.domain+"\ -
                \ - \ -
                \ -
                \ -
                \ - 备注\ -
                \ - \ -
                \ -
                \ -
                \ - 根目录\ -
                \ - \ - \ -
                \ -
                \ - "+php_version+"\ -
                \ - \ - \ -
                \ - ", - }); - - $(function() { - var placeholder = "
                "+lan.site.domain_help+"
                "; - $('#mainDomain').after(placeholder); - $(".placeholder").click(function(){ - $(this).hide(); - $('#mainDomain').focus(); - }) - $('#mainDomain').focus(function() { - $(".placeholder").hide(); - }); - - $('#mainDomain').blur(function() { - if($(this).val().length==0){ - $(".placeholder").show(); - } - }); - - //验证PHP版本 - $("select[name='version']").change(function(){ - if($(this).val() == '52'){ - var msgerr = 'PHP5.2在您的站点有漏洞时有跨站风险,请尽量使用PHP5.3以上版本!'; - $('#php_w').text(msgerr); - }else{ - $('#php_w').text(''); - } - }) - - $('#mainDomain').on('input', function() { - var array; - var res,ress; - var str = $(this).val().replace('http://','').replace('https://',''); - var len = str.replace(/[^\x00-\xff]/g, "**").length; - array = str.split("\n"); - ress =array[0].split(":")[0]; - res = ress.replace(new RegExp(/([-.])/g), '_'); - if(res.length > 15){ - res = res.substr(0,15); - } - - var placeholder = $("#inputPath").attr('placeholder'); - $("#inputPath").val(placeholder+'/'+ress); - - if(res.length > 15){ - res = res.substr(0,15); - } - - $("#Wbeizhu").val(ress); - }) - - //备注 - $('#Wbeizhu').on('input', function() { - var str = $(this).val(); - var len = str.replace(/[^\x00-\xff]/g, "**").length; - if (len > 20) { - str = str.substring(0, 20); - $(this).val(str); - layer.msg('不能超出20个字符!', { - icon: 0 - }); - } - }) - //获取当前时间时间戳,截取后6位 - var timestamp = new Date().getTime().toString(); - var dtpw = timestamp.substring(7); - }); - }, 'json'); -} - -//修改网站目录 -function webPathEdit(id){ - $.post('/site/get_dir_user_ini','&id='+id, function(data){ - var userini = data['data']; - var webpath = userini['path']; - var siteName = userini['name']; - var runPath = userini['runPath']['runPath']; - var userinicheckeds = userini.userini?'checked':''; - var logscheckeds = userini.logs?'checked':''; - var opt = '' - var selected = ''; - for(var i=0;i'+ userini.runPath.dirs[i] +'' - } - var webPathHtml = "
                \ -
                \ - \ - \ -
                \ -
                \ - 网站目录\ - \ - \ - \ -
                \ -
                \ - 运行目录\ - \ - \ -
                \ -
                  \ -
                • 部分程序需要指定二级目录作为运行目录,如ThinkPHP5,Laravel
                • \ -
                • 选择您的运行目录,点保存即可
                • \ -
                " - +'
                ' - +'密码访问' - +'' - +'' - +'' - +'
                ' - +'
                ' - +'

                授权账号

                ' - +'

                访问密码

                ' - +'

                重复密码

                ' - +'

                ' - +'
                ' - +'
                '; - - $("#webedit-con").html(webPathHtml); - $("#userini").change(function(){ - $.post('/site/set_dir_user_ini',{ - 'path':webpath, - 'runPath':runPath, - },function(userini){ - layer.msg(userini.msg+'

                注意:设置防跨站需要重启PHP才能生效!

                ',{icon:userini.status?1:2}); - tryRestartPHP(siteName); - },'json'); - }); - - $("#logs").change(function(){ - var loadT = layer.msg("正在设置中...",{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/logs_open','id='+id, function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }); - - },'json'); -} - -//是否设置访问密码 -function pathSafe(id){ - var isPass = $('#pathSafe').prop('checked'); - if(!isPass){ - $(".user_pw").show(); - } else { - var loadT = layer.msg(lan.public.the,{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/close_has_pwd',{id:id},function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - $(".user_pw").hide(); - },'json'); - } -} - -//设置访问密码 -function setPathSafe(id){ - var username = $("#username_get").val(); - var pass1 = $("#password_get_1").val(); - var pass2 = $("#password_get_2").val(); - if(pass1 != pass2){ - layer.msg('两次输入的密码不一致!',{icon:2}); - return; - } - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_has_pwd',{id:id,username:username,password:pass1},function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//提交运行目录 -function setSiteRunPath(id){ - var NewPath = $("#runPath").val(); - var loadT = layer.msg(lan.public.the,{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_site_run_path','id='+id+'&runPath='+NewPath,function(rdata){ - layer.close(loadT); - var ico = rdata.status?1:2; - layer.msg(rdata.msg,{icon:ico}); - },'json'); -} - -//提交网站目录 -function setSitePath(id){ - var NewPath = $("#inputPath").val(); - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_path','id='+id+'&path='+NewPath,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//修改网站备注 -function webBakEdit(id){ - $.post("/data?action=getKey','table=sites&key=ps&id="+id,function(rdata){ - var webBakHtml = "
                \ -
                \ - \ -
                \ - \ -

                \ -
                \ -
                "; - $("#webedit-con").html(webBakHtml); - }); -} - - -//设置默认文档 -function setIndexEdit(id){ - $.post('/site/get_index','id='+id,function(data){ - var rdata = data['index']; - rdata = rdata.replace(new RegExp(/(,)/g), "\n"); - var setIndexHtml = "
                \ -
                \ - \ - \ -
                \ -
                  \ -
                • 默认文档,每行一个,优先级由上至下。
                • \ -
                \ -
                "; - $("#webedit-con").html(setIndexHtml); - },'json'); -} - -/** - * 停止一个站点 - * @param {Int} wid 网站ID - * @param {String} wname 网站名称 - */ -function webStop(wid, wname) { - layer.confirm('站点停用后将无法访问,您真的要停用这个站点吗?', {icon:3,closeBtn:2},function(index) { - if (index > 0) { - var loadT = layer.load(); - $.post("/site/stop","id=" + wid + "&name=" + wname, function(ret) { - layer.msg(ret.msg,{icon:ret.status?1:2}) - layer.close(loadT); - getWeb(1); - },'json'); - } - }); -} - -/** - * 启动一个网站 - * @param {Number} wid 网站ID - * @param {String} wname 网站名称 - */ -function webStart(wid, wname) { - layer.confirm('即将启动站点,您真的要启用这个站点吗?',{icon:3,closeBtn:2}, function(index) { - if (index > 0) { - var loadT = layer.load() - $.post("/site/start","id=" + wid + "&name=" + wname, function(ret) { - layer.msg(ret.msg,{icon:ret.status?1:2}) - layer.close(loadT); - getWeb(1); - },'json'); - } - }); -} - -/** - * 删除一个网站 - * @param {Number} wid 网站ID - * @param {String} wname 网站名称 - */ -function webDelete(wid, wname){ - var thtml = "
                \ - \ -
                "; - var info = '是否要删除同名根目录'; - safeMessage('删除站点'+"["+wname+"]",info, function(){ - var path=''; - if($("#delpath").is(":checked")){ - path='&path=1'; - } - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:10000,shade: [0.3, '#000']}); - $.post("/site/delete","id=" + wid + "&webname=" + wname + path, function(ret){ - layer.closeAll(); - layer.msg(ret.msg,{icon:ret.status?1:2}) - getWeb(1); - },'json'); - },thtml); -} - - -//批量删除 -function allDeleteSite(){ - var checkList = $("input[name=id]"); - var dataList = new Array(); - for(var i=0;i"+lan.site.all_del_info+"\ -
                "; - safeMessage(lan.site.all_del_site,""+lan.get('del_all_site',[dataList.length])+"",function(){ - layer.closeAll(); - var path = ''; - if($("#delpath").is(":checked")){ - path='&path=1'; - } - syncDeleteSite(dataList, 0,'',path); - },thtml); -} - -//模拟同步开始批量删除 -function syncDeleteSite(dataList,successCount,errorMsg,path){ - if(dataList.length < 1) { - showMsg(lan.get('del_all_site_ok',[successCount]), function(){ - // location.reload(); - },{icon:1}); - return; - } - var loadT = layer.msg(lan.get('del_all_task_the',[dataList[0].name]),{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/delete', 'id='+dataList[0].id+'&webname='+dataList[0].name+path , function(rdata){ - layer.close(loadT); - if(rdata.status){ - successCount++; - $("input[title='"+dataList[0].name+"']").parents("tr").remove(); - } else { - if(!errorMsg){ - errorMsg = '

                '+lan.site.del_err+':

                '; - } - errorMsg += '
              • '+dataList[0].name+' -> '+rdata.msg+'
              • ' - } - dataList.splice(0,1); - syncDeleteSite(dataList,successCount,errorMsg,path); - },'json'); -} - - -/** - * 域名管理 - * @param {Int} id 网站ID - */ -function domainEdit(id, name, msg, status) { - $.post('/site/get_domain' ,{pid:id}, function(domain) { - - var echoHtml = ""; - for (var i = 0; i < domain.length; i++) { - echoHtml += "\ - " + domain[i].name + "\ - " + domain[i].port + "\ - \ - "; - } - var bodyHtml = "\ - \ - \ -
                \ - \ - \ - " + echoHtml + "\ -
                "+lan.site.domain+"端口操作
                \ -
                "; - $("#webedit-con").html(bodyHtml); - if(msg != undefined){ - layer.msg(msg,{icon:status?1:5}); - } - var placeholder = "
                每行填写一个域名,默认为80端口
                泛解析添加方法 *.domain.com
                如另加端口格式为 www.domain.com:88
                "; - $('#newdomain').after(placeholder); - $(".placeholder").click(function(){ - $(this).hide(); - $('#newdomain').focus(); - }) - $('#newdomain').focus(function() { - $(".placeholder").hide(); - }); - - $('#newdomain').blur(function() { - if($(this).val().length==0){ - $(".placeholder").show(); - } - }); - $("#newdomain").on("input",function(){ - var str = $(this).val(); - if(isChineseChar(str)) { - $('.btn-zhm').show(); - } else{ - $('.btn-zhm').hide(); - } - }) - //checkDomain(); - },'json'); -} - -//编辑域名/端口 -function cancelSend(){ - $(".changeDomain,.changePort").hide().prev().show(); - $(".changeDomain,.changePort").remove(); -} -//遍历域名 -function checkDomain() { - $("#checkDomain tr").each(function() { - var $this = $(this); - var domain = $(this).find("td:first-child").text(); - $(this).find("td:first-child").append(""); - }); -} - -/** - * 添加域名 - * @param {Int} id 网站ID - * @param {String} webname 主域名 - */ -function domainAdd(id, webname,type) { - var Domain = $("#newdomain").val().split("\n"); - - var domainlist = ''; - for(var i=0; i下载 | "; - body += ""+frdata.data[i].name+"\ - " + (toSize(frdata.data[i].size)) + "\ - " + frdata.data[i].addtime + "\ - "+ ftpdown + "删除\ - " - } - - var ftpdown = ''; - frdata.page = frdata.page.replace(/'/g,'"').replace(/getBackup\(/g,"getBackup(" + id + ",0,"); - - if(name == 0){ - var sBody = "\ - \ - "+body+"\ -
                文件名称文件大小打包时间操作
                " - $("#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 + "\ -
                文件名称文件大小打包时间操作
                \ -
                " + frdata.page + "
                \ -
                \ -
                " - }); - },'json'); -} - -function goSet(num) { - //取选中对象 - var el = document.getElementsByTagName('input'); - var len = el.length; - var data = ''; - var a = ''; - var count = 0; - //构造POST数据 - for (var i = 0; i < len; i++) { - if (el[i].checked == true && el[i].value != 'on') { - data += a + count + '=' + el[i].value; - a = '&'; - count++; - } - } - //判断操作类别 - if(num==1){ - reAdd(data); - } - else if(num==2){ - shift(data); - } -} - - -//设置默认文档 -function setIndex(id){ - var quanju = (id==undefined)?lan.site.public_set:lan.site.local_site; - var data=id==undefined?"":"id="+id; - $.post('/site?action=GetIndex',data,function(rdata){ - rdata= rdata.replace(new RegExp(/(,)/g), "\n"); - layer.open({ - type: 1, - area: '500px', - title: lan.site.setindex, - closeBtn: 1, - shift: 5, - shadeClose: true, - content:"
                " - +"
                " - +" "+lan.site.default_doc+"" - +"
                " - +" " - +"

                "+quanju+lan.site.default_doc_help+"

                " - +"
                " - +"
                " - +"
                " - +" " - +" " - +"
                " - +"
                " - }); - }); -} - -//设置默认站点 -function setDefaultSite(){ - var name = $("#default_site").val(); - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_default_site','name='+name,function(rdata){ - layer.closeAll(); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - },'json'); -} - - -//默认站点 -function getDefaultSite(){ - $.post('/site/get_default_site','',function(rdata){ - var opt = ''; - var selected = ''; - for(var i=0;i' + rdata.sites[i].name + ''; - } - - layer.open({ - type: 1, - area: '530px', - title: '设置默认站点', - closeBtn: 1, - shift: 5, - shadeClose: true, - content:'
                \ -

                \ - 默认站点\ - \ -

                \ -
                  \ -
                • 设置默认站点后,所有未绑定的域名和IP都被定向到默认站点
                • \ -
                • 可有效防止恶意解析
                • \ -
                \ -
                \ - \ - \ -
                \ -
                ' - }); - },'json'); -} - -function setPHPVer(){ - $.post('/site/get_cli_php_version','',function(rdata){ - if(typeof(rdata['status'])!='undefined'){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - return; - } - - var opt = ''; - var selected = ''; - for(var i=0;i-1){ - continue; - } - - if (rdata.versions[i].version.indexOf("apt")>-1){ - continue; - } - - opt += ''; - } - - var phpver_layer = layer.open({ - type: 1, - area: '530px', - title: '设置PHP-CLI(命令行)版本', - closeBtn: 1, - shift: 5, - shadeClose: true, - btn:["确定","取消"], - content:'
                \ -

                \ - PHP-CLI版本\ - \ -

                \ -
                  \ -
                • 此处可设置命令行运行php时使用的PHP版本
                • \ -
                • 安装新的PHP版本后此处需要重新设置
                • \ -
                \ -
                ', - yes:function(layero,index){ - var version = $("#default_ver").val(); - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_cli_php_version','version='+version,function(rdata){ - layer.close(loadT); - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.close(phpver_layer); - } - },{icon:rdata.status?1:5},2000); - },'json'); - }, - }); - },'json'); -} - -function setIndexList(id){ - var Dindex = $("#Dindex").val().replace(new RegExp(/(\n)/g), ","); - if(id == undefined ){ - var data="id=&index="+Dindex; - } else{ - var data="id="+id+"&index="+Dindex; - } - var loadT= layer.load(2); - $.post('/site/set_index',data,function(rdata){ - layer.close(loadT); - var ico = rdata.status? 1:5; - layer.msg(rdata.msg,{icon:ico}); - },'json'); -} - - -/*站点修改*/ -function webEdit(id,website,endTime,addtime){ - layer.open({ - type: 1, - area: '700px', - title: '站点修改['+website+'] -- 添加时间['+addtime+']', - closeBtn: 1, - shift: 0, - content: "
                \ -
                \ -

                域名管理

                \ -

                子目录绑定

                \ -

                网站目录

                \ -

                流量限制

                \ -

                伪静态

                \ -

                默认文档

                \ -

                配置文件

                \ -

                SSL

                \ -

                PHP版本

                \ -

                重定向

                \ -

                反向代理

                \ -

                防盗链

                \ -

                响应日志

                \ -

                错误日志

                \ -
                \ -
                \ -
                ", - success:function(){ - //域名输入提示 - var placeholder = "
                每行填写一个域名,默认为80端口
                泛解析添加方法 *.domain.com
                如另加端口格式为 www.domain.com:88
                "; - $('#newdomain').after(placeholder); - $(".placeholder").click(function(){ - $(this).hide(); - $('#newdomain').focus(); - }); - - $('#newdomain').focus(function() { - $(".placeholder").hide(); - }); - - $('#newdomain').blur(function() { - if($(this).val().length == 0){ - $(".placeholder").show(); - } - }); - - //切换 - $(".bt-w-menu p").click(function(){ - $(this).addClass("bgw").siblings().removeClass("bgw"); - }); - - domainEdit(id,website); - } - }); -} - -//取网站日志 -function getSiteLogs(siteName){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_logs',{siteName:siteName},function(logs){ - layer.close(loadT); - if(logs.status !== true){ - logs.msg = ''; - } - if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); - var ob = document.getElementById('error_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); -} - -//取网站错误日志 -function getSiteErrorLogs(siteName){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_error_logs',{siteName:siteName},function(logs){ - // console.log(logs); - layer.close(loadT); - if(logs.status !== true){ - logs.msg = ''; - } - if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); - var ob = document.getElementById('error_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); -} - - -//防盗链 -function security(id,name){ - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_security',{id:id,name:name},function(rdata){ - console.log(rdata); - layer.close(loadT); - var mbody = '
                ' - +'

                URL后缀

                ' - +'

                许可域名

                ' - +'
                ' - +'
                ' - +'
                  ' - +'
                • 默认允许资源被直接访问,即不限制HTTP_REFERER为空的请求
                • ' - +'
                • 多个URL后缀与域名请使用逗号(,)隔开,如: png,jpeg,zip,js
                • ' - +'
                • 当触发防盗链时,将直接返回404状态
                • ' - +'
                ' - +'
                ' - $("#webedit-con").html(mbody); - },'json'); -} - -//设置防盗链 -function setSecurity(name,id, none){ - setTimeout(function(){ - var data = { - fix:$("input[name='sec_fix']").val(), - domains:$("input[name='sec_domains']").val(), - status:$("input[name='sec_status']").prop("checked"), - none:$("input[name='sec_none_status']").prop("checked"), - name:name, - id:id - } - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_security',data,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - if(rdata.status) setTimeout(function(){security(id,name);},1000); - },'json'); - },100); -} - - -//流量限制 -function limitNet(id){ - $.post('/site/get_limit_net','id='+id, function(rdata){ - var status_selected = rdata.perserver != 0?'checked':''; - if(rdata.perserver == 0){ - rdata.perserver = 300; - rdata.perip = 25; - rdata.limit_rate = 512; - } - var limitList = "" - +"" - +"" - +"" - +"" - +"" - +"" - var body = "
                " - +'

                ' - +"

                "+lan.site.limit_net_9+":

                " - +"

                "+lan.site.limit_net_10+":

                " - +"

                "+lan.site.limit_net_12+":

                " - +"

                "+lan.site.limit_net_14+":

                " - +"" - +"
                " - +"
                • "+lan.site.limit_net_11+"
                • "+lan.site.limit_net_13+"
                • "+lan.site.limit_net_15+"
                " - $("#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].port+""+rdata.binding[i].path+"伪静态 | 删除"; - } - - var dirList = ''; - for(var n=0;n"+rdata.dirs[n]+""; - } - - var body = "
                " - + "域名:" - + "子目录:" - + "" - + "
                " - + "
                " - + "" - + "" + echoHtml + "" - + "
                域名端口子目录操作
                "; - - $("#webedit-con").html(body); - },'json'); -} - -//子目录伪静态 -function setDirRewrite(id){ - $.post('/site/get_dir_bind_rewrite','id='+id,function(rdata){ - if(!rdata.status){ - var confirmObj = layer.confirm('你真的要为这个子目录创建独立的伪静态规则吗?',{icon:3,closeBtn:2},function(){ - $.post('/site/get_dir_bind_rewrite','id='+id+'&add=1',function(rdata){ - layer.close(confirmObj); - showRewrite(rdata); - },'json'); - }); - return; - } - showRewrite(rdata); - },'json'); -} - -//显示伪静态 -function showRewrite(rdata){ - var rList = ''; - for(var i=0;i"+rdata.rlist[i]+""; - } - var webBakHtml = "
                \ -
                \ - \ - \ -
                \ - \ -
                  \ -
                • 请选择您的应用,若设置伪静态后,网站无法正常访问,请尝试设置回default
                • \ -
                • 您可以对伪静态规则进行修改,修改完后保存即可。
                • \ -
                \ -
                "; - layer.open({ - type: 1, - area: '500px', - title: '配置伪静态规则', - closeBtn: 1, - shift: 5, - shadeClose: true, - content:webBakHtml, - success:function(){ - - $("#myRewrite").change(function(){ - var rewriteName = $(this).val(); - $.post('/files/get_body','path='+rdata['rewrite_dir']+'/'+rewriteName+'.conf',function(fileBody){ - $("#rewriteBody").val(fileBody.data.data); - },'json'); - }); - - $('#setRewriteBtn').click(function(){ - var data = $("#rewriteBody").val(); - setRewrite(rdata.filename, encodeURIComponent(data)); - }); - } - }); -} - -//添加子目录绑定 -function addDirBinding(id){ - var domain = $("input[name='domain']").val(); - var dirName = $("select[name='dirName']").val(); - if(domain == '' || dirName == '' || dirName == null){ - layer.msg(lan.site.d_s_empty,{icon:2}); - return; - } - - var data = 'id='+id+'&domain='+domain+'&dirName='+dirName; - $.post('/site/add_dir_bind',data,function(rdata){ - dirBinding(id); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//删除子目录绑定 -function delDirBind(id,siteId){ - layer.confirm(lan.site.s_bin_del,{icon:3,closeBtn:2},function(){ - $.post('/site/del_dir_bind','id='+id,function(rdata){ - dirBinding(siteId); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }); -} - -//301重定向 -function to301(siteName, type, obj){ - - // 设置 页面展示 - if(type == 1) { - obj = { - to: 'http://', - from: '', - r_type: '', - type: 1, - type: 'path', - keep_path: 1 - } - var redirect_form = layer.open({ - type: 1, - skin: 'demo-class', - area: '650px', - title: type == 1 ? '创建重定向' : '修改重定向[' + obj.redirectname + ']', - closeBtn: 1, - shift: 5, - shadeClose: false, - content: "
                " + - "
                " + - "
                " + - "保留URI参数" + - "" + - "
                " + - "
                " + - "
                " + - "重定向类型" + - "
                " + - "" + - "重定向方式" + - "
                " + - "
                " + - "
                " + - "重定向源" + - "
                " + - "" + - "目标URL" + - "" + - "
                " + - "
                " + - "
                " + - "
                " + - "" - }); - setTimeout(function() { - $('.btn-colse-prosy').click(function() { - layer.close(redirect_form); - }); - $('.btn-submit-redirect').click(function() { - var keep_path = $('[name="keep_path"]').prop('checked') ? 1 : 0; - var r_type = $('[name="r_type"]').val(); - var type = $('[name="type"]').val(); - var from = $('[name="from"]').val(); - var to = $('[name="to"]').val(); - - $.post('/site/set_redirect', { - siteName: siteName, - type: type, - r_type: r_type, - from: from, - to: to, - keep_path: keep_path - }, function(res) { - res = JSON.parse(res); - if (res.status) { - layer.close(redirect_form); - to301(siteName) - } else { - layer.msg(res.msg, { - icon: 2 - }); - } - }); - }); - }, 100); - } - - if (type == 2) { - $.post('/site/del_redirect', { - siteName: siteName, - id: obj, - }, function(res) { - res = JSON.parse(res); - if (res.status == true) { - layer.msg('删除成功', {time: 1000,icon: 1}); - to301(siteName); - } else { - layer.msg(res.msg, {time: 1000,icon: 2}); - } - }); - return - } - - if (type == 3) { - var laoding = layer.load(); - var data = {siteName: siteName,id: obj}; - $.post('/site/get_redirect_conf', data, function(res) { - layer.close(laoding); - res = JSON.parse(res); - if (res.status == true) { - var mBody = "
                \ - \ -
                \ -
                  \ -
                • 此处为重定向配置文件,若您不了解配置规则,请勿随意修改.
                • \ -
                \ -
                \ -
                "; - var editor; - var index = layer.open({ - type: 1, - title: '编辑配置文件', - closeBtn: 1, - shadeClose: true, - area: ['500px', '500px'], - btn: ['提交','关闭'], - content: mBody, - success: function () { - editor = CodeMirror.fromTextArea(document.getElementById("configRedirectBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").unbind('click'); - }, - yes:function(index,layero){ - $("#configRedirectBody").empty().text(editor.getValue()); - var load = layer.load(); - var data = { - siteName: siteName, - id: obj, - config: editor.getValue(), - }; - $.post('/site/save_redirect_conf', data, function(res) { - layer.close(load) - if (res.status == true) { - layer.msg('保存成功', {icon: 1}); - layer.close(index); - } else { - layer.msg(res.msg, {time: 3000,icon: 2}); - } - },'json'); - return true; - }, - }); - - } else { - layer.msg('请求错误!!', {time: 3000,icon: 2}); - } - }); - return - } - - var body = '
                \ -
                \ - \ -
                \ -
                \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                重定向类型重定向方式保留URL参数操作
                \ -
                \ -
                '; - $("#webedit-con").html(body); - - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_redirect','siteName='+siteName, function(res) { - layer.close(loadT); - $("#md-301-loading").remove(); - if (res.status === true) { - let data = res.data.result; - data.forEach(function(item){ - lan_r_type = item.r_type == 0 ? "永久重定向" : "临时重定向" - keep_path = item.keep_path == 0 ? "不保留" : "保留" - let tmp = '\ - '+item.r_from+'\ - '+lan_r_type+'\ - '+keep_path+'\ - 详细 | 删除\ - '; - $("#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: "
                \ -
                \ - 开启代理\ -
                \ - \ - \ -
                \ - 是否缓存\ - \ - \ -
                \ -
                \ -
                \ -
                \ - 名称\ -
                \ - \ -
                \ -
                \ - \ -
                \ - 代理目录\ -
                \ - \ -
                \ -
                \ -
                \ - 目标URL\ -
                \ - \ - 发送域名\ - \ -
                \ -
                \ - \ -
                \ -
                  \ -
                • 代理目录:访问这个目录时将会把目标URL的内容返回并显示
                • \ -
                • 目标URL:可以填写你需要代理的站点,目标URL必须为可正常访问的URL,否则将返回错误
                • \ -
                • 发送域名:将域名添加到请求头传递到代理服务器,默认为目标URL域名,若设置不当可能导致代理无法正常运行
                • \ -
                \ -
                \ -
                ", - 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 = "
                \ - \ -
                \ -
                  \ -
                • 此处为反向代理配置文件,若您不了解配置规则,请勿随意修改.
                • \ -
                \ -
                \ -
                "; - var editor; - var index = layer.open({ - type: 1, - title: '编辑配置文件', - closeBtn: 1, - shadeClose: true, - area: ['500px', '500px'], - btn: ['提交','关闭'], - content: mBody, - success: function () { - editor = CodeMirror.fromTextArea(document.getElementById("configProxyBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").unbind('click'); - }, - yes:function(index,layero){ - $("#configProxyBody").empty().text(editor.getValue()); - var load = layer.load(); - var data = { - siteName: siteName, - id: obj, - config: editor.getValue(), - }; - - $.post('/site/save_proxy_conf', data, function(res) { - layer.close(load) - if (res.status == true) { - layer.msg('保存成功', {icon: 1}); - layer.close(index); - } else { - layer.msg(res.msg, {time: 3000,icon: 2}); - } - },'json'); - return true; - }, - }); - },'json'); - return; - } - - if (type == 10 || type == 11) { - //[11]启动 或 停止[10] - status = type==10 ? '0' : '1'; - var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_proxy_status', {siteName: siteName,'status':status,'id':obj}, function(rdata) { - layer.close(loading); - if (!rdata.status){ - layer.msg(res.msg, {time: 3000,icon: 2}); - return; - } - - showMsg("设置成功",function(){ - toProxy(siteName); - },{icon: 1,time:2000}); - },'json'); - return; - } - - if (type == 20 || type == 21) { - //[20] 开始缓存 或 [21] 停止缓存 - var status = type == 20 ? 'on' : ''; - obj['open_cache'] = status; - obj['siteName'] = siteName; - - var loading = layer.msg('正在提交请求...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_proxy',obj, function(rdata) { - layer.close(loading); - if (!rdata.status){ - layer.msg(rdata.msg, {icon: 2,time:2000}); - return; - } - - showMsg("设置成功!",function(){ - toProxy(siteName); - },{icon: 1, time:2000}); - },'json'); - return; - } - - var body = '
                \ -
                \ - \ -
                \ -
                \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                名称代理目录目标地址缓存状态操作
                \ -
                \ -
                '; - $("#webedit-con").html(body); - - var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post("/site/get_proxy_list", {siteName: siteName},function (res) { - layer.close(loading); - if (!res.status){ - layer.msg(res.msg, {icon:2}); - return; - } - - var data = res.data.result; - for (var i = 0; i < data.length; i++) { - var item = data[i]; - - var switchProxy = ''; - if (!item['status']){ - switchProxy = ''; - } - - var openCache = '未开启'; - if (item['open_cache'] == 'on'){ - openCache = '已开启'; - } - - let tmp = '\ - '+item.name+'\ - '+item.from+'\ - '+item.to+'\ - '+openCache+'\ - '+switchProxy+'\ - \ - 详细 |\ - 编辑 |\ - 删除\ - \ - '; - $("#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\ - '+rdata[i].dns.join('
                ')+'\ - '+rdata[i].notAfter+'\ - '+rdata[i].issuer.split(' ')[0]+'\ - 部署 | 删除\ - ' - } - var txt = '
                \ - \ -
                \ - '+tbody+'\ -
                域名信任名称到期时间品牌操作
                '; - $(".tab-con").html(txt); - },'json'); -} - -//删除证书 -function removeSsl(certName){ - safeMessage('删除证书','您真的要从证书夹删除证书吗?',function(){ - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/remove_cert',{certName:certName},function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - $("#ssl_admin").click(); - },'json'); - }); -} - -//从证书夹部署 -function setCertSsl(certName,siteName){ - var loadT = layer.msg('正在部署证书...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_cert_to_site',{certName:certName,siteName:siteName},function(rdata){ - layer.close(loadT); - showMsg(rdata.msg, function(){ - $(".tab-nav span:first-child").click(); - },{icon:rdata.status?1:2},2000); - },'json'); -} - -//ssl -function setSSL(id,siteName){ - var sslHtml = '\ -
                \ - 当前证书 - [未部署SSL]\ - Let\'s Encrypt\ - ACME\ - 证书夹' - + '
                \ -
                ' - + '
                '; - $("#webedit-con").html(sslHtml); - $(".tab-nav span").click(function(){ - $(this).addClass("on").siblings().removeClass("on"); - }); - opSSL('now',id,siteName); -} - -//设置httpToHttps -function httpToHttps(siteName){ - var isHttps = $("#toHttps").prop('checked'); - if(isHttps){ - layer.confirm('关闭强制HTTPS后需要清空浏览器缓存才能看到效果,继续吗?',{icon:3,title:"关闭强制HTTPS"},function(){ - $.post('/site/close_to_https','siteName='+siteName,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }); - }else{ - $.post('/site/http_to_https','siteName='+siteName,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - } -} - - -function deleteSSL(type,id,siteName){ - $.post('/site/delete_ssl','site_name='+siteName+'&ssl_type='+type,function(rdata){ - showMsg(rdata.msg, function(){ - opSSL(type,id,siteName); - },{icon:rdata.status?1:2}, 2000); - },'json'); -} - -function deploySSL(type,id,siteName){ - $.post('/site/deploy_ssl','site_name='+siteName+'&ssl_type='+type,function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - $('#now_ssl').click(); - } else{ - opSSL(type,id,siteName); - } - },{icon:rdata.status?1:2}, 2000); - },'json'); -} - -function renewSSL(type,id,siteName){ - showSpeedWindow('正在续签...', 'site.get_let_logs', function(layers,index){ - $.post('/site/renew_ssl','site_name='+siteName+'&ssl_type='+type,function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - opSSL(type,id,siteName); - } - },{icon:rdata.status?1:2}, 2000); - },'json'); - }); -} - -function fileCheck(){ - -} - -function dnsCheck(){ - -} - -//SSL -function opSSL(type, id, siteName, callback){ - - var now = '
                \ - \ -
                \ -
                密钥(KEY)
                \ -
                证书(PEM格式)
                \ -
                \ -
                \ - \ -
                \ -
                \ -
                  \ -
                • 粘贴您的*.key以及*.pem内容,然后保存即可。
                • \ -
                • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
                • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
                • \ -
                • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
                • \ -
                '; - - // \ - // \ - var lets = '
                \ -
                \ -
                \ -
                \ - 验证方式\ -
                \ - \ - \ -
                \ -
                \ -
                \ -
                \ -
                \ - \ - \ -
                \ -
                \ -
                \ -
                \ - 管理员邮箱\ - \ -
                \ -
                \ - 域名\ -
                  \ -
                  \ -
                  \ - \ -
                  \ -
                    \ -
                  • 申请之前,请确保域名已解析,如未解析会导致审核失败
                  • \ -
                  • Let\'s Encrypt免费证书,有效期3个月,支持多域名。默认会自动续签
                  • \ -
                  • 若您的站点使用了CDN或301重定向会导致续签失败
                  • \ -
                  • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
                  • \ -
                  \ -
                  '; - - // \ - // \ - var acme = '
                  \ -
                  \ -
                  \ -
                  \ - 验证方式\ -
                  \ - \ - \ -
                  \ -
                  \ -
                  \ -
                  \ -
                  \ - \ - \ -
                  \ -
                  \ -
                  \ -
                  \ - 管理员邮箱\ - \ -
                  \ -
                  \ - 域名\ -
                    \ -
                    \ -
                    \ - \ -
                    \ -
                      \ -
                    • 申请之前,请确保域名已解析,如未解析会导致审核失败
                    • \ -
                    • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
                    • \ -
                    • 若您的站点使用了CDN或301重定向会导致续签失败
                    • \ -
                    • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
                    \ - \ -
                    '; - - - switch(type){ - case 'lets': - $(".tab-con").html(lets); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name+'' - } - } - $("input[name='admin_email']").val(data.email); - $("#ymlist").html(opt); - $("#ymlist li input").click(function(e){ - e.stopPropagation(); - }) - $("#ymlist li").click(function(){ - var o = $(this).find("input"); - if(o.prop("checked")){ - o.prop("checked",false) - } - else{ - o.prop("checked",true); - } - }) - $(".letsApply").click(function(){ - var c = $("#ymlist input[type='checkbox']"); - var str = []; - var domains = ''; - for(var i=0; i
                    \ -
                    证书(PEM格式)
                    \ -
                    \ -
                    \ - \ - \ - \ -
                    \ -
                    \ -
                      \ -
                    • 已为您自动生成Let\'s Encrypt免费证书
                    • \ -
                    • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
                    • \ -
                    • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                    • \ -
                    '; - $(".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 = "
                    \ -
                    证书品牌:"+issuer[0]+"
                    \ -
                    到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                    \ -
                    \ -
                    \ -
                    认证域名:"+domains+"
                    \ -
                    "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'acme': - $(".tab-con").html(acme); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=acme', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name - +''; - } - } - $("input[name='admin_email']").val(data.email); - $("#ymlist").html(opt); - $("#ymlist li input").click(function(e){ - e.stopPropagation(); - }) - $("#ymlist li").click(function(){ - var o = $(this).find("input"); - if(o.prop("checked")){ - o.prop("checked",false) - } - else{ - o.prop("checked",true); - } - }) - $(".letsApply").click(function(){ - var c = $("#ymlist input[type='checkbox']"); - var str = []; - var domains = ''; - for(var i=0; i\ -
                    证书(PEM格式)
                    \ - \ -
                    \ - \ - \ -
                    \ - \ -
                      \ -
                    • 已为您自动生成ACME免费证书
                    • \ -
                    • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
                    • \ -
                    • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                    • \ -
                    '; - $(".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 = "
                    \ -
                    证书品牌:"+issuer[0]+"
                    \ -
                    到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                    \ -
                    \ -
                    \ -
                    认证域名:"+domains+"
                    \ -
                    "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'now': - $(".tab-con").html(now); - var key = ''; - var csr = ''; - var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('site/get_ssl','site_name='+siteName,function(data){ - layer.close(loadT); - var rdata = data['data']; - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
                    \ -
                    证书品牌:"+issuer[0]+"
                    \ -
                    到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                    \ -
                    \ -
                    \ -
                    认证域名:"+domains+"
                    \ -
                    强制HTTPS:\ - \ -
                    \ -
                    "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - - if(rdata.key == false){ - rdata.key = ''; - } else { - $(".ssl-btn").append(''); - } - - if(rdata.csr == false){ - rdata.csr = ''; - } - $("#key").val(rdata.key); - $("#csr").val(rdata.csr); - - $("#toHttps").attr('checked',rdata.httpTohttps); - if(rdata.status){ - $('.warning_info').css('display','none'); - - $(".ssl-btn").append(""); - $('#now_ssl').html('当前证书 - [已部署SSL]'); - } else{ - $('.warning_info').css('display','block'); - $('#now_ssl').html('当前证书 - [未部署SSL]'); - } - - - if (typeof (callback) != 'undefined'){ - callback(rdata); - } - },'json'); - break; - default: - layer.msg("错误类型", {icon:5}); - break; - } -} - - -//开启与关闭SSL -function ocSSL(action,siteName){ - var loadT = layer.msg('正在获取证书列表,请稍后..',{icon:16,time:0,shade: [0.3, '#000']}); - $.post("/site/"+action,'siteName='+siteName+'&updateOf=1',function(rdata){ - layer.close(loadT) - - if(!rdata.status){ - if(!rdata.out){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - setSSL(siteName); - return; - } - data = "

                    证书获取失败:


                    " - for(var i=0;i" - + "

                    错误类型: "+rdata.out[i].Type+"

                    " - + "

                    详情: "+rdata.out[i].Detail+"

                    " - + "
                    "; - } - layer.msg(data,{icon:2,time:0,shade:0.3,shadeClose:true}); - return; - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - if(action == 'close_ssl_conf'){ - layer.msg('已关闭SSL,请务必清除浏览器缓存后再访问站点!',{icon:1,time:5000}); - } - $(".tab-nav .on").click(); - },'json'); -} - -//生成SSL -function newSSL(siteName, id, domains){ - showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){ - var force = ''; - if ($("#checkDomain").prop("checked")){ - force = '&force=true'; - } - var email = $("input[name='admin_email']").val(); - $.post('/site/create_let','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ - $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); - },'json'); - }); -} - -function newAcmeSSL(siteName, id, domains){ - showSpeedWindow('正在由ACME申请...', 'site.get_acme_logs', function(layers,index){ - var force = ''; - if($("#checkDomain").prop("checked")){ - force = '&force=true'; - } - var email = $("input[name='admin_email']").val(); - $.post('/site/create_acme','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ - $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); - },'json'); - }); -} - -//保存SSL -function saveSSL(siteName){ - var data = 'type=1&siteName='+siteName+'&key='+encodeURIComponent($("#key").val())+'&csr='+encodeURIComponent($("#csr").val()); - var loadT = layer.msg(lan.site.saving_txt,{icon:16,time:20000,shade: [0.3, '#000']}) - $.post('/site/set_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.msg(rdata.msg,{icon:1}); - $(".ssl-btn").find(".btn-default").remove(); - $(".ssl-btn").append(""); - } else { - layer.msg(rdata.msg,{icon:2,time:0,shade:0.3,shadeClose:true}); - } - },'json'); -} - -//PHP版本 -function phpVersion(siteName){ - $.post('/site/get_site_php_version','siteName='+siteName,function(version){ - // console.log(version); - if(version.status === false){ - layer.msg(version.msg,{icon:5}); - return; - } - $.post('/site/get_php_version',function(rdata){ - var versionSelect = "
                    \ -
                    \ - PHP版本\ -
                    \ - \ - \ -
                    \ - \ -
                    \ -
                      \ -
                    • 请根据您的程序需求选择版本
                    • \ -
                    • 若非必要,请尽量不要使用PHP5.2,这会降低您的服务器安全性;
                    • \ -
                    • PHP7不支持mysql扩展,默认安装mysqli以及mysql-pdo。
                    • \ -
                    \ -
                    \ - "; - $("#webedit-con").html(versionSelect); - //验证PHP版本 - $("select[name='phpVersion']").change(function(){ - if($(this).val() == '52'){ - var msgerr = 'PHP5.2在您的站点有漏洞时有跨站风险,请尽量使用PHP5.3以上版本!'; - $('#php_w').text(msgerr); - }else{ - $('#php_w').text(''); - } - }) - },'json'); - },'json'); -} - - -//设置PHP版本 -function setPHPVersion(siteName){ - var data = 'version='+$("#phpVersion").val()+'&siteName='+siteName; - var loadT = layer.msg('正在保存...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_php_version',data,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//配置文件 -function configFile(webSite){ - var info = syncPost('/site/get_host_conf', {siteName:webSite}); - $.post('/files/get_body','path='+info['host'],function(rdata){ - var mBody = "
                    \ - \ -
                    \ - \ -
                      \ -
                    • 此处为站点主配置文件,若您不了解配置规则,请勿随意修改.
                    • \ -
                    \ -
                    \ -
                    "; - $("#webedit-con").html(mBody); - var editor = CodeMirror.fromTextArea(document.getElementById("configBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#SaveConfigFileBtn").click(function(){ - $("#configBody").empty(); - $("#configBody").text(editor.getValue()); - saveConfigFile(webSite,rdata.data.encoding, info['host']); - }) - },'json'); -} - -//保存配置文件 -function saveConfigFile(webSite,encoding,path){ - var data = 'encoding='+encoding+'&data='+encodeURIComponent($("#configBody").val())+'&path='+path; - var loadT = layer.msg('保存中...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/save_host_conf',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.msg(rdata.msg,{icon:1}); - }else{ - layer.msg(rdata.msg,{icon:2,time:0,shade:0.3,shadeClose:true}); - } - },'json'); -} - -//伪静态 -function rewrite(siteName){ - $.post("/site/get_rewrite_list", 'siteName='+siteName,function(rdata){ - var info = syncPost('/site/get_rewrite_conf', {siteName:siteName}); - var filename = info['rewrite']; - $.post('/files/get_body','path='+filename,function(fileBody){ - var centent = fileBody['data']['data']; - var rList = ''; - for(var i=0;i"; - } else { - rList += ""; - } - } - var webBakHtml = "
                    \ -
                    \ - \ -
                    \ - \ - \ -
                      \ -
                    • 请选择您的应用,若设置伪静态后,网站无法正常访问,请尝试设置回default
                    • \ -
                    • 您可以对伪静态规则进行修改,修改完后保存即可。
                    • \ -
                    \ -
                    "; - $("#webedit-con").html(webBakHtml); - - var editor = CodeMirror.fromTextArea(document.getElementById("rewriteBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#SetRewriteBtn").click(function(){ - $("#rewriteBody").empty(); - $("#rewriteBody").text(editor.getValue()); - setRewrite(filename, encodeURIComponent(editor.getValue())); - }); - $("#SetRewriteBtnTel").click(function(){ - $("#rewriteBody").empty(); - $("#rewriteBody").text(editor.getValue()); - setRewriteTel(); - }); - - $("#myRewrite").change(function(){ - var rewriteName = $(this).val(); - if(rewriteName == '0'){ - rpath = filename; - }else{ - var info = syncPost('/site/get_rewrite_tpl', {tplname:rewriteName}); - if (!info['status']){ - layer.msg(info['msg']); - return; - } - rpath = info['data']; - } - - $.post('/files/get_body','path='+rpath,function(fileBody){ - $("#rewriteBody").val(fileBody['data']['data']); - editor.setValue(fileBody['data']['data']); - },'json'); - }); - },'json'); - },'json'); -} - - -//设置伪静态 -function setRewrite(filename,data){ - var data = 'data='+data+'&path='+filename+'&encoding=utf-8'; - var loadT = layer.msg(lan.site.saving_txt,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_rewrite',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.msg(rdata.msg,{icon:1}); - }else{ - layer.msg(rdata.msg,{icon:2,time:0,shade:0.3,shadeClose:true}); - } - },'json'); -} -var aindex = null; - -//保存为模板 -function setRewriteTel(act){ - aindex = layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', //宽高 - title: '保存为Rewrite模板', - btn:[lan.public.ok,lan.public.cancel], - content: '
                    \ -
                    \ - \ -
                    \ -
                    ', - success:function(index){ - $("#rewriteName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#rewriteNameBtn").click(); - }); - }, - yes:function(index){ - name = $("#rewriteName").val(); - if(name == ''){ - layer.msg(lan.site.template_empty,{icon:5}); - return; - } - var data = 'data='+encodeURIComponent($("#rewriteBody").val())+'&name='+name; - var loadT = layer.msg(lan.site.saving_txt,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_rewrite_tpl',data,function(rdata){ - layer.close(loadT); - layer.close(index); - layer.msg(rdata.msg, {icon:rdata.status?1:5}); - },'json'); - return; - } - }); -} -//修改默认页 -function siteDefaultPage(){ - stype = getCookie('serverType'); - layer.open({ - type: 1, - area: '460px', - title: '修改默认页', - closeBtn: 1, - shift: 0, - content: '
                    \ - \ - \ - \ - \ -
                    ' - }); -} - -function changeDefault(type){ - $.post('/site/get_site_doc','type='+type, function(rdata){ - showMsg('操作成功!',function(){ - if (rdata.status){ - vhref = rdata.data.path; - onlineEditFile(0,vhref); - } - },{icon:rdata.status?1:2}); - },'json'); -} - -function getClassType(){ - var select = $('.site_type > select'); - $.post('/site/get_site_types',function(rdata){ - $(select).html(''); - $(select).append(''); - for (var i = 0; i'+rdata[i]['name']+''); - } - - $(select).bind('change',function(){ - var select_id = $(this).val(); - getWeb(1,select_id, ''); - }) - },'json'); -} -getClassType(); - -function setClassType(){ - $.post('/site/get_site_types',function(rdata){ - var list = ''; - for (var i = 0; i\ - 编辑 | 删除\ - '; - } - - layer.open({ - type: 1, - area: '350px', - title: '网站分类管理', - closeBtn: 1, - shift: 0, - content: '
                    \ -
                    \ -
                    \ -
                    \ -
                    \ - \ - \ - '+list+'\ -
                    名称操作
                    \ -
                    \ -
                    ' - }); - },'json'); -} - -function addClassType(){ - var name = $("input[name=type_name]").val(); - $.post('/site/add_site_type','name='+name, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - setClassType(); - getClassType(); - } - },{icon:rdata.status?1:2}); - },'json'); -} - -function removeClassType(id,name){ - if (id == 0){ - layer.msg('默认分类不可删除/不可编辑!',{icon:2}); - return; - } - layer.confirm('是否确定删除分类?',{title: '删除分类【'+ name +'】' }, function(){ - $.post('/site/remove_site_type','id='+id, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - setClassType(); - getClassType(); - } - },{icon:rdata.status?1:2}); - },'json'); - }); -} - -function editClassType(id,name){ - if (id == 0){ - layer.msg('默认分类不可删除/不可编辑!',{icon:2}); - return; - } - - layer.open({ - type: 1, - area: '350px', - title: '修改分类管理【' + name + '】', - closeBtn: 1, - shift: 0, - content: "
                    \ -
                    \ - 分类名称\ -
                    \ -
                    \ -
                    \ - \ -
                    \ -
                    " - }); - - $('#site_type_mod').unbind().click(function(){ - var _name = $('input[name=site_type_mod]').val(); - $.post('/site/modify_site_type_name','id='+id+'&name='+_name, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - setClassType(); - getClassType(); - } - },{icon:rdata.status?1:2}); - },'json'); - - }); -} - - -function moveClassTYpe(){ - $.post('/site/get_site_types',function(rdata){ - var option = ''; - for (var i = 0; i'+rdata[i]['name']+''; - } - - layer.open({ - type: 1, - area: '350px', - title: '设置站点分类', - closeBtn: 1, - shift: 0, - content: '
                    \ -
                    \ -
                    默认站点\ -
                    \ - \ -
                    \ -
                    \ -
                    \ -
                    \ -
                    ' - }); - },'json'); -} - - -function setSizeClassType(){ - var data = {}; - data['id'] = $('select[name=type_id]').val(); - var ids = []; - $('table').find('td').find('input').each(function(i,obj){ - checked = $(this).prop('checked'); - if (checked) { - ids.push($(this).val()); - } - }); - data['site_ids'] = JSON.stringify(ids); - $.post('/site/set_site_type',data, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - } - },{icon:rdata.status?1:2}); - },'json'); -} - - -// 尝试重启PHP -function tryRestartPHP(siteName){ - $.post('/site/get_site_php_version','siteName='+siteName,function(data){ - var phpversion = data.phpversion; - - if (phpversion == "00"){ - return - } - - var php_sign = 'php'; - if (phpversion.indexOf('yum') > -1){ - php_sign = 'php-yum'; - phpversion = phpversion.replace('yum',''); - } - - if (phpversion.indexOf('apt') > -1){ - php_sign = 'php-apt'; - phpversion = phpversion.replace('apt',''); - } - - var reqData = {name: php_sign, func:'restart'} - reqData['version'] = phpversion; - - // console.log(reqData); - var loadT = layer.msg('尝试自动重启PHP['+phpversion+']...', { icon: 16, time: 0, shade: 0.3 }); - $.post('/plugins/run', reqData, function(data) { - layer.close(loadT); - layer.msg(data.msg,{icon:data.status?1:2,time:3000,shade: [0.3, '#000']}); - },'json'); - },'json'); -} \ No newline at end of file diff --git a/route/static/app/soft.js b/route/static/app/soft.js deleted file mode 100755 index eacbfcf5f..000000000 --- a/route/static/app/soft.js +++ /dev/null @@ -1,517 +0,0 @@ - - -//重置插件弹出框宽度 -function resetPluginWinWidth(width){ - $("div[id^='layui-layer'][class*='layui-layer-page']").width(width); -} - -//重置插件弹出框宽度 -function resetPluginWinHeight(height){ - $("div[id^='layui-layer'][class*='layui-layer-page']").height(height); - $(".bt-form .bt-w-main").height(height-42); -} - -//软件管理窗口 -function softMain(name, title, version) { - - var _title = title.replace('-'+version,'') - - var loadT = layer.msg("正在处理,请稍后...", { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/plugins/setting?name='+name, function(rdata) { - layer.close(loadT); - layer.open({ - type: 1, - area: '640px', - title: _title + '【' + version + "】管理", - closeBtn: 1, - shift: 0, - content: rdata - }); - $(".bt-w-menu p").click(function() { - $(this).addClass("bgw").siblings().removeClass("bgw"); - }); - - //version to - $(".plugin_version").attr('version',version).hide(); - }); -} - -//取软件列表 -function getSList(isdisplay) { - if (isdisplay !== true) { - var loadT = layer.msg('正在获取列表...', { icon: 16, time: 0, shade: [0.3, '#000'] }) - } - if (!isdisplay || isdisplay === true) - isdisplay = getCookie('p' + getCookie('soft_type')); - if (isdisplay == true || isdisplay == 'true') isdisplay = 1; - - var search = $("#SearchValue").val(); - if (search != '') { - search = '&search=' + search; - } - var type = ''; - var istype = getCookie('soft_type'); - if (istype == 'undefined' || istype == 'null' || !istype) { - istype = '0'; - } - - type = '&type=' + istype; - var page = ''; - if (isdisplay) { - page = '&p=' + isdisplay; - setCookie('p' + getCookie('soft_type'), isdisplay); - } - - var condition = (search + type + page).slice(1); - $.get('/plugins/list?' + condition, '', function(rdata) { - layer.close(loadT); - var tBody = ''; - var sBody = ''; - var pBody = ''; - - for (var i = 0; i < rdata.type.length; i++) { - var c = ''; - if (istype == rdata.type[i].type) { - c = 'class="on"'; - } - tBody += '' + rdata.type[i].title + ''; - } - - $(".softtype").html(tBody); - $("#softPage").html(rdata.list); - $("#softPage .Pcount").css({ "position": "absolute", "left": "0" }) - - $(".task").text(rdata.data[rdata.length - 1]); - for (var i = 0; i < rdata.data.length; i++) { - var plugin = rdata.data[i]; - var len = plugin.versions.length; - var version_info = ''; - var version = ''; - var softPath = ''; - var titleClick = ''; - var state = ''; - var indexshow = ''; - var checked = ''; - - checked = plugin.display ? 'checked' : ''; - - if (typeof(plugin.versions) == "string" ){ - version_info += plugin.versions + '|'; - } else { - for (var j = 0; j < len; j++) { - version_info += plugin.versions[j] + '|'; - } - } - if (version_info != '') { - version_info = version_info.substring(0, version_info.length - 1); - } - - var handle = '安装'; - - if (plugin.setup == true) { - - var mupdate = '';//(plugin.versions[n] == plugin.updates[n]) '' : '更新 | '; - // if (plugin.versions[n] == '') mupdate = ''; - handle = mupdate + '设置 | 卸载'; - titleClick = 'onclick="softMain(\'' + plugin.name + '\',\'' + plugin.title + '\',\'' + plugin.setup_version + '\')" style="cursor:pointer"'; - - softPath = ''; - if (plugin.coexist){ - indexshow = '
                    \ - \ - \ -
                    '; - } else { - indexshow = '
                    \ - \ - \ -
                    '; - } - - if (plugin.status == true) { - state = '' - } else { - state = '' - } - } - - if (plugin.task == '-2') { - handle = '正在卸载...'; - } else if (plugin.task == '-1') { - handle = '正在安装...'; - } else if (plugin.task == '0') { - handle = '等待中...'; - } - - var plugin_title = plugin.title; - if (plugin.setup && !plugin.coexist){ - plugin_title = plugin.title + ' ' + plugin.setup_version; - } - - icon_link = "/plugins/file?name="+plugin.name+"&f=ico.png"; - if (plugin.icon != ''){ - icon_link = "/plugins/file?name="+plugin.name+"&f="+plugin.icon; - } - - sBody += '' + - ''+ - '' + plugin_title + '' + - '' + plugin.ps + '' + - '' + softPath + '' + - '' + state + '' + - '' + indexshow + '' + - '' + handle + '' + - ''; - } - - sBody += pBody; - - - $("#softList").html(sBody); - $(".menu-sub span").click(function() { - setCookie('soft_type', $(this).attr('typeid')); - $(this).addClass("on").siblings().removeClass("on"); - getSList(); - }); - - loadImage(); - },'json'); -} - -function installPreInspection(name, ver, callback){ - var loading = layer.msg('正在检查安装环境...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post("/plugins/run", {'name':name,'version':ver,'func':'install_pre_inspection'}, function(rdata) { - layer.close(loading); - if (rdata.status){ - if (rdata.data == 'ok'){ - callback(); - } else { - layer.msg(rdata.data, { icon: 2 }); - } - } else { - layer.msg(rdata.data, { icon: rdata.status ? 1 : 2 }); - } - },'json'); -} - -function runInstall(data){ - var loadT = layer.msg('正在添加到安装器...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post("/plugins/install", data, function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - getSList(); - },'json'); -} - -function addVersion(name, ver, type, obj, title, install_pre_inspection) { - var option = ''; - var titlename = title.replace("-"+ver,""); - if (ver.indexOf('|') >= 0){ - var veropt = ver.split("|"); - var selectVersion = ''; - for (var i = 0; i < veropt.length; i++) { - selectVersion += ''; - } - option = ""; - } else { - option = '【' + titlename + '】 ' + ver + ''; - } - - layer.open({ - type: 1, - title: "【"+titlename + "】软件安装", - area: '350px', - closeBtn: 1, - shadeClose: true, - btn: ['提交','关闭'], - content: "
                    \ -
                    安装版本:" + option + "
                    \ -
                    ", - success:function(){ - $('.fangshi input').click(function() { - $(this).attr('checked', 'checked').parent().siblings().find("input").removeAttr('checked'); - }); - installTips(); - }, - yes:function(index,layero){ - var info = $("#selectVersion").val().toLowerCase(); - if (info == ''){ - info = $("#selectVersion").attr('val').toLowerCase(); - } - var info_split = info.split(' '); - var name = info_split[0]; - var version = info_split[1]; - - var type = $('.fangshi').prop("checked") ? '1' : '0'; - var request_args = "name=" + name + "&version=" + version + "&type=" + type; - - if (install_pre_inspection){ - //安装检查 - installPreInspection(name, version, function(){ - runInstall(request_args); - flySlow('layui-layer-btn0'); - }); - return; - } - - runInstall(request_args); - flySlow('layui-layer-btn0'); - } - }); -} - -//卸载软件 -function uninstallPreInspection(name, title, ver, callback){ - var loading = layer.msg('正在检查卸载环境...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post("/plugins/run", {'name':name,'version':ver,'func':'uninstall_pre_inspection'}, function(rdata) { - layer.close(loading); - if (rdata.status){ - if (rdata.data == 'ok'){ - if (typeof(callback) == 'function'){ - callback(); - } - } else { - layer.msg(rdata.data, { icon: 2 , time: 6666}); - } - } else { - layer.msg(rdata.data, { icon: rdata.status ? 1 : 2, time: 6666 }); - } - },'json'); -} - - -function runUninstallVersion(name, title, version){ - var title = title.replace("-"+version,""); - layer.confirm(msgTpl('您真的要卸载【{1}-{2}】吗?', [title, version]), { icon: 3, closeBtn: 1 }, function() { - var data = 'name=' + name + '&version=' + version; - var loadT = layer.msg('正在处理,请稍候...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/plugins/uninstall', data, function(rdata) { - layer.close(loadT) - getSList(); - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - },'json'); - }); -} - - -function uninstallVersion(name, title, version, uninstall_pre_inspection) { - if (uninstall_pre_inspection) { - uninstallPreInspection(name,title,version,function(){ - runUninstallVersion(name,title,version); - }); - } else { - runUninstallVersion(name,title,version); - } -} - - -//首页显示 -function toIndexDisplay(name, version, coexist) { - - var status = $("#index_" + name).prop("checked") ? "0" : "1"; - if (coexist == "true") { - var verinfo = version.replace(/\./, ""); - status = $("#index_" + name + verinfo).prop("checked") ? "0" : "1"; - } - - var data = "name=" + name + "&status=" + status + "&version=" + version; - $.post("/plugins/set_index", data, function(rdata) { - if (rdata.status) { - layer.msg(rdata.msg, { icon: 1 }) - } else { - layer.msg(rdata.msg, { icon: 2 }) - } - },'json'); -} - -function indexListHtml(callback){ - - - // init - $("#indexsoft").html(''); - var index_soft = ''; - for (var i = 0; i < 12; i++) { - index_soft += '
                    '; - } - $("#indexsoft").html(index_soft); - - // var loadT = layer.msg('正在获取列表...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/plugins/index_list', function(rdata) { - // layer.close(loadT); - $("#indexsoft").html(''); - var con = ''; - for (var i = 0; i < rdata.length; i++) { - var plugin = rdata[i]; - var len = plugin.versions.length; - var version_info = ''; - - if (typeof plugin.versions == "string"){ - version_info += plugin.versions + '|'; - } else { - for (var j = 0; j < len; j++) { - version_info += plugin.versions[j] + '|'; - } - } - if (version_info != '') { - version_info = version_info.substring(0, version_info.length - 1); - } - - if (plugin.status == true) { - state = '' - } else { - state = '' - } - - var name = plugin.title + ' ' + plugin.setup_version + ' '; - var data_id = plugin.name + '-' + plugin.setup_version; - if (plugin.coexist){ - name = plugin.title + ' '; - data_id = plugin.name + '-' + plugin.versions; - } - - con += '
                    \ - \ -
                    \ -
                    \ -
                    ' + name + state + '
                    \ -
                    \ -
                    '; - - // loadImage(); - } - - $("#indexsoft").html(con); - //软件位置移动 - var softboxlen = $("#indexsoft > div").length; - var softboxsum = 12; - var softboxcon = ''; - var softboxn = softboxlen; - if (softboxlen <= softboxsum) { - for (var i = 0; i < softboxsum - softboxlen; i++) { - softboxcon += '
                    '; - } - $("#indexsoft").append(softboxcon); - } - - if (typeof callback=='function'){ - callback(); - } - },'json'); -} - - -//首页软件列表 -function indexSoft() { - indexListHtml(function(){ - $("#indexsoft").dragsort({ - dragSelector: ".spanmove", - dragBetween: true, - dragEnd: saveOrder, - placeHolderTemplate: "
                    " - }); - }); - - function saveOrder() { - var data = $("#indexsoft > div").map(function() { return $(this).attr("data-id"); }).get(); - tmp = []; - for(i in data){ - // console.log(data[i]); - if (data[i] != ''){ - tmp.push($.trim(data[i])); - } - } - var ssort = tmp.join("|"); - $("input[name=list1SortOrder]").val(ssort); - $.post("/plugins/index_sort", 'ssort=' + ssort, function(rdata) { - if (!rdata.status){ - showMsg('设置失败:'+ rdata.msg, function(){ - indexListHtml(); - }, { icon: 16, time: 0, shade: [0.3, '#000'] }); - } - },'json'); - }; -} - - -function importPluginOpen(){ - $("#update_zip").on("change", function () { - var files = $("#update_zip")[0].files; - if (files.length == 0) { - return; - } - importPlugin(files[0]); - $("#update_zip").val('') - }); - $("#update_zip").click(); -} - - -function importPlugin(file){ - var formData = new FormData(); - formData.append("plugin_zip", file); - $.ajax({ - url: "/plugins/update_zip", - type: "POST", - data: formData, - processData: false, - dataType:'json', - contentType: false, - success: function (data) { - if (data.status === false) { - layer.msg(data.msg, { icon: 2 }); - return; - } - var loadT = layer.open({ - type: 1, - area: "500px", - title: "安装第三方插件包", - closeBtn: 1, - shift: 5, - shadeClose: false, - content: '\ -
                    \ - \ -
                      \ -
                    • 此为第三方开发的插件,无法验证其可靠性!
                    • \ -
                    • 安装过程可能需要几分钟时间,请耐心等候!
                    • \ -
                    • 如果已存在此插件,将被替换!
                    • \ -
                    \ -
                    \ -
                    ' - }); - - },error: function (responseStr) { - layer.msg('上传失败2!:' + responseStr, { icon: 2 }); - } - }); -} - - -function importPluginInstall(plugin_name, tmp_path) { - layer.msg('正在安装,这可能需要几分钟时间...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/plugins/input_zip', { plugin_name: plugin_name, tmp_path: tmp_path }, function (rdata) { - layer.closeAll() - if (rdata.status) { - getSList(true); - } - setTimeout(function () { layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }) }, 1000); - },'json'); -} - -$(function() { - if (window.document.location.pathname == '/soft/') { - setInterval(function() { getSList(); }, 8000); - } -}); \ No newline at end of file diff --git a/route/static/app/upload.js b/route/static/app/upload.js deleted file mode 100755 index cdeaddd4b..000000000 --- a/route/static/app/upload.js +++ /dev/null @@ -1,238 +0,0 @@ -function MyAjax() { - this.serverdata = this.erromsg = this.timeout = this.stop = this.xmlhttp = !1, this.transit = !0 -} -MyAjax.prototype.handle = function(d, f) { - if(4 == d.readyState) { - if(this.stop === !0) { - return - } - if(this.transit = !0, f.timeout && f.async && (clearTimeout(this.timeout), this.timeout = !1), 200 == d.status) { - var e = this.serverdata = d.responseText.replace(/(^\s*)|(\s*$)/g, ""); - "function" == typeof f.success && f.success(e) - } else { - this.erromsg = d.status, "function" == typeof f.error && f.error(d.status) - } - } else { - if(0 == d.readyState) { - if(this.stop === !0) { - return - } - f.timeout && f.async && (clearTimeout(this.timeout), this.timeout = !1), this.erromsg = d.readyState, this.transit = !0, "function" == typeof f.error && f.error(d.readyState) - } - } -}, MyAjax.prototype.out = function(b) { - this.transit = !0, this.erromsg = 504, this.stop = !0, "function" == typeof b.error && b.error(504) -}, MyAjax.prototype.carry = function(j) { - var i, h, g, l; - if(j.lock && !this.transit) { - return !1 - } - this.transit = !1, this.stop = this.erromsg = !1, i = window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject("Microsoft.XMLHTTP"), j.type = j.type.toUpperCase(), h = function() {}, "string" == typeof j.data ? (j.data = j.data.replace(/(^\s*)|(\s*$)/g, ""), h = function() { - i.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") - }) : "[object FormData]" !== Object.prototype.toString.call(j.data) ? (j.data = "", h = function() { - i.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") - }) : ("function" == typeof j.progress && i.upload.addEventListener("progress", j.progress, !1), j.type = "POST"), g = "" == j.data ? [null, ""] : [j.data, "?" + j.data], l = this, "function" == typeof j.complete && j.complete(), j.timeout && j.async && (this.timeout = setTimeout(function() { - l.out(j) - }, j.timeout)), j.async === !0 && (i.onreadystatechange = function() { - l.handle(i, j) - }); - try { - switch(j.type) { - case "POST": - i.open("POST", j.url, j.async), h(); - break; - default: - i.open("GET", j.url + g[1], j.async), j.cache === !0 || i.setRequestHeader("If-Modified-Since", "0") - } - } catch(k) { - return this.erromsg = 505, j.timeout && j.async && (clearTimeout(this.timeout), this.timeout = !1), this.transit = !0, "function" == typeof j.error && j.error(505), void 0 - } - i.send(g[0]), j.async === !1 && l.handle(i, j) -}; - -function uploadStart(d) { - var a = function(e) { - this.uptype = e.UpType; - this.url = e.url; - this.oldurl = e.url; - this.filessize = e.FilesSize, this.MaxUpNum = e.MaxUpNum, this.str = new MyAjax(); - this.file_input = document.getElementById("file_input"); - this.opt = document.getElementById("opt"); - this.up = document.getElementById("up"); - this.up_box = document.getElementById("up_box"); - this.filesalllength = this.FilesArrayLength = this.up_box_li = FileProgress = 0; - this.FilesArray = new Array(); - this.num = 0 - }; - a.prototype = { - SelectFile: function() { - if(this.FilesArrayLength === 0) { - this.up_box.innerHTML = ""; - this.un() - } - var h = this.file_input.files, - e, g, f = h.length; - if(this.filesalllength + f > this.MaxUpNum) { - f = this.MaxUpNum - this.filesalllength; - layer.msg(lan.get('update_num',[this.MaxUpNum]), { - icon: 5 - }) - } - for(var j = 0; j < f; j++) { - e = h[j]; - g = e.name.split("."); - g = Object.prototype.toString.call(g) === "[object Array]" ? g[g.length - 1] : ""; - if(d) { - if(g != "sql" && g != "zip" && g != "gz" && g != "tgz") { - layer.msg(lan.upload.file_type_err, { - icon: 5 - }); - return - } - } - if(!e) { - this.up_box.insertAdjacentHTML("beforeEnd", "
                  • " + e.name + ""+lan.upload.file_err+"
                  • ") - } else { - if(this.uptype.length > 0 && this.uptype.indexOf(g.toLowerCase()) === -1) { - this.up_box.insertAdjacentHTML("beforeEnd", "
                  • " + e.name + ""+lan.upload.file_type_err+"
                  • ") - } else { - if(e.size <= 0) { - this.up_box.insertAdjacentHTML("beforeEnd", "
                  • " + e.name + ""+lan.upload.file_err_empty+"
                  • ") - } else { - this.up_box.insertAdjacentHTML("beforeEnd", "
                  • " + e.name + "" + (toSize(e.size)) + ""+lan.upload.up_sleep+"
                  • "); - this.FilesArray.push([e, (this.filesalllength - 1 < 0 ? 0 : this.filesalllength) + j]) - } - } - } - } - this.filesalllength += f; - this.FilesArrayLength = this.FilesArray.length - }, - read: function() { - if(this.filesalllength == 0) { - layer.msg(lan.upload.select_file, { - icon: 5 - }); - return - } - this.url = this.oldurl + "&codeing=" + document.getElementById("fileCodeing").value; - if(this.FilesArrayLength > 0) { - this.opt.disabled = true; - this.up.disabled = true; - this.file_input.disabled = true; - this.up_box_li = this.up_box.getElementsByTagName("li"); - this.ready(this.FilesArray, 0, this.FilesArrayLength - 1) - } else { - layer.msg(an.upload.select_empty, { - icon: 5 - }) - } - }, - un: function() { - this.opt.disabled = this.up.disabled = this.file_input.disabled; - this.filesalllength = this.FilesArrayLength = this.up_box_li = 0; - this.FilesArray = new Array() - }, - ready: function(i, f, h) { - if(f > h) { - this.un(); - return - } - try { - var g = new FormData(); - g.append("zunfile", i[f][0]) - } catch(e) { - this.opt.disabled = true; - this.up.disabled = true; - this.file_input.disabled = true; - layer.msg(lan.upload.ie_err, { - icon: 5 - }) - } - if(i.length > 1) { - $("#totalProgress").html("

                    "+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必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot deleted file mode 100755 index b93a4953fff68df523aa7656497ee339d6026d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20127 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 \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf deleted file mode 100755 index 1413fc609ab6f21774de0cb7e01360095584f65b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45404 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 G`u_lkUM?>H diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100755 index 64539b54c3751a6d9adb44c8e3a45ba5a73b77f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18028 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 diff --git a/route/static/bootstrap-3.3.5/fonts/index.html b/route/static/bootstrap-3.3.5/fonts/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/bootstrap-3.3.5/fonts/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/index.html b/route/static/bootstrap-3.3.5/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/bootstrap-3.3.5/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/build/addons/attach/attach.js b/route/static/build/addons/attach/attach.js deleted file mode 100755 index 847cb62e8..000000000 --- a/route/static/build/addons/attach/attach.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * @license MIT - * - * Implements the attach method, that attaches the terminal to a WebSocket stream. - */ - -(function (attach) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = attach(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], attach); - } else { - /* - * Plain browser environment - */ - attach(window.Terminal); - } -})(function (Terminal) { - 'use strict'; - - var exports = {}; - - /** - * Attaches the given terminal to the given socket. - * - * @param {Terminal} term - The terminal to be attached to the given socket. - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - exports.attach = function (term, socket, bidirectional, buffered) { - bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; - term.socket = socket; - - term._flushBuffer = function () { - term.write(term._attachSocketBuffer); - term._attachSocketBuffer = null; - }; - - term._pushToBuffer = function (data) { - if (term._attachSocketBuffer) { - term._attachSocketBuffer += data; - } else { - term._attachSocketBuffer = data; - setTimeout(term._flushBuffer, 10); - } - }; - - var myTextDecoder; - - term._getMessage = function (ev) { - var str; - if (typeof ev.data === "object") { - if (ev.data instanceof ArrayBuffer) { - if (!myTextDecoder) { - myTextDecoder = new TextDecoder(); - } - - str = myTextDecoder.decode( ev.data ); - } - else { - throw "TODO: handle Blob?"; - } - } - - if (buffered) { - term._pushToBuffer(str || ev.data); - } else { - term.write(str || ev.data); - } - }; - - term._sendData = function (data) { - socket.send(data); - }; - - socket.addEventListener('message', term._getMessage); - - if (bidirectional) { - term.on('data', term._sendData); - } - - socket.addEventListener('close', term.detach.bind(term, socket)); - socket.addEventListener('error', term.detach.bind(term, socket)); - }; - - /** - * Detaches the given terminal from the given socket - * - * @param {Terminal} term - The terminal to be detached from the given socket. - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - exports.detach = function (term, socket) { - term.off('data', term._sendData); - - socket = (typeof socket == 'undefined') ? term.socket : socket; - - if (socket) { - socket.removeEventListener('message', term._getMessage); - } - - delete term.socket; - }; - - /** - * Attaches the current terminal to the given socket - * - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - Terminal.prototype.attach = function (socket, bidirectional, buffered) { - return exports.attach(this, socket, bidirectional, buffered); - }; - - /** - * Detaches the current terminal from the given socket. - * - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - Terminal.prototype.detach = function (socket) { - return exports.detach(this, socket); - }; - - return exports; -}); diff --git a/route/static/build/addons/attach/index.html b/route/static/build/addons/attach/index.html deleted file mode 100755 index 4a41fbde2..000000000 --- a/route/static/build/addons/attach/index.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - -
                    - -

                    - xterm.js: socket attach -

                    -

                    - Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your - Docker containers. -

                    -

                    - Socket information -

                    -
                    - - -
                    -
                    - -
                    - - - \ No newline at end of file diff --git a/route/static/build/addons/attach/package.json b/route/static/build/addons/attach/package.json deleted file mode 100755 index e8f6b2ca2..000000000 --- a/route/static/build/addons/attach/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.attach", - "main": "attach.js", - "private": true -} diff --git a/route/static/build/addons/fit/fit.js b/route/static/build/addons/fit/fit.js deleted file mode 100755 index 01e0c2f21..000000000 --- a/route/static/build/addons/fit/fit.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * @license MIT - * - * Fit terminal columns and rows to the dimensions of its DOM element. - * - * ## Approach - * - * Rows: Truncate the division of the terminal parent element height by the - * terminal row height. - * Columns: Truncate the division of the terminal parent element width by the - * terminal character width (apply display: inline at the terminal - * row and truncate its width with the current number of columns). - */ - -(function (fit) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = fit(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], fit); - } else { - /* - * Plain browser environment - */ - fit(window.Terminal); - } -})(function (Terminal) { - var exports = {}; - - exports.proposeGeometry = function (term) { - if (!term.element.parentElement) { - return null; - } - var parentElementStyle = window.getComputedStyle(term.element.parentElement); - var parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); - var parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')) - 17); - var elementStyle = window.getComputedStyle(term.element); - var elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')); - var elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')); - var availableHeight = parentElementHeight - elementPaddingVer; - var availableWidth = parentElementWidth - elementPaddingHor; - var geometry = { - cols: Math.floor(availableWidth / term.charMeasure.width), - rows: Math.floor(availableHeight / Math.floor(term.charMeasure.height * term.getOption('lineHeight'))) - }; - - return geometry; - }; - - exports.fit = function (term) { - // Wrap fit in a setTimeout as charMeasure needs time to get initialized - // after calling Terminal.open - setTimeout(function () { - var geometry = exports.proposeGeometry(term); - - if (geometry) { - // Force a full render - if (term.rows !== geometry.rows || term.cols !== geometry.cols) { - term.renderer.clear(); - term.resize(geometry.cols, geometry.rows); - } - } - }, 0); - }; - - Terminal.prototype.proposeGeometry = function () { - return exports.proposeGeometry(this); - }; - - Terminal.prototype.fit = function () { - return exports.fit(this); - }; - - return exports; -}); diff --git a/route/static/build/addons/fit/package.json b/route/static/build/addons/fit/package.json deleted file mode 100755 index aec1bbd6d..000000000 --- a/route/static/build/addons/fit/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.fit", - "main": "fit.js", - "private": true -} diff --git a/route/static/build/addons/fullscreen/fullscreen.css b/route/static/build/addons/fullscreen/fullscreen.css deleted file mode 100755 index 37229932f..000000000 --- a/route/static/build/addons/fullscreen/fullscreen.css +++ /dev/null @@ -1,10 +0,0 @@ -.xterm.fullscreen { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - width: auto; - height: auto; - z-index: 255; -} diff --git a/route/static/build/addons/fullscreen/fullscreen.js b/route/static/build/addons/fullscreen/fullscreen.js deleted file mode 100755 index f1dc56a26..000000000 --- a/route/static/build/addons/fullscreen/fullscreen.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * @license MIT - */ - -(function (fullscreen) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = fullscreen(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], fullscreen); - } else { - /* - * Plain browser environment - */ - fullscreen(window.Terminal); - } -})(function (Terminal) { - var exports = {}; - - /** - * Toggle the given terminal's fullscreen mode. - * @param {Terminal} term - The terminal to toggle full screen mode - * @param {boolean} fullscreen - Toggle fullscreen on (true) or off (false) - */ - exports.toggleFullScreen = function (term, fullscreen) { - var fn; - - if (typeof fullscreen == 'undefined') { - fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add'; - } else if (!fullscreen) { - fn = 'remove'; - } else { - fn = 'add'; - } - - term.element.classList[fn]('fullscreen'); - }; - - Terminal.prototype.toggleFullscreen = function (fullscreen) { - exports.toggleFullScreen(this, fullscreen); - }; - - return exports; -}); diff --git a/route/static/build/addons/fullscreen/package.json b/route/static/build/addons/fullscreen/package.json deleted file mode 100755 index 9c359a052..000000000 --- a/route/static/build/addons/fullscreen/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.fullscreen", - "main": "fullscreen.js", - "private": true -} diff --git a/route/static/build/addons/search/search.js b/route/static/build/addons/search/search.js deleted file mode 100755 index 088e28347..000000000 --- a/route/static/build/addons/search/search.js +++ /dev/null @@ -1,116 +0,0 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0; y--) { - result = this._findInLine(term, y); - if (result) { - break; - } - } - if (!result) { - for (var y = this._terminal.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) { - result = this._findInLine(term, y); - if (result) { - break; - } - } - } - return this._selectResult(result); - }; - SearchHelper.prototype._findInLine = function (term, y) { - var lowerStringLine = this._terminal.buffer.translateBufferLineToString(y, true).toLowerCase(); - var lowerTerm = term.toLowerCase(); - var searchIndex = lowerStringLine.indexOf(lowerTerm); - if (searchIndex >= 0) { - return { - term: term, - col: searchIndex, - row: y - }; - } - }; - SearchHelper.prototype._selectResult = function (result) { - if (!result) { - return false; - } - this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length); - this._terminal.scrollLines(result.row - this._terminal.buffer.ydisp, false); - return true; - }; - return SearchHelper; -}()); -exports.SearchHelper = SearchHelper; - - - -},{}],2:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var SearchHelper_1 = require("./SearchHelper"); -(function (addon) { - if (typeof window !== 'undefined' && 'Terminal' in window) { - addon(window.Terminal); - } - else if (typeof exports === 'object' && typeof module === 'object') { - module.exports = addon(require('../../Terminal').Terminal); - } - else if (typeof define === 'function') { - define(['../../xterm'], addon); - } -})(function (Terminal) { - Terminal.prototype.findNext = function (term) { - if (!this._searchHelper) { - this.searchHelper = new SearchHelper_1.SearchHelper(this); - } - return this.searchHelper.findNext(term); - }; - Terminal.prototype.findPrevious = function (term) { - if (!this._searchHelper) { - this.searchHelper = new SearchHelper_1.SearchHelper(this); - } - return this.searchHelper.findPrevious(term); - }; -}); - - - -},{"../../Terminal":undefined,"./SearchHelper":1}]},{},[2]) -//# sourceMappingURL=search.js.map diff --git a/route/static/build/addons/search/search.js.map b/route/static/build/addons/search/search.js.map deleted file mode 100755 index baa06e886..000000000 --- a/route/static/build/addons/search/search.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"search.js","sources":["../../../src/addons/search/search.ts","../../../src/addons/search/SearchHelper.ts","../../../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { SearchHelper } from './SearchHelper';\n\ndeclare var exports: any;\ndeclare var module: any;\ndeclare var define: any;\ndeclare var require: any;\ndeclare var window: any;\n\n(function (addon) {\n if (typeof window !== 'undefined' && 'Terminal' in window) {\n /**\n * Plain browser environment\n */\n addon(window.Terminal);\n } else if (typeof exports === 'object' && typeof module === 'object') {\n /**\n * CommonJS environment\n */\n module.exports = addon(require('../../Terminal').Terminal);\n } else if (typeof define === 'function') {\n /**\n * Require.js is available\n */\n define(['../../xterm'], addon);\n }\n})((Terminal: any) => {\n /**\n * Find the next instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n Terminal.prototype.findNext = function(term: string): boolean {\n if (!this._searchHelper) {\n this.searchHelper = new SearchHelper(this);\n }\n return (this.searchHelper).findNext(term);\n };\n\n /**\n * Find the previous instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n Terminal.prototype.findPrevious = function(term: string): boolean {\n if (!this._searchHelper) {\n this.searchHelper = new SearchHelper(this);\n }\n return (this.searchHelper).findPrevious(term);\n };\n});\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n// import { ITerminal } from '../../Interfaces';\n// import { translateBufferLineToString } from '../../utils/BufferLine';\n\ninterface ISearchResult {\n term: string;\n col: number;\n row: number;\n}\n\n/**\n * A class that knows how to search the terminal and how to display the results.\n */\nexport class SearchHelper {\n constructor(private _terminal: any) {\n // TODO: Search for multiple instances on 1 line\n // TODO: Don't use the actual selection, instead use a \"find selection\" so multiple instances can be highlighted\n // TODO: Highlight other instances in the viewport\n // TODO: Support regex, case sensitivity, etc.\n }\n\n /**\n * Find the next instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n public findNext(term: string): boolean {\n if (!term || term.length === 0) {\n return false;\n }\n\n let result: ISearchResult;\n\n let startRow = this._terminal.buffer.ydisp;\n if (this._terminal.selectionManager.selectionEnd) {\n // Start from the selection end if there is a selection\n startRow = this._terminal.selectionManager.selectionEnd[1];\n }\n\n // Search from ydisp + 1 to end\n for (let y = startRow + 1; y < this._terminal.buffer.ybase + this._terminal.rows; y++) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n\n // Search from the top to the current ydisp\n if (!result) {\n for (let y = 0; y < startRow; y++) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n }\n\n // Set selection and scroll if a result was found\n return this._selectResult(result);\n }\n\n /**\n * Find the previous instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n public findPrevious(term: string): boolean {\n if (!term || term.length === 0) {\n return false;\n }\n\n let result: ISearchResult;\n\n let startRow = this._terminal.buffer.ydisp;\n if (this._terminal.selectionManager.selectionStart) {\n // Start from the selection end if there is a selection\n startRow = this._terminal.selectionManager.selectionStart[1];\n }\n\n // Search from ydisp + 1 to end\n for (let y = startRow - 1; y >= 0; y--) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n\n // Search from the top to the current ydisp\n if (!result) {\n for (let y = this._terminal.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n }\n\n // Set selection and scroll if a result was found\n return this._selectResult(result);\n }\n\n /**\n * Searches a line for a search term.\n * @param term Tne search term.\n * @param y The line to search.\n * @return The search result if it was found.\n */\n private _findInLine(term: string, y: number): ISearchResult {\n const lowerStringLine = this._terminal.buffer.translateBufferLineToString(y, true).toLowerCase();\n const lowerTerm = term.toLowerCase();\n const searchIndex = lowerStringLine.indexOf(lowerTerm);\n if (searchIndex >= 0) {\n return {\n term,\n col: searchIndex,\n row: y\n };\n }\n }\n\n /**\n * Selects and scrolls to a result.\n * @param result The result to select.\n * @return Whethera result was selected.\n */\n private _selectResult(result: ISearchResult): boolean {\n if (!result) {\n return false;\n }\n this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length);\n this._terminal.scrollLines(result.row - this._terminal.buffer.ydisp, false);\n return true;\n }\n}\n",null],"names":[],"mappings":"AEAA;;;ADiBA;AACA;AAAA;AAKA;AAQA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAQA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AA1Ha;;;;;;;ADZb;AAQA;AACA;AAIA;AACA;AAAA;AAIA;AACA;AAAA;AAIA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;;;"} \ No newline at end of file diff --git a/route/static/build/addons/terminado/package.json b/route/static/build/addons/terminado/package.json deleted file mode 100755 index 9af1851bc..000000000 --- a/route/static/build/addons/terminado/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.terminado", - "main": "terminado.js", - "private": true -} diff --git a/route/static/build/addons/terminado/terminado.js b/route/static/build/addons/terminado/terminado.js deleted file mode 100755 index 702351b9c..000000000 --- a/route/static/build/addons/terminado/terminado.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. - * @license MIT - * - * This module provides methods for attaching a terminal to a terminado - * WebSocket stream. - */ - -(function (attach) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = attach(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], attach); - } else { - /* - * Plain browser environment - */ - attach(window.Terminal); - } -})(function (Terminal) { - 'use strict'; - - var exports = {}; - - /** - * Attaches the given terminal to the given socket. - * - * @param {Terminal} term - The terminal to be attached to the given socket. - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - exports.terminadoAttach = function (term, socket, bidirectional, buffered) { - bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; - term.socket = socket; - - term._flushBuffer = function () { - term.write(term._attachSocketBuffer); - term._attachSocketBuffer = null; - }; - - term._pushToBuffer = function (data) { - if (term._attachSocketBuffer) { - term._attachSocketBuffer += data; - } else { - term._attachSocketBuffer = data; - setTimeout(term._flushBuffer, 10); - } - }; - - term._getMessage = function (ev) { - var data = JSON.parse(ev.data) - if( data[0] == "stdout" ) { - if (buffered) { - term._pushToBuffer(data[1]); - } else { - term.write(data[1]); - } - } - }; - - term._sendData = function (data) { - socket.send(JSON.stringify(['stdin', data])); - }; - - term._setSize = function (size) { - socket.send(JSON.stringify(['set_size', size.rows, size.cols])); - }; - - socket.addEventListener('message', term._getMessage); - - if (bidirectional) { - term.on('data', term._sendData); - } - term.on('resize', term._setSize); - - socket.addEventListener('close', term.terminadoDetach.bind(term, socket)); - socket.addEventListener('error', term.terminadoDetach.bind(term, socket)); - }; - - /** - * Detaches the given terminal from the given socket - * - * @param {Xterm} term - The terminal to be detached from the given socket. - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - exports.terminadoDetach = function (term, socket) { - term.off('data', term._sendData); - - socket = (typeof socket == 'undefined') ? term.socket : socket; - - if (socket) { - socket.removeEventListener('message', term._getMessage); - } - - delete term.socket; - }; - - /** - * Attaches the current terminal to the given socket - * - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - Terminal.prototype.terminadoAttach = function (socket, bidirectional, buffered) { - return exports.terminadoAttach(this, socket, bidirectional, buffered); - }; - - /** - * Detaches the current terminal from the given socket. - * - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - Terminal.prototype.terminadoDetach = function (socket) { - return exports.terminadoDetach(this, socket); - }; - - return exports; -}); diff --git a/route/static/build/addons/winptyCompat/winptyCompat.js b/route/static/build/addons/winptyCompat/winptyCompat.js deleted file mode 100755 index 89c0d8795..000000000 --- a/route/static/build/addons/winptyCompat/winptyCompat.js +++ /dev/null @@ -1,29 +0,0 @@ -(function (addon) { - if (typeof window !== 'undefined' && 'Terminal' in window) { - addon(window.Terminal); - } - else if (typeof exports === 'object' && typeof module === 'object') { - module.exports = addon(require('../../Terminal').Terminal); - } - else if (typeof define === 'function') { - define(['../../xterm'], addon); - } -})(function (Terminal) { - Terminal.prototype.winptyCompatInit = function () { - var _this = this; - var isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; - if (!isWindows) { - return; - } - this.on('lineFeed', function () { - var line = _this.buffer.lines.get(_this.buffer.ybase + _this.buffer.y - 1); - var lastChar = line[_this.cols - 1]; - if (lastChar[3] !== 32) { - var nextLine = _this.buffer.lines.get(_this.buffer.ybase + _this.buffer.y); - nextLine.isWrapped = true; - } - }); - }; -}); - -//# sourceMappingURL=winptyCompat.js.map diff --git a/route/static/build/addons/winptyCompat/winptyCompat.js.map b/route/static/build/addons/winptyCompat/winptyCompat.js.map deleted file mode 100755 index 826e69714..000000000 --- a/route/static/build/addons/winptyCompat/winptyCompat.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/addons/winptyCompat/winptyCompat.ts"],"names":[],"mappings":"AAQA,CAAC,UAAU,KAAK;IACd,EAAE,CAAC,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC;QAI1D,KAAK,CAAO,MAAO,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC;QAIrE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC7D,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC;QAIxC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC,CAAC,UAAC,QAAa;IACf,QAAQ,CAAC,SAAS,CAAC,gBAAgB,GAAG;QAAA,iBAyBrC;QAvBC,IAAM,SAAS,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1F,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,CAAC;QACT,CAAC;QAYD,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE;YAClB,IAAM,IAAI,GAAG,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAI,CAAC,MAAM,CAAC,KAAK,GAAG,KAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1E,IAAM,QAAQ,GAAG,IAAI,CAAC,KAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;YACrC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAY,CAAC,CAAC,CAAC;gBACjC,IAAM,QAAQ,GAAG,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAI,CAAC,MAAM,CAAC,KAAK,GAAG,KAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACpE,QAAS,CAAC,SAAS,GAAG,IAAI,CAAC;YACnC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","file":"winptyCompat.js","sourceRoot":"."} \ No newline at end of file diff --git a/route/static/build/addons/zmodem/demo/app.js b/route/static/build/addons/zmodem/demo/app.js deleted file mode 100755 index 13a40a6d2..000000000 --- a/route/static/build/addons/zmodem/demo/app.js +++ /dev/null @@ -1,87 +0,0 @@ -var express = require('express'); -var app = express(); -var expressWs = require('express-ws')(app); -var os = require('os'); -var pty = require('node-pty'); - -var terminals = {}, - logs = {}; - -app.use('/build', express.static(__dirname + '/../../../../build')); -app.use('/demo', express.static(__dirname + '/../../../../demo')); -app.use('/zmodemjs', express.static(__dirname + '/../../../../node_modules/zmodem.js/dist')); - -app.get('/', function(req, res){ - res.sendFile(__dirname + '/index.html'); -}); - -app.get('/style.css', function(req, res){ - res.sendFile(__dirname + '/style.css'); -}); - -app.get('/main.js', function(req, res){ - res.sendFile(__dirname + '/main.js'); -}); - -app.post('/terminals', function (req, res) { - var cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { - encoding: null, - name: 'xterm-color', - cols: cols || 80, - rows: rows || 24, - cwd: process.env.PWD, - env: process.env - }); - - console.log('Created terminal with PID: ' + term.pid); - terminals[term.pid] = term; - logs[term.pid] = ''; - term.on('data', function(data) { - logs[term.pid] += data; - }); - res.send(term.pid.toString()); - res.end(); -}); - -app.post('/terminals/:pid/size', function (req, res) { - var pid = parseInt(req.params.pid), - cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = terminals[pid]; - - term.resize(cols, rows); - console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.'); - res.end(); -}); - -app.ws('/terminals/:pid', function (ws, req) { - var term = terminals[parseInt(req.params.pid)]; - console.log('Connected to terminal ' + term.pid); - ws.send(logs[term.pid]); - - term.on('data', function(data) { - try { - ws.send(data); - } catch (ex) { - // The WebSocket is not open, ignore - } - }); - ws.on('message', function(msg) { - term.write(msg); - }); - ws.on('close', function () { - term.kill(); - console.log('Closed terminal ' + term.pid); - // Clean things up - delete terminals[term.pid]; - delete logs[term.pid]; - }); -}); - -var port = process.env.PORT || 3000, - host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; - -console.log('App listening to http://' + host + ':' + port); -app.listen(port, host); diff --git a/route/static/build/addons/zmodem/demo/index.html b/route/static/build/addons/zmodem/demo/index.html deleted file mode 100755 index 0b38cf4f1..000000000 --- a/route/static/build/addons/zmodem/demo/index.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - xterm.js demo - - - - - - - - - - - - - - - - -

                    xterm.js: xterm, in the browser

                    - -
                    - -
                    - - - - - - - - - -
                    - -
                    -

                    Actions

                    -

                    - - -

                    -
                    -
                    -

                    Options

                    -

                    - -

                    -

                    - -

                    -

                    - -

                    -

                    - -

                    -

                    - -

                    -

                    - -

                    -
                    -

                    Size

                    -
                    -
                    - - -
                    -
                    - - -
                    -
                    -
                    -
                    -

                    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.

                    - - - diff --git a/route/static/build/addons/zmodem/demo/main.js b/route/static/build/addons/zmodem/demo/main.js deleted file mode 100755 index aca441355..000000000 --- a/route/static/build/addons/zmodem/demo/main.js +++ /dev/null @@ -1,383 +0,0 @@ -"use strict"; - -var term, - protocol, - socketURL, - socket, - pid; - -var terminalContainer = document.getElementById('terminal-container'), - actionElements = { - findNext: document.querySelector('#find-next'), - findPrevious: document.querySelector('#find-previous') - }, - optionElements = { - cursorBlink: document.querySelector('#option-cursor-blink'), - cursorStyle: document.querySelector('#option-cursor-style'), - scrollback: document.querySelector('#option-scrollback'), - tabstopwidth: document.querySelector('#option-tabstopwidth'), - bellStyle: document.querySelector('#option-bell-style') - }, - colsElement = document.getElementById('cols'), - rowsElement = document.getElementById('rows'); - -function setTerminalSize() { - var cols = parseInt(colsElement.value, 10); - var rows = parseInt(rowsElement.value, 10); - var viewportElement = document.querySelector('.xterm-viewport'); - var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth; - var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px'; - var height = (rows * term.charMeasure.height).toString() + 'px'; - - terminalContainer.style.width = width; - terminalContainer.style.height = height; - term.resize(cols, rows); -} - -colsElement.addEventListener('change', setTerminalSize); -rowsElement.addEventListener('change', setTerminalSize); - -actionElements.findNext.addEventListener('keypress', function (e) { - if (e.key === "Enter") { - e.preventDefault(); - term.findNext(actionElements.findNext.value); - } -}); -actionElements.findPrevious.addEventListener('keypress', function (e) { - if (e.key === "Enter") { - e.preventDefault(); - term.findPrevious(actionElements.findPrevious.value); - } -}); - -optionElements.cursorBlink.addEventListener('change', function () { - term.setOption('cursorBlink', optionElements.cursorBlink.checked); -}); -optionElements.cursorStyle.addEventListener('change', function () { - term.setOption('cursorStyle', optionElements.cursorStyle.value); -}); -optionElements.bellStyle.addEventListener('change', function () { - term.setOption('bellStyle', optionElements.bellStyle.value); -}); -optionElements.scrollback.addEventListener('change', function () { - term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10)); -}); -optionElements.tabstopwidth.addEventListener('change', function () { - term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10)); -}); - -createTerminal(); - -function createTerminal() { - // Clean terminal - while (terminalContainer.children.length) { - terminalContainer.removeChild(terminalContainer.children[0]); - } - term = new Terminal({ - cursorBlink: optionElements.cursorBlink.checked, - scrollback: parseInt(optionElements.scrollback.value, 10), - tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10) - }); - term.on('resize', function (size) { - if (!pid) { - return; - } - var cols = size.cols, - rows = size.rows, - url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows; - - fetch(url, {method: 'POST'}); - }); - protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; - socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/'; - - term.open(terminalContainer); - term.fit(); - - // fit is called within a setTimeout, cols and rows need this. - setTimeout(function () { - colsElement.value = term.cols; - rowsElement.value = term.rows; - - // Set terminal size again to set the specific dimensions on the demo - setTerminalSize(); - - fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) { - - res.text().then(function (pid) { - window.pid = pid; - socketURL += pid; - socket = new WebSocket(socketURL); - socket.onopen = runRealTerminal; - socket.onclose = runFakeTerminal; - socket.onerror = runFakeTerminal; - - term.zmodemAttach(socket, { - noTerminalWriteOutsideSession: true, - } ); - - term.on("zmodemRetract", () => { - start_form.style.display = "none"; - start_form.onsubmit = null; - }); - - term.on("zmodemDetect", (detection) => { - function do_zmodem() { - term.detach(); - let zsession = detection.confirm(); - - var promise; - - if (zsession.type === "receive") { - promise = _handle_receive_session(zsession); - } - else { - promise = _handle_send_session(zsession); - } - - promise.catch( console.error.bind(console) ).then( () => { - term.attach(socket); - } ); - } - - if (_auto_zmodem()) { - do_zmodem(); - } - else { - start_form.style.display = ""; - start_form.onsubmit = function(e) { - start_form.style.display = "none"; - - if (document.getElementById("zmstart_yes").checked) { - do_zmodem(); - } - else { - detection.deny(); - } - }; - } - }); - }); - }); - }, 0); -} - -//---------------------------------------------------------------------- -// UI STUFF - -function _show_file_info(xfer) { - var file_info = xfer.get_details(); - - document.getElementById("name").textContent = file_info.name; - document.getElementById("size").textContent = file_info.size; - document.getElementById("mtime").textContent = file_info.mtime; - document.getElementById("files_remaining").textContent = file_info.files_remaining; - document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining; - - document.getElementById("mode").textContent = "0" + file_info.mode.toString(8); - - var xfer_opts = xfer.get_options(); - ["conversion", "management", "transport", "sparse"].forEach( (lbl) => { - document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl]; - } ); - - document.getElementById("zm_file").style.display = ""; -} -function _hide_file_info() { - document.getElementById("zm_file").style.display = "none"; -} - -function _save_to_disk(xfer, buffer) { - return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name); -} - -var skipper_button = document.getElementById("zm_progress_skipper"); -var skipper_button_orig_text = skipper_button.textContent; - -function _show_progress() { - skipper_button.disabled = false; - skipper_button.textContent = skipper_button_orig_text; - - document.getElementById("bytes_received").textContent = 0; - document.getElementById("percent_received").textContent = 0; - - document.getElementById("zm_progress").style.display = ""; -} - -function _update_progress(xfer) { - var total_in = xfer.get_offset(); - - document.getElementById("bytes_received").textContent = total_in; - - var percent_received = 100 * total_in / xfer.get_details().size; - document.getElementById("percent_received").textContent = percent_received.toFixed(2); -} - -function _hide_progress() { - document.getElementById("zm_progress").style.display = "none"; -} - -var start_form = document.getElementById("zm_start"); - -function _auto_zmodem() { - return document.getElementById("zmodem-auto").checked; -} - -// END UI STUFF -//---------------------------------------------------------------------- - -function _handle_receive_session(zsession) { - zsession.on("offer", function(xfer) { - current_receive_xfer = xfer; - - _show_file_info(xfer); - - var offer_form = document.getElementById("zm_offer"); - - function on_form_submit() { - offer_form.style.display = "none"; - - //START - //if (offer_form.zmaccept.value) { - if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) { - _show_progress(); - - var FILE_BUFFER = []; - xfer.on("input", (payload) => { - _update_progress(xfer); - FILE_BUFFER.push( new Uint8Array(payload) ); - }); - xfer.accept().then( - () => { - _save_to_disk(xfer, FILE_BUFFER); - }, - console.error.bind(console) - ); - } - else { - xfer.skip(); - } - //END - } - - if (_auto_zmodem()) { - on_form_submit(); - } - else { - offer_form.onsubmit = on_form_submit; - offer_form.style.display = ""; - } - } ); - - var promise = new Promise( (res) => { - zsession.on("session_end", () => { - _hide_file_info(); - _hide_progress(); - res(); - } ); - } ); - - zsession.start(); - - return promise; -} - -function _handle_send_session(zsession) { - var choose_form = document.getElementById("zm_choose"); - choose_form.style.display = ""; - - var file_el = document.getElementById("zm_files"); - - var promise = new Promise( (res) => { - file_el.onchange = function(e) { - choose_form.style.display = "none"; - - var files_obj = file_el.files; - - Zmodem.Browser.send_files( - zsession, - files_obj, - { - on_offer_response(obj, xfer) { - if (xfer) _show_progress(); - //console.log("offer", xfer ? "accepted" : "skipped"); - }, - on_progress(obj, xfer) { - _update_progress(xfer); - }, - on_file_complete(obj) { - //console.log("COMPLETE", obj); - _hide_progress(); - }, - } - ).then(_hide_progress).then( - zsession.close.bind(zsession), - console.error.bind(console) - ).then( () => { - _hide_file_info(); - _hide_progress(); - res(); - } ); - }; - } ); - - return promise; -} - -//This is here to allow canceling of an in-progress ZMODEM transfer. -var current_receive_xfer; - -//Called from HTML directly. -function skip_current_file() { - current_receive_xfer.skip(); - - skipper_button.disabled = true; - skipper_button.textContent = "Waiting for server to acknowledge skip …"; -} - -function runRealTerminal() { - term.attach(socket); - - term._initialized = true; -} - -function runFakeTerminal() { - if (term._initialized) { - return; - } - - term._initialized = true; - - var shellprompt = '$ '; - - term.prompt = function () { - term.write('\r\n' + shellprompt); - }; - - term.writeln('Welcome to xterm.js'); - term.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); - term.writeln('Type some keys and commands to play around.'); - term.writeln(''); - term.prompt(); - - term.on('key', function (key, ev) { - var printable = ( - !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey - ); - - if (ev.keyCode == 13) { - term.prompt(); - } else if (ev.keyCode == 8) { - // Do not delete the prompt - if (term.x > 2) { - term.write('\b \b'); - } - } else if (printable) { - term.write(key); - } - }); - - term.on('paste', function (data, ev) { - term.write(data); - }); -} diff --git a/route/static/build/addons/zmodem/zmodem.js b/route/static/build/addons/zmodem/zmodem.js deleted file mode 100755 index aeb2fd5f0..000000000 --- a/route/static/build/addons/zmodem/zmodem.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * - * Allow xterm.js to handle ZMODEM uploads and downloads. - * - * This addon is a wrapper around zmodem.js. It adds the following to the - * Terminal class: - * - * - function `zmodemAttach(, )` - creates a Zmodem.Sentry - * on the passed WebSocket object. The Object passed is optional and - * can contain: - * - noTerminalWriteOutsideSession: Suppress writes from the Sentry - * object to the Terminal while there is no active Session. This - * is necessary for compatibility with, for example, the - * `attach.js` addon. - * - * - event `zmodemDetect` - fired on Zmodem.Sentry’s `on_detect` callback. - * Passes the zmodem.js Detection object. - * - * - event `zmodemRetract` - fired on Zmodem.Sentry’s `on_retract` callback. - * - * You’ll need to provide logic to handle uploads and downloads. - * See zmodem.js’s documentation for more details. - * - * **IMPORTANT:** After you confirm() a zmodem.js Detection, if you have - * used the `attach` or `terminado` addons, you’ll need to suspend their - * operation for the duration of the ZMODEM session. (The demo does this - * via `detach()` and a re-`attach()`.) - */ -(function (addon) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = addon(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], addon); - } else { - /* - * Plain browser environment - */ - addon(window.Terminal); - } -})(function _zmodemAddon(Terminal) { - Object.assign( - Terminal.prototype, - { - zmodemAttach: function zmodemAttach(ws, opts) { - var term = this; - - if (!opts) opts = {}; - - var senderFunc = function _ws_sender_func(octets) { - ws.send( new Uint8Array(octets) ); - }; - - var zsentry; - - function _shouldWrite() { - return !!zsentry.get_confirmed_session() || !opts.noTerminalWriteOutsideSession; - } - - zsentry = new Zmodem.Sentry( { - to_terminal: function _to_terminal(octets) { - if (_shouldWrite()) { - term.write( - String.fromCharCode.apply(String, octets) - ); - } - }, - - sender: senderFunc, - - on_retract: function _on_retract() { - term.emit("zmodemRetract"); - }, - - on_detect: function _on_detect(detection) { - term.emit("zmodemDetect", detection); - }, - } ); - - function handleWSMessage(evt) { - - //In testing with xterm.js’s demo the first message was - //always text even if the rest were binary. While that - //may be specific to xterm.js’s demo, ultimately we - //should reject anything that isn’t binary. - if (typeof evt.data === "string") { - if (_shouldWrite()) { - term.write(evt.data); - } - } - else { - zsentry.consume(evt.data); - } - } - - ws.binaryType = "arraybuffer"; - ws.addEventListener("message", handleWSMessage); - }, - - zmodemBrowser: Zmodem.Browser, - } - ); -}); diff --git a/route/static/build/xterm.css b/route/static/build/xterm.css deleted file mode 100755 index c668af403..000000000 --- a/route/static/build/xterm.css +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) - * https://github.com/chjj/term.js - * @license MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * Originally forked from (with the author's permission): - * Fabrice Bellard's javascript vt100 for jslinux: - * http://bellard.org/jslinux/ - * Copyright (c) 2011 Fabrice Bellard - * The original design remains. The terminal itself - * has been extended to include xterm CSI codes, among - * other features. - */ - -/** - * Default styles for xterm.js - */ - -.xterm { - font-family: courier-new, courier, monospace; - font-feature-settings: "liga" 0; - position: relative; - user-select: none; - -ms-user-select: none; - -webkit-user-select: none; -} - -.xterm.focus, -.xterm:focus { - outline: none; -} - -.xterm .xterm-helpers { - position: absolute; - top: 0; - /** - * The z-index of the helpers must be higher than the canvases in order for - * IMEs to appear on top. - */ - z-index: 10; -} - -.xterm .xterm-helper-textarea { - /* - * HACK: to fix IE's blinking cursor - * Move textarea out of the screen to the far left, so that the cursor is not visible. - */ - position: absolute; - opacity: 0; - left: -9999em; - top: 0; - width: 0; - height: 0; - z-index: -10; - /** Prevent wrapping so the IME appears against the textarea at the correct position */ - white-space: nowrap; - overflow: hidden; - resize: none; -} - -.xterm .composition-view { - /* TODO: Composition position got messed up somewhere */ - background: #333; - color: #FFF; - display: none; - position: absolute; - white-space: nowrap; - z-index: 1; -} - -.xterm .composition-view.active { - display: block; -} - -.xterm .xterm-viewport { - /* On OS X this is required in order for the scroll bar to appear fully opaque */ - background-color: #333; - overflow-y: scroll; -} - -.xterm canvas { - position: absolute; - left: 0; - top: 0; -} - -.xterm .xterm-scroll-area { - visibility: hidden; -} - -.xterm .xterm-char-measure-element { - display: inline-block; - visibility: hidden; - position: absolute; - left: -9999em; -} - -.xterm.enable-mouse-events { - /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ - cursor: default; -} - -.xterm:not(.enable-mouse-events) { - cursor: text; -} diff --git a/route/static/build/xterm.js b/route/static/build/xterm.js deleted file mode 100755 index 80b0ba5d3..000000000 --- a/route/static/build/xterm.js +++ /dev/null @@ -1,6241 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Terminal = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o this._terminal.rows; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Buffer.prototype, "isCursorInViewport", { - get: function () { - var absoluteY = this.ybase + this.y; - var relativeY = absoluteY - this.ydisp; - return (relativeY >= 0 && relativeY < this._terminal.rows); - }, - enumerable: true, - configurable: true - }); - Buffer.prototype._getCorrectBufferLength = function (rows) { - if (!this._hasScrollback) { - return rows; - } - var correctBufferLength = rows + this._terminal.options.scrollback; - return correctBufferLength > exports.MAX_BUFFER_SIZE ? exports.MAX_BUFFER_SIZE : correctBufferLength; - }; - Buffer.prototype.fillViewportRows = function () { - if (this._lines.length === 0) { - var i = this._terminal.rows; - while (i--) { - this.lines.push(this._terminal.blankLine()); - } - } - }; - Buffer.prototype.clear = function () { - this.ydisp = 0; - this.ybase = 0; - this.y = 0; - this.x = 0; - this._lines = new CircularList_1.CircularList(this._getCorrectBufferLength(this._terminal.rows)); - this.scrollTop = 0; - this.scrollBottom = this._terminal.rows - 1; - this.setupTabStops(); - }; - Buffer.prototype.resize = function (newCols, newRows) { - var newMaxLength = this._getCorrectBufferLength(newRows); - if (newMaxLength > this._lines.maxLength) { - this._lines.maxLength = newMaxLength; - } - if (this._lines.length > 0) { - if (this._terminal.cols < newCols) { - var ch = [this._terminal.defAttr, ' ', 1, 32]; - for (var i = 0; i < this._lines.length; i++) { - if (this._lines.get(i) === undefined) { - this._lines.set(i, this._terminal.blankLine(undefined, undefined, newCols)); - } - while (this._lines.get(i).length < newCols) { - this._lines.get(i).push(ch); - } - } - } - var addToY = 0; - if (this._terminal.rows < newRows) { - for (var y = this._terminal.rows; y < newRows; y++) { - if (this._lines.length < newRows + this.ybase) { - if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) { - this.ybase--; - addToY++; - if (this.ydisp > 0) { - this.ydisp--; - } - } - else { - this._lines.push(this._terminal.blankLine(undefined, undefined, newCols)); - } - } - } - } - else { - for (var y = this._terminal.rows; y > newRows; y--) { - if (this._lines.length > newRows + this.ybase) { - if (this._lines.length > this.ybase + this.y + 1) { - this._lines.pop(); - } - else { - this.ybase++; - this.ydisp++; - } - } - } - } - if (newMaxLength < this._lines.maxLength) { - var amountToTrim = this._lines.length - newMaxLength; - if (amountToTrim > 0) { - this._lines.trimStart(amountToTrim); - this.ybase = Math.max(this.ybase - amountToTrim, 0); - this.ydisp = Math.max(this.ydisp - amountToTrim, 0); - } - this._lines.maxLength = newMaxLength; - } - if (this.y >= newRows) { - this.y = newRows - 1; - } - if (addToY) { - this.y += addToY; - } - if (this.x >= newCols) { - this.x = newCols - 1; - } - this.scrollTop = 0; - } - this.scrollBottom = newRows - 1; - }; - Buffer.prototype.translateBufferLineToString = function (lineIndex, trimRight, startCol, endCol) { - if (startCol === void 0) { startCol = 0; } - if (endCol === void 0) { endCol = null; } - var lineString = ''; - var line = this.lines.get(lineIndex); - if (!line) { - return ''; - } - var startIndex = startCol; - endCol = endCol || line.length; - var endIndex = endCol; - for (var i = 0; i < line.length; i++) { - var char = line[i]; - lineString += char[exports.CHAR_DATA_CHAR_INDEX]; - if (char[exports.CHAR_DATA_WIDTH_INDEX] === 0) { - if (startCol >= i) { - startIndex--; - } - if (endCol >= i) { - endIndex--; - } - } - else { - if (char[exports.CHAR_DATA_CHAR_INDEX].length > 1) { - if (startCol > i) { - startIndex += char[exports.CHAR_DATA_CHAR_INDEX].length - 1; - } - if (endCol > i) { - endIndex += char[exports.CHAR_DATA_CHAR_INDEX].length - 1; - } - } - } - } - if (trimRight) { - var rightWhitespaceIndex = lineString.search(/\s+$/); - if (rightWhitespaceIndex !== -1) { - endIndex = Math.min(endIndex, rightWhitespaceIndex); - } - if (endIndex <= startIndex) { - return ''; - } - } - return lineString.substring(startIndex, endIndex); - }; - Buffer.prototype.setupTabStops = function (i) { - if (i != null) { - if (!this.tabs[i]) { - i = this.prevStop(i); - } - } - else { - this.tabs = {}; - i = 0; - } - for (; i < this._terminal.cols; i += this._terminal.options.tabStopWidth) { - this.tabs[i] = true; - } - }; - Buffer.prototype.prevStop = function (x) { - if (x == null) { - x = this.x; - } - while (!this.tabs[--x] && x > 0) - ; - return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; - }; - Buffer.prototype.nextStop = function (x) { - if (x == null) { - x = this.x; - } - while (!this.tabs[++x] && x < this._terminal.cols) - ; - return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; - }; - return Buffer; -}()); -exports.Buffer = Buffer; - - - -},{"./utils/CircularList":30}],2:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Buffer_1 = require("./Buffer"); -var EventEmitter_1 = require("./EventEmitter"); -var BufferSet = (function (_super) { - __extends(BufferSet, _super); - function BufferSet(_terminal) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._normal = new Buffer_1.Buffer(_this._terminal, true); - _this._normal.fillViewportRows(); - _this._alt = new Buffer_1.Buffer(_this._terminal, false); - _this._activeBuffer = _this._normal; - _this.setupTabStops(); - return _this; - } - Object.defineProperty(BufferSet.prototype, "alt", { - get: function () { - return this._alt; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(BufferSet.prototype, "active", { - get: function () { - return this._activeBuffer; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(BufferSet.prototype, "normal", { - get: function () { - return this._normal; - }, - enumerable: true, - configurable: true - }); - BufferSet.prototype.activateNormalBuffer = function () { - this._alt.clear(); - this._activeBuffer = this._normal; - this.emit('activate', this._normal); - }; - BufferSet.prototype.activateAltBuffer = function () { - this._alt.fillViewportRows(); - this._activeBuffer = this._alt; - this.emit('activate', this._alt); - }; - BufferSet.prototype.resize = function (newCols, newRows) { - this._normal.resize(newCols, newRows); - this._alt.resize(newCols, newRows); - }; - BufferSet.prototype.setupTabStops = function (i) { - this._normal.setupTabStops(i); - this._alt.setupTabStops(i); - }; - return BufferSet; -}(EventEmitter_1.EventEmitter)); -exports.BufferSet = BufferSet; - - - -},{"./Buffer":1,"./EventEmitter":7}],3:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.wcwidth = (function (opts) { - var COMBINING_BMP = [ - [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], - [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], - [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], - [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], - [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], - [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], - [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], - [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], - [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], - [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], - [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], - [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], - [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], - [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], - [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], - [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], - [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], - [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], - [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], - [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], - [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], - [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], - [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], - [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], - [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], - [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], - [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], - [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], - [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], - [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], - [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], - [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], - [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], - [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], - [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], - [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], - [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB], - ]; - var COMBINING_HIGH = [ - [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], - [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] - ]; - function bisearch(ucs, data) { - var min = 0; - var max = data.length - 1; - var mid; - if (ucs < data[0][0] || ucs > data[max][1]) - return false; - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) - min = mid + 1; - else if (ucs < data[mid][0]) - max = mid - 1; - else - return true; - } - return false; - } - function wcwidthBMP(ucs) { - if (ucs === 0) - return opts.nul; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return opts.control; - if (bisearch(ucs, COMBINING_BMP)) - return 0; - if (isWideBMP(ucs)) { - return 2; - } - return 1; - } - function isWideBMP(ucs) { - return (ucs >= 0x1100 && (ucs <= 0x115f || - ucs === 0x2329 || - ucs === 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || - (ucs >= 0xac00 && ucs <= 0xd7a3) || - (ucs >= 0xf900 && ucs <= 0xfaff) || - (ucs >= 0xfe10 && ucs <= 0xfe19) || - (ucs >= 0xfe30 && ucs <= 0xfe6f) || - (ucs >= 0xff00 && ucs <= 0xff60) || - (ucs >= 0xffe0 && ucs <= 0xffe6))); - } - function wcwidthHigh(ucs) { - if (bisearch(ucs, COMBINING_HIGH)) - return 0; - if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) { - return 2; - } - return 1; - } - var control = opts.control | 0; - var table = null; - function init_table() { - var CODEPOINTS = 65536; - var BITWIDTH = 2; - var ITEMSIZE = 32; - var CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE; - var CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH; - table = (typeof Uint32Array === 'undefined') - ? new Array(CONTAINERSIZE) - : new Uint32Array(CONTAINERSIZE); - for (var i = 0; i < CONTAINERSIZE; ++i) { - var num = 0; - var pos = CODEPOINTS_PER_ITEM; - while (pos--) - num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos); - table[i] = num; - } - return table; - } - return function (num) { - num = num | 0; - if (num < 32) - return control | 0; - if (num < 127) - return 1; - var t = table || init_table(); - if (num < 65536) - return t[num >> 4] >> ((num & 15) << 1) & 3; - return wcwidthHigh(num); - }; -})({ nul: 0, control: 0 }); - - - -},{}],4:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CHARSETS = {}; -exports.DEFAULT_CHARSET = exports.CHARSETS['B']; -exports.CHARSETS['0'] = { - '`': '\u25c6', - 'a': '\u2592', - 'b': '\u0009', - 'c': '\u000c', - 'd': '\u000d', - 'e': '\u000a', - 'f': '\u00b0', - 'g': '\u00b1', - 'h': '\u2424', - 'i': '\u000b', - 'j': '\u2518', - 'k': '\u2510', - 'l': '\u250c', - 'm': '\u2514', - 'n': '\u253c', - 'o': '\u23ba', - 'p': '\u23bb', - 'q': '\u2500', - 'r': '\u23bc', - 's': '\u23bd', - 't': '\u251c', - 'u': '\u2524', - 'v': '\u2534', - 'w': '\u252c', - 'x': '\u2502', - 'y': '\u2264', - 'z': '\u2265', - '{': '\u03c0', - '|': '\u2260', - '}': '\u00a3', - '~': '\u00b7' -}; -exports.CHARSETS['A'] = { - '#': '£' -}; -exports.CHARSETS['B'] = null; -exports.CHARSETS['4'] = { - '#': '£', - '@': '¾', - '[': 'ij', - '\\': '½', - ']': '|', - '{': '¨', - '|': 'f', - '}': '¼', - '~': '´' -}; -exports.CHARSETS['C'] = - exports.CHARSETS['5'] = { - '[': 'Ä', - '\\': 'Ö', - ']': 'Å', - '^': 'Ü', - '`': 'é', - '{': 'ä', - '|': 'ö', - '}': 'å', - '~': 'ü' - }; -exports.CHARSETS['R'] = { - '#': '£', - '@': 'à', - '[': '°', - '\\': 'ç', - ']': '§', - '{': 'é', - '|': 'ù', - '}': 'è', - '~': '¨' -}; -exports.CHARSETS['Q'] = { - '@': 'à', - '[': 'â', - '\\': 'ç', - ']': 'ê', - '^': 'î', - '`': 'ô', - '{': 'é', - '|': 'ù', - '}': 'è', - '~': 'û' -}; -exports.CHARSETS['K'] = { - '@': '§', - '[': 'Ä', - '\\': 'Ö', - ']': 'Ü', - '{': 'ä', - '|': 'ö', - '}': 'ü', - '~': 'ß' -}; -exports.CHARSETS['Y'] = { - '#': '£', - '@': '§', - '[': '°', - '\\': 'ç', - ']': 'é', - '`': 'ù', - '{': 'à', - '|': 'ò', - '}': 'è', - '~': 'ì' -}; -exports.CHARSETS['E'] = - exports.CHARSETS['6'] = { - '@': 'Ä', - '[': 'Æ', - '\\': 'Ø', - ']': 'Å', - '^': 'Ü', - '`': 'ä', - '{': 'æ', - '|': 'ø', - '}': 'å', - '~': 'ü' - }; -exports.CHARSETS['Z'] = { - '#': '£', - '@': '§', - '[': '¡', - '\\': 'Ñ', - ']': '¿', - '{': '°', - '|': 'ñ', - '}': 'ç' -}; -exports.CHARSETS['H'] = - exports.CHARSETS['7'] = { - '@': 'É', - '[': 'Ä', - '\\': 'Ö', - ']': 'Å', - '^': 'Ü', - '`': 'é', - '{': 'ä', - '|': 'ö', - '}': 'å', - '~': 'ü' - }; -exports.CHARSETS['='] = { - '#': 'ù', - '@': 'à', - '[': 'é', - '\\': 'ç', - ']': 'ê', - '^': 'î', - '_': 'è', - '`': 'ô', - '{': 'ä', - '|': 'ö', - '}': 'ü', - '~': 'û' -}; - - - -},{}],5:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var CompositionHelper = (function () { - function CompositionHelper(textarea, compositionView, terminal) { - this.textarea = textarea; - this.compositionView = compositionView; - this.terminal = terminal; - this.isComposing = false; - this.isSendingComposition = false; - this.compositionPosition = { start: null, end: null }; - } - CompositionHelper.prototype.compositionstart = function () { - this.isComposing = true; - this.compositionPosition.start = this.textarea.value.length; - this.compositionView.textContent = ''; - this.compositionView.classList.add('active'); - }; - CompositionHelper.prototype.compositionupdate = function (ev) { - var _this = this; - this.compositionView.textContent = ev.data; - this.updateCompositionElements(); - setTimeout(function () { - _this.compositionPosition.end = _this.textarea.value.length; - }, 0); - }; - CompositionHelper.prototype.compositionend = function () { - this.finalizeComposition(true); - }; - CompositionHelper.prototype.keydown = function (ev) { - if (this.isComposing || this.isSendingComposition) { - if (ev.keyCode === 229) { - return false; - } - else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) { - return false; - } - else { - this.finalizeComposition(false); - } - } - if (ev.keyCode === 229) { - this.handleAnyTextareaChanges(); - return false; - } - return true; - }; - CompositionHelper.prototype.finalizeComposition = function (waitForPropogation) { - var _this = this; - this.compositionView.classList.remove('active'); - this.isComposing = false; - this.clearTextareaPosition(); - if (!waitForPropogation) { - this.isSendingComposition = false; - var input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end); - this.terminal.handler(input); - } - else { - var currentCompositionPosition_1 = { - start: this.compositionPosition.start, - end: this.compositionPosition.end, - }; - this.isSendingComposition = true; - setTimeout(function () { - if (_this.isSendingComposition) { - _this.isSendingComposition = false; - var input = void 0; - if (_this.isComposing) { - input = _this.textarea.value.substring(currentCompositionPosition_1.start, currentCompositionPosition_1.end); - } - else { - input = _this.textarea.value.substring(currentCompositionPosition_1.start); - } - _this.terminal.handler(input); - } - }, 0); - } - }; - CompositionHelper.prototype.handleAnyTextareaChanges = function () { - var _this = this; - var oldValue = this.textarea.value; - setTimeout(function () { - if (!_this.isComposing) { - var newValue = _this.textarea.value; - var diff = newValue.replace(oldValue, ''); - if (diff.length > 0) { - _this.terminal.handler(diff); - } - } - }, 0); - }; - CompositionHelper.prototype.updateCompositionElements = function (dontRecurse) { - var _this = this; - if (!this.isComposing) { - return; - } - if (this.terminal.buffer.isCursorInViewport) { - var cellHeight = Math.ceil(this.terminal.charMeasure.height * this.terminal.options.lineHeight); - var cursorTop = this.terminal.buffer.y * cellHeight; - var cursorLeft = this.terminal.buffer.x * this.terminal.charMeasure.width; - this.compositionView.style.left = cursorLeft + 'px'; - this.compositionView.style.top = cursorTop + 'px'; - this.compositionView.style.height = cellHeight + 'px'; - this.compositionView.style.lineHeight = cellHeight + 'px'; - var compositionViewBounds = this.compositionView.getBoundingClientRect(); - this.textarea.style.left = cursorLeft + 'px'; - this.textarea.style.top = cursorTop + 'px'; - this.textarea.style.width = compositionViewBounds.width + 'px'; - this.textarea.style.height = compositionViewBounds.height + 'px'; - this.textarea.style.lineHeight = compositionViewBounds.height + 'px'; - } - if (!dontRecurse) { - setTimeout(function () { return _this.updateCompositionElements(true); }, 0); - } - }; - ; - CompositionHelper.prototype.clearTextareaPosition = function () { - this.textarea.style.left = ''; - this.textarea.style.top = ''; - }; - ; - return CompositionHelper; -}()); -exports.CompositionHelper = CompositionHelper; - - - -},{}],6:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var C0; -(function (C0) { - C0.NUL = '\x00'; - C0.SOH = '\x01'; - C0.STX = '\x02'; - C0.ETX = '\x03'; - C0.EOT = '\x04'; - C0.ENQ = '\x05'; - C0.ACK = '\x06'; - C0.BEL = '\x07'; - C0.BS = '\x08'; - C0.HT = '\x09'; - C0.LF = '\x0a'; - C0.VT = '\x0b'; - C0.FF = '\x0c'; - C0.CR = '\x0d'; - C0.SO = '\x0e'; - C0.SI = '\x0f'; - C0.DLE = '\x10'; - C0.DC1 = '\x11'; - C0.DC2 = '\x12'; - C0.DC3 = '\x13'; - C0.DC4 = '\x14'; - C0.NAK = '\x15'; - C0.SYN = '\x16'; - C0.ETB = '\x17'; - C0.CAN = '\x18'; - C0.EM = '\x19'; - C0.SUB = '\x1a'; - C0.ESC = '\x1b'; - C0.FS = '\x1c'; - C0.GS = '\x1d'; - C0.RS = '\x1e'; - C0.US = '\x1f'; - C0.SP = '\x20'; - C0.DEL = '\x7f'; -})(C0 = exports.C0 || (exports.C0 = {})); -; - - - -},{}],7:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var EventEmitter = (function () { - function EventEmitter() { - this._events = this._events || {}; - } - EventEmitter.prototype.on = function (type, listener) { - this._events[type] = this._events[type] || []; - this._events[type].push(listener); - }; - EventEmitter.prototype.off = function (type, listener) { - if (!this._events[type]) { - return; - } - var obj = this._events[type]; - var i = obj.length; - while (i--) { - if (obj[i] === listener || obj[i].listener === listener) { - obj.splice(i, 1); - return; - } - } - }; - EventEmitter.prototype.removeAllListeners = function (type) { - if (this._events[type]) { - delete this._events[type]; - } - }; - EventEmitter.prototype.once = function (type, listener) { - function on() { - var args = Array.prototype.slice.call(arguments); - this.off(type, on); - listener.apply(this, args); - } - on.listener = listener; - this.on(type, on); - }; - EventEmitter.prototype.emit = function (type) { - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - if (!this._events[type]) { - return; - } - var obj = this._events[type]; - for (var i = 0; i < obj.length; i++) { - obj[i].apply(this, args); - } - }; - EventEmitter.prototype.listeners = function (type) { - return this._events[type] || []; - }; - EventEmitter.prototype.destroy = function () { - this._events = {}; - }; - return EventEmitter; -}()); -exports.EventEmitter = EventEmitter; - - - -},{}],8:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var EscapeSequences_1 = require("./EscapeSequences"); -var Charsets_1 = require("./Charsets"); -var Buffer_1 = require("./Buffer"); -var Types_1 = require("./renderer/Types"); -var CharWidth_1 = require("./CharWidth"); -var InputHandler = (function () { - function InputHandler(_terminal) { - this._terminal = _terminal; - } - InputHandler.prototype.addChar = function (char, code) { - if (char >= ' ') { - var ch_width = CharWidth_1.wcwidth(code); - if (this._terminal.charset && this._terminal.charset[char]) { - char = this._terminal.charset[char]; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - if (!ch_width && this._terminal.buffer.x) { - if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) { - if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][Buffer_1.CHAR_DATA_WIDTH_INDEX]) { - if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2]) { - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][Buffer_1.CHAR_DATA_CHAR_INDEX] += char; - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][3] = char.charCodeAt(0); - } - } - else { - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][Buffer_1.CHAR_DATA_CHAR_INDEX] += char; - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][3] = char.charCodeAt(0); - } - this._terminal.updateRange(this._terminal.buffer.y); - } - return; - } - if (this._terminal.buffer.x + ch_width - 1 >= this._terminal.cols) { - if (this._terminal.wraparoundMode) { - this._terminal.buffer.x = 0; - this._terminal.buffer.y++; - if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { - this._terminal.buffer.y--; - this._terminal.scroll(true); - } - else { - this._terminal.buffer.lines.get(this._terminal.buffer.y).isWrapped = true; - } - } - else { - if (ch_width === 2) - return; - } - } - row = this._terminal.buffer.y + this._terminal.buffer.ybase; - if (this._terminal.insertMode) { - for (var moves = 0; moves < ch_width; ++moves) { - var removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop(); - if (removed[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0 - && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] - && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)]; - } - this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 0, [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)]); - } - } - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, char, ch_width, char.charCodeAt(0)]; - this._terminal.buffer.x++; - this._terminal.updateRange(this._terminal.buffer.y); - if (ch_width === 2) { - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, '', 0, undefined]; - this._terminal.buffer.x++; - } - } - }; - InputHandler.prototype.bell = function () { - this._terminal.bell(); - }; - InputHandler.prototype.lineFeed = function () { - if (this._terminal.convertEol) { - this._terminal.buffer.x = 0; - } - this._terminal.buffer.y++; - if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { - this._terminal.buffer.y--; - this._terminal.scroll(); - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - this._terminal.emit('lineFeed'); - }; - InputHandler.prototype.carriageReturn = function () { - this._terminal.buffer.x = 0; - }; - InputHandler.prototype.backspace = function () { - if (this._terminal.buffer.x > 0) { - this._terminal.buffer.x--; - } - }; - InputHandler.prototype.tab = function () { - this._terminal.buffer.x = this._terminal.buffer.nextStop(); - }; - InputHandler.prototype.shiftOut = function () { - this._terminal.setgLevel(1); - }; - InputHandler.prototype.shiftIn = function () { - this._terminal.setgLevel(0); - }; - InputHandler.prototype.insertChars = function (params) { - var param = params[0]; - if (param < 1) - param = 1; - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var j = this._terminal.buffer.x; - var ch = [this._terminal.eraseAttr(), ' ', 1, 32]; - while (param-- && j < this._terminal.cols) { - this._terminal.buffer.lines.get(row).splice(j++, 0, ch); - this._terminal.buffer.lines.get(row).pop(); - } - }; - InputHandler.prototype.cursorUp = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } - }; - InputHandler.prototype.cursorDown = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - }; - InputHandler.prototype.cursorForward = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.cursorBackward = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - this._terminal.buffer.x -= param; - if (this._terminal.buffer.x < 0) { - this._terminal.buffer.x = 0; - } - }; - InputHandler.prototype.cursorNextLine = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - this._terminal.buffer.x = 0; - }; - InputHandler.prototype.cursorPrecedingLine = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } - this._terminal.buffer.x = 0; - }; - InputHandler.prototype.cursorCharAbsolute = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x = param - 1; - }; - InputHandler.prototype.cursorPosition = function (params) { - var col; - var row = params[0] - 1; - if (params.length >= 2) { - col = params[1] - 1; - } - else { - col = 0; - } - if (row < 0) { - row = 0; - } - else if (row >= this._terminal.rows) { - row = this._terminal.rows - 1; - } - if (col < 0) { - col = 0; - } - else if (col >= this._terminal.cols) { - col = this._terminal.cols - 1; - } - this._terminal.buffer.x = col; - this._terminal.buffer.y = row; - }; - InputHandler.prototype.cursorForwardTab = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.x = this._terminal.buffer.nextStop(); - } - }; - InputHandler.prototype.eraseInDisplay = function (params) { - var j; - switch (params[0]) { - case 0: - this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); - j = this._terminal.buffer.y + 1; - for (; j < this._terminal.rows; j++) { - this._terminal.eraseLine(j); - } - break; - case 1: - this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); - j = this._terminal.buffer.y; - while (j--) { - this._terminal.eraseLine(j); - } - break; - case 2: - j = this._terminal.rows; - while (j--) - this._terminal.eraseLine(j); - break; - case 3: - var scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows; - if (scrollBackSize > 0) { - this._terminal.buffer.lines.trimStart(scrollBackSize); - this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0); - this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0); - this._terminal.emit('scroll', 0); - } - break; - } - }; - InputHandler.prototype.eraseInLine = function (params) { - switch (params[0]) { - case 0: - this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); - break; - case 1: - this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); - break; - case 2: - this._terminal.eraseLine(this._terminal.buffer.y); - break; - } - }; - InputHandler.prototype.insertLines = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var scrollBottomRowsOffset = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom; - var scrollBottomAbsolute = this._terminal.rows - 1 + this._terminal.buffer.ybase - scrollBottomRowsOffset + 1; - while (param--) { - this._terminal.buffer.lines.splice(scrollBottomAbsolute - 1, 1); - this._terminal.buffer.lines.splice(row, 0, this._terminal.blankLine(true)); - } - this._terminal.updateRange(this._terminal.buffer.y); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.deleteLines = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var j; - j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom; - j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j; - while (param--) { - this._terminal.buffer.lines.splice(row, 1); - this._terminal.buffer.lines.splice(j, 0, this._terminal.blankLine(true)); - } - this._terminal.updateRange(this._terminal.buffer.y); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.deleteChars = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var ch = [this._terminal.eraseAttr(), ' ', 1, 32]; - while (param--) { - this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 1); - this._terminal.buffer.lines.get(row).push(ch); - } - this._terminal.updateRange(this._terminal.buffer.y); - }; - InputHandler.prototype.scrollUp = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 1); - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 0, this._terminal.blankLine()); - } - this._terminal.updateRange(this._terminal.buffer.scrollTop); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.scrollDown = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 1); - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 0, this._terminal.blankLine()); - } - this._terminal.updateRange(this._terminal.buffer.scrollTop); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.eraseChars = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var j = this._terminal.buffer.x; - var ch = [this._terminal.eraseAttr(), ' ', 1, 32]; - while (param-- && j < this._terminal.cols) { - this._terminal.buffer.lines.get(row)[j++] = ch; - } - }; - InputHandler.prototype.cursorBackwardTab = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.x = this._terminal.buffer.prevStop(); - } - }; - InputHandler.prototype.charPosAbsolute = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x = param - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.HPositionRelative = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.repeatPrecedingCharacter = function (params) { - var param = params[0] || 1; - var line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y); - var ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1, 32]; - while (param--) { - line[this._terminal.buffer.x++] = ch; - } - }; - InputHandler.prototype.sendDeviceAttributes = function (params) { - if (params[0] > 0) { - return; - } - if (!this._terminal.prefix) { - if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[?1;2c'); - } - else if (this._terminal.is('linux')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[?6c'); - } - } - else if (this._terminal.prefix === '>') { - if (this._terminal.is('xterm')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[>0;276;0c'); - } - else if (this._terminal.is('rxvt-unicode')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[>85;95;0c'); - } - else if (this._terminal.is('linux')) { - this._terminal.send(params[0] + 'c'); - } - else if (this._terminal.is('screen')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[>83;40003;0c'); - } - } - }; - InputHandler.prototype.linePosAbsolute = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y = param - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - }; - InputHandler.prototype.VPositionRelative = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - }; - InputHandler.prototype.HVPosition = function (params) { - if (params[0] < 1) - params[0] = 1; - if (params[1] < 1) - params[1] = 1; - this._terminal.buffer.y = params[0] - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - this._terminal.buffer.x = params[1] - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.tabClear = function (params) { - var param = params[0]; - if (param <= 0) { - delete this._terminal.buffer.tabs[this._terminal.buffer.x]; - } - else if (param === 3) { - this._terminal.buffer.tabs = {}; - } - }; - InputHandler.prototype.setMode = function (params) { - if (params.length > 1) { - for (var i = 0; i < params.length; i++) { - this.setMode([params[i]]); - } - return; - } - if (!this._terminal.prefix) { - switch (params[0]) { - case 4: - this._terminal.insertMode = true; - break; - case 20: - break; - } - } - else if (this._terminal.prefix === '?') { - switch (params[0]) { - case 1: - this._terminal.applicationCursor = true; - break; - case 2: - this._terminal.setgCharset(0, Charsets_1.DEFAULT_CHARSET); - this._terminal.setgCharset(1, Charsets_1.DEFAULT_CHARSET); - this._terminal.setgCharset(2, Charsets_1.DEFAULT_CHARSET); - this._terminal.setgCharset(3, Charsets_1.DEFAULT_CHARSET); - break; - case 3: - this._terminal.savedCols = this._terminal.cols; - this._terminal.resize(132, this._terminal.rows); - break; - case 6: - this._terminal.originMode = true; - break; - case 7: - this._terminal.wraparoundMode = true; - break; - case 12: - break; - case 66: - this._terminal.log('Serial port requested application keypad.'); - this._terminal.applicationKeypad = true; - this._terminal.viewport.syncScrollArea(); - break; - case 9: - case 1000: - case 1002: - case 1003: - this._terminal.x10Mouse = params[0] === 9; - this._terminal.vt200Mouse = params[0] === 1000; - this._terminal.normalMouse = params[0] > 1000; - this._terminal.mouseEvents = true; - this._terminal.element.classList.add('enable-mouse-events'); - this._terminal.selectionManager.disable(); - this._terminal.log('Binding to mouse events.'); - break; - case 1004: - this._terminal.sendFocus = true; - break; - case 1005: - this._terminal.utfMouse = true; - break; - case 1006: - this._terminal.sgrMouse = true; - break; - case 1015: - this._terminal.urxvtMouse = true; - break; - case 25: - this._terminal.cursorHidden = false; - break; - case 1049: - case 47: - case 1047: - this._terminal.buffers.activateAltBuffer(); - this._terminal.selectionManager.setBuffer(this._terminal.buffer); - this._terminal.viewport.syncScrollArea(); - this._terminal.showCursor(); - break; - case 2004: - this._terminal.bracketedPasteMode = true; - break; - } - } - }; - InputHandler.prototype.resetMode = function (params) { - if (params.length > 1) { - for (var i = 0; i < params.length; i++) { - this.resetMode([params[i]]); - } - return; - } - if (!this._terminal.prefix) { - switch (params[0]) { - case 4: - this._terminal.insertMode = false; - break; - case 20: - break; - } - } - else if (this._terminal.prefix === '?') { - switch (params[0]) { - case 1: - this._terminal.applicationCursor = false; - break; - case 3: - if (this._terminal.cols === 132 && this._terminal.savedCols) { - this._terminal.resize(this._terminal.savedCols, this._terminal.rows); - } - delete this._terminal.savedCols; - break; - case 6: - this._terminal.originMode = false; - break; - case 7: - this._terminal.wraparoundMode = false; - break; - case 12: - break; - case 66: - this._terminal.log('Switching back to normal keypad.'); - this._terminal.applicationKeypad = false; - this._terminal.viewport.syncScrollArea(); - break; - case 9: - case 1000: - case 1002: - case 1003: - this._terminal.x10Mouse = false; - this._terminal.vt200Mouse = false; - this._terminal.normalMouse = false; - this._terminal.mouseEvents = false; - this._terminal.element.classList.remove('enable-mouse-events'); - this._terminal.selectionManager.enable(); - break; - case 1004: - this._terminal.sendFocus = false; - break; - case 1005: - this._terminal.utfMouse = false; - break; - case 1006: - this._terminal.sgrMouse = false; - break; - case 1015: - this._terminal.urxvtMouse = false; - break; - case 25: - this._terminal.cursorHidden = true; - break; - case 1049: - case 47: - case 1047: - this._terminal.buffers.activateNormalBuffer(); - this._terminal.selectionManager.setBuffer(this._terminal.buffer); - this._terminal.refresh(0, this._terminal.rows - 1); - this._terminal.viewport.syncScrollArea(); - this._terminal.showCursor(); - break; - case 2004: - this._terminal.bracketedPasteMode = false; - break; - } - } - }; - InputHandler.prototype.charAttributes = function (params) { - if (params.length === 1 && params[0] === 0) { - this._terminal.curAttr = this._terminal.defAttr; - return; - } - var l = params.length; - var flags = this._terminal.curAttr >> 18; - var fg = (this._terminal.curAttr >> 9) & 0x1ff; - var bg = this._terminal.curAttr & 0x1ff; - var p; - for (var i = 0; i < l; i++) { - p = params[i]; - if (p >= 30 && p <= 37) { - fg = p - 30; - } - else if (p >= 40 && p <= 47) { - bg = p - 40; - } - else if (p >= 90 && p <= 97) { - p += 8; - fg = p - 90; - } - else if (p >= 100 && p <= 107) { - p += 8; - bg = p - 100; - } - else if (p === 0) { - flags = this._terminal.defAttr >> 18; - fg = (this._terminal.defAttr >> 9) & 0x1ff; - bg = this._terminal.defAttr & 0x1ff; - } - else if (p === 1) { - flags |= Types_1.FLAGS.BOLD; - } - else if (p === 4) { - flags |= Types_1.FLAGS.UNDERLINE; - } - else if (p === 5) { - flags |= Types_1.FLAGS.BLINK; - } - else if (p === 7) { - flags |= Types_1.FLAGS.INVERSE; - } - else if (p === 8) { - flags |= Types_1.FLAGS.INVISIBLE; - } - else if (p === 2) { - flags |= Types_1.FLAGS.DIM; - } - else if (p === 22) { - flags &= ~Types_1.FLAGS.BOLD; - flags &= ~Types_1.FLAGS.DIM; - } - else if (p === 24) { - flags &= ~Types_1.FLAGS.UNDERLINE; - } - else if (p === 25) { - flags &= ~Types_1.FLAGS.BLINK; - } - else if (p === 27) { - flags &= ~Types_1.FLAGS.INVERSE; - } - else if (p === 28) { - flags &= ~Types_1.FLAGS.INVISIBLE; - } - else if (p === 39) { - fg = (this._terminal.defAttr >> 9) & 0x1ff; - } - else if (p === 49) { - bg = this._terminal.defAttr & 0x1ff; - } - else if (p === 38) { - if (params[i + 1] === 2) { - i += 2; - fg = this._terminal.matchColor(params[i] & 0xff, params[i + 1] & 0xff, params[i + 2] & 0xff); - if (fg === -1) - fg = 0x1ff; - i += 2; - } - else if (params[i + 1] === 5) { - i += 2; - p = params[i] & 0xff; - fg = p; - } - } - else if (p === 48) { - if (params[i + 1] === 2) { - i += 2; - bg = this._terminal.matchColor(params[i] & 0xff, params[i + 1] & 0xff, params[i + 2] & 0xff); - if (bg === -1) - bg = 0x1ff; - i += 2; - } - else if (params[i + 1] === 5) { - i += 2; - p = params[i] & 0xff; - bg = p; - } - } - else if (p === 100) { - fg = (this._terminal.defAttr >> 9) & 0x1ff; - bg = this._terminal.defAttr & 0x1ff; - } - else { - this._terminal.error('Unknown SGR attribute: %d.', p); - } - } - this._terminal.curAttr = (flags << 18) | (fg << 9) | bg; - }; - InputHandler.prototype.deviceStatus = function (params) { - if (!this._terminal.prefix) { - switch (params[0]) { - case 5: - this._terminal.send(EscapeSequences_1.C0.ESC + '[0n'); - break; - case 6: - this._terminal.send(EscapeSequences_1.C0.ESC + '[' - + (this._terminal.buffer.y + 1) - + ';' - + (this._terminal.buffer.x + 1) - + 'R'); - break; - } - } - else if (this._terminal.prefix === '?') { - switch (params[0]) { - case 6: - this._terminal.send(EscapeSequences_1.C0.ESC + '[?' - + (this._terminal.buffer.y + 1) - + ';' - + (this._terminal.buffer.x + 1) - + 'R'); - break; - case 15: - break; - case 25: - break; - case 26: - break; - case 53: - break; - } - } - }; - InputHandler.prototype.softReset = function (params) { - this._terminal.cursorHidden = false; - this._terminal.insertMode = false; - this._terminal.originMode = false; - this._terminal.wraparoundMode = true; - this._terminal.applicationKeypad = false; - this._terminal.viewport.syncScrollArea(); - this._terminal.applicationCursor = false; - this._terminal.buffer.scrollTop = 0; - this._terminal.buffer.scrollBottom = this._terminal.rows - 1; - this._terminal.curAttr = this._terminal.defAttr; - this._terminal.buffer.x = this._terminal.buffer.y = 0; - this._terminal.charset = null; - this._terminal.glevel = 0; - this._terminal.charsets = [null]; - }; - InputHandler.prototype.setCursorStyle = function (params) { - var param = params[0] < 1 ? 1 : params[0]; - switch (param) { - case 1: - case 2: - this._terminal.setOption('cursorStyle', 'block'); - break; - case 3: - case 4: - this._terminal.setOption('cursorStyle', 'underline'); - break; - case 5: - case 6: - this._terminal.setOption('cursorStyle', 'bar'); - break; - } - var isBlinking = param % 2 === 1; - this._terminal.setOption('cursorBlink', isBlinking); - }; - InputHandler.prototype.setScrollRegion = function (params) { - if (this._terminal.prefix) - return; - this._terminal.buffer.scrollTop = (params[0] || 1) - 1; - this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1; - this._terminal.buffer.x = 0; - this._terminal.buffer.y = 0; - }; - InputHandler.prototype.saveCursor = function (params) { - this._terminal.buffer.savedX = this._terminal.buffer.x; - this._terminal.buffer.savedY = this._terminal.buffer.y; - }; - InputHandler.prototype.restoreCursor = function (params) { - this._terminal.buffer.x = this._terminal.buffer.savedX || 0; - this._terminal.buffer.y = this._terminal.buffer.savedY || 0; - }; - return InputHandler; -}()); -exports.InputHandler = InputHandler; - - - -},{"./Buffer":1,"./CharWidth":3,"./Charsets":4,"./EscapeSequences":6,"./renderer/Types":27}],9:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Types_1 = require("./Types"); -var MouseZoneManager_1 = require("./input/MouseZoneManager"); -var EventEmitter_1 = require("./EventEmitter"); -var protocolClause = '(https?:\\/\\/)'; -var domainCharacterSet = '[\\da-z\\.-]+'; -var negatedDomainCharacterSet = '[^\\da-z\\.-]+'; -var domainBodyClause = '(' + domainCharacterSet + ')'; -var tldClause = '([a-z\\.]{2,6})'; -var ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; -var localHostClause = '(localhost)'; -var portClause = '(:\\d{1,5})'; -var hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?'; -var pathClause = '(\\/[\\/\\w\\.\\-%~]*)*'; -var queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*'; -var queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?'; -var hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?'; -var negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+'; -var bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause; -var start = '(?:^|' + negatedDomainCharacterSet + ')('; -var end = ')($|' + negatedPathCharacterSet + ')'; -var strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end); -var HYPERTEXT_LINK_MATCHER_ID = 0; -var Linkifier = (function (_super) { - __extends(Linkifier, _super); - function Linkifier(_terminal) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._linkMatchers = []; - _this._nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID; - _this._rowsToLinkify = { - start: null, - end: null - }; - _this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 }); - return _this; - } - Linkifier.prototype.attachToDom = function (mouseZoneManager) { - this._mouseZoneManager = mouseZoneManager; - }; - Linkifier.prototype.linkifyRows = function (start, end) { - var _this = this; - if (!this._mouseZoneManager) { - return; - } - if (!this._rowsToLinkify.start) { - this._rowsToLinkify.start = start; - this._rowsToLinkify.end = end; - } - else { - this._rowsToLinkify.start = this._rowsToLinkify.start < start ? this._rowsToLinkify.start : start; - this._rowsToLinkify.end = this._rowsToLinkify.end > end ? this._rowsToLinkify.end : end; - } - this._mouseZoneManager.clearAll(start, end); - if (this._rowsTimeoutId) { - clearTimeout(this._rowsTimeoutId); - } - this._rowsTimeoutId = setTimeout(function () { return _this._linkifyRows(); }, Linkifier.TIME_BEFORE_LINKIFY); - }; - Linkifier.prototype._linkifyRows = function () { - this._rowsTimeoutId = null; - for (var i = this._rowsToLinkify.start; i <= this._rowsToLinkify.end; i++) { - this._linkifyRow(i); - } - this._rowsToLinkify.start = null; - this._rowsToLinkify.end = null; - }; - Linkifier.prototype.setHypertextLinkHandler = function (handler) { - this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler; - }; - Linkifier.prototype.setHypertextValidationCallback = function (callback) { - this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].validationCallback = callback; - }; - Linkifier.prototype.registerLinkMatcher = function (regex, handler, options) { - if (options === void 0) { options = {}; } - if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) { - throw new Error('handler must be defined'); - } - var matcher = { - id: this._nextLinkMatcherId++, - regex: regex, - handler: handler, - matchIndex: options.matchIndex, - validationCallback: options.validationCallback, - hoverTooltipCallback: options.tooltipCallback, - hoverLeaveCallback: options.leaveCallback, - priority: options.priority || 0 - }; - this._addLinkMatcherToList(matcher); - return matcher.id; - }; - Linkifier.prototype._addLinkMatcherToList = function (matcher) { - if (this._linkMatchers.length === 0) { - this._linkMatchers.push(matcher); - return; - } - for (var i = this._linkMatchers.length - 1; i >= 0; i--) { - if (matcher.priority <= this._linkMatchers[i].priority) { - this._linkMatchers.splice(i + 1, 0, matcher); - return; - } - } - this._linkMatchers.splice(0, 0, matcher); - }; - Linkifier.prototype.deregisterLinkMatcher = function (matcherId) { - for (var i = 1; i < this._linkMatchers.length; i++) { - if (this._linkMatchers[i].id === matcherId) { - this._linkMatchers.splice(i, 1); - return true; - } - } - return false; - }; - Linkifier.prototype._linkifyRow = function (rowIndex) { - var absoluteRowIndex = this._terminal.buffer.ydisp + rowIndex; - if (absoluteRowIndex >= this._terminal.buffer.lines.length) { - return; - } - var text = this._terminal.buffer.translateBufferLineToString(absoluteRowIndex, false); - for (var i = 0; i < this._linkMatchers.length; i++) { - this._doLinkifyRow(rowIndex, text, this._linkMatchers[i]); - } - }; - Linkifier.prototype._doLinkifyRow = function (rowIndex, text, matcher, offset) { - var _this = this; - if (offset === void 0) { offset = 0; } - var result = []; - var isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID; - var match = text.match(matcher.regex); - if (!match || match.length === 0) { - return; - } - var uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex]; - var index = text.indexOf(uri); - if (matcher.validationCallback) { - matcher.validationCallback(uri, function (isValid) { - if (_this._rowsTimeoutId) { - return; - } - if (isValid) { - _this._addLink(offset + index, rowIndex, uri, matcher); - } - }); - } - else { - this._addLink(offset + index, rowIndex, uri, matcher); - } - var remainingStartIndex = index + uri.length; - var remainingText = text.substr(remainingStartIndex); - if (remainingText.length > 0) { - this._doLinkifyRow(rowIndex, remainingText, matcher, offset + remainingStartIndex); - } - }; - Linkifier.prototype._addLink = function (x, y, uri, matcher) { - var _this = this; - this._mouseZoneManager.add(new MouseZoneManager_1.MouseZone(x + 1, x + 1 + uri.length, y + 1, function (e) { - if (matcher.handler) { - return matcher.handler(e, uri); - } - window.open(uri, '_blank'); - }, function (e) { - _this.emit(Types_1.LinkHoverEventTypes.HOVER, { x: x, y: y, length: uri.length }); - _this._terminal.element.style.cursor = 'pointer'; - }, function (e) { - _this.emit(Types_1.LinkHoverEventTypes.TOOLTIP, { x: x, y: y, length: uri.length }); - if (matcher.hoverTooltipCallback) { - matcher.hoverTooltipCallback(e, uri); - } - }, function () { - _this.emit(Types_1.LinkHoverEventTypes.LEAVE, { x: x, y: y, length: uri.length }); - _this._terminal.element.style.cursor = ''; - if (matcher.hoverLeaveCallback) { - matcher.hoverLeaveCallback(); - } - })); - }; - Linkifier.TIME_BEFORE_LINKIFY = 200; - return Linkifier; -}(EventEmitter_1.EventEmitter)); -exports.Linkifier = Linkifier; - - - -},{"./EventEmitter":7,"./Types":14,"./input/MouseZoneManager":17}],10:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var EscapeSequences_1 = require("./EscapeSequences"); -var Charsets_1 = require("./Charsets"); -var normalStateHandler = {}; -normalStateHandler[EscapeSequences_1.C0.BEL] = function (parser, handler) { return handler.bell(); }; -normalStateHandler[EscapeSequences_1.C0.LF] = function (parser, handler) { return handler.lineFeed(); }; -normalStateHandler[EscapeSequences_1.C0.VT] = normalStateHandler[EscapeSequences_1.C0.LF]; -normalStateHandler[EscapeSequences_1.C0.FF] = normalStateHandler[EscapeSequences_1.C0.LF]; -normalStateHandler[EscapeSequences_1.C0.CR] = function (parser, handler) { return handler.carriageReturn(); }; -normalStateHandler[EscapeSequences_1.C0.BS] = function (parser, handler) { return handler.backspace(); }; -normalStateHandler[EscapeSequences_1.C0.HT] = function (parser, handler) { return handler.tab(); }; -normalStateHandler[EscapeSequences_1.C0.SO] = function (parser, handler) { return handler.shiftOut(); }; -normalStateHandler[EscapeSequences_1.C0.SI] = function (parser, handler) { return handler.shiftIn(); }; -normalStateHandler[EscapeSequences_1.C0.ESC] = function (parser, handler) { return parser.setState(ParserState.ESCAPED); }; -var escapedStateHandler = {}; -escapedStateHandler['['] = function (parser, terminal) { - terminal.params = []; - terminal.currentParam = 0; - parser.setState(ParserState.CSI_PARAM); -}; -escapedStateHandler[']'] = function (parser, terminal) { - terminal.params = []; - terminal.currentParam = 0; - parser.setState(ParserState.OSC); -}; -escapedStateHandler['P'] = function (parser, terminal) { - terminal.params = []; - terminal.currentParam = 0; - parser.setState(ParserState.DCS); -}; -escapedStateHandler['_'] = function (parser, terminal) { - parser.setState(ParserState.IGNORE); -}; -escapedStateHandler['^'] = function (parser, terminal) { - parser.setState(ParserState.IGNORE); -}; -escapedStateHandler['c'] = function (parser, terminal) { - terminal.reset(); -}; -escapedStateHandler['E'] = function (parser, terminal) { - terminal.buffer.x = 0; - terminal.index(); - parser.setState(ParserState.NORMAL); -}; -escapedStateHandler['D'] = function (parser, terminal) { - terminal.index(); - parser.setState(ParserState.NORMAL); -}; -escapedStateHandler['M'] = function (parser, terminal) { - terminal.reverseIndex(); - parser.setState(ParserState.NORMAL); -}; -escapedStateHandler['%'] = function (parser, terminal) { - terminal.setgLevel(0); - terminal.setgCharset(0, Charsets_1.DEFAULT_CHARSET); - parser.setState(ParserState.NORMAL); - parser.skipNextChar(); -}; -escapedStateHandler[EscapeSequences_1.C0.CAN] = function (parser) { return parser.setState(ParserState.NORMAL); }; -var csiParamStateHandler = {}; -csiParamStateHandler['?'] = function (parser) { return parser.setPrefix('?'); }; -csiParamStateHandler['>'] = function (parser) { return parser.setPrefix('>'); }; -csiParamStateHandler['!'] = function (parser) { return parser.setPrefix('!'); }; -csiParamStateHandler['0'] = function (parser) { return parser.setParam(parser.getParam() * 10); }; -csiParamStateHandler['1'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 1); }; -csiParamStateHandler['2'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 2); }; -csiParamStateHandler['3'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 3); }; -csiParamStateHandler['4'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 4); }; -csiParamStateHandler['5'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 5); }; -csiParamStateHandler['6'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 6); }; -csiParamStateHandler['7'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 7); }; -csiParamStateHandler['8'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 8); }; -csiParamStateHandler['9'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 9); }; -csiParamStateHandler['$'] = function (parser) { return parser.setPostfix('$'); }; -csiParamStateHandler['"'] = function (parser) { return parser.setPostfix('"'); }; -csiParamStateHandler[' '] = function (parser) { return parser.setPostfix(' '); }; -csiParamStateHandler['\''] = function (parser) { return parser.setPostfix('\''); }; -csiParamStateHandler[';'] = function (parser) { return parser.finalizeParam(); }; -csiParamStateHandler[EscapeSequences_1.C0.CAN] = function (parser) { return parser.setState(ParserState.NORMAL); }; -var csiStateHandler = {}; -csiStateHandler['@'] = function (handler, params, prefix) { return handler.insertChars(params); }; -csiStateHandler['A'] = function (handler, params, prefix) { return handler.cursorUp(params); }; -csiStateHandler['B'] = function (handler, params, prefix) { return handler.cursorDown(params); }; -csiStateHandler['C'] = function (handler, params, prefix) { return handler.cursorForward(params); }; -csiStateHandler['D'] = function (handler, params, prefix) { return handler.cursorBackward(params); }; -csiStateHandler['E'] = function (handler, params, prefix) { return handler.cursorNextLine(params); }; -csiStateHandler['F'] = function (handler, params, prefix) { return handler.cursorPrecedingLine(params); }; -csiStateHandler['G'] = function (handler, params, prefix) { return handler.cursorCharAbsolute(params); }; -csiStateHandler['H'] = function (handler, params, prefix) { return handler.cursorPosition(params); }; -csiStateHandler['I'] = function (handler, params, prefix) { return handler.cursorForwardTab(params); }; -csiStateHandler['J'] = function (handler, params, prefix) { return handler.eraseInDisplay(params); }; -csiStateHandler['K'] = function (handler, params, prefix) { return handler.eraseInLine(params); }; -csiStateHandler['L'] = function (handler, params, prefix) { return handler.insertLines(params); }; -csiStateHandler['M'] = function (handler, params, prefix) { return handler.deleteLines(params); }; -csiStateHandler['P'] = function (handler, params, prefix) { return handler.deleteChars(params); }; -csiStateHandler['S'] = function (handler, params, prefix) { return handler.scrollUp(params); }; -csiStateHandler['T'] = function (handler, params, prefix) { - if (params.length < 2 && !prefix) { - handler.scrollDown(params); - } -}; -csiStateHandler['X'] = function (handler, params, prefix) { return handler.eraseChars(params); }; -csiStateHandler['Z'] = function (handler, params, prefix) { return handler.cursorBackwardTab(params); }; -csiStateHandler['`'] = function (handler, params, prefix) { return handler.charPosAbsolute(params); }; -csiStateHandler['a'] = function (handler, params, prefix) { return handler.HPositionRelative(params); }; -csiStateHandler['b'] = function (handler, params, prefix) { return handler.repeatPrecedingCharacter(params); }; -csiStateHandler['c'] = function (handler, params, prefix) { return handler.sendDeviceAttributes(params); }; -csiStateHandler['d'] = function (handler, params, prefix) { return handler.linePosAbsolute(params); }; -csiStateHandler['e'] = function (handler, params, prefix) { return handler.VPositionRelative(params); }; -csiStateHandler['f'] = function (handler, params, prefix) { return handler.HVPosition(params); }; -csiStateHandler['g'] = function (handler, params, prefix) { return handler.tabClear(params); }; -csiStateHandler['h'] = function (handler, params, prefix) { return handler.setMode(params); }; -csiStateHandler['l'] = function (handler, params, prefix) { return handler.resetMode(params); }; -csiStateHandler['m'] = function (handler, params, prefix) { return handler.charAttributes(params); }; -csiStateHandler['n'] = function (handler, params, prefix) { return handler.deviceStatus(params); }; -csiStateHandler['p'] = function (handler, params, prefix) { - switch (prefix) { - case '!': - handler.softReset(params); - break; - } -}; -csiStateHandler['q'] = function (handler, params, prefix, postfix) { - if (postfix === ' ') { - handler.setCursorStyle(params); - } -}; -csiStateHandler['r'] = function (handler, params) { return handler.setScrollRegion(params); }; -csiStateHandler['s'] = function (handler, params) { return handler.saveCursor(params); }; -csiStateHandler['u'] = function (handler, params) { return handler.restoreCursor(params); }; -csiStateHandler[EscapeSequences_1.C0.CAN] = function (handler, params, prefix, postfix, parser) { return parser.setState(ParserState.NORMAL); }; -var ParserState; -(function (ParserState) { - ParserState[ParserState["NORMAL"] = 0] = "NORMAL"; - ParserState[ParserState["ESCAPED"] = 1] = "ESCAPED"; - ParserState[ParserState["CSI_PARAM"] = 2] = "CSI_PARAM"; - ParserState[ParserState["CSI"] = 3] = "CSI"; - ParserState[ParserState["OSC"] = 4] = "OSC"; - ParserState[ParserState["CHARSET"] = 5] = "CHARSET"; - ParserState[ParserState["DCS"] = 6] = "DCS"; - ParserState[ParserState["IGNORE"] = 7] = "IGNORE"; -})(ParserState = exports.ParserState || (exports.ParserState = {})); -var Parser = (function () { - function Parser(_inputHandler, _terminal) { - this._inputHandler = _inputHandler; - this._terminal = _terminal; - this._state = ParserState.NORMAL; - } - Parser.prototype.parse = function (data) { - var l = data.length; - var j; - var cs; - var ch; - var code; - var low; - var cursorStartX = this._terminal.buffer.x; - var cursorStartY = this._terminal.buffer.y; - if (this._terminal.debug) { - this._terminal.log('data: ' + data); - } - this._position = 0; - if (this._terminal.surrogate_high) { - data = this._terminal.surrogate_high + data; - this._terminal.surrogate_high = ''; - } - for (; this._position < l; this._position++) { - ch = data[this._position]; - code = data.charCodeAt(this._position); - if (0xD800 <= code && code <= 0xDBFF) { - low = data.charCodeAt(this._position + 1); - if (isNaN(low)) { - this._terminal.surrogate_high = ch; - continue; - } - code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; - ch += data.charAt(this._position + 1); - } - if (0xDC00 <= code && code <= 0xDFFF) - continue; - switch (this._state) { - case ParserState.NORMAL: - if (ch in normalStateHandler) { - normalStateHandler[ch](this, this._inputHandler); - } - else { - this._inputHandler.addChar(ch, code); - } - break; - case ParserState.ESCAPED: - if (ch in escapedStateHandler) { - escapedStateHandler[ch](this, this._terminal); - break; - } - switch (ch) { - case '(': - case ')': - case '*': - case '+': - case '-': - case '.': - switch (ch) { - case '(': - this._terminal.gcharset = 0; - break; - case ')': - this._terminal.gcharset = 1; - break; - case '*': - this._terminal.gcharset = 2; - break; - case '+': - this._terminal.gcharset = 3; - break; - case '-': - this._terminal.gcharset = 1; - break; - case '.': - this._terminal.gcharset = 2; - break; - } - this._state = ParserState.CHARSET; - break; - case '/': - this._terminal.gcharset = 3; - this._state = ParserState.CHARSET; - this._position--; - break; - case 'N': - break; - case 'O': - break; - case 'n': - this._terminal.setgLevel(2); - break; - case 'o': - this._terminal.setgLevel(3); - break; - case '|': - this._terminal.setgLevel(3); - break; - case '}': - this._terminal.setgLevel(2); - break; - case '~': - this._terminal.setgLevel(1); - break; - case '7': - this._inputHandler.saveCursor(); - this._state = ParserState.NORMAL; - break; - case '8': - this._inputHandler.restoreCursor(); - this._state = ParserState.NORMAL; - break; - case '#': - this._state = ParserState.NORMAL; - this._position++; - break; - case 'H': - this._terminal.tabSet(); - this._state = ParserState.NORMAL; - break; - case '=': - this._terminal.log('Serial port requested application keypad.'); - this._terminal.applicationKeypad = true; - if (this._terminal.viewport) { - this._terminal.viewport.syncScrollArea(); - } - this._state = ParserState.NORMAL; - break; - case '>': - this._terminal.log('Switching back to normal keypad.'); - this._terminal.applicationKeypad = false; - if (this._terminal.viewport) { - this._terminal.viewport.syncScrollArea(); - } - this._state = ParserState.NORMAL; - break; - default: - this._state = ParserState.NORMAL; - this._terminal.error('Unknown ESC control: %s.', ch); - break; - } - break; - case ParserState.CHARSET: - if (ch in Charsets_1.CHARSETS) { - cs = Charsets_1.CHARSETS[ch]; - if (ch === '/') { - this.skipNextChar(); - } - } - else { - cs = Charsets_1.DEFAULT_CHARSET; - } - this._terminal.setgCharset(this._terminal.gcharset, cs); - this._terminal.gcharset = null; - this._state = ParserState.NORMAL; - break; - case ParserState.OSC: - if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) { - if (ch === EscapeSequences_1.C0.ESC) - this._position++; - this._terminal.params.push(this._terminal.currentParam); - switch (this._terminal.params[0]) { - case 0: - case 1: - case 2: - if (this._terminal.params[1]) { - this._terminal.title = this._terminal.params[1]; - this._terminal.handleTitle(this._terminal.title); - } - break; - case 3: - break; - case 4: - case 5: - break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - break; - case 46: - break; - case 50: - break; - case 51: - break; - case 52: - break; - case 104: - case 105: - case 110: - case 111: - case 112: - case 113: - case 114: - case 115: - case 116: - case 117: - case 118: - break; - } - this._terminal.params = []; - this._terminal.currentParam = 0; - this._state = ParserState.NORMAL; - } - else { - if (!this._terminal.params.length) { - if (ch >= '0' && ch <= '9') { - this._terminal.currentParam = - this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48; - } - else if (ch === ';') { - this._terminal.params.push(this._terminal.currentParam); - this._terminal.currentParam = ''; - } - } - else { - this._terminal.currentParam += ch; - } - } - break; - case ParserState.CSI_PARAM: - if (ch in csiParamStateHandler) { - csiParamStateHandler[ch](this); - break; - } - this.finalizeParam(); - this._state = ParserState.CSI; - case ParserState.CSI: - if (ch in csiStateHandler) { - if (this._terminal.debug) { - this._terminal.log("CSI " + (this._terminal.prefix ? this._terminal.prefix : '') + " " + (this._terminal.params ? this._terminal.params.join(';') : '') + " " + (this._terminal.postfix ? this._terminal.postfix : '') + " " + ch); - } - csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this); - } - else { - this._terminal.error('Unknown CSI code: %s.', ch); - } - this._state = ParserState.NORMAL; - this._terminal.prefix = ''; - this._terminal.postfix = ''; - break; - case ParserState.DCS: - if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) { - if (ch === EscapeSequences_1.C0.ESC) - this._position++; - var pt = void 0; - var valid = void 0; - switch (this._terminal.prefix) { - case '': - break; - case '$q': - pt = this._terminal.currentParam; - valid = false; - switch (pt) { - case '"q': - pt = '0"q'; - break; - case '"p': - pt = '61"p'; - break; - case 'r': - pt = '' - + (this._terminal.buffer.scrollTop + 1) - + ';' - + (this._terminal.buffer.scrollBottom + 1) - + 'r'; - break; - case 'm': - pt = '0m'; - break; - default: - this._terminal.error('Unknown DCS Pt: %s.', pt); - pt = ''; - break; - } - this._terminal.send(EscapeSequences_1.C0.ESC + 'P' + +valid + '$r' + pt + EscapeSequences_1.C0.ESC + '\\'); - break; - case '+p': - break; - case '+q': - pt = this._terminal.currentParam; - valid = false; - this._terminal.send(EscapeSequences_1.C0.ESC + 'P' + +valid + '+r' + pt + EscapeSequences_1.C0.ESC + '\\'); - break; - default: - this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix); - break; - } - this._terminal.currentParam = 0; - this._terminal.prefix = ''; - this._state = ParserState.NORMAL; - } - else if (!this._terminal.currentParam) { - if (!this._terminal.prefix && ch !== '$' && ch !== '+') { - this._terminal.currentParam = ch; - } - else if (this._terminal.prefix.length === 2) { - this._terminal.currentParam = ch; - } - else { - this._terminal.prefix += ch; - } - } - else { - this._terminal.currentParam += ch; - } - break; - case ParserState.IGNORE: - if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) { - if (ch === EscapeSequences_1.C0.ESC) - this._position++; - this._state = ParserState.NORMAL; - } - break; - } - } - if (this._terminal.buffer.x !== cursorStartX || this._terminal.buffer.y !== cursorStartY) { - this._terminal.emit('cursormove'); - } - return this._state; - }; - Parser.prototype.setState = function (state) { - this._state = state; - }; - Parser.prototype.setPrefix = function (prefix) { - this._terminal.prefix = prefix; - }; - Parser.prototype.setPostfix = function (postfix) { - this._terminal.postfix = postfix; - }; - Parser.prototype.setParam = function (param) { - this._terminal.currentParam = param; - }; - Parser.prototype.getParam = function () { - return this._terminal.currentParam; - }; - Parser.prototype.finalizeParam = function () { - this._terminal.params.push(this._terminal.currentParam); - this._terminal.currentParam = 0; - }; - Parser.prototype.skipNextChar = function () { - this._position++; - }; - return Parser; -}()); -exports.Parser = Parser; - - - -},{"./Charsets":4,"./EscapeSequences":6}],11:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var MouseHelper_1 = require("./utils/MouseHelper"); -var Browser = require("./utils/Browser"); -var EventEmitter_1 = require("./EventEmitter"); -var SelectionModel_1 = require("./SelectionModel"); -var Buffer_1 = require("./Buffer"); -var DRAG_SCROLL_MAX_THRESHOLD = 50; -var DRAG_SCROLL_MAX_SPEED = 15; -var DRAG_SCROLL_INTERVAL = 50; -var WORD_SEPARATORS = ' ()[]{}\'"'; -var NON_BREAKING_SPACE_CHAR = String.fromCharCode(160); -var ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g'); -var SelectionMode; -(function (SelectionMode) { - SelectionMode[SelectionMode["NORMAL"] = 0] = "NORMAL"; - SelectionMode[SelectionMode["WORD"] = 1] = "WORD"; - SelectionMode[SelectionMode["LINE"] = 2] = "LINE"; -})(SelectionMode || (SelectionMode = {})); -var SelectionManager = (function (_super) { - __extends(SelectionManager, _super); - function SelectionManager(_terminal, _buffer, _charMeasure) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._buffer = _buffer; - _this._charMeasure = _charMeasure; - _this._enabled = true; - _this._initListeners(); - _this.enable(); - _this._model = new SelectionModel_1.SelectionModel(_terminal); - _this._activeSelectionMode = SelectionMode.NORMAL; - return _this; - } - SelectionManager.prototype._initListeners = function () { - var _this = this; - this._mouseMoveListener = function (event) { return _this._onMouseMove(event); }; - this._mouseUpListener = function (event) { return _this._onMouseUp(event); }; - this._buffer.lines.on('trim', function (amount) { return _this._onTrim(amount); }); - }; - SelectionManager.prototype.disable = function () { - this.clearSelection(); - this._enabled = false; - }; - SelectionManager.prototype.enable = function () { - this._enabled = true; - }; - SelectionManager.prototype.setBuffer = function (buffer) { - this._buffer = buffer; - this.clearSelection(); - }; - Object.defineProperty(SelectionManager.prototype, "selectionStart", { - get: function () { return this._model.finalSelectionStart; }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionManager.prototype, "selectionEnd", { - get: function () { return this._model.finalSelectionEnd; }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionManager.prototype, "hasSelection", { - get: function () { - var start = this._model.finalSelectionStart; - var end = this._model.finalSelectionEnd; - if (!start || !end) { - return false; - } - return start[0] !== end[0] || start[1] !== end[1]; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionManager.prototype, "selectionText", { - get: function () { - var start = this._model.finalSelectionStart; - var end = this._model.finalSelectionEnd; - if (!start || !end) { - return ''; - } - var startRowEndCol = start[1] === end[1] ? end[0] : null; - var result = []; - result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol)); - for (var i = start[1] + 1; i <= end[1] - 1; i++) { - var bufferLine = this._buffer.lines.get(i); - var lineText = this._buffer.translateBufferLineToString(i, true); - if (bufferLine.isWrapped) { - result[result.length - 1] += lineText; - } - else { - result.push(lineText); - } - } - if (start[1] !== end[1]) { - var bufferLine = this._buffer.lines.get(end[1]); - var lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]); - if (bufferLine.isWrapped) { - result[result.length - 1] += lineText; - } - else { - result.push(lineText); - } - } - var formattedResult = result.map(function (line) { - return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' '); - }).join(Browser.isMSWindows ? '\r\n' : '\n'); - return formattedResult; - }, - enumerable: true, - configurable: true - }); - SelectionManager.prototype.clearSelection = function () { - this._model.clearSelection(); - this._removeMouseDownListeners(); - this.refresh(); - }; - SelectionManager.prototype.refresh = function (isNewSelection) { - var _this = this; - if (!this._refreshAnimationFrame) { - this._refreshAnimationFrame = window.requestAnimationFrame(function () { return _this._refresh(); }); - } - if (Browser.isLinux && isNewSelection) { - var selectionText = this.selectionText; - if (selectionText.length) { - this.emit('newselection', this.selectionText); - } - } - }; - SelectionManager.prototype._refresh = function () { - this._refreshAnimationFrame = null; - this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd }); - }; - SelectionManager.prototype.selectAll = function () { - this._model.isSelectAllActive = true; - this.refresh(); - this.emit('selection'); - }; - SelectionManager.prototype._onTrim = function (amount) { - var needsRefresh = this._model.onTrim(amount); - if (needsRefresh) { - this.refresh(); - } - }; - SelectionManager.prototype._getMouseBufferCoords = function (event) { - var coords = this._terminal.mouseHelper.getCoords(event, this._terminal.element, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true); - if (!coords) { - return null; - } - coords[0]--; - coords[1]--; - coords[1] += this._terminal.buffer.ydisp; - return coords; - }; - SelectionManager.prototype._getMouseEventScrollAmount = function (event) { - var offset = MouseHelper_1.MouseHelper.getCoordsRelativeToElement(event, this._terminal.element)[1]; - var terminalHeight = this._terminal.rows * Math.ceil(this._charMeasure.height * this._terminal.options.lineHeight); - if (offset >= 0 && offset <= terminalHeight) { - return 0; - } - if (offset > terminalHeight) { - offset -= terminalHeight; - } - offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD); - offset /= DRAG_SCROLL_MAX_THRESHOLD; - return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1)); - }; - SelectionManager.prototype.shouldForceSelection = function (event) { - return Browser.isMac ? event.altKey : event.shiftKey; - }; - SelectionManager.prototype.onMouseDown = function (event) { - if (event.button === 2 && this.hasSelection) { - return; - } - if (event.button !== 0) { - return; - } - if (!this._enabled) { - if (!this.shouldForceSelection(event)) { - return; - } - event.stopPropagation(); - } - event.preventDefault(); - this._dragScrollAmount = 0; - if (this._enabled && event.shiftKey) { - this._onIncrementalClick(event); - } - else { - if (event.detail === 1) { - this._onSingleClick(event); - } - else if (event.detail === 2) { - this._onDoubleClick(event); - } - else if (event.detail === 3) { - this._onTripleClick(event); - } - } - this._addMouseDownListeners(); - this.refresh(true); - }; - SelectionManager.prototype._addMouseDownListeners = function () { - var _this = this; - this._terminal.element.ownerDocument.addEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.ownerDocument.addEventListener('mouseup', this._mouseUpListener); - this._dragScrollIntervalTimer = setInterval(function () { return _this._dragScroll(); }, DRAG_SCROLL_INTERVAL); - }; - SelectionManager.prototype._removeMouseDownListeners = function () { - this._terminal.element.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.ownerDocument.removeEventListener('mouseup', this._mouseUpListener); - clearInterval(this._dragScrollIntervalTimer); - this._dragScrollIntervalTimer = null; - }; - SelectionManager.prototype._onIncrementalClick = function (event) { - if (this._model.selectionStart) { - this._model.selectionEnd = this._getMouseBufferCoords(event); - } - }; - SelectionManager.prototype._onSingleClick = function (event) { - this._model.selectionStartLength = 0; - this._model.isSelectAllActive = false; - this._activeSelectionMode = SelectionMode.NORMAL; - this._model.selectionStart = this._getMouseBufferCoords(event); - if (!this._model.selectionStart) { - return; - } - this._model.selectionEnd = null; - var line = this._buffer.lines.get(this._model.selectionStart[1]); - if (!line) { - return; - } - if (line.length >= this._model.selectionStart[0]) { - return; - } - var char = line[this._model.selectionStart[0]]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - this._model.selectionStart[0]++; - } - }; - SelectionManager.prototype._onDoubleClick = function (event) { - var coords = this._getMouseBufferCoords(event); - if (coords) { - this._activeSelectionMode = SelectionMode.WORD; - this._selectWordAt(coords); - } - }; - SelectionManager.prototype._onTripleClick = function (event) { - var coords = this._getMouseBufferCoords(event); - if (coords) { - this._activeSelectionMode = SelectionMode.LINE; - this._selectLineAt(coords[1]); - } - }; - SelectionManager.prototype._onMouseMove = function (event) { - event.stopImmediatePropagation(); - var previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null; - this._model.selectionEnd = this._getMouseBufferCoords(event); - if (!this._model.selectionEnd) { - this.refresh(true); - return; - } - if (this._activeSelectionMode === SelectionMode.LINE) { - if (this._model.selectionEnd[1] < this._model.selectionStart[1]) { - this._model.selectionEnd[0] = 0; - } - else { - this._model.selectionEnd[0] = this._terminal.cols; - } - } - else if (this._activeSelectionMode === SelectionMode.WORD) { - this._selectToWordAt(this._model.selectionEnd); - } - this._dragScrollAmount = this._getMouseEventScrollAmount(event); - if (this._dragScrollAmount > 0) { - this._model.selectionEnd[0] = this._terminal.cols; - } - else if (this._dragScrollAmount < 0) { - this._model.selectionEnd[0] = 0; - } - if (this._model.selectionEnd[1] < this._buffer.lines.length) { - var char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]]; - if (char && char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - this._model.selectionEnd[0]++; - } - } - if (!previousSelectionEnd || - previousSelectionEnd[0] !== this._model.selectionEnd[0] || - previousSelectionEnd[1] !== this._model.selectionEnd[1]) { - this.refresh(true); - } - }; - SelectionManager.prototype._dragScroll = function () { - if (this._dragScrollAmount) { - this._terminal.scrollLines(this._dragScrollAmount, false); - if (this._dragScrollAmount > 0) { - this._model.selectionEnd = [this._terminal.cols - 1, this._terminal.buffer.ydisp + this._terminal.rows]; - } - else { - this._model.selectionEnd = [0, this._terminal.buffer.ydisp]; - } - this.refresh(); - } - }; - SelectionManager.prototype._onMouseUp = function (event) { - this._removeMouseDownListeners(); - if (this.hasSelection) - this.emit('selection'); - }; - SelectionManager.prototype._convertViewportColToCharacterIndex = function (bufferLine, coords) { - var charIndex = coords[0]; - for (var i = 0; coords[0] >= i; i++) { - var char = bufferLine[i]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - charIndex--; - } - else if (char[Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1 && coords[0] !== i) { - charIndex += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - } - return charIndex; - }; - SelectionManager.prototype.setSelection = function (col, row, length) { - this._model.clearSelection(); - this._removeMouseDownListeners(); - this._model.selectionStart = [col, row]; - this._model.selectionStartLength = length; - this.refresh(); - }; - SelectionManager.prototype._getWordAt = function (coords) { - var bufferLine = this._buffer.lines.get(coords[1]); - if (!bufferLine) { - return null; - } - var line = this._buffer.translateBufferLineToString(coords[1], false); - var startIndex = this._convertViewportColToCharacterIndex(bufferLine, coords); - var endIndex = startIndex; - var charOffset = coords[0] - startIndex; - var leftWideCharCount = 0; - var rightWideCharCount = 0; - var leftLongCharOffset = 0; - var rightLongCharOffset = 0; - if (line.charAt(startIndex) === ' ') { - while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') { - startIndex--; - } - while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') { - endIndex++; - } - } - else { - var startCol = coords[0]; - var endCol = coords[0]; - if (bufferLine[startCol][Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - leftWideCharCount++; - startCol--; - } - if (bufferLine[endCol][Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - rightWideCharCount++; - endCol++; - } - if (bufferLine[endCol][Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1) { - rightLongCharOffset += bufferLine[endCol][Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - endIndex += bufferLine[endCol][Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine[startCol - 1])) { - var char = bufferLine[startCol - 1]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - leftWideCharCount++; - startCol--; - } - else if (char[Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1) { - leftLongCharOffset += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - startIndex -= char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - startIndex--; - startCol--; - } - while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine[endCol + 1])) { - var char = bufferLine[endCol + 1]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - rightWideCharCount++; - endCol++; - } - else if (char[Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1) { - rightLongCharOffset += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - endIndex += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - endIndex++; - endCol++; - } - } - endIndex++; - var start = startIndex - + charOffset - - leftWideCharCount - + leftLongCharOffset; - var length = Math.min(this._terminal.cols, endIndex - - startIndex - + leftWideCharCount - + rightWideCharCount - - leftLongCharOffset - - rightLongCharOffset); - return { start: start, length: length }; - }; - SelectionManager.prototype._selectWordAt = function (coords) { - var wordPosition = this._getWordAt(coords); - if (wordPosition) { - this._model.selectionStart = [wordPosition.start, coords[1]]; - this._model.selectionStartLength = wordPosition.length; - } - }; - SelectionManager.prototype._selectToWordAt = function (coords) { - var wordPosition = this._getWordAt(coords); - if (wordPosition) { - this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]]; - } - }; - SelectionManager.prototype._isCharWordSeparator = function (charData) { - if (charData[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - return false; - } - return WORD_SEPARATORS.indexOf(charData[Buffer_1.CHAR_DATA_CHAR_INDEX]) >= 0; - }; - SelectionManager.prototype._selectLineAt = function (line) { - this._model.selectionStart = [0, line]; - this._model.selectionStartLength = this._terminal.cols; - }; - return SelectionManager; -}(EventEmitter_1.EventEmitter)); -exports.SelectionManager = SelectionManager; - - - -},{"./Buffer":1,"./EventEmitter":7,"./SelectionModel":12,"./utils/Browser":28,"./utils/MouseHelper":32}],12:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var SelectionModel = (function () { - function SelectionModel(_terminal) { - this._terminal = _terminal; - this.clearSelection(); - } - SelectionModel.prototype.clearSelection = function () { - this.selectionStart = null; - this.selectionEnd = null; - this.isSelectAllActive = false; - this.selectionStartLength = 0; - }; - Object.defineProperty(SelectionModel.prototype, "finalSelectionStart", { - get: function () { - if (this.isSelectAllActive) { - return [0, 0]; - } - if (!this.selectionEnd || !this.selectionStart) { - return this.selectionStart; - } - return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionModel.prototype, "finalSelectionEnd", { - get: function () { - if (this.isSelectAllActive) { - return [this._terminal.cols, this._terminal.buffer.ybase + this._terminal.rows - 1]; - } - if (!this.selectionStart) { - return null; - } - if (!this.selectionEnd || this.areSelectionValuesReversed()) { - return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]]; - } - if (this.selectionStartLength) { - if (this.selectionEnd[1] === this.selectionStart[1]) { - return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]]; - } - } - return this.selectionEnd; - }, - enumerable: true, - configurable: true - }); - SelectionModel.prototype.areSelectionValuesReversed = function () { - var start = this.selectionStart; - var end = this.selectionEnd; - if (!start || !end) { - return false; - } - return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]); - }; - SelectionModel.prototype.onTrim = function (amount) { - if (this.selectionStart) { - this.selectionStart[1] -= amount; - } - if (this.selectionEnd) { - this.selectionEnd[1] -= amount; - } - if (this.selectionEnd && this.selectionEnd[1] < 0) { - this.clearSelection(); - return true; - } - if (this.selectionStart && this.selectionStart[1] < 0) { - this.selectionStart[1] = 0; - } - return false; - }; - return SelectionModel; -}()); -exports.SelectionModel = SelectionModel; - - - -},{}],13:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var BufferSet_1 = require("./BufferSet"); -var Buffer_1 = require("./Buffer"); -var CompositionHelper_1 = require("./CompositionHelper"); -var EventEmitter_1 = require("./EventEmitter"); -var Viewport_1 = require("./Viewport"); -var Clipboard_1 = require("./handlers/Clipboard"); -var EscapeSequences_1 = require("./EscapeSequences"); -var InputHandler_1 = require("./InputHandler"); -var Parser_1 = require("./Parser"); -var Renderer_1 = require("./renderer/Renderer"); -var Linkifier_1 = require("./Linkifier"); -var SelectionManager_1 = require("./SelectionManager"); -var CharMeasure_1 = require("./utils/CharMeasure"); -var Browser = require("./utils/Browser"); -var MouseHelper_1 = require("./utils/MouseHelper"); -var Sounds_1 = require("./utils/Sounds"); -var ColorManager_1 = require("./renderer/ColorManager"); -var MouseZoneManager_1 = require("./input/MouseZoneManager"); -var CharAtlas_1 = require("./renderer/CharAtlas"); -var document = (typeof window !== 'undefined') ? window.document : null; -var WRITE_BUFFER_PAUSE_THRESHOLD = 5; -var WRITE_BATCH_SIZE = 300; -var DEFAULT_OPTIONS = { - convertEol: false, - termName: 'xterm', - geometry: [80, 24], - cursorBlink: false, - cursorStyle: 'block', - bellSound: Sounds_1.BellSound, - bellStyle: 'none', - enableBold: true, - fontFamily: 'courier-new, courier, monospace', - fontSize: 15, - lineHeight: 1.0, - letterSpacing: 0, - scrollback: 1000, - screenKeys: false, - debug: false, - cancelEvents: false, - disableStdin: false, - useFlowControl: false, - tabStopWidth: 8, - theme: null -}; -var Terminal = (function (_super) { - __extends(Terminal, _super); - function Terminal(options) { - if (options === void 0) { options = {}; } - var _this = _super.call(this) || this; - _this.browser = Browser; - _this.options = options; - _this.setup(); - return _this; - } - Terminal.prototype.setup = function () { - var _this = this; - Object.keys(DEFAULT_OPTIONS).forEach(function (key) { - if (_this.options[key] == null) { - _this.options[key] = DEFAULT_OPTIONS[key]; - } - _this[key] = _this.options[key]; - }); - this.parent = document ? document.body : null; - this.cols = this.options.cols || this.options.geometry[0]; - this.rows = this.options.rows || this.options.geometry[1]; - this.geometry = [this.cols, this.rows]; - if (this.options.handler) { - this.on('data', this.options.handler); - } - this.cursorState = 0; - this.cursorHidden = false; - this.sendDataQueue = ''; - this.customKeyEventHandler = null; - this.applicationKeypad = false; - this.applicationCursor = false; - this.originMode = false; - this.insertMode = false; - this.wraparoundMode = true; - this.bracketedPasteMode = false; - this.charset = null; - this.gcharset = null; - this.glevel = 0; - this.charsets = [null]; - this.readable = true; - this.writable = true; - this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); - this.curAttr = (0 << 18) | (257 << 9) | (256 << 0); - this.params = []; - this.currentParam = 0; - this.prefix = ''; - this.postfix = ''; - this.writeBuffer = []; - this.writeInProgress = false; - this.xoffSentToCatchUp = false; - this.writeStopped = false; - this.surrogate_high = ''; - this.userScrolling = false; - this.inputHandler = new InputHandler_1.InputHandler(this); - this.parser = new Parser_1.Parser(this.inputHandler, this); - this.renderer = this.renderer || null; - this.selectionManager = this.selectionManager || null; - this.linkifier = this.linkifier || new Linkifier_1.Linkifier(this); - this._mouseZoneManager = this._mouseZoneManager || null; - this.buffers = new BufferSet_1.BufferSet(this); - this.buffer = this.buffers.active; - this.buffers.on('activate', function (buffer) { - _this.buffer = buffer; - }); - if (this.selectionManager) { - this.selectionManager.setBuffer(this.buffer); - } - }; - Terminal.prototype.eraseAttr = function () { - return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); - }; - Terminal.prototype.focus = function () { - this.textarea.focus(); - }; - Object.defineProperty(Terminal.prototype, "isFocused", { - get: function () { - return document.activeElement === this.textarea; - }, - enumerable: true, - configurable: true - }); - Terminal.prototype.getOption = function (key) { - if (!(key in DEFAULT_OPTIONS)) { - throw new Error('No option with key "' + key + '"'); - } - if (typeof this.options[key] !== 'undefined') { - return this.options[key]; - } - return this[key]; - }; - Terminal.prototype.setOption = function (key, value) { - if (!(key in DEFAULT_OPTIONS)) { - throw new Error('No option with key "' + key + '"'); - } - switch (key) { - case 'bellStyle': - if (!value) { - value = 'none'; - } - break; - case 'cursorStyle': - if (!value) { - value = 'block'; - } - break; - case 'lineHeight': - if (value < 1) { - console.warn(key + " cannot be less than 1, value: " + value); - return; - } - case 'tabStopWidth': - if (value < 1) { - console.warn(key + " cannot be less than 1, value: " + value); - return; - } - break; - case 'theme': - if (this.renderer) { - this._setTheme(value); - return; - } - break; - case 'scrollback': - value = Math.min(value, Buffer_1.MAX_BUFFER_SIZE); - if (value < 0) { - console.warn(key + " cannot be less than 0, value: " + value); - return; - } - if (this.options[key] !== value) { - var newBufferLength = this.rows + value; - if (this.buffer.lines.length > newBufferLength) { - var amountToTrim = this.buffer.lines.length - newBufferLength; - var needsRefresh = (this.buffer.ydisp - amountToTrim < 0); - this.buffer.lines.trimStart(amountToTrim); - this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0); - this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0); - if (needsRefresh) { - this.refresh(0, this.rows - 1); - } - } - } - break; - } - this[key] = value; - this.options[key] = value; - switch (key) { - case 'fontFamily': - case 'fontSize': - this.renderer.clear(); - this.charMeasure.measure(this.options); - break; - case 'enableBold': - case 'letterSpacing': - case 'lineHeight': - this.renderer.clear(); - this.renderer.onResize(this.cols, this.rows, false); - this.refresh(0, this.rows - 1); - case 'scrollback': - this.buffers.resize(this.cols, this.rows); - this.viewport.syncScrollArea(); - break; - case 'tabStopWidth': - this.buffers.setupTabStops(); - break; - case 'bellSound': - case 'bellStyle': - this.syncBellSound(); - break; - } - if (this.renderer) { - this.renderer.onOptionsChanged(); - } - }; - Terminal.prototype._onTextAreaFocus = function () { - if (this.sendFocus) { - this.send(EscapeSequences_1.C0.ESC + '[I'); - } - this.element.classList.add('focus'); - this.showCursor(); - this.emit('focus'); - }; - ; - Terminal.prototype.blur = function () { - return this.textarea.blur(); - }; - Terminal.prototype._onTextAreaBlur = function () { - this.refresh(this.buffer.y, this.buffer.y); - if (this.sendFocus) { - this.send(EscapeSequences_1.C0.ESC + '[O'); - } - this.element.classList.remove('focus'); - this.emit('blur'); - }; - Terminal.prototype.initGlobal = function () { - var _this = this; - this.bindKeys(); - on(this.element, 'copy', function (event) { - if (!_this.hasSelection()) { - return; - } - Clipboard_1.copyHandler(event, _this, _this.selectionManager); - }); - var pasteHandlerWrapper = function (event) { return Clipboard_1.pasteHandler(event, _this); }; - on(this.textarea, 'paste', pasteHandlerWrapper); - on(this.element, 'paste', pasteHandlerWrapper); - if (Browser.isFirefox) { - on(this.element, 'mousedown', function (event) { - if (event.button === 2) { - Clipboard_1.rightClickHandler(event, _this.textarea, _this.selectionManager); - } - }); - } - else { - on(this.element, 'contextmenu', function (event) { - Clipboard_1.rightClickHandler(event, _this.textarea, _this.selectionManager); - }); - } - if (Browser.isLinux) { - on(this.element, 'auxclick', function (event) { - if (event.button === 1) { - Clipboard_1.moveTextAreaUnderMouseCursor(event, _this.textarea); - } - }); - } - }; - Terminal.prototype.bindKeys = function () { - var _this = this; - var self = this; - on(this.element, 'keydown', function (ev) { - if (document.activeElement !== this) { - return; - } - self._keyDown(ev); - }, true); - on(this.element, 'keypress', function (ev) { - if (document.activeElement !== this) { - return; - } - self._keyPress(ev); - }, true); - on(this.element, 'keyup', function (ev) { - if (!wasMondifierKeyOnlyEvent(ev)) { - _this.focus(); - } - }, true); - on(this.textarea, 'keydown', function (ev) { - _this._keyDown(ev); - }, true); - on(this.textarea, 'keypress', function (ev) { - _this._keyPress(ev); - _this.textarea.value = ''; - }, true); - on(this.textarea, 'compositionstart', function () { return _this.compositionHelper.compositionstart(); }); - on(this.textarea, 'compositionupdate', function (e) { return _this.compositionHelper.compositionupdate(e); }); - on(this.textarea, 'compositionend', function () { return _this.compositionHelper.compositionend(); }); - this.on('refresh', function () { return _this.compositionHelper.updateCompositionElements(); }); - this.on('refresh', function (data) { return _this.queueLinkification(data.start, data.end); }); - }; - Terminal.prototype.open = function (parent) { - var _this = this; - var i = 0; - var div; - this.parent = parent || this.parent; - if (!this.parent) { - throw new Error('Terminal requires a parent element.'); - } - this.context = this.parent.ownerDocument.defaultView; - this.document = this.parent.ownerDocument; - this.body = this.document.body; - CharAtlas_1.initialize(this.document); - this.element = this.document.createElement('div'); - this.element.classList.add('terminal'); - this.element.classList.add('xterm'); - this.element.setAttribute('tabindex', '0'); - this.viewportElement = document.createElement('div'); - this.viewportElement.classList.add('xterm-viewport'); - this.element.appendChild(this.viewportElement); - this.viewportScrollArea = document.createElement('div'); - this.viewportScrollArea.classList.add('xterm-scroll-area'); - this.viewportElement.appendChild(this.viewportScrollArea); - this.syncBellSound(); - this._mouseZoneManager = new MouseZoneManager_1.MouseZoneManager(this); - this.on('scroll', function () { return _this._mouseZoneManager.clearAll(); }); - this.linkifier.attachToDom(this._mouseZoneManager); - this.helperContainer = document.createElement('div'); - this.helperContainer.classList.add('xterm-helpers'); - this.element.appendChild(this.helperContainer); - this.textarea = document.createElement('textarea'); - this.textarea.classList.add('xterm-helper-textarea'); - this.textarea.setAttribute('autocorrect', 'off'); - this.textarea.setAttribute('autocapitalize', 'off'); - this.textarea.setAttribute('spellcheck', 'false'); - this.textarea.tabIndex = 0; - this.textarea.addEventListener('focus', function () { return _this._onTextAreaFocus(); }); - this.textarea.addEventListener('blur', function () { return _this._onTextAreaBlur(); }); - this.helperContainer.appendChild(this.textarea); - this.compositionView = document.createElement('div'); - this.compositionView.classList.add('composition-view'); - this.compositionHelper = new CompositionHelper_1.CompositionHelper(this.textarea, this.compositionView, this); - this.helperContainer.appendChild(this.compositionView); - this.charSizeStyleElement = document.createElement('style'); - this.helperContainer.appendChild(this.charSizeStyleElement); - this.parent.appendChild(this.element); - this.charMeasure = new CharMeasure_1.CharMeasure(document, this.helperContainer); - this.renderer = new Renderer_1.Renderer(this, this.options.theme); - this.options.theme = null; - this.viewport = new Viewport_1.Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure); - this.viewport.onThemeChanged(this.renderer.colorManager.colors); - this.on('cursormove', function () { return _this.renderer.onCursorMove(); }); - this.on('resize', function () { return _this.renderer.onResize(_this.cols, _this.rows, false); }); - this.on('blur', function () { return _this.renderer.onBlur(); }); - this.on('focus', function () { return _this.renderer.onFocus(); }); - window.addEventListener('resize', function () { return _this.renderer.onWindowResize(window.devicePixelRatio); }); - this.charMeasure.on('charsizechanged', function () { return _this.renderer.onResize(_this.cols, _this.rows, true); }); - this.renderer.on('resize', function (dimensions) { return _this.viewport.syncScrollArea(); }); - this.selectionManager = new SelectionManager_1.SelectionManager(this, this.buffer, this.charMeasure); - this.element.addEventListener('mousedown', function (e) { return _this.selectionManager.onMouseDown(e); }); - this.selectionManager.on('refresh', function (data) { return _this.renderer.onSelectionChanged(data.start, data.end); }); - this.selectionManager.on('newselection', function (text) { - _this.textarea.value = text; - _this.textarea.focus(); - _this.textarea.select(); - }); - this.on('scroll', function () { - _this.viewport.syncScrollArea(); - _this.selectionManager.refresh(); - }); - this.viewportElement.addEventListener('scroll', function () { return _this.selectionManager.refresh(); }); - this.mouseHelper = new MouseHelper_1.MouseHelper(this.renderer); - this.charMeasure.measure(this.options); - this.refresh(0, this.rows - 1); - this.initGlobal(); - this.bindMouse(); - }; - Terminal.prototype._setTheme = function (theme) { - var colors = this.renderer.setTheme(theme); - if (this.viewport) { - this.viewport.onThemeChanged(colors); - } - }; - Terminal.loadAddon = function (addon, callback) { - if (typeof exports === 'object' && typeof module === 'object') { - return require('./addons/' + addon + '/' + addon); - } - else if (typeof define === 'function') { - return require(['./addons/' + addon + '/' + addon], callback); - } - else { - console.error('Cannot load a module without a CommonJS or RequireJS environment.'); - return false; - } - }; - Terminal.prototype.bindMouse = function () { - var _this = this; - var el = this.element; - var self = this; - var pressed = 32; - function sendButton(ev) { - var button; - var pos; - button = getButton(ev); - pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows); - if (!pos) - return; - sendEvent(button, pos); - switch (ev.overrideType || ev.type) { - case 'mousedown': - pressed = button; - break; - case 'mouseup': - pressed = 32; - break; - case 'wheel': - break; - } - } - function sendMove(ev) { - var button = pressed; - var pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows); - if (!pos) - return; - button += 32; - sendEvent(button, pos); - } - function encode(data, ch) { - if (!self.utfMouse) { - if (ch === 255) { - data.push(0); - return; - } - if (ch > 127) - ch = 127; - data.push(ch); - } - else { - if (ch === 2047) { - data.push(0); - return; - } - if (ch < 127) { - data.push(ch); - } - else { - if (ch > 2047) - ch = 2047; - data.push(0xC0 | (ch >> 6)); - data.push(0x80 | (ch & 0x3F)); - } - } - } - function sendEvent(button, pos) { - if (self.vt300Mouse) { - button &= 3; - pos.x -= 32; - pos.y -= 32; - var data_1 = EscapeSequences_1.C0.ESC + '[24'; - if (button === 0) - data_1 += '1'; - else if (button === 1) - data_1 += '3'; - else if (button === 2) - data_1 += '5'; - else if (button === 3) - return; - else - data_1 += '0'; - data_1 += '~[' + pos.x + ',' + pos.y + ']\r'; - self.send(data_1); - return; - } - if (self.decLocator) { - button &= 3; - pos.x -= 32; - pos.y -= 32; - if (button === 0) - button = 2; - else if (button === 1) - button = 4; - else if (button === 2) - button = 6; - else if (button === 3) - button = 3; - self.send(EscapeSequences_1.C0.ESC + '[' - + button - + ';' - + (button === 3 ? 4 : 0) - + ';' - + pos.y - + ';' - + pos.x - + ';' - + pos.page || 0 - + '&w'); - return; - } - if (self.urxvtMouse) { - pos.x -= 32; - pos.y -= 32; - pos.x++; - pos.y++; - self.send(EscapeSequences_1.C0.ESC + '[' + button + ';' + pos.x + ';' + pos.y + 'M'); - return; - } - if (self.sgrMouse) { - pos.x -= 32; - pos.y -= 32; - self.send(EscapeSequences_1.C0.ESC + '[<' - + (((button & 3) === 3 ? button & ~3 : button) - 32) - + ';' - + pos.x - + ';' - + pos.y - + ((button & 3) === 3 ? 'm' : 'M')); - return; - } - var data = []; - encode(data, button); - encode(data, pos.x); - encode(data, pos.y); - self.send(EscapeSequences_1.C0.ESC + '[M' + String.fromCharCode.apply(String, data)); - } - function getButton(ev) { - var button; - var shift; - var meta; - var ctrl; - var mod; - switch (ev.overrideType || ev.type) { - case 'mousedown': - button = ev.button != null - ? +ev.button - : ev.which != null - ? ev.which - 1 - : null; - if (Browser.isMSIE) { - button = button === 1 ? 0 : button === 4 ? 1 : button; - } - break; - case 'mouseup': - button = 3; - break; - case 'DOMMouseScroll': - button = ev.detail < 0 - ? 64 - : 65; - break; - case 'wheel': - button = ev.wheelDeltaY > 0 - ? 64 - : 65; - break; - } - shift = ev.shiftKey ? 4 : 0; - meta = ev.metaKey ? 8 : 0; - ctrl = ev.ctrlKey ? 16 : 0; - mod = shift | meta | ctrl; - if (self.vt200Mouse) { - mod &= ctrl; - } - else if (!self.normalMouse) { - mod = 0; - } - button = (32 + (mod << 2)) + button; - return button; - } - on(el, 'mousedown', function (ev) { - ev.preventDefault(); - _this.focus(); - if (!_this.mouseEvents || _this.selectionManager.shouldForceSelection(ev)) { - return; - } - sendButton(ev); - if (_this.vt200Mouse) { - ev.overrideType = 'mouseup'; - sendButton(ev); - return _this.cancel(ev); - } - if (_this.normalMouse) - on(_this.document, 'mousemove', sendMove); - if (!_this.x10Mouse) { - var handler_1 = function (ev) { - sendButton(ev); - if (_this.normalMouse) - off(_this.document, 'mousemove', sendMove); - off(_this.document, 'mouseup', handler_1); - return _this.cancel(ev); - }; - on(_this.document, 'mouseup', handler_1); - } - return _this.cancel(ev); - }); - on(el, 'wheel', function (ev) { - if (!_this.mouseEvents) - return; - if (_this.x10Mouse || _this.vt300Mouse || _this.decLocator) - return; - sendButton(ev); - ev.preventDefault(); - }); - on(el, 'wheel', function (ev) { - if (_this.mouseEvents) - return; - _this.viewport.onWheel(ev); - return _this.cancel(ev); - }); - on(el, 'touchstart', function (ev) { - if (_this.mouseEvents) - return; - _this.viewport.onTouchStart(ev); - return _this.cancel(ev); - }); - on(el, 'touchmove', function (ev) { - if (_this.mouseEvents) - return; - _this.viewport.onTouchMove(ev); - return _this.cancel(ev); - }); - }; - Terminal.prototype.destroy = function () { - _super.prototype.destroy.call(this); - this.readable = false; - this.writable = false; - this.handler = function () { }; - this.write = function () { }; - if (this.element && this.element.parentNode) { - this.element.parentNode.removeChild(this.element); - } - }; - Terminal.prototype.refresh = function (start, end) { - if (this.renderer) { - this.renderer.queueRefresh(start, end); - } - }; - Terminal.prototype.queueLinkification = function (start, end) { - if (this.linkifier) { - this.linkifier.linkifyRows(start, end); - } - }; - Terminal.prototype.showCursor = function () { - if (!this.cursorState) { - this.cursorState = 1; - this.refresh(this.buffer.y, this.buffer.y); - } - }; - Terminal.prototype.scroll = function (isWrapped) { - var newLine = this.blankLine(undefined, isWrapped); - var topRow = this.buffer.ybase + this.buffer.scrollTop; - var bottomRow = this.buffer.ybase + this.buffer.scrollBottom; - if (this.buffer.scrollTop === 0) { - var willBufferBeTrimmed = this.buffer.lines.length === this.buffer.lines.maxLength; - if (bottomRow === this.buffer.lines.length - 1) { - this.buffer.lines.push(newLine); - } - else { - this.buffer.lines.splice(bottomRow + 1, 0, newLine); - } - if (!willBufferBeTrimmed) { - this.buffer.ybase++; - if (!this.userScrolling) { - this.buffer.ydisp++; - } - } - else { - if (this.userScrolling) { - this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0); - } - } - } - else { - var scrollRegionHeight = bottomRow - topRow + 1; - this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); - this.buffer.lines.set(bottomRow, newLine); - } - if (!this.userScrolling) { - this.buffer.ydisp = this.buffer.ybase; - } - this.updateRange(this.buffer.scrollTop); - this.updateRange(this.buffer.scrollBottom); - this.emit('scroll', this.buffer.ydisp); - }; - Terminal.prototype.scrollLines = function (disp, suppressScrollEvent) { - if (disp < 0) { - if (this.buffer.ydisp === 0) { - return; - } - this.userScrolling = true; - } - else if (disp + this.buffer.ydisp >= this.buffer.ybase) { - this.userScrolling = false; - } - var oldYdisp = this.buffer.ydisp; - this.buffer.ydisp = Math.max(Math.min(this.buffer.ydisp + disp, this.buffer.ybase), 0); - if (oldYdisp === this.buffer.ydisp) { - return; - } - if (!suppressScrollEvent) { - this.emit('scroll', this.buffer.ydisp); - } - this.refresh(0, this.rows - 1); - }; - Terminal.prototype.scrollPages = function (pageCount) { - this.scrollLines(pageCount * (this.rows - 1)); - }; - Terminal.prototype.scrollToTop = function () { - this.scrollLines(-this.buffer.ydisp); - }; - Terminal.prototype.scrollToBottom = function () { - this.scrollLines(this.buffer.ybase - this.buffer.ydisp); - }; - Terminal.prototype.write = function (data) { - var _this = this; - this.writeBuffer.push(data); - if (this.options.useFlowControl && !this.xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) { - this.send(EscapeSequences_1.C0.DC3); - this.xoffSentToCatchUp = true; - } - if (!this.writeInProgress && this.writeBuffer.length > 0) { - this.writeInProgress = true; - setTimeout(function () { - _this.innerWrite(); - }); - } - }; - Terminal.prototype.innerWrite = function () { - var _this = this; - var writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE); - while (writeBatch.length > 0) { - var data = writeBatch.shift(); - if (this.xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) { - this.send(EscapeSequences_1.C0.DC1); - this.xoffSentToCatchUp = false; - } - this.refreshStart = this.buffer.y; - this.refreshEnd = this.buffer.y; - var state = this.parser.parse(data); - this.parser.setState(state); - this.updateRange(this.buffer.y); - this.refresh(this.refreshStart, this.refreshEnd); - } - if (this.writeBuffer.length > 0) { - setTimeout(function () { return _this.innerWrite(); }, 0); - } - else { - this.writeInProgress = false; - } - }; - Terminal.prototype.writeln = function (data) { - this.write(data + '\r\n'); - }; - Terminal.prototype.attachCustomKeyEventHandler = function (customKeyEventHandler) { - this.customKeyEventHandler = customKeyEventHandler; - }; - Terminal.prototype.setHypertextLinkHandler = function (handler) { - if (!this.linkifier) { - throw new Error('Cannot attach a hypertext link handler before Terminal.open is called'); - } - this.linkifier.setHypertextLinkHandler(handler); - this.refresh(0, this.rows - 1); - }; - Terminal.prototype.setHypertextValidationCallback = function (callback) { - if (!this.linkifier) { - throw new Error('Cannot attach a hypertext validation callback before Terminal.open is called'); - } - this.linkifier.setHypertextValidationCallback(callback); - this.refresh(0, this.rows - 1); - }; - Terminal.prototype.registerLinkMatcher = function (regex, handler, options) { - if (this.linkifier) { - var matcherId = this.linkifier.registerLinkMatcher(regex, handler, options); - this.refresh(0, this.rows - 1); - return matcherId; - } - return 0; - }; - Terminal.prototype.deregisterLinkMatcher = function (matcherId) { - if (this.linkifier) { - if (this.linkifier.deregisterLinkMatcher(matcherId)) { - this.refresh(0, this.rows - 1); - } - } - }; - Terminal.prototype.hasSelection = function () { - return this.selectionManager ? this.selectionManager.hasSelection : false; - }; - Terminal.prototype.getSelection = function () { - return this.selectionManager ? this.selectionManager.selectionText : ''; - }; - Terminal.prototype.clearSelection = function () { - if (this.selectionManager) { - this.selectionManager.clearSelection(); - } - }; - Terminal.prototype.selectAll = function () { - if (this.selectionManager) { - this.selectionManager.selectAll(); - } - }; - Terminal.prototype._keyDown = function (ev) { - if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { - return false; - } - if (!this.compositionHelper.keydown(ev)) { - if (this.buffer.ybase !== this.buffer.ydisp) { - this.scrollToBottom(); - } - return false; - } - var result = this._evaluateKeyEscapeSequence(ev); - if (result.key === EscapeSequences_1.C0.DC3) { - this.writeStopped = true; - } - else if (result.key === EscapeSequences_1.C0.DC1) { - this.writeStopped = false; - } - if (result.scrollLines) { - this.scrollLines(result.scrollLines); - return this.cancel(ev, true); - } - if (isThirdLevelShift(this.browser, ev)) { - return true; - } - if (result.cancel) { - this.cancel(ev, true); - } - if (!result.key) { - return true; - } - this.emit('keydown', ev); - this.emit('key', result.key, ev); - this.showCursor(); - this.handler(result.key); - return this.cancel(ev, true); - }; - Terminal.prototype._evaluateKeyEscapeSequence = function (ev) { - var result = { - cancel: false, - key: undefined, - scrollLines: undefined - }; - var modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); - switch (ev.keyCode) { - case 0: - if (ev.key === 'UIKeyInputUpArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OA'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[A'; - } - } - else if (ev.key === 'UIKeyInputLeftArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OD'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[D'; - } - } - else if (ev.key === 'UIKeyInputRightArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OC'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[C'; - } - } - else if (ev.key === 'UIKeyInputDownArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OB'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[B'; - } - } - break; - case 8: - if (ev.shiftKey) { - result.key = EscapeSequences_1.C0.BS; - break; - } - result.key = EscapeSequences_1.C0.DEL; - break; - case 9: - if (ev.shiftKey) { - result.key = EscapeSequences_1.C0.ESC + '[Z'; - break; - } - result.key = EscapeSequences_1.C0.HT; - result.cancel = true; - break; - case 13: - result.key = EscapeSequences_1.C0.CR; - result.cancel = true; - break; - case 27: - result.key = EscapeSequences_1.C0.ESC; - result.cancel = true; - break; - case 37: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'D'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3D') { - result.key = (this.browser.isMac) ? EscapeSequences_1.C0.ESC + 'b' : EscapeSequences_1.C0.ESC + '[1;5D'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OD'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[D'; - } - break; - case 39: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'C'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3C') { - result.key = (this.browser.isMac) ? EscapeSequences_1.C0.ESC + 'f' : EscapeSequences_1.C0.ESC + '[1;5C'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OC'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[C'; - } - break; - case 38: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'A'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3A') { - result.key = EscapeSequences_1.C0.ESC + '[1;5A'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OA'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[A'; - } - break; - case 40: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'B'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3B') { - result.key = EscapeSequences_1.C0.ESC + '[1;5B'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OB'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[B'; - } - break; - case 45: - if (!ev.shiftKey && !ev.ctrlKey) { - result.key = EscapeSequences_1.C0.ESC + '[2~'; - } - break; - case 46: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[3;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[3~'; - } - break; - case 36: - if (modifiers) - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'H'; - else if (this.applicationCursor) - result.key = EscapeSequences_1.C0.ESC + 'OH'; - else - result.key = EscapeSequences_1.C0.ESC + '[H'; - break; - case 35: - if (modifiers) - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'F'; - else if (this.applicationCursor) - result.key = EscapeSequences_1.C0.ESC + 'OF'; - else - result.key = EscapeSequences_1.C0.ESC + '[F'; - break; - case 33: - if (ev.shiftKey) { - result.scrollLines = -(this.rows - 1); - } - else { - result.key = EscapeSequences_1.C0.ESC + '[5~'; - } - break; - case 34: - if (ev.shiftKey) { - result.scrollLines = this.rows - 1; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[6~'; - } - break; - case 112: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'P'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OP'; - } - break; - case 113: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'Q'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OQ'; - } - break; - case 114: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'R'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OR'; - } - break; - case 115: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'S'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OS'; - } - break; - case 116: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[15;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[15~'; - } - break; - case 117: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[17;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[17~'; - } - break; - case 118: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[18;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[18~'; - } - break; - case 119: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[19;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[19~'; - } - break; - case 120: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[20;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[20~'; - } - break; - case 121: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[21;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[21~'; - } - break; - case 122: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[23;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[23~'; - } - break; - case 123: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[24;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[24~'; - } - break; - default: - if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { - if (ev.keyCode >= 65 && ev.keyCode <= 90) { - result.key = String.fromCharCode(ev.keyCode - 64); - } - else if (ev.keyCode === 32) { - result.key = String.fromCharCode(0); - } - else if (ev.keyCode >= 51 && ev.keyCode <= 55) { - result.key = String.fromCharCode(ev.keyCode - 51 + 27); - } - else if (ev.keyCode === 56) { - result.key = String.fromCharCode(127); - } - else if (ev.keyCode === 219) { - result.key = String.fromCharCode(27); - } - else if (ev.keyCode === 220) { - result.key = String.fromCharCode(28); - } - else if (ev.keyCode === 221) { - result.key = String.fromCharCode(29); - } - } - else if (!this.browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) { - if (ev.keyCode >= 65 && ev.keyCode <= 90) { - result.key = EscapeSequences_1.C0.ESC + String.fromCharCode(ev.keyCode + 32); - } - else if (ev.keyCode === 192) { - result.key = EscapeSequences_1.C0.ESC + '`'; - } - else if (ev.keyCode >= 48 && ev.keyCode <= 57) { - result.key = EscapeSequences_1.C0.ESC + (ev.keyCode - 48); - } - } - else if (this.browser.isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { - if (ev.keyCode === 65) { - this.selectAll(); - } - } - break; - } - return result; - }; - Terminal.prototype.setgLevel = function (g) { - this.glevel = g; - this.charset = this.charsets[g]; - }; - Terminal.prototype.setgCharset = function (g, charset) { - this.charsets[g] = charset; - if (this.glevel === g) { - this.charset = charset; - } - }; - Terminal.prototype._keyPress = function (ev) { - var key; - if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { - return false; - } - this.cancel(ev); - if (ev.charCode) { - key = ev.charCode; - } - else if (ev.which == null) { - key = ev.keyCode; - } - else if (ev.which !== 0 && ev.charCode !== 0) { - key = ev.which; - } - else { - return false; - } - if (!key || ((ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this.browser, ev))) { - return false; - } - key = String.fromCharCode(key); - this.emit('keypress', key, ev); - this.emit('key', key, ev); - this.showCursor(); - this.handler(key); - return true; - }; - Terminal.prototype.send = function (data) { - var _this = this; - if (!this.sendDataQueue) { - setTimeout(function () { - _this.handler(_this.sendDataQueue); - _this.sendDataQueue = ''; - }, 1); - } - this.sendDataQueue += data; - }; - Terminal.prototype.bell = function () { - var _this = this; - this.emit('bell'); - if (this.soundBell()) - this.bellAudioElement.play(); - if (this.visualBell()) { - this.element.classList.add('visual-bell-active'); - clearTimeout(this.visualBellTimer); - this.visualBellTimer = window.setTimeout(function () { - _this.element.classList.remove('visual-bell-active'); - }, 200); - } - }; - Terminal.prototype.log = function (text, data) { - if (!this.options.debug) - return; - if (!this.context.console || !this.context.console.log) - return; - this.context.console.log(text, data); - }; - Terminal.prototype.error = function (text, data) { - if (!this.options.debug) - return; - if (!this.context.console || !this.context.console.error) - return; - this.context.console.error(text, data); - }; - Terminal.prototype.resize = function (x, y) { - if (isNaN(x) || isNaN(y)) { - return; - } - if (x === this.cols && y === this.rows) { - if (!this.charMeasure.width || !this.charMeasure.height) { - this.charMeasure.measure(this.options); - } - return; - } - if (x < 1) - x = 1; - if (y < 1) - y = 1; - this.buffers.resize(x, y); - this.cols = x; - this.rows = y; - this.buffers.setupTabStops(this.cols); - this.charMeasure.measure(this.options); - this.refresh(0, this.rows - 1); - this.geometry = [this.cols, this.rows]; - this.emit('resize', { cols: x, rows: y }); - }; - Terminal.prototype.updateRange = function (y) { - if (y < this.refreshStart) - this.refreshStart = y; - if (y > this.refreshEnd) - this.refreshEnd = y; - }; - Terminal.prototype.maxRange = function () { - this.refreshStart = 0; - this.refreshEnd = this.rows - 1; - }; - Terminal.prototype.eraseRight = function (x, y) { - var line = this.buffer.lines.get(this.buffer.ybase + y); - if (!line) { - return; - } - var ch = [this.eraseAttr(), ' ', 1, 32]; - for (; x < this.cols; x++) { - line[x] = ch; - } - this.updateRange(y); - }; - Terminal.prototype.eraseLeft = function (x, y) { - var line = this.buffer.lines.get(this.buffer.ybase + y); - if (!line) { - return; - } - var ch = [this.eraseAttr(), ' ', 1, 32]; - x++; - while (x--) { - line[x] = ch; - } - this.updateRange(y); - }; - Terminal.prototype.clear = function () { - if (this.buffer.ybase === 0 && this.buffer.y === 0) { - return; - } - this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)); - this.buffer.lines.length = 1; - this.buffer.ydisp = 0; - this.buffer.ybase = 0; - this.buffer.y = 0; - for (var i = 1; i < this.rows; i++) { - this.buffer.lines.push(this.blankLine()); - } - this.refresh(0, this.rows - 1); - this.emit('scroll', this.buffer.ydisp); - }; - Terminal.prototype.eraseLine = function (y) { - this.eraseRight(0, y); - }; - Terminal.prototype.blankLine = function (cur, isWrapped, cols) { - var attr = cur ? this.eraseAttr() : this.defAttr; - var ch = [attr, ' ', 1, 32]; - var line = []; - if (isWrapped) { - line.isWrapped = isWrapped; - } - cols = cols || this.cols; - for (var i = 0; i < cols; i++) { - line[i] = ch; - } - return line; - }; - Terminal.prototype.ch = function (cur) { - if (cur) { - return [this.eraseAttr(), ' ', 1, 32]; - } - return [this.defAttr, ' ', 1, 32]; - }; - Terminal.prototype.is = function (term) { - return (this.options.termName + '').indexOf(term) === 0; - }; - Terminal.prototype.handler = function (data) { - if (this.options.disableStdin) { - return; - } - if (this.selectionManager && this.selectionManager.hasSelection) { - this.selectionManager.clearSelection(); - } - if (this.buffer.ybase !== this.buffer.ydisp) { - this.scrollToBottom(); - } - this.emit('data', data); - }; - Terminal.prototype.handleTitle = function (title) { - this.emit('title', title); - }; - Terminal.prototype.index = function () { - this.buffer.y++; - if (this.buffer.y > this.buffer.scrollBottom) { - this.buffer.y--; - this.scroll(); - } - if (this.buffer.x >= this.cols) { - this.buffer.x--; - } - }; - Terminal.prototype.reverseIndex = function () { - if (this.buffer.y === this.buffer.scrollTop) { - var scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop; - this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1); - this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.blankLine(true)); - this.updateRange(this.buffer.scrollTop); - this.updateRange(this.buffer.scrollBottom); - } - else { - this.buffer.y--; - } - }; - Terminal.prototype.reset = function () { - this.options.rows = this.rows; - this.options.cols = this.cols; - var customKeyEventHandler = this.customKeyEventHandler; - var inputHandler = this.inputHandler; - var buffers = this.buffers; - this.setup(); - this.customKeyEventHandler = customKeyEventHandler; - this.inputHandler = inputHandler; - this.buffers = buffers; - this.refresh(0, this.rows - 1); - this.viewport.syncScrollArea(); - }; - Terminal.prototype.tabSet = function () { - this.buffer.tabs[this.buffer.x] = true; - }; - Terminal.prototype.cancel = function (ev, force) { - if (!this.options.cancelEvents && !force) { - return; - } - ev.preventDefault(); - ev.stopPropagation(); - return false; - }; - Terminal.prototype.matchColor = function (r1, g1, b1) { - return matchColor_(r1, g1, b1); - }; - Terminal.prototype.visualBell = function () { - return this.options.bellStyle === 'visual' || - this.options.bellStyle === 'both'; - }; - Terminal.prototype.soundBell = function () { - return this.options.bellStyle === 'sound' || - this.options.bellStyle === 'both'; - }; - Terminal.prototype.syncBellSound = function () { - if (this.soundBell() && this.bellAudioElement) { - this.bellAudioElement.setAttribute('src', this.options.bellSound); - } - else if (this.soundBell()) { - this.bellAudioElement = document.createElement('audio'); - this.bellAudioElement.setAttribute('preload', 'auto'); - this.bellAudioElement.setAttribute('src', this.options.bellSound); - this.helperContainer.appendChild(this.bellAudioElement); - } - else if (this.bellAudioElement) { - this.helperContainer.removeChild(this.bellAudioElement); - } - }; - return Terminal; -}(EventEmitter_1.EventEmitter)); -exports.Terminal = Terminal; -function globalOn(el, type, handler, capture) { - if (!Array.isArray(el)) { - el = [el]; - } - el.forEach(function (element) { - element.addEventListener(type, handler, capture || false); - }); -} -var on = globalOn; -function off(el, type, handler, capture) { - if (capture === void 0) { capture = false; } - el.removeEventListener(type, handler, capture); -} -function isThirdLevelShift(browser, ev) { - var thirdLevelKey = (browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) || - (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); - if (ev.type === 'keypress') { - return thirdLevelKey; - } - return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47); -} -function wasMondifierKeyOnlyEvent(ev) { - return ev.keyCode === 16 || - ev.keyCode === 17 || - ev.keyCode === 18; -} -var vcolors = (function () { - var result = ColorManager_1.DEFAULT_ANSI_COLORS.map(function (c) { - c = c.substring(1); - return [ - parseInt(c.substring(0, 2), 16), - parseInt(c.substring(2, 4), 16), - parseInt(c.substring(4, 6), 16) - ]; - }); - var r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; - for (var i = 0; i < 216; i++) { - result.push([ - r[(i / 36) % 6 | 0], - r[(i / 6) % 6 | 0], - r[i % 6] - ]); - } - var c; - for (var i = 0; i < 24; i++) { - c = 8 + i * 10; - result.push([c, c, c]); - } - return result; -})(); -var matchColorCache = {}; -function matchColorDistance(r1, g1, b1, r2, g2, b2) { - return Math.pow(30 * (r1 - r2), 2) - + Math.pow(59 * (g1 - g2), 2) - + Math.pow(11 * (b1 - b2), 2); -} -; -function matchColor_(r1, g1, b1) { - var hash = (r1 << 16) | (g1 << 8) | b1; - if (matchColorCache[hash] != null) { - return matchColorCache[hash]; - } - var ldiff = Infinity; - var li = -1; - var i = 0; - var c; - var r2; - var g2; - var b2; - var diff; - for (; i < vcolors.length; i++) { - c = vcolors[i]; - r2 = c[0]; - g2 = c[1]; - b2 = c[2]; - diff = matchColorDistance(r1, g1, b1, r2, g2, b2); - if (diff === 0) { - li = i; - break; - } - if (diff < ldiff) { - ldiff = diff; - li = i; - } - } - return matchColorCache[hash] = li; -} - - - -},{"./Buffer":1,"./BufferSet":2,"./CompositionHelper":5,"./EscapeSequences":6,"./EventEmitter":7,"./InputHandler":8,"./Linkifier":9,"./Parser":10,"./SelectionManager":11,"./Viewport":15,"./handlers/Clipboard":16,"./input/MouseZoneManager":17,"./renderer/CharAtlas":19,"./renderer/ColorManager":20,"./renderer/Renderer":24,"./utils/Browser":28,"./utils/CharMeasure":29,"./utils/MouseHelper":32,"./utils/Sounds":33}],14:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var LinkHoverEventTypes; -(function (LinkHoverEventTypes) { - LinkHoverEventTypes["HOVER"] = "linkhover"; - LinkHoverEventTypes["TOOLTIP"] = "linktooltip"; - LinkHoverEventTypes["LEAVE"] = "linkleave"; -})(LinkHoverEventTypes = exports.LinkHoverEventTypes || (exports.LinkHoverEventTypes = {})); -; - - - -},{}],15:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Viewport = (function () { - function Viewport(terminal, viewportElement, scrollArea, charMeasure) { - var _this = this; - this.terminal = terminal; - this.viewportElement = viewportElement; - this.scrollArea = scrollArea; - this.charMeasure = charMeasure; - this.currentRowHeight = 0; - this.lastRecordedBufferLength = 0; - this.lastRecordedViewportHeight = 0; - this.lastRecordedBufferHeight = 0; - this.viewportElement.addEventListener('scroll', this.onScroll.bind(this)); - setTimeout(function () { return _this.syncScrollArea(); }, 0); - } - Viewport.prototype.onThemeChanged = function (colors) { - this.viewportElement.style.backgroundColor = colors.background; - }; - Viewport.prototype.refresh = function () { - if (this.charMeasure.height > 0) { - this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio; - if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) { - this.lastRecordedViewportHeight = this.terminal.renderer.dimensions.canvasHeight; - this.viewportElement.style.height = this.lastRecordedViewportHeight + 'px'; - } - var newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength); - if (this.lastRecordedBufferHeight !== newBufferHeight) { - this.lastRecordedBufferHeight = newBufferHeight; - this.scrollArea.style.height = this.lastRecordedBufferHeight + 'px'; - } - } - }; - Viewport.prototype.syncScrollArea = function () { - if (this.lastRecordedBufferLength !== this.terminal.buffer.lines.length) { - this.lastRecordedBufferLength = this.terminal.buffer.lines.length; - this.refresh(); - } - else if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) { - this.refresh(); - } - else { - if (this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this.currentRowHeight) { - this.refresh(); - } - } - var scrollTop = this.terminal.buffer.ydisp * this.currentRowHeight; - if (this.viewportElement.scrollTop !== scrollTop) { - this.viewportElement.scrollTop = scrollTop; - } - }; - Viewport.prototype.onScroll = function (ev) { - var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight); - var diff = newRow - this.terminal.buffer.ydisp; - this.terminal.scrollLines(diff, true); - }; - Viewport.prototype.onWheel = function (ev) { - if (ev.deltaY === 0) { - return; - } - var multiplier = 1; - if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { - multiplier = this.currentRowHeight; - } - else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { - multiplier = this.currentRowHeight * this.terminal.rows; - } - this.viewportElement.scrollTop += ev.deltaY * multiplier; - ev.preventDefault(); - }; - ; - Viewport.prototype.onTouchStart = function (ev) { - this.lastTouchY = ev.touches[0].pageY; - }; - ; - Viewport.prototype.onTouchMove = function (ev) { - var deltaY = this.lastTouchY - ev.touches[0].pageY; - this.lastTouchY = ev.touches[0].pageY; - if (deltaY === 0) { - return; - } - this.viewportElement.scrollTop += deltaY; - ev.preventDefault(); - }; - ; - return Viewport; -}()); -exports.Viewport = Viewport; - - - -},{}],16:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -function prepareTextForTerminal(text, isMSWindows) { - if (isMSWindows) { - return text.replace(/\r?\n/g, '\r'); - } - return text; -} -exports.prepareTextForTerminal = prepareTextForTerminal; -function bracketTextForPaste(text, bracketedPasteMode) { - if (bracketedPasteMode) { - return '\x1b[200~' + text + '\x1b[201~'; - } - return text; -} -exports.bracketTextForPaste = bracketTextForPaste; -function copyHandler(ev, term, selectionManager) { - if (term.browser.isMSIE) { - window.clipboardData.setData('Text', selectionManager.selectionText); - } - else { - ev.clipboardData.setData('text/plain', selectionManager.selectionText); - } - ev.preventDefault(); -} -exports.copyHandler = copyHandler; -function pasteHandler(ev, term) { - ev.stopPropagation(); - var text; - var dispatchPaste = function (text) { - text = prepareTextForTerminal(text, term.browser.isMSWindows); - text = bracketTextForPaste(text, term.bracketedPasteMode); - term.handler(text); - term.textarea.value = ''; - term.emit('paste', text); - term.cancel(ev); - }; - if (term.browser.isMSIE) { - if (window.clipboardData) { - text = window.clipboardData.getData('Text'); - dispatchPaste(text); - } - } - else { - if (ev.clipboardData) { - text = ev.clipboardData.getData('text/plain'); - dispatchPaste(text); - } - } -} -exports.pasteHandler = pasteHandler; -function moveTextAreaUnderMouseCursor(ev, textarea) { - textarea.style.position = 'fixed'; - textarea.style.width = '20px'; - textarea.style.height = '20px'; - textarea.style.left = (ev.clientX - 10) + 'px'; - textarea.style.top = (ev.clientY - 10) + 'px'; - textarea.style.zIndex = '1000'; - textarea.focus(); - setTimeout(function () { - textarea.style.position = null; - textarea.style.width = null; - textarea.style.height = null; - textarea.style.left = null; - textarea.style.top = null; - textarea.style.zIndex = null; - }, 4); -} -exports.moveTextAreaUnderMouseCursor = moveTextAreaUnderMouseCursor; -function rightClickHandler(ev, textarea, selectionManager) { - moveTextAreaUnderMouseCursor(ev, textarea); - textarea.value = selectionManager.selectionText; - textarea.select(); -} -exports.rightClickHandler = rightClickHandler; - - - -},{}],17:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var HOVER_DURATION = 500; -var MouseZoneManager = (function () { - function MouseZoneManager(_terminal) { - var _this = this; - this._terminal = _terminal; - this._zones = []; - this._areZonesActive = false; - this._tooltipTimeout = null; - this._currentZone = null; - this._lastHoverCoords = [null, null]; - this._terminal.element.addEventListener('mousedown', function (e) { return _this._onMouseDown(e); }); - this._mouseMoveListener = function (e) { return _this._onMouseMove(e); }; - this._clickListener = function (e) { return _this._onClick(e); }; - } - MouseZoneManager.prototype.add = function (zone) { - this._zones.push(zone); - if (this._zones.length === 1) { - this._activate(); - } - }; - MouseZoneManager.prototype.clearAll = function (start, end) { - if (this._zones.length === 0) { - return; - } - if (!end) { - start = 0; - end = this._terminal.rows - 1; - } - for (var i = 0; i < this._zones.length; i++) { - var zone = this._zones[i]; - if (zone.y > start && zone.y <= end + 1) { - if (this._currentZone && this._currentZone === zone) { - this._currentZone.leaveCallback(); - this._currentZone = null; - } - this._zones.splice(i--, 1); - } - } - if (this._zones.length === 0) { - this._deactivate(); - } - }; - MouseZoneManager.prototype._activate = function () { - if (!this._areZonesActive) { - this._areZonesActive = true; - this._terminal.element.addEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.addEventListener('click', this._clickListener); - } - }; - MouseZoneManager.prototype._deactivate = function () { - if (this._areZonesActive) { - this._areZonesActive = false; - this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.removeEventListener('click', this._clickListener); - } - }; - MouseZoneManager.prototype._onMouseMove = function (e) { - if (this._lastHoverCoords[0] !== e.pageX || this._lastHoverCoords[1] !== e.pageY) { - this._onHover(e); - this._lastHoverCoords = [e.pageX, e.pageY]; - } - }; - MouseZoneManager.prototype._onHover = function (e) { - var _this = this; - var zone = this._findZoneEventAt(e); - if (zone === this._currentZone) { - return; - } - if (this._currentZone) { - this._currentZone.leaveCallback(); - this._currentZone = null; - if (this._tooltipTimeout) { - clearTimeout(this._tooltipTimeout); - } - } - if (!zone) { - return; - } - this._currentZone = zone; - if (zone.hoverCallback) { - zone.hoverCallback(e); - } - this._tooltipTimeout = setTimeout(function () { return _this._onTooltip(e); }, HOVER_DURATION); - }; - MouseZoneManager.prototype._onTooltip = function (e) { - this._tooltipTimeout = null; - var zone = this._findZoneEventAt(e); - if (zone && zone.tooltipCallback) { - zone.tooltipCallback(e); - } - }; - MouseZoneManager.prototype._onMouseDown = function (e) { - if (!this._areZonesActive) { - return; - } - var zone = this._findZoneEventAt(e); - if (zone) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - }; - MouseZoneManager.prototype._onClick = function (e) { - var zone = this._findZoneEventAt(e); - if (zone) { - zone.clickCallback(e); - e.preventDefault(); - e.stopImmediatePropagation(); - } - }; - MouseZoneManager.prototype._findZoneEventAt = function (e) { - var coords = this._terminal.mouseHelper.getCoords(e, this._terminal.element, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows); - if (!coords) { - return null; - } - for (var i = 0; i < this._zones.length; i++) { - var zone = this._zones[i]; - if (zone.y === coords[1] && zone.x1 <= coords[0] && zone.x2 > coords[0]) { - return zone; - } - } - ; - return null; - }; - return MouseZoneManager; -}()); -exports.MouseZoneManager = MouseZoneManager; -var MouseZone = (function () { - function MouseZone(x1, x2, y, clickCallback, hoverCallback, tooltipCallback, leaveCallback) { - this.x1 = x1; - this.x2 = x2; - this.y = y; - this.clickCallback = clickCallback; - this.hoverCallback = hoverCallback; - this.tooltipCallback = tooltipCallback; - this.leaveCallback = leaveCallback; - } - return MouseZone; -}()); -exports.MouseZone = MouseZone; - - - -},{}],18:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var CharAtlas_1 = require("./CharAtlas"); -var Buffer_1 = require("../Buffer"); -exports.INVERTED_DEFAULT_COLOR = -1; -var DIM_OPACITY = 0.5; -var BaseRenderLayer = (function () { - function BaseRenderLayer(container, id, zIndex, _alpha, _colors) { - this._alpha = _alpha; - this._colors = _colors; - this._scaledCharWidth = 0; - this._scaledCharHeight = 0; - this._scaledCellWidth = 0; - this._scaledCellHeight = 0; - this._scaledCharLeft = 0; - this._scaledCharTop = 0; - this._canvas = document.createElement('canvas'); - this._canvas.id = "xterm-" + id + "-layer"; - this._canvas.style.zIndex = zIndex.toString(); - this._ctx = this._canvas.getContext('2d', { alpha: _alpha }); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - if (!_alpha) { - this.clearAll(); - } - container.appendChild(this._canvas); - } - BaseRenderLayer.prototype.onOptionsChanged = function (terminal) { }; - BaseRenderLayer.prototype.onBlur = function (terminal) { }; - BaseRenderLayer.prototype.onFocus = function (terminal) { }; - BaseRenderLayer.prototype.onCursorMove = function (terminal) { }; - BaseRenderLayer.prototype.onGridChanged = function (terminal, startRow, endRow) { }; - BaseRenderLayer.prototype.onSelectionChanged = function (terminal, start, end) { }; - BaseRenderLayer.prototype.onThemeChanged = function (terminal, colorSet) { - this._refreshCharAtlas(terminal, colorSet); - }; - BaseRenderLayer.prototype._refreshCharAtlas = function (terminal, colorSet) { - var _this = this; - if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { - return; - } - this._charAtlas = null; - var result = CharAtlas_1.acquireCharAtlas(terminal, this._colors, this._scaledCharWidth, this._scaledCharHeight); - if (result instanceof HTMLCanvasElement) { - this._charAtlas = result; - } - else { - result.then(function (bitmap) { return _this._charAtlas = bitmap; }); - } - }; - BaseRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - this._scaledCellWidth = dim.scaledCellWidth; - this._scaledCellHeight = dim.scaledCellHeight; - this._scaledCharWidth = dim.scaledCharWidth; - this._scaledCharHeight = dim.scaledCharHeight; - this._scaledCharLeft = dim.scaledCharLeft; - this._scaledCharTop = dim.scaledCharTop; - this._canvas.width = dim.scaledCanvasWidth; - this._canvas.height = dim.scaledCanvasHeight; - this._canvas.style.width = dim.canvasWidth + "px"; - this._canvas.style.height = dim.canvasHeight + "px"; - if (!this._alpha) { - this.clearAll(); - } - if (charSizeChanged) { - this._refreshCharAtlas(terminal, this._colors); - } - }; - BaseRenderLayer.prototype.fillCells = function (x, y, width, height) { - this._ctx.fillRect(x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); - }; - BaseRenderLayer.prototype.fillBottomLineAtCells = function (x, y, width) { - if (width === void 0) { width = 1; } - this._ctx.fillRect(x * this._scaledCellWidth, (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1, width * this._scaledCellWidth, window.devicePixelRatio); - }; - BaseRenderLayer.prototype.fillLeftLineAtCell = function (x, y) { - this._ctx.fillRect(x * this._scaledCellWidth, y * this._scaledCellHeight, window.devicePixelRatio, this._scaledCellHeight); - }; - BaseRenderLayer.prototype.strokeRectAtCell = function (x, y, width, height) { - this._ctx.lineWidth = window.devicePixelRatio; - this._ctx.strokeRect(x * this._scaledCellWidth + window.devicePixelRatio / 2, y * this._scaledCellHeight + (window.devicePixelRatio / 2), width * this._scaledCellWidth - window.devicePixelRatio, (height * this._scaledCellHeight) - window.devicePixelRatio); - }; - BaseRenderLayer.prototype.clearAll = function () { - if (this._alpha) { - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - } - else { - this._ctx.fillStyle = this._colors.background; - this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); - } - }; - BaseRenderLayer.prototype.clearCells = function (x, y, width, height) { - if (this._alpha) { - this._ctx.clearRect(x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); - } - else { - this._ctx.fillStyle = this._colors.background; - this._ctx.fillRect(x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); - } - }; - BaseRenderLayer.prototype.fillCharTrueColor = function (terminal, charData, x, y) { - this._ctx.font = terminal.options.fontSize * window.devicePixelRatio + "px " + terminal.options.fontFamily; - this._ctx.textBaseline = 'top'; - this._clipRow(terminal, y); - this._ctx.fillText(charData[Buffer_1.CHAR_DATA_CHAR_INDEX], x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); - }; - BaseRenderLayer.prototype.drawChar = function (terminal, char, code, width, x, y, fg, bg, bold, dim) { - var colorIndex = 0; - if (fg < 256) { - colorIndex = fg + 2; - } - else { - if (bold && terminal.options.enableBold) { - colorIndex = 1; - } - } - var isAscii = code < 256; - var isBasicColor = (colorIndex > 1 && fg < 16) && (fg < 8 || bold); - var isDefaultColor = fg >= 256; - var isDefaultBackground = bg >= 256; - if (this._charAtlas && isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground) { - var charAtlasCellWidth = this._scaledCharWidth + CharAtlas_1.CHAR_ATLAS_CELL_SPACING; - var charAtlasCellHeight = this._scaledCharHeight + CharAtlas_1.CHAR_ATLAS_CELL_SPACING; - if (dim) { - this._ctx.globalAlpha = DIM_OPACITY; - } - if (bold && !terminal.options.enableBold) { - if (colorIndex > 1) { - colorIndex -= 8; - } - } - this._ctx.drawImage(this._charAtlas, code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, charAtlasCellWidth, this._scaledCharHeight, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop, charAtlasCellWidth, this._scaledCharHeight); - } - else { - this._drawUncachedChar(terminal, char, width, fg, x, y, bold, dim); - } - }; - BaseRenderLayer.prototype._drawUncachedChar = function (terminal, char, width, fg, x, y, bold, dim) { - this._ctx.save(); - this._ctx.font = terminal.options.fontSize * window.devicePixelRatio + "px " + terminal.options.fontFamily; - if (bold && terminal.options.enableBold) { - this._ctx.font = "bold " + this._ctx.font; - } - this._ctx.textBaseline = 'top'; - if (fg === exports.INVERTED_DEFAULT_COLOR) { - this._ctx.fillStyle = this._colors.background; - } - else if (fg < 256) { - this._ctx.fillStyle = this._colors.ansi[fg]; - } - else { - this._ctx.fillStyle = this._colors.foreground; - } - this._clipRow(terminal, y); - if (dim) { - this._ctx.globalAlpha = DIM_OPACITY; - } - this._ctx.fillText(char, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); - this._ctx.restore(); - }; - BaseRenderLayer.prototype._clipRow = function (terminal, y) { - this._ctx.beginPath(); - this._ctx.rect(0, y * this._scaledCellHeight, terminal.cols * this._scaledCellWidth, this._scaledCellHeight); - this._ctx.clip(); - }; - return BaseRenderLayer; -}()); -exports.BaseRenderLayer = BaseRenderLayer; - - - -},{"../Buffer":1,"./CharAtlas":19}],19:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Browser_1 = require("../utils/Browser"); -exports.CHAR_ATLAS_CELL_SPACING = 1; -var charAtlasCache = []; -function acquireCharAtlas(terminal, colors, scaledCharWidth, scaledCharHeight) { - var newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors); - for (var i = 0; i < charAtlasCache.length; i++) { - var entry = charAtlasCache[i]; - var ownedByIndex = entry.ownedBy.indexOf(terminal); - if (ownedByIndex >= 0) { - if (configEquals(entry.config, newConfig)) { - return entry.bitmap; - } - else { - if (entry.ownedBy.length === 1) { - charAtlasCache.splice(i, 1); - } - else { - entry.ownedBy.splice(ownedByIndex, 1); - } - break; - } - } - } - for (var i = 0; i < charAtlasCache.length; i++) { - var entry = charAtlasCache[i]; - if (configEquals(entry.config, newConfig)) { - entry.ownedBy.push(terminal); - return entry.bitmap; - } - } - var newEntry = { - bitmap: generator.generate(scaledCharWidth, scaledCharHeight, terminal.options.fontSize, terminal.options.fontFamily, colors.background, colors.foreground, colors.ansi), - config: newConfig, - ownedBy: [terminal] - }; - charAtlasCache.push(newEntry); - return newEntry.bitmap; -} -exports.acquireCharAtlas = acquireCharAtlas; -function generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors) { - var clonedColors = { - foreground: colors.foreground, - background: colors.background, - cursor: null, - cursorAccent: null, - selection: null, - ansi: colors.ansi.slice(0, 16) - }; - return { - scaledCharWidth: scaledCharWidth, - scaledCharHeight: scaledCharHeight, - fontFamily: terminal.options.fontFamily, - fontSize: terminal.options.fontSize, - colors: clonedColors - }; -} -function configEquals(a, b) { - for (var i = 0; i < a.colors.ansi.length; i++) { - if (a.colors.ansi[i] !== b.colors.ansi[i]) { - return false; - } - } - return a.fontFamily === b.fontFamily && - a.fontSize === b.fontSize && - a.scaledCharWidth === b.scaledCharWidth && - a.scaledCharHeight === b.scaledCharHeight && - a.colors.foreground === b.colors.foreground && - a.colors.background === b.colors.background; -} -var generator; -function initialize(document) { - if (!generator) { - generator = new CharAtlasGenerator(document); - } -} -exports.initialize = initialize; -var CharAtlasGenerator = (function () { - function CharAtlasGenerator(_document) { - this._document = _document; - this._canvas = this._document.createElement('canvas'); - this._ctx = this._canvas.getContext('2d', { alpha: false }); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - } - CharAtlasGenerator.prototype.generate = function (scaledCharWidth, scaledCharHeight, fontSize, fontFamily, background, foreground, ansiColors) { - var cellWidth = scaledCharWidth + exports.CHAR_ATLAS_CELL_SPACING; - var cellHeight = scaledCharHeight + exports.CHAR_ATLAS_CELL_SPACING; - this._canvas.width = 255 * cellWidth; - this._canvas.height = (2 + 16) * cellHeight; - this._ctx.fillStyle = background; - this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); - this._ctx.save(); - this._ctx.fillStyle = foreground; - this._ctx.font = fontSize * window.devicePixelRatio + "px " + fontFamily; - this._ctx.textBaseline = 'top'; - for (var i = 0; i < 256; i++) { - this._ctx.save(); - this._ctx.beginPath(); - this._ctx.rect(i * cellWidth, 0, cellWidth, cellHeight); - this._ctx.clip(); - this._ctx.fillText(String.fromCharCode(i), i * cellWidth, 0); - this._ctx.restore(); - } - this._ctx.save(); - this._ctx.font = "bold " + this._ctx.font; - for (var i = 0; i < 256; i++) { - this._ctx.save(); - this._ctx.beginPath(); - this._ctx.rect(i * cellWidth, cellHeight, cellWidth, cellHeight); - this._ctx.clip(); - this._ctx.fillText(String.fromCharCode(i), i * cellWidth, cellHeight); - this._ctx.restore(); - } - this._ctx.restore(); - this._ctx.font = fontSize * window.devicePixelRatio + "px " + fontFamily; - for (var colorIndex = 0; colorIndex < 16; colorIndex++) { - if (colorIndex === 8) { - this._ctx.font = "bold " + this._ctx.font; - } - var y = (colorIndex + 2) * cellHeight; - for (var i = 0; i < 256; i++) { - this._ctx.save(); - this._ctx.beginPath(); - this._ctx.rect(i * cellWidth, y, cellWidth, cellHeight); - this._ctx.clip(); - this._ctx.fillStyle = ansiColors[colorIndex]; - this._ctx.fillText(String.fromCharCode(i), i * cellWidth, y); - this._ctx.restore(); - } - } - this._ctx.restore(); - if (!('createImageBitmap' in window) || Browser_1.isFirefox) { - var result = this._canvas; - this._canvas = this._document.createElement('canvas'); - this._ctx = this._canvas.getContext('2d'); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - return result; - } - var charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); - var r = parseInt(background.substr(1, 2), 16); - var g = parseInt(background.substr(3, 2), 16); - var b = parseInt(background.substr(5, 2), 16); - this._clearColor(charAtlasImageData, r, g, b); - var promise = window.createImageBitmap(charAtlasImageData); - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - return promise; - }; - CharAtlasGenerator.prototype._clearColor = function (imageData, r, g, b) { - for (var offset = 0; offset < imageData.data.length; offset += 4) { - if (imageData.data[offset] === r && - imageData.data[offset + 1] === g && - imageData.data[offset + 2] === b) { - imageData.data[offset + 3] = 0; - } - } - }; - return CharAtlasGenerator; -}()); - - - -},{"../utils/Browser":28}],20:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var DEFAULT_FOREGROUND = '#ffffff'; -var DEFAULT_BACKGROUND = '#333333'; -var DEFAULT_CURSOR = '#ffffff'; -var DEFAULT_CURSOR_ACCENT = '#333333'; -var DEFAULT_SELECTION = 'rgba(255, 255, 255, 0.3)'; -exports.DEFAULT_ANSI_COLORS = [ - '#2e3436', - '#cc0000', - '#4e9a06', - '#c4a000', - '#3465a4', - '#75507b', - '#06989a', - '#d3d7cf', - '#555753', - '#ef2929', - '#8ae234', - '#fce94f', - '#729fcf', - '#ad7fa8', - '#34e2e2', - '#eeeeec' -]; -function generate256Colors(first16Colors) { - var colors = first16Colors.slice(); - var v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; - for (var i = 0; i < 216; i++) { - var r = toPaddedHex(v[(i / 36) % 6 | 0]); - var g = toPaddedHex(v[(i / 6) % 6 | 0]); - var b = toPaddedHex(v[i % 6]); - colors.push("#" + r + g + b); - } - for (var i = 0; i < 24; i++) { - var c = toPaddedHex(8 + i * 10); - colors.push("#" + c + c + c); - } - return colors; -} -function toPaddedHex(c) { - var s = c.toString(16); - return s.length < 2 ? '0' + s : s; -} -var ColorManager = (function () { - function ColorManager() { - this.colors = { - foreground: DEFAULT_FOREGROUND, - background: DEFAULT_BACKGROUND, - cursor: DEFAULT_CURSOR, - cursorAccent: DEFAULT_CURSOR_ACCENT, - selection: DEFAULT_SELECTION, - ansi: generate256Colors(exports.DEFAULT_ANSI_COLORS) - }; - } - ColorManager.prototype.setTheme = function (theme) { - this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND; - this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND); - this.colors.cursor = theme.cursor || DEFAULT_CURSOR; - this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT; - this.colors.selection = theme.selection || DEFAULT_SELECTION; - this.colors.ansi[0] = theme.black || exports.DEFAULT_ANSI_COLORS[0]; - this.colors.ansi[1] = theme.red || exports.DEFAULT_ANSI_COLORS[1]; - this.colors.ansi[2] = theme.green || exports.DEFAULT_ANSI_COLORS[2]; - this.colors.ansi[3] = theme.yellow || exports.DEFAULT_ANSI_COLORS[3]; - this.colors.ansi[4] = theme.blue || exports.DEFAULT_ANSI_COLORS[4]; - this.colors.ansi[5] = theme.magenta || exports.DEFAULT_ANSI_COLORS[5]; - this.colors.ansi[6] = theme.cyan || exports.DEFAULT_ANSI_COLORS[6]; - this.colors.ansi[7] = theme.white || exports.DEFAULT_ANSI_COLORS[7]; - this.colors.ansi[8] = theme.brightBlack || exports.DEFAULT_ANSI_COLORS[8]; - this.colors.ansi[9] = theme.brightRed || exports.DEFAULT_ANSI_COLORS[9]; - this.colors.ansi[10] = theme.brightGreen || exports.DEFAULT_ANSI_COLORS[10]; - this.colors.ansi[11] = theme.brightYellow || exports.DEFAULT_ANSI_COLORS[11]; - this.colors.ansi[12] = theme.brightBlue || exports.DEFAULT_ANSI_COLORS[12]; - this.colors.ansi[13] = theme.brightMagenta || exports.DEFAULT_ANSI_COLORS[13]; - this.colors.ansi[14] = theme.brightCyan || exports.DEFAULT_ANSI_COLORS[14]; - this.colors.ansi[15] = theme.brightWhite || exports.DEFAULT_ANSI_COLORS[15]; - }; - ColorManager.prototype._validateColor = function (color, fallback) { - if (!color) { - return fallback; - } - if (color.length === 7 && color.charAt(0) === '#') { - return color; - } - if (color.length === 4 && color.charAt(0) === '#') { - var r = color.charAt(1); - var g = color.charAt(2); - var b = color.charAt(3); - return "#" + r + r + g + g + b + b; - } - return fallback; - }; - return ColorManager; -}()); -exports.ColorManager = ColorManager; - - - -},{}],21:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Buffer_1 = require("../Buffer"); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var BLINK_INTERVAL = 600; -var CursorRenderLayer = (function (_super) { - __extends(CursorRenderLayer, _super); - function CursorRenderLayer(container, zIndex, colors) { - var _this = _super.call(this, container, 'cursor', zIndex, true, colors) || this; - _this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null, - }; - _this._cursorRenderers = { - 'bar': _this._renderBarCursor.bind(_this), - 'block': _this._renderBlockCursor.bind(_this), - 'underline': _this._renderUnderlineCursor.bind(_this) - }; - return _this; - } - CursorRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null, - }; - }; - CursorRenderLayer.prototype.reset = function (terminal) { - this._clearCursor(); - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.dispose(); - this._cursorBlinkStateManager = null; - this.onOptionsChanged(terminal); - } - }; - CursorRenderLayer.prototype.onBlur = function (terminal) { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.pause(); - } - terminal.refresh(terminal.buffer.y, terminal.buffer.y); - }; - CursorRenderLayer.prototype.onFocus = function (terminal) { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.resume(terminal); - } - else { - terminal.refresh(terminal.buffer.y, terminal.buffer.y); - } - }; - CursorRenderLayer.prototype.onOptionsChanged = function (terminal) { - var _this = this; - if (terminal.options.cursorBlink) { - if (!this._cursorBlinkStateManager) { - this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, function () { - _this._render(terminal, true); - }); - } - } - else { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.dispose(); - this._cursorBlinkStateManager = null; - } - terminal.refresh(terminal.buffer.y, terminal.buffer.y); - } - }; - CursorRenderLayer.prototype.onCursorMove = function (terminal) { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.restartBlinkAnimation(terminal); - } - }; - CursorRenderLayer.prototype.onGridChanged = function (terminal, startRow, endRow) { - if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) { - this._render(terminal, false); - } - }; - CursorRenderLayer.prototype._render = function (terminal, triggeredByAnimationFrame) { - if (!terminal.cursorState || terminal.cursorHidden) { - this._clearCursor(); - return; - } - var cursorY = terminal.buffer.ybase + terminal.buffer.y; - var viewportRelativeCursorY = cursorY - terminal.buffer.ydisp; - if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= terminal.rows) { - this._clearCursor(); - return; - } - var charData = terminal.buffer.lines.get(cursorY)[terminal.buffer.x]; - if (!charData) { - return; - } - if (!terminal.isFocused) { - this._clearCursor(); - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, charData); - this._ctx.restore(); - this._state.x = terminal.buffer.x; - this._state.y = viewportRelativeCursorY; - this._state.isFocused = false; - this._state.style = terminal.options.cursorStyle; - this._state.width = charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]; - return; - } - if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) { - this._clearCursor(); - return; - } - if (this._state) { - if (this._state.x === terminal.buffer.x && - this._state.y === viewportRelativeCursorY && - this._state.isFocused === terminal.isFocused && - this._state.style === terminal.options.cursorStyle && - this._state.width === charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]) { - return; - } - this._clearCursor(); - } - this._ctx.save(); - this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, charData); - this._ctx.restore(); - this._state.x = terminal.buffer.x; - this._state.y = viewportRelativeCursorY; - this._state.isFocused = false; - this._state.style = terminal.options.cursorStyle; - this._state.width = charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]; - }; - CursorRenderLayer.prototype._clearCursor = function () { - if (this._state) { - this.clearCells(this._state.x, this._state.y, this._state.width, 1); - this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null, - }; - } - }; - CursorRenderLayer.prototype._renderBarCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this.fillLeftLineAtCell(x, y); - this._ctx.restore(); - }; - CursorRenderLayer.prototype._renderBlockCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this.fillCells(x, y, charData[Buffer_1.CHAR_DATA_WIDTH_INDEX], 1); - this._ctx.fillStyle = this._colors.cursorAccent; - this.fillCharTrueColor(terminal, charData, x, y); - this._ctx.restore(); - }; - CursorRenderLayer.prototype._renderUnderlineCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this.fillBottomLineAtCells(x, y); - this._ctx.restore(); - }; - CursorRenderLayer.prototype._renderBlurCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.strokeStyle = this._colors.cursor; - this.strokeRectAtCell(x, y, charData[Buffer_1.CHAR_DATA_WIDTH_INDEX], 1); - this._ctx.restore(); - }; - return CursorRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.CursorRenderLayer = CursorRenderLayer; -var CursorBlinkStateManager = (function () { - function CursorBlinkStateManager(terminal, renderCallback) { - this.renderCallback = renderCallback; - this.isCursorVisible = true; - if (terminal.isFocused) { - this._restartInterval(); - } - } - Object.defineProperty(CursorBlinkStateManager.prototype, "isPaused", { - get: function () { return !(this._blinkStartTimeout || this._blinkInterval); }, - enumerable: true, - configurable: true - }); - CursorBlinkStateManager.prototype.dispose = function () { - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - this._blinkInterval = null; - } - if (this._blinkStartTimeout) { - window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = null; - } - if (this._animationFrame) { - window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = null; - } - }; - CursorBlinkStateManager.prototype.restartBlinkAnimation = function (terminal) { - var _this = this; - if (this.isPaused) { - return; - } - this._animationTimeRestarted = Date.now(); - this.isCursorVisible = true; - if (!this._animationFrame) { - this._animationFrame = window.requestAnimationFrame(function () { - _this.renderCallback(); - _this._animationFrame = null; - }); - } - }; - CursorBlinkStateManager.prototype._restartInterval = function (timeToStart) { - var _this = this; - if (timeToStart === void 0) { timeToStart = BLINK_INTERVAL; } - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - } - this._blinkStartTimeout = setTimeout(function () { - if (_this._animationTimeRestarted) { - var time = BLINK_INTERVAL - (Date.now() - _this._animationTimeRestarted); - _this._animationTimeRestarted = null; - if (time > 0) { - _this._restartInterval(time); - return; - } - } - _this.isCursorVisible = false; - _this._animationFrame = window.requestAnimationFrame(function () { - _this.renderCallback(); - _this._animationFrame = null; - }); - _this._blinkInterval = setInterval(function () { - if (_this._animationTimeRestarted) { - var time = BLINK_INTERVAL - (Date.now() - _this._animationTimeRestarted); - _this._animationTimeRestarted = null; - _this._restartInterval(time); - return; - } - _this.isCursorVisible = !_this.isCursorVisible; - _this._animationFrame = window.requestAnimationFrame(function () { - _this.renderCallback(); - _this._animationFrame = null; - }); - }, BLINK_INTERVAL); - }, timeToStart); - }; - CursorBlinkStateManager.prototype.pause = function () { - this.isCursorVisible = true; - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - this._blinkInterval = null; - } - if (this._blinkStartTimeout) { - window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = null; - } - if (this._animationFrame) { - window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = null; - } - }; - CursorBlinkStateManager.prototype.resume = function (terminal) { - this._animationTimeRestarted = null; - this._restartInterval(); - this.restartBlinkAnimation(terminal); - }; - return CursorBlinkStateManager; -}()); - - - -},{"../Buffer":1,"./BaseRenderLayer":18}],22:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var GridCache = (function () { - function GridCache() { - this.cache = []; - } - GridCache.prototype.resize = function (width, height) { - for (var x = 0; x < width; x++) { - if (this.cache.length <= x) { - this.cache.push([]); - } - for (var y = this.cache[x].length; y < height; y++) { - this.cache[x].push(null); - } - this.cache[x].length = height; - } - this.cache.length = width; - }; - GridCache.prototype.clear = function () { - for (var x = 0; x < this.cache.length; x++) { - for (var y = 0; y < this.cache[x].length; y++) { - this.cache[x][y] = null; - } - } - }; - return GridCache; -}()); -exports.GridCache = GridCache; - - - -},{}],23:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var Types_1 = require("../Types"); -var LinkRenderLayer = (function (_super) { - __extends(LinkRenderLayer, _super); - function LinkRenderLayer(container, zIndex, colors, terminal) { - var _this = _super.call(this, container, 'link', zIndex, true, colors) || this; - _this._state = null; - terminal.linkifier.on(Types_1.LinkHoverEventTypes.HOVER, function (e) { return _this._onLinkHover(e); }); - terminal.linkifier.on(Types_1.LinkHoverEventTypes.LEAVE, function (e) { return _this._onLinkLeave(e); }); - return _this; - } - LinkRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - this._state = null; - }; - LinkRenderLayer.prototype.reset = function (terminal) { - this._clearCurrentLink(); - }; - LinkRenderLayer.prototype._clearCurrentLink = function () { - if (this._state) { - this.clearCells(this._state.x, this._state.y, this._state.length, 1); - this._state = null; - } - }; - LinkRenderLayer.prototype._onLinkHover = function (e) { - this._ctx.fillStyle = this._colors.foreground; - this.fillBottomLineAtCells(e.x, e.y, e.length); - this._state = e; - }; - LinkRenderLayer.prototype._onLinkLeave = function (e) { - this._clearCurrentLink(); - }; - return LinkRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.LinkRenderLayer = LinkRenderLayer; - - - -},{"../Types":14,"./BaseRenderLayer":18}],24:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TextRenderLayer_1 = require("./TextRenderLayer"); -var SelectionRenderLayer_1 = require("./SelectionRenderLayer"); -var CursorRenderLayer_1 = require("./CursorRenderLayer"); -var ColorManager_1 = require("./ColorManager"); -var LinkRenderLayer_1 = require("./LinkRenderLayer"); -var EventEmitter_1 = require("../EventEmitter"); -var Renderer = (function (_super) { - __extends(Renderer, _super); - function Renderer(_terminal, theme) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._refreshRowsQueue = []; - _this._refreshAnimationFrame = null; - _this.colorManager = new ColorManager_1.ColorManager(); - if (theme) { - _this.colorManager.setTheme(theme); - } - _this._renderLayers = [ - new TextRenderLayer_1.TextRenderLayer(_this._terminal.element, 0, _this.colorManager.colors), - new SelectionRenderLayer_1.SelectionRenderLayer(_this._terminal.element, 1, _this.colorManager.colors), - new LinkRenderLayer_1.LinkRenderLayer(_this._terminal.element, 2, _this.colorManager.colors, _this._terminal), - new CursorRenderLayer_1.CursorRenderLayer(_this._terminal.element, 3, _this.colorManager.colors) - ]; - _this.dimensions = { - scaledCharWidth: null, - scaledCharHeight: null, - scaledCellWidth: null, - scaledCellHeight: null, - scaledCharLeft: null, - scaledCharTop: null, - scaledCanvasWidth: null, - scaledCanvasHeight: null, - canvasWidth: null, - canvasHeight: null, - actualCellWidth: null, - actualCellHeight: null - }; - _this._devicePixelRatio = window.devicePixelRatio; - return _this; - } - Renderer.prototype.onWindowResize = function (devicePixelRatio) { - if (this._devicePixelRatio !== devicePixelRatio) { - this._devicePixelRatio = devicePixelRatio; - this.onResize(this._terminal.cols, this._terminal.rows, true); - } - }; - Renderer.prototype.setTheme = function (theme) { - var _this = this; - this.colorManager.setTheme(theme); - this._renderLayers.forEach(function (l) { - l.onThemeChanged(_this._terminal, _this.colorManager.colors); - l.reset(_this._terminal); - }); - this._terminal.refresh(0, this._terminal.rows - 1); - return this.colorManager.colors; - }; - Renderer.prototype.onResize = function (cols, rows, didCharSizeChange) { - var _this = this; - if (!this._terminal.charMeasure.width || !this._terminal.charMeasure.height) { - return; - } - this.dimensions.scaledCharWidth = Math.floor(this._terminal.charMeasure.width * window.devicePixelRatio); - this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * window.devicePixelRatio); - this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight); - this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2); - this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing); - this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2); - this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight; - this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth; - this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio); - this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio); - this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows; - this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols; - this._renderLayers.forEach(function (l) { return l.resize(_this._terminal, _this.dimensions, didCharSizeChange); }); - this._terminal.refresh(0, this._terminal.rows - 1); - this.emit('resize', { - width: this.dimensions.canvasWidth, - height: this.dimensions.canvasHeight - }); - }; - Renderer.prototype.onCharSizeChanged = function () { - this.onResize(this._terminal.cols, this._terminal.rows, true); - }; - Renderer.prototype.onBlur = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onBlur(_this._terminal); }); - }; - Renderer.prototype.onFocus = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onFocus(_this._terminal); }); - }; - Renderer.prototype.onSelectionChanged = function (start, end) { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onSelectionChanged(_this._terminal, start, end); }); - }; - Renderer.prototype.onCursorMove = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onCursorMove(_this._terminal); }); - }; - Renderer.prototype.onOptionsChanged = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onOptionsChanged(_this._terminal); }); - }; - Renderer.prototype.clear = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.reset(_this._terminal); }); - }; - Renderer.prototype.queueRefresh = function (start, end) { - this._refreshRowsQueue.push({ start: start, end: end }); - if (!this._refreshAnimationFrame) { - this._refreshAnimationFrame = window.requestAnimationFrame(this._refreshLoop.bind(this)); - } - }; - Renderer.prototype._refreshLoop = function () { - var _this = this; - var start; - var end; - if (this._refreshRowsQueue.length > 4) { - start = 0; - end = this._terminal.rows - 1; - } - else { - start = this._refreshRowsQueue[0].start; - end = this._refreshRowsQueue[0].end; - for (var i = 1; i < this._refreshRowsQueue.length; i++) { - if (this._refreshRowsQueue[i].start < start) { - start = this._refreshRowsQueue[i].start; - } - if (this._refreshRowsQueue[i].end > end) { - end = this._refreshRowsQueue[i].end; - } - } - } - this._refreshRowsQueue = []; - this._refreshAnimationFrame = null; - start = Math.max(start, 0); - end = Math.min(end, this._terminal.rows - 1); - this._renderLayers.forEach(function (l) { return l.onGridChanged(_this._terminal, start, end); }); - this._terminal.emit('refresh', { start: start, end: end }); - }; - return Renderer; -}(EventEmitter_1.EventEmitter)); -exports.Renderer = Renderer; - - - -},{"../EventEmitter":7,"./ColorManager":20,"./CursorRenderLayer":21,"./LinkRenderLayer":23,"./SelectionRenderLayer":25,"./TextRenderLayer":26}],25:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var SelectionRenderLayer = (function (_super) { - __extends(SelectionRenderLayer, _super); - function SelectionRenderLayer(container, zIndex, colors) { - var _this = _super.call(this, container, 'selection', zIndex, true, colors) || this; - _this._state = { - start: null, - end: null - }; - return _this; - } - SelectionRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - this._state = { - start: null, - end: null - }; - }; - SelectionRenderLayer.prototype.reset = function (terminal) { - if (this._state.start && this._state.end) { - this._state = { - start: null, - end: null - }; - this.clearAll(); - } - }; - SelectionRenderLayer.prototype.onSelectionChanged = function (terminal, start, end) { - if (this._state.start === start || this._state.end === end) { - return; - } - this.clearAll(); - if (!start || !end) { - return; - } - var viewportStartRow = start[1] - terminal.buffer.ydisp; - var viewportEndRow = end[1] - terminal.buffer.ydisp; - var viewportCappedStartRow = Math.max(viewportStartRow, 0); - var viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1); - if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) { - return; - } - var startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; - var startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols; - this._ctx.fillStyle = this._colors.selection; - this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1); - var middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0); - this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount); - if (viewportCappedStartRow !== viewportCappedEndRow) { - var endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols; - this.fillCells(0, viewportCappedEndRow, endCol, 1); - } - this._state.start = [start[0], start[1]]; - this._state.end = [end[0], end[1]]; - }; - return SelectionRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.SelectionRenderLayer = SelectionRenderLayer; - - - -},{"./BaseRenderLayer":18}],26:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Buffer_1 = require("../Buffer"); -var Types_1 = require("./Types"); -var GridCache_1 = require("./GridCache"); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var OVERLAP_OWNED_CHAR_DATA = [null, '', 0, -1]; -var TextRenderLayer = (function (_super) { - __extends(TextRenderLayer, _super); - function TextRenderLayer(container, zIndex, colors) { - var _this = _super.call(this, container, 'text', zIndex, false, colors) || this; - _this._characterOverlapCache = {}; - _this._state = new GridCache_1.GridCache(); - return _this; - } - TextRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - var terminalFont = terminal.options.fontSize * window.devicePixelRatio + "px " + terminal.options.fontFamily; - if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) { - this._characterWidth = dim.scaledCharWidth; - this._characterFont = terminalFont; - this._characterOverlapCache = {}; - } - this._state.clear(); - this._state.resize(terminal.cols, terminal.rows); - }; - TextRenderLayer.prototype.reset = function (terminal) { - this._state.clear(); - this.clearAll(); - }; - TextRenderLayer.prototype.onGridChanged = function (terminal, startRow, endRow) { - if (this._state.cache.length === 0) { - return; - } - for (var y = startRow; y <= endRow; y++) { - var row = y + terminal.buffer.ydisp; - var line = terminal.buffer.lines.get(row); - this.clearCells(0, y, terminal.cols, 1); - for (var x = 0; x < terminal.cols; x++) { - var charData = line[x]; - var code = charData[Buffer_1.CHAR_DATA_CODE_INDEX]; - var char = charData[Buffer_1.CHAR_DATA_CHAR_INDEX]; - var attr = charData[Buffer_1.CHAR_DATA_ATTR_INDEX]; - var width = charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]; - if (width === 0) { - continue; - } - if (code === 32) { - if (x > 0) { - var previousChar = line[x - 1]; - if (this._isOverlapping(previousChar)) { - continue; - } - } - } - var flags = attr >> 18; - var bg = attr & 0x1ff; - var isDefaultBackground = bg >= 256; - var isInvisible = flags & Types_1.FLAGS.INVISIBLE; - var isInverted = flags & Types_1.FLAGS.INVERSE; - if (!code || (code === 32 && isDefaultBackground && !isInverted) || isInvisible) { - continue; - } - if (width !== 0 && this._isOverlapping(charData)) { - if (x < line.length - 1 && line[x + 1][Buffer_1.CHAR_DATA_CODE_INDEX] === 32) { - width = 2; - } - } - var fg = (attr >> 9) & 0x1ff; - if (isInverted) { - var temp = bg; - bg = fg; - fg = temp; - if (fg === 256) { - fg = BaseRenderLayer_1.INVERTED_DEFAULT_COLOR; - } - if (bg === 257) { - bg = BaseRenderLayer_1.INVERTED_DEFAULT_COLOR; - } - } - if (width === 2) { - } - if (bg < 256) { - this._ctx.save(); - this._ctx.fillStyle = (bg === BaseRenderLayer_1.INVERTED_DEFAULT_COLOR ? this._colors.foreground : this._colors.ansi[bg]); - this.fillCells(x, y, width, 1); - this._ctx.restore(); - } - this._ctx.save(); - if (flags & Types_1.FLAGS.BOLD) { - this._ctx.font = "bold " + this._ctx.font; - if (fg < 8) { - fg += 8; - } - } - if (flags & Types_1.FLAGS.UNDERLINE) { - if (fg === BaseRenderLayer_1.INVERTED_DEFAULT_COLOR) { - this._ctx.fillStyle = this._colors.background; - } - else if (fg < 256) { - this._ctx.fillStyle = this._colors.ansi[fg]; - } - else { - this._ctx.fillStyle = this._colors.foreground; - } - this.fillBottomLineAtCells(x, y); - } - this.drawChar(terminal, char, code, width, x, y, fg, bg, !!(flags & Types_1.FLAGS.BOLD), !!(flags & Types_1.FLAGS.DIM)); - this._ctx.restore(); - } - } - }; - TextRenderLayer.prototype._isOverlapping = function (charData) { - if (charData[Buffer_1.CHAR_DATA_WIDTH_INDEX] !== 1) { - return false; - } - var code = charData[Buffer_1.CHAR_DATA_CODE_INDEX]; - if (code < 256) { - return false; - } - var char = charData[Buffer_1.CHAR_DATA_CHAR_INDEX]; - if (this._characterOverlapCache.hasOwnProperty(char)) { - return this._characterOverlapCache[char]; - } - this._ctx.save(); - this._ctx.font = this._characterFont; - var overlaps = Math.floor(this._ctx.measureText(char).width) > this._characterWidth; - this._ctx.restore(); - this._characterOverlapCache[char] = overlaps; - return overlaps; - }; - TextRenderLayer.prototype._clearChar = function (x, y) { - var colsToClear = 1; - var state = this._state.cache[x][y]; - if (state && state[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - colsToClear = 2; - } - this.clearCells(x, y, colsToClear, 1); - }; - return TextRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.TextRenderLayer = TextRenderLayer; - - - -},{"../Buffer":1,"./BaseRenderLayer":18,"./GridCache":22,"./Types":27}],27:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var FLAGS; -(function (FLAGS) { - FLAGS[FLAGS["BOLD"] = 1] = "BOLD"; - FLAGS[FLAGS["UNDERLINE"] = 2] = "UNDERLINE"; - FLAGS[FLAGS["BLINK"] = 4] = "BLINK"; - FLAGS[FLAGS["INVERSE"] = 8] = "INVERSE"; - FLAGS[FLAGS["INVISIBLE"] = 16] = "INVISIBLE"; - FLAGS[FLAGS["DIM"] = 32] = "DIM"; -})(FLAGS = exports.FLAGS || (exports.FLAGS = {})); -; - - - -},{}],28:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Generic_1 = require("./Generic"); -var isNode = (typeof navigator === 'undefined') ? true : false; -var userAgent = (isNode) ? 'node' : navigator.userAgent; -var platform = (isNode) ? 'node' : navigator.platform; -exports.isFirefox = !!~userAgent.indexOf('Firefox'); -exports.isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident'); -exports.isMac = Generic_1.contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform); -exports.isIpad = platform === 'iPad'; -exports.isIphone = platform === 'iPhone'; -exports.isMSWindows = Generic_1.contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform); -exports.isLinux = platform.indexOf('Linux') >= 0; - - - -},{"./Generic":31}],29:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var EventEmitter_1 = require("../EventEmitter"); -var CharMeasure = (function (_super) { - __extends(CharMeasure, _super); - function CharMeasure(document, parentElement) { - var _this = _super.call(this) || this; - _this._document = document; - _this._parentElement = parentElement; - return _this; - } - Object.defineProperty(CharMeasure.prototype, "width", { - get: function () { - return this._width; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(CharMeasure.prototype, "height", { - get: function () { - return this._height; - }, - enumerable: true, - configurable: true - }); - CharMeasure.prototype.measure = function (options) { - var _this = this; - if (!this._measureElement) { - this._measureElement = this._document.createElement('span'); - this._measureElement.style.position = 'absolute'; - this._measureElement.style.top = '0'; - this._measureElement.style.left = '-9999em'; - this._measureElement.style.lineHeight = 'normal'; - this._measureElement.textContent = 'W'; - this._measureElement.setAttribute('aria-hidden', 'true'); - this._parentElement.appendChild(this._measureElement); - setTimeout(function () { return _this._doMeasure(options); }, 0); - } - else { - this._doMeasure(options); - } - }; - CharMeasure.prototype._doMeasure = function (options) { - this._measureElement.style.fontFamily = options.fontFamily; - this._measureElement.style.fontSize = options.fontSize + "px"; - var geometry = this._measureElement.getBoundingClientRect(); - if (geometry.width === 0 || geometry.height === 0) { - return; - } - if (this._width !== geometry.width || this._height !== geometry.height) { - this._width = geometry.width; - this._height = Math.ceil(geometry.height); - this.emit('charsizechanged'); - } - }; - return CharMeasure; -}(EventEmitter_1.EventEmitter)); -exports.CharMeasure = CharMeasure; - - - -},{"../EventEmitter":7}],30:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var EventEmitter_1 = require("../EventEmitter"); -var CircularList = (function (_super) { - __extends(CircularList, _super); - function CircularList(_maxLength) { - var _this = _super.call(this) || this; - _this._maxLength = _maxLength; - _this._array = new Array(_this._maxLength); - _this._startIndex = 0; - _this._length = 0; - return _this; - } - Object.defineProperty(CircularList.prototype, "maxLength", { - get: function () { - return this._maxLength; - }, - set: function (newMaxLength) { - if (this._maxLength === newMaxLength) { - return; - } - var newArray = new Array(newMaxLength); - for (var i = 0; i < Math.min(newMaxLength, this.length); i++) { - newArray[i] = this._array[this._getCyclicIndex(i)]; - } - this._array = newArray; - this._maxLength = newMaxLength; - this._startIndex = 0; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(CircularList.prototype, "length", { - get: function () { - return this._length; - }, - set: function (newLength) { - if (newLength > this._length) { - for (var i = this._length; i < newLength; i++) { - this._array[i] = undefined; - } - } - this._length = newLength; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(CircularList.prototype, "forEach", { - get: function () { - var _this = this; - return function (callbackfn) { - var i = 0; - var length = _this.length; - for (var i_1 = 0; i_1 < length; i_1++) { - callbackfn(_this.get(i_1), i_1); - } - }; - }, - enumerable: true, - configurable: true - }); - CircularList.prototype.get = function (index) { - return this._array[this._getCyclicIndex(index)]; - }; - CircularList.prototype.set = function (index, value) { - this._array[this._getCyclicIndex(index)] = value; - }; - CircularList.prototype.push = function (value) { - this._array[this._getCyclicIndex(this._length)] = value; - if (this._length === this._maxLength) { - this._startIndex++; - if (this._startIndex === this._maxLength) { - this._startIndex = 0; - } - this.emit('trim', 1); - } - else { - this._length++; - } - }; - CircularList.prototype.pop = function () { - return this._array[this._getCyclicIndex(this._length-- - 1)]; - }; - CircularList.prototype.splice = function (start, deleteCount) { - var items = []; - for (var _i = 2; _i < arguments.length; _i++) { - items[_i - 2] = arguments[_i]; - } - if (deleteCount) { - for (var i = start; i < this._length - deleteCount; i++) { - this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)]; - } - this._length -= deleteCount; - } - if (items && items.length) { - for (var i = this._length - 1; i >= start; i--) { - this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)]; - } - for (var i = 0; i < items.length; i++) { - this._array[this._getCyclicIndex(start + i)] = items[i]; - } - if (this._length + items.length > this.maxLength) { - var countToTrim = (this._length + items.length) - this.maxLength; - this._startIndex += countToTrim; - this._length = this.maxLength; - this.emit('trim', countToTrim); - } - else { - this._length += items.length; - } - } - }; - CircularList.prototype.trimStart = function (count) { - if (count > this._length) { - count = this._length; - } - this._startIndex += count; - this._length -= count; - this.emit('trim', count); - }; - CircularList.prototype.shiftElements = function (start, count, offset) { - if (count <= 0) { - return; - } - if (start < 0 || start >= this._length) { - throw new Error('start argument out of range'); - } - if (start + offset < 0) { - throw new Error('Cannot shift elements in list beyond index 0'); - } - if (offset > 0) { - for (var i = count - 1; i >= 0; i--) { - this.set(start + i + offset, this.get(start + i)); - } - var expandListBy = (start + count + offset) - this._length; - if (expandListBy > 0) { - this._length += expandListBy; - while (this._length > this.maxLength) { - this._length--; - this._startIndex++; - this.emit('trim', 1); - } - } - } - else { - for (var i = 0; i < count; i++) { - this.set(start + i + offset, this.get(start + i)); - } - } - }; - CircularList.prototype._getCyclicIndex = function (index) { - return (this._startIndex + index) % this.maxLength; - }; - return CircularList; -}(EventEmitter_1.EventEmitter)); -exports.CircularList = CircularList; - - - -},{"../EventEmitter":7}],31:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -function contains(arr, el) { - return arr.indexOf(el) >= 0; -} -exports.contains = contains; -; - - - -},{}],32:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var MouseHelper = (function () { - function MouseHelper(_renderer) { - this._renderer = _renderer; - } - MouseHelper.getCoordsRelativeToElement = function (event, element) { - if (event.pageX == null) { - return null; - } - var originalElement = element; - var x = event.pageX; - var y = event.pageY; - while (element) { - x -= element.offsetLeft; - y -= element.offsetTop; - element = 'offsetParent' in element ? element.offsetParent : element.parentElement; - } - element = originalElement; - while (element && element !== element.ownerDocument.body) { - x += element.scrollLeft; - y += element.scrollTop; - element = element.parentElement; - } - return [x, y]; - }; - MouseHelper.prototype.getCoords = function (event, element, charMeasure, lineHeight, colCount, rowCount, isSelection) { - if (!charMeasure.width || !charMeasure.height) { - return null; - } - var coords = MouseHelper.getCoordsRelativeToElement(event, element); - if (!coords) { - return null; - } - coords[0] = Math.ceil((coords[0] + (isSelection ? this._renderer.dimensions.actualCellWidth / 2 : 0)) / this._renderer.dimensions.actualCellWidth); - coords[1] = Math.ceil(coords[1] / this._renderer.dimensions.actualCellHeight); - coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); - coords[1] = Math.min(Math.max(coords[1], 1), rowCount); - return coords; - }; - MouseHelper.prototype.getRawByteCoords = function (event, element, charMeasure, lineHeight, colCount, rowCount) { - var coords = this.getCoords(event, element, charMeasure, lineHeight, colCount, rowCount); - var x = coords[0]; - var y = coords[1]; - x += 32; - y += 32; - return { x: x, y: y }; - }; - return MouseHelper; -}()); -exports.MouseHelper = MouseHelper; - - - -},{}],33:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.BellSound = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg=='; - - - -},{}],34:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Terminal_1 = require("./Terminal"); -module.exports = Terminal_1.Terminal; - - - -},{"./Terminal":13}]},{},[34])(34) -}); -//# sourceMappingURL=xterm.js.map diff --git a/route/static/build/xterm.js.map b/route/static/build/xterm.js.map deleted file mode 100755 index 9716c5e22..000000000 --- a/route/static/build/xterm.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"xterm.js","sources":["../src/xterm.ts","../src/utils/Sounds.ts","../src/utils/MouseHelper.ts","../src/utils/Generic.ts","../src/utils/CircularList.ts","../src/utils/CharMeasure.ts","../src/utils/Browser.ts","../src/renderer/Types.ts","../src/renderer/TextRenderLayer.ts","../src/renderer/SelectionRenderLayer.ts","../src/renderer/Renderer.ts","../src/renderer/LinkRenderLayer.ts","../src/renderer/GridCache.ts","../src/renderer/CursorRenderLayer.ts","../src/renderer/ColorManager.ts","../src/renderer/CharAtlas.ts","../src/renderer/BaseRenderLayer.ts","../src/input/MouseZoneManager.ts","../src/handlers/Clipboard.ts","../src/Viewport.ts","../src/Types.ts","../src/Terminal.ts","../src/SelectionModel.ts","../src/SelectionManager.ts","../src/Parser.ts","../src/Linkifier.ts","../src/InputHandler.ts","../src/EventEmitter.ts","../src/EscapeSequences.ts","../src/CompositionHelper.ts","../src/Charsets.ts","../src/CharWidth.ts","../src/BufferSet.ts","../src/Buffer.ts","../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n *\n * This file is the entry point for browserify.\n */\n\nimport { Terminal } from './Terminal';\n\nmodule.exports = Terminal;\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n// Source: https://freesound.org/people/altemark/sounds/45759/\n// This sound is released under the Creative Commons Attribution 3.0 Unported\n// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been\n// made, apart from the conversion to base64.\nexport const BellSound = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ICharMeasure } from '../Interfaces';\nimport { IRenderer } from '../renderer/Interfaces';\n\nexport class MouseHelper {\n constructor(private _renderer: IRenderer) {}\n\n public static getCoordsRelativeToElement(event: {pageX: number, pageY: number}, element: HTMLElement): [number, number] {\n // Ignore browsers that don't support MouseEvent.pageX\n if (event.pageX == null) {\n return null;\n }\n\n const originalElement = element;\n let x = event.pageX;\n let y = event.pageY;\n\n // Converts the coordinates from being relative to the document to being\n // relative to the terminal.\n while (element) {\n x -= element.offsetLeft;\n y -= element.offsetTop;\n element = 'offsetParent' in element ? element.offsetParent : element.parentElement;\n }\n element = originalElement;\n while (element && element !== element.ownerDocument.body) {\n x += element.scrollLeft;\n y += element.scrollTop;\n element = element.parentElement;\n }\n return [x, y];\n }\n\n /**\n * Gets coordinates within the terminal for a particular mouse event. The result\n * is returned as an array in the form [x, y] instead of an object as it's a\n * little faster and this function is used in some low level code.\n * @param event The mouse event.\n * @param element The terminal's container element.\n * @param charMeasure The char measure object used to determine character sizes.\n * @param colCount The number of columns in the terminal.\n * @param rowCount The number of rows n the terminal.\n * @param isSelection Whether the request is for the selection or not. This will\n * apply an offset to the x value such that the left half of the cell will\n * select that cell and the right half will select the next cell.\n */\n public getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number] {\n // Coordinates cannot be measured if charMeasure has not been initialized\n if (!charMeasure.width || !charMeasure.height) {\n return null;\n }\n\n const coords = MouseHelper.getCoordsRelativeToElement(event, element);\n if (!coords) {\n return null;\n }\n\n coords[0] = Math.ceil((coords[0] + (isSelection ? this._renderer.dimensions.actualCellWidth / 2 : 0)) / this._renderer.dimensions.actualCellWidth);\n coords[1] = Math.ceil(coords[1] / this._renderer.dimensions.actualCellHeight);\n\n // Ensure coordinates are within the terminal viewport. Note that selections\n // need an addition point of precision to cover the end point (as characters\n // cover half of one char and half of the next).\n coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0));\n coords[1] = Math.min(Math.max(coords[1], 1), rowCount);\n\n return coords;\n }\n\n /**\n * Gets coordinates within the terminal for a particular mouse event, wrapping\n * them to the bounds of the terminal and adding 32 to both the x and y values\n * as expected by xterm.\n * @param event The mouse event.\n * @param element The terminal's container element.\n * @param charMeasure The char measure object used to determine character sizes.\n * @param colCount The number of columns in the terminal.\n * @param rowCount The number of rows in the terminal.\n */\n public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number } {\n const coords = this.getCoords(event, element, charMeasure, lineHeight, colCount, rowCount);\n let x = coords[0];\n let y = coords[1];\n\n // xterm sends raw bytes and starts at 32 (SP) for each.\n x += 32;\n y += 32;\n\n return { x, y };\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n/**\n * Return if the given array contains the given element\n * @param {Array} array The array to search for the given element.\n * @param {Object} el The element to look for into the array\n */\nexport function contains(arr: any[], el: any): boolean {\n return arr.indexOf(el) >= 0;\n};\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { EventEmitter } from '../EventEmitter';\nimport { ICircularList } from '../Interfaces';\n\n/**\n * Represents a circular list; a list with a maximum size that wraps around when push is called,\n * overriding values at the start of the list.\n */\nexport class CircularList extends EventEmitter implements ICircularList {\n protected _array: T[];\n private _startIndex: number;\n private _length: number;\n\n constructor(\n private _maxLength: number\n ) {\n super();\n this._array = new Array(this._maxLength);\n this._startIndex = 0;\n this._length = 0;\n }\n\n public get maxLength(): number {\n return this._maxLength;\n }\n\n public set maxLength(newMaxLength: number) {\n // There was no change in maxLength, return early.\n if (this._maxLength === newMaxLength) {\n return;\n }\n\n // Reconstruct array, starting at index 0. Only transfer values from the\n // indexes 0 to length.\n let newArray = new Array(newMaxLength);\n for (let i = 0; i < Math.min(newMaxLength, this.length); i++) {\n newArray[i] = this._array[this._getCyclicIndex(i)];\n }\n this._array = newArray;\n this._maxLength = newMaxLength;\n this._startIndex = 0;\n }\n\n public get length(): number {\n return this._length;\n }\n\n public set length(newLength: number) {\n if (newLength > this._length) {\n for (let i = this._length; i < newLength; i++) {\n this._array[i] = undefined;\n }\n }\n this._length = newLength;\n }\n\n public get forEach(): (callbackfn: (value: T, index: number) => void) => void {\n return (callbackfn: (value: T, index: number) => void) => {\n let i = 0;\n let length = this.length;\n for (let i = 0; i < length; i++) {\n callbackfn(this.get(i), i);\n }\n };\n }\n\n /**\n * Gets the value at an index.\n *\n * Note that for performance reasons there is no bounds checking here, the index reference is\n * circular so this should always return a value and never throw.\n * @param index The index of the value to get.\n * @return The value corresponding to the index.\n */\n public get(index: number): T {\n return this._array[this._getCyclicIndex(index)];\n }\n\n /**\n * Sets the value at an index.\n *\n * Note that for performance reasons there is no bounds checking here, the index reference is\n * circular so this should always return a value and never throw.\n * @param index The index to set.\n * @param value The value to set.\n */\n public set(index: number, value: T): void {\n this._array[this._getCyclicIndex(index)] = value;\n }\n\n /**\n * Pushes a new value onto the list, wrapping around to the start of the array, overriding index 0\n * if the maximum length is reached.\n * @param value The value to push onto the list.\n */\n public push(value: T): void {\n this._array[this._getCyclicIndex(this._length)] = value;\n if (this._length === this._maxLength) {\n this._startIndex++;\n if (this._startIndex === this._maxLength) {\n this._startIndex = 0;\n }\n this.emit('trim', 1);\n } else {\n this._length++;\n }\n }\n\n /**\n * Removes and returns the last value on the list.\n * @return The popped value.\n */\n public pop(): T {\n return this._array[this._getCyclicIndex(this._length-- - 1)];\n }\n\n /**\n * Deletes and/or inserts items at a particular index (in that order). Unlike\n * Array.prototype.splice, this operation does not return the deleted items as a new array in\n * order to save creating a new array. Note that this operation may shift all values in the list\n * in the worst case.\n * @param start The index to delete and/or insert.\n * @param deleteCount The number of elements to delete.\n * @param items The items to insert.\n */\n public splice(start: number, deleteCount: number, ...items: T[]): void {\n // Delete items\n if (deleteCount) {\n for (let i = start; i < this._length - deleteCount; i++) {\n this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)];\n }\n this._length -= deleteCount;\n }\n\n if (items && items.length) {\n // Add items\n for (let i = this._length - 1; i >= start; i--) {\n this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)];\n }\n for (let i = 0; i < items.length; i++) {\n this._array[this._getCyclicIndex(start + i)] = items[i];\n }\n\n // Adjust length as needed\n if (this._length + items.length > this.maxLength) {\n const countToTrim = (this._length + items.length) - this.maxLength;\n this._startIndex += countToTrim;\n this._length = this.maxLength;\n this.emit('trim', countToTrim);\n } else {\n this._length += items.length;\n }\n }\n }\n\n /**\n * Trims a number of items from the start of the list.\n * @param count The number of items to remove.\n */\n public trimStart(count: number): void {\n if (count > this._length) {\n count = this._length;\n }\n this._startIndex += count;\n this._length -= count;\n this.emit('trim', count);\n }\n\n public shiftElements(start: number, count: number, offset: number): void {\n if (count <= 0) {\n return;\n }\n if (start < 0 || start >= this._length) {\n throw new Error('start argument out of range');\n }\n if (start + offset < 0) {\n throw new Error('Cannot shift elements in list beyond index 0');\n }\n\n if (offset > 0) {\n for (let i = count - 1; i >= 0; i--) {\n this.set(start + i + offset, this.get(start + i));\n }\n const expandListBy = (start + count + offset) - this._length;\n if (expandListBy > 0) {\n this._length += expandListBy;\n while (this._length > this.maxLength) {\n this._length--;\n this._startIndex++;\n this.emit('trim', 1);\n }\n }\n } else {\n for (let i = 0; i < count; i++) {\n this.set(start + i + offset, this.get(start + i));\n }\n }\n }\n\n /**\n * Gets the cyclic index for the specified regular index. The cyclic index can then be used on the\n * backing array to get the element associated with the regular index.\n * @param index The regular index.\n * @returns The cyclic index.\n */\n private _getCyclicIndex(index: number): number {\n return (this._startIndex + index) % this.maxLength;\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { EventEmitter } from '../EventEmitter';\nimport { ICharMeasure, ITerminal, ITerminalOptions } from '../Interfaces';\n\n/**\n * Utility class that measures the size of a character. Measurements are done in\n * the DOM rather than with a canvas context because support for extracting the\n * height of characters is patchy across browsers.\n */\nexport class CharMeasure extends EventEmitter implements ICharMeasure {\n private _document: Document;\n private _parentElement: HTMLElement;\n private _measureElement: HTMLElement;\n private _width: number;\n private _height: number;\n\n constructor(document: Document, parentElement: HTMLElement) {\n super();\n this._document = document;\n this._parentElement = parentElement;\n }\n\n public get width(): number {\n return this._width;\n }\n\n public get height(): number {\n return this._height;\n }\n\n public measure(options: ITerminalOptions): void {\n if (!this._measureElement) {\n this._measureElement = this._document.createElement('span');\n this._measureElement.style.position = 'absolute';\n this._measureElement.style.top = '0';\n this._measureElement.style.left = '-9999em';\n this._measureElement.style.lineHeight = 'normal';\n this._measureElement.textContent = 'W';\n this._measureElement.setAttribute('aria-hidden', 'true');\n this._parentElement.appendChild(this._measureElement);\n // Perform _doMeasure async if the element was just attached as sometimes\n // getBoundingClientRect does not return accurate values without this.\n setTimeout(() => this._doMeasure(options), 0);\n } else {\n this._doMeasure(options);\n }\n }\n\n private _doMeasure(options: ITerminalOptions): void {\n this._measureElement.style.fontFamily = options.fontFamily;\n this._measureElement.style.fontSize = `${options.fontSize}px`;\n const geometry = this._measureElement.getBoundingClientRect();\n // The element is likely currently display:none, we should retain the\n // previous value.\n if (geometry.width === 0 || geometry.height === 0) {\n return;\n }\n if (this._width !== geometry.width || this._height !== geometry.height) {\n this._width = geometry.width;\n this._height = Math.ceil(geometry.height);\n this.emit('charsizechanged');\n }\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { contains } from './Generic';\n\nconst isNode = (typeof navigator === 'undefined') ? true : false;\nconst userAgent = (isNode) ? 'node' : navigator.userAgent;\nconst platform = (isNode) ? 'node' : navigator.platform;\n\nexport const isFirefox = !!~userAgent.indexOf('Firefox');\nexport const isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident');\n\n// Find the users platform. We use this to interpret the meta key\n// and ISO third level shifts.\n// http://stackoverflow.com/q/19877924/577598\nexport const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);\nexport const isIpad = platform === 'iPad';\nexport const isIphone = platform === 'iPhone';\nexport const isMSWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);\nexport const isLinux = platform.indexOf('Linux') >= 0;\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n /**\n * Flags used to render terminal text properly.\n */\nexport enum FLAGS {\n BOLD = 1,\n UNDERLINE = 2,\n BLINK = 4,\n INVERSE = 8,\n INVISIBLE = 16,\n DIM = 32\n};\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal } from '../Interfaces';\nimport { CHAR_DATA_ATTR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from '../Buffer';\nimport { FLAGS } from './Types';\nimport { GridCache } from './GridCache';\nimport { CharData } from '../Types';\nimport { BaseRenderLayer, INVERTED_DEFAULT_COLOR } from './BaseRenderLayer';\n\n/**\n * This CharData looks like a null character, which will forc a clear and render\n * when the character changes (a regular space ' ' character may not as it's\n * drawn state is a cleared cell).\n */\nconst OVERLAP_OWNED_CHAR_DATA: CharData = [null, '', 0, -1];\n\nexport class TextRenderLayer extends BaseRenderLayer {\n private _state: GridCache;\n private _characterWidth: number;\n private _characterFont: string;\n private _characterOverlapCache: { [key: string]: boolean } = {};\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {\n super(container, 'text', zIndex, false, colors);\n this._state = new GridCache();\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n\n // Clear the character width cache if the font or width has changed\n const terminalFont = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;\n if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {\n this._characterWidth = dim.scaledCharWidth;\n this._characterFont = terminalFont;\n this._characterOverlapCache = {};\n }\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state.clear();\n this._state.resize(terminal.cols, terminal.rows);\n }\n\n public reset(terminal: ITerminal): void {\n this._state.clear();\n this.clearAll();\n }\n\n public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {\n // Resize has not been called yet\n if (this._state.cache.length === 0) {\n return;\n }\n\n for (let y = startRow; y <= endRow; y++) {\n const row = y + terminal.buffer.ydisp;\n const line = terminal.buffer.lines.get(row);\n\n this.clearCells(0, y, terminal.cols, 1);\n // for (let x = 0; x < terminal.cols; x++) {\n // this._state.cache[x][y] = null;\n // }\n\n for (let x = 0; x < terminal.cols; x++) {\n const charData = line[x];\n const code: number = charData[CHAR_DATA_CODE_INDEX];\n const char: string = charData[CHAR_DATA_CHAR_INDEX];\n const attr: number = charData[CHAR_DATA_ATTR_INDEX];\n let width: number = charData[CHAR_DATA_WIDTH_INDEX];\n\n // The character to the left is a wide character, drawing is owned by\n // the char at x-1\n if (width === 0) {\n // this._state.cache[x][y] = null;\n continue;\n }\n\n // If the character is a space and the character to the left is an\n // overlapping character, skip the character and allow the overlapping\n // char to take full control over this character's cell.\n if (code === 32 /*' '*/) {\n if (x > 0) {\n const previousChar: CharData = line[x - 1];\n if (this._isOverlapping(previousChar)) {\n continue;\n }\n }\n }\n\n // Skip rendering if the character is identical\n // const state = this._state.cache[x][y];\n // if (state && state[CHAR_DATA_CHAR_INDEX] === char && state[CHAR_DATA_ATTR_INDEX] === attr) {\n // // Skip render, contents are identical\n // this._state.cache[x][y] = charData;\n // continue;\n // }\n\n // Clear the old character was not a space with the default background\n // const wasInverted = !!(state && state[CHAR_DATA_ATTR_INDEX] && state[CHAR_DATA_ATTR_INDEX] >> 18 & FLAGS.INVERSE);\n // if (state && !(state[CHAR_DATA_CODE_INDEX] === 32 /*' '*/ && (state[CHAR_DATA_ATTR_INDEX] & 0x1ff) >= 256 && !wasInverted)) {\n // this._clearChar(x, y);\n // }\n // this._state.cache[x][y] = charData;\n\n const flags = attr >> 18;\n let bg = attr & 0x1ff;\n\n // Skip rendering if the character is invisible\n const isDefaultBackground = bg >= 256;\n const isInvisible = flags & FLAGS.INVISIBLE;\n const isInverted = flags & FLAGS.INVERSE;\n if (!code || (code === 32 /*' '*/ && isDefaultBackground && !isInverted) || isInvisible) {\n continue;\n }\n\n // If the character is an overlapping char and the character to the right is a\n // space, take ownership of the cell to the right.\n if (width !== 0 && this._isOverlapping(charData)) {\n // If the character is overlapping, we want to force a re-render on every\n // frame. This is specifically to work around the case where two\n // overlaping chars `a` and `b` are adjacent, the cursor is moved to b and a\n // space is added. Without this, the first half of `b` would never\n // get removed, and `a` would not re-render because it thinks it's\n // already in the correct state.\n // this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;\n if (x < line.length - 1 && line[x + 1][CHAR_DATA_CODE_INDEX] === 32 /*' '*/) {\n width = 2;\n // this._clearChar(x + 1, y);\n // The overlapping char's char data will force a clear and render when the\n // overlapping char is no longer to the left of the character and also when\n // the space changes to another character.\n // this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;\n }\n }\n\n let fg = (attr >> 9) & 0x1ff;\n\n // If inverse flag is on, the foreground should become the background.\n if (isInverted) {\n const temp = bg;\n bg = fg;\n fg = temp;\n if (fg === 256) {\n fg = INVERTED_DEFAULT_COLOR;\n }\n if (bg === 257) {\n bg = INVERTED_DEFAULT_COLOR;\n }\n }\n\n // Clear the cell next to this character if it's wide\n if (width === 2) {\n // this.clearCells(x + 1, y, 1, 1);\n }\n\n // Draw background\n if (bg < 256) {\n this._ctx.save();\n this._ctx.fillStyle = (bg === INVERTED_DEFAULT_COLOR ? this._colors.foreground : this._colors.ansi[bg]);\n this.fillCells(x, y, width, 1);\n this._ctx.restore();\n }\n\n this._ctx.save();\n if (flags & FLAGS.BOLD) {\n this._ctx.font = `bold ${this._ctx.font}`;\n // Convert the FG color to the bold variant\n if (fg < 8) {\n fg += 8;\n }\n }\n\n if (flags & FLAGS.UNDERLINE) {\n if (fg === INVERTED_DEFAULT_COLOR) {\n this._ctx.fillStyle = this._colors.background;\n } else if (fg < 256) {\n // 256 color support\n this._ctx.fillStyle = this._colors.ansi[fg];\n } else {\n this._ctx.fillStyle = this._colors.foreground;\n }\n this.fillBottomLineAtCells(x, y);\n }\n\n this.drawChar(terminal, char, code, width, x, y, fg, bg, !!(flags & FLAGS.BOLD), !!(flags & FLAGS.DIM));\n\n this._ctx.restore();\n }\n }\n }\n\n\t/**\n\t * Whether a character is overlapping to the next cell.\n\t */\n private _isOverlapping(charData: CharData): boolean {\n // Only single cell characters can be overlapping, rendering issues can\n // occur without this check\n if (charData[CHAR_DATA_WIDTH_INDEX] !== 1) {\n return false;\n }\n\n // We assume that any ascii character will not overlap\n const code = charData[CHAR_DATA_CODE_INDEX];\n if (code < 256) {\n return false;\n }\n\n // Deliver from cache if available\n const char = charData[CHAR_DATA_CHAR_INDEX];\n if (this._characterOverlapCache.hasOwnProperty(char)) {\n return this._characterOverlapCache[char];\n }\n\n // Setup the font\n this._ctx.save();\n this._ctx.font = this._characterFont;\n\n // Measure the width of the character, but Math.floor it\n // because that is what the renderer does when it calculates\n // the character dimensions we are comparing against\n const overlaps = Math.floor(this._ctx.measureText(char).width) > this._characterWidth;\n\n // Restore the original context\n this._ctx.restore();\n\n // Cache and return\n this._characterOverlapCache[char] = overlaps;\n return overlaps;\n }\n\n /**\n * Clear the charcater at the cell specified.\n * @param x The column of the char.\n * @param y The row of the char.\n */\n private _clearChar(x: number, y: number): void {\n let colsToClear = 1;\n // Clear the adjacent character if it was wide\n const state = this._state.cache[x][y];\n if (state && state[CHAR_DATA_WIDTH_INDEX] === 2) {\n colsToClear = 2;\n }\n this.clearCells(x, y, colsToClear, 1);\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal } from '../Interfaces';\nimport { CHAR_DATA_ATTR_INDEX } from '../Buffer';\nimport { GridCache } from './GridCache';\nimport { FLAGS } from './Types';\nimport { BaseRenderLayer } from './BaseRenderLayer';\n\nexport class SelectionRenderLayer extends BaseRenderLayer {\n private _state: {start: [number, number], end: [number, number]};\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {\n super(container, 'selection', zIndex, true, colors);\n this._state = {\n start: null,\n end: null\n };\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = {\n start: null,\n end: null\n };\n }\n\n public reset(terminal: ITerminal): void {\n if (this._state.start && this._state.end) {\n this._state = {\n start: null,\n end: null\n };\n this.clearAll();\n }\n }\n\n public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {\n // Selection has not changed\n if (this._state.start === start || this._state.end === end) {\n return;\n }\n\n // Remove all selections\n this.clearAll();\n\n // Selection does not exist\n if (!start || !end) {\n return;\n }\n\n // Translate from buffer position to viewport position\n const viewportStartRow = start[1] - terminal.buffer.ydisp;\n const viewportEndRow = end[1] - terminal.buffer.ydisp;\n const viewportCappedStartRow = Math.max(viewportStartRow, 0);\n const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1);\n\n // No need to draw the selection\n if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) {\n return;\n }\n\n // Draw first row\n const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;\n const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols;\n this._ctx.fillStyle = this._colors.selection;\n this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);\n\n // Draw middle rows\n const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);\n this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount);\n\n // Draw final row\n if (viewportCappedStartRow !== viewportCappedEndRow) {\n // Only draw viewportEndRow if it's not the same as viewportStartRow\n const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols;\n this.fillCells(0, viewportCappedEndRow, endCol, 1);\n }\n\n // Save state for next render\n this._state.start = [start[0], start[1]];\n this._state.end = [end[0], end[1]];\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, ITheme } from '../Interfaces';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer';\nimport { TextRenderLayer } from './TextRenderLayer';\nimport { SelectionRenderLayer } from './SelectionRenderLayer';\nimport { CursorRenderLayer } from './CursorRenderLayer';\nimport { ColorManager } from './ColorManager';\nimport { BaseRenderLayer } from './BaseRenderLayer';\nimport { IRenderLayer, IColorSet, IRenderer, IRenderDimensions } from './Interfaces';\nimport { LinkRenderLayer } from './LinkRenderLayer';\nimport { EventEmitter } from '../EventEmitter';\n\nexport class Renderer extends EventEmitter implements IRenderer {\n /** A queue of the rows to be refreshed */\n private _refreshRowsQueue: {start: number, end: number}[] = [];\n private _refreshAnimationFrame = null;\n\n private _renderLayers: IRenderLayer[];\n private _devicePixelRatio: number;\n\n public colorManager: ColorManager;\n public dimensions: IRenderDimensions;\n\n constructor(private _terminal: ITerminal, theme: ITheme) {\n super();\n this.colorManager = new ColorManager();\n if (theme) {\n this.colorManager.setTheme(theme);\n }\n this._renderLayers = [\n new TextRenderLayer(this._terminal.element, 0, this.colorManager.colors),\n new SelectionRenderLayer(this._terminal.element, 1, this.colorManager.colors),\n new LinkRenderLayer(this._terminal.element, 2, this.colorManager.colors, this._terminal),\n new CursorRenderLayer(this._terminal.element, 3, this.colorManager.colors)\n ];\n this.dimensions = {\n scaledCharWidth: null,\n scaledCharHeight: null,\n scaledCellWidth: null,\n scaledCellHeight: null,\n scaledCharLeft: null,\n scaledCharTop: null,\n scaledCanvasWidth: null,\n scaledCanvasHeight: null,\n canvasWidth: null,\n canvasHeight: null,\n actualCellWidth: null,\n actualCellHeight: null\n };\n this._devicePixelRatio = window.devicePixelRatio;\n }\n\n public onWindowResize(devicePixelRatio: number): void {\n // If the device pixel ratio changed, the char atlas needs to be regenerated\n // and the terminal needs to refreshed\n if (this._devicePixelRatio !== devicePixelRatio) {\n this._devicePixelRatio = devicePixelRatio;\n this.onResize(this._terminal.cols, this._terminal.rows, true);\n }\n }\n\n public setTheme(theme: ITheme): IColorSet {\n this.colorManager.setTheme(theme);\n\n // Clear layers and force a full render\n this._renderLayers.forEach(l => {\n l.onThemeChanged(this._terminal, this.colorManager.colors);\n l.reset(this._terminal);\n });\n\n this._terminal.refresh(0, this._terminal.rows - 1);\n\n return this.colorManager.colors;\n }\n\n public onResize(cols: number, rows: number, didCharSizeChange: boolean): void {\n if (!this._terminal.charMeasure.width || !this._terminal.charMeasure.height) {\n return;\n }\n\n // Calculate the scaled character width. Width is floored as it must be\n // drawn to an integer grid in order for the CharAtlas \"stamps\" to not be\n // blurry. When text is drawn to the grid not using the CharAtlas, it is\n // clipped to ensure there is no overlap with the next cell.\n this.dimensions.scaledCharWidth = Math.floor(this._terminal.charMeasure.width * window.devicePixelRatio);\n\n // Calculate the scaled character height. Height is ceiled in case\n // devicePixelRatio is a floating point number in order to ensure there is\n // enough space to draw the character to the cell.\n this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * window.devicePixelRatio);\n\n // Calculate the scaled cell height, if lineHeight is not 1 then the value\n // will be floored because since lineHeight can never be lower then 1, there\n // is a guarentee that the scaled line height will always be larger than\n // scaled char height.\n this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight);\n\n // Calculate the y coordinate within a cell that text should draw from in\n // order to draw in the center of a cell.\n this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);\n\n // Calculate the scaled cell width, taking the letterSpacing into account.\n this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing);\n\n // Calculate the x coordinate with a cell that text should draw from in\n // order to draw in the center of a cell.\n this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2);\n\n // Recalculate the canvas dimensions; scaled* define the actual number of\n // pixel in the canvas\n this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight;\n this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth;\n\n // The the size of the canvas on the page. It's very important that this\n // rounds to nearest integer and not ceils as browsers often set\n // window.devicePixelRatio as something like 1.100000023841858, when it's\n // actually 1.1. Ceiling causes blurriness as the backing canvas image is 1\n // pixel too large for the canvas element size.\n this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio);\n this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio);\n\n // Get the _actual_ dimensions of an individual cell. This needs to be\n // derived from the canvasWidth/Height calculated above which takes into\n // account window.devicePixelRatio. CharMeasure.width/height by itself is\n // insufficient when the page is not at 100% zoom level as CharMeasure is\n // measured in CSS pixels, but the actual char size on the canvas can\n // differ.\n this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows;\n this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols;\n\n // Resize all render layers\n this._renderLayers.forEach(l => l.resize(this._terminal, this.dimensions, didCharSizeChange));\n\n // Force a refresh\n this._terminal.refresh(0, this._terminal.rows - 1);\n\n this.emit('resize', {\n width: this.dimensions.canvasWidth,\n height: this.dimensions.canvasHeight\n });\n }\n\n public onCharSizeChanged(): void {\n this.onResize(this._terminal.cols, this._terminal.rows, true);\n }\n\n public onBlur(): void {\n this._renderLayers.forEach(l => l.onBlur(this._terminal));\n }\n\n public onFocus(): void {\n this._renderLayers.forEach(l => l.onFocus(this._terminal));\n }\n\n public onSelectionChanged(start: [number, number], end: [number, number]): void {\n this._renderLayers.forEach(l => l.onSelectionChanged(this._terminal, start, end));\n }\n\n public onCursorMove(): void {\n this._renderLayers.forEach(l => l.onCursorMove(this._terminal));\n }\n\n public onOptionsChanged(): void {\n this._renderLayers.forEach(l => l.onOptionsChanged(this._terminal));\n }\n\n public clear(): void {\n this._renderLayers.forEach(l => l.reset(this._terminal));\n }\n\n /**\n * Queues a refresh between two rows (inclusive), to be done on next animation\n * frame.\n * @param {number} start The start row.\n * @param {number} end The end row.\n */\n public queueRefresh(start: number, end: number): void {\n this._refreshRowsQueue.push({ start: start, end: end });\n if (!this._refreshAnimationFrame) {\n this._refreshAnimationFrame = window.requestAnimationFrame(this._refreshLoop.bind(this));\n }\n }\n\n /**\n * Performs the refresh loop callback, calling refresh only if a refresh is\n * necessary before queueing up the next one.\n */\n private _refreshLoop(): void {\n let start;\n let end;\n if (this._refreshRowsQueue.length > 4) {\n // Just do a full refresh when 5+ refreshes are queued\n start = 0;\n end = this._terminal.rows - 1;\n } else {\n // Get start and end rows that need refreshing\n start = this._refreshRowsQueue[0].start;\n end = this._refreshRowsQueue[0].end;\n for (let i = 1; i < this._refreshRowsQueue.length; i++) {\n if (this._refreshRowsQueue[i].start < start) {\n start = this._refreshRowsQueue[i].start;\n }\n if (this._refreshRowsQueue[i].end > end) {\n end = this._refreshRowsQueue[i].end;\n }\n }\n }\n this._refreshRowsQueue = [];\n this._refreshAnimationFrame = null;\n\n // Render\n start = Math.max(start, 0);\n end = Math.min(end, this._terminal.rows - 1);\n this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end));\n this._terminal.emit('refresh', {start, end});\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal, ILinkifierAccessor } from '../Interfaces';\nimport { CHAR_DATA_ATTR_INDEX } from '../Buffer';\nimport { GridCache } from './GridCache';\nimport { FLAGS } from './Types';\nimport { BaseRenderLayer, INVERTED_DEFAULT_COLOR } from './BaseRenderLayer';\nimport { LinkHoverEvent, LinkHoverEventTypes } from '../Types';\n\nexport class LinkRenderLayer extends BaseRenderLayer {\n private _state: LinkHoverEvent = null;\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) {\n super(container, 'link', zIndex, true, colors);\n terminal.linkifier.on(LinkHoverEventTypes.HOVER, (e: LinkHoverEvent) => this._onLinkHover(e));\n terminal.linkifier.on(LinkHoverEventTypes.LEAVE, (e: LinkHoverEvent) => this._onLinkLeave(e));\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = null;\n }\n\n public reset(terminal: ITerminal): void {\n this._clearCurrentLink();\n }\n\n private _clearCurrentLink(): void {\n if (this._state) {\n this.clearCells(this._state.x, this._state.y, this._state.length, 1);\n this._state = null;\n }\n }\n\n private _onLinkHover(e: LinkHoverEvent): void {\n this._ctx.fillStyle = this._colors.foreground;\n this.fillBottomLineAtCells(e.x, e.y, e.length);\n this._state = e;\n }\n\n private _onLinkLeave(e: LinkHoverEvent): void {\n this._clearCurrentLink();\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport class GridCache {\n public cache: T[][];\n\n public constructor() {\n this.cache = [];\n }\n\n public resize(width: number, height: number): void {\n for (let x = 0; x < width; x++) {\n if (this.cache.length <= x) {\n this.cache.push([]);\n }\n for (let y = this.cache[x].length; y < height; y++) {\n this.cache[x].push(null);\n }\n this.cache[x].length = height;\n }\n this.cache.length = width;\n }\n\n public clear(): void {\n for (let x = 0; x < this.cache.length; x++) {\n for (let y = 0; y < this.cache[x].length; y++) {\n this.cache[x][y] = null;\n }\n }\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal, ITerminalOptions } from '../Interfaces';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer';\nimport { GridCache } from './GridCache';\nimport { FLAGS } from './Types';\nimport { BaseRenderLayer } from './BaseRenderLayer';\nimport { CharData } from '../Types';\n\ninterface CursorState {\n x: number;\n y: number;\n isFocused: boolean;\n style: string;\n width: number;\n}\n\n/**\n * The time between cursor blinks.\n */\nconst BLINK_INTERVAL = 600;\n\nexport class CursorRenderLayer extends BaseRenderLayer {\n private _state: CursorState;\n private _cursorRenderers: {[key: string]: (terminal: ITerminal, x: number, y: number, charData: CharData) => void};\n private _cursorBlinkStateManager: CursorBlinkStateManager;\n private _isFocused: boolean;\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {\n super(container, 'cursor', zIndex, true, colors);\n this._state = {\n x: null,\n y: null,\n isFocused: null,\n style: null,\n width: null,\n };\n this._cursorRenderers = {\n 'bar': this._renderBarCursor.bind(this),\n 'block': this._renderBlockCursor.bind(this),\n 'underline': this._renderUnderlineCursor.bind(this)\n };\n // TODO: Consider initial options? Maybe onOptionsChanged should be called at the end of open?\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = {\n x: null,\n y: null,\n isFocused: null,\n style: null,\n width: null,\n };\n }\n\n public reset(terminal: ITerminal): void {\n this._clearCursor();\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.dispose();\n this._cursorBlinkStateManager = null;\n this.onOptionsChanged(terminal);\n }\n }\n\n public onBlur(terminal: ITerminal): void {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.pause();\n }\n terminal.refresh(terminal.buffer.y, terminal.buffer.y);\n }\n\n public onFocus(terminal: ITerminal): void {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.resume(terminal);\n } else {\n terminal.refresh(terminal.buffer.y, terminal.buffer.y);\n }\n }\n\n public onOptionsChanged(terminal: ITerminal): void {\n if (terminal.options.cursorBlink) {\n if (!this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, () => {\n this._render(terminal, true);\n });\n }\n } else {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.dispose();\n this._cursorBlinkStateManager = null;\n }\n // Request a refresh from the terminal as management of rendering is being\n // moved back to the terminal\n terminal.refresh(terminal.buffer.y, terminal.buffer.y);\n }\n }\n\n public onCursorMove(terminal: ITerminal): void {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.restartBlinkAnimation(terminal);\n }\n }\n\n public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {\n // Only render if the animation frame is not active\n if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) {\n this._render(terminal, false);\n }\n }\n\n private _render(terminal: ITerminal, triggeredByAnimationFrame: boolean): void {\n // Don't draw the cursor if it's hidden\n if (!terminal.cursorState || terminal.cursorHidden) {\n this._clearCursor();\n return;\n }\n\n const cursorY = terminal.buffer.ybase + terminal.buffer.y;\n const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp;\n\n // Don't draw the cursor if it's off-screen\n if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= terminal.rows) {\n this._clearCursor();\n return;\n }\n\n const charData = terminal.buffer.lines.get(cursorY)[terminal.buffer.x];\n if (!charData) {\n return;\n }\n\n if (!terminal.isFocused) {\n this._clearCursor();\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, charData);\n this._ctx.restore();\n this._state.x = terminal.buffer.x;\n this._state.y = viewportRelativeCursorY;\n this._state.isFocused = false;\n this._state.style = terminal.options.cursorStyle;\n this._state.width = charData[CHAR_DATA_WIDTH_INDEX];\n return;\n }\n\n // Don't draw the cursor if it's blinking\n if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) {\n this._clearCursor();\n return;\n }\n\n if (this._state) {\n // The cursor is already in the correct spot, don't redraw\n if (this._state.x === terminal.buffer.x &&\n this._state.y === viewportRelativeCursorY &&\n this._state.isFocused === terminal.isFocused &&\n this._state.style === terminal.options.cursorStyle &&\n this._state.width === charData[CHAR_DATA_WIDTH_INDEX]) {\n return;\n }\n this._clearCursor();\n }\n\n this._ctx.save();\n this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, charData);\n this._ctx.restore();\n\n this._state.x = terminal.buffer.x;\n this._state.y = viewportRelativeCursorY;\n this._state.isFocused = false;\n this._state.style = terminal.options.cursorStyle;\n this._state.width = charData[CHAR_DATA_WIDTH_INDEX];\n }\n\n private _clearCursor(): void {\n if (this._state) {\n this.clearCells(this._state.x, this._state.y, this._state.width, 1);\n this._state = {\n x: null,\n y: null,\n isFocused: null,\n style: null,\n width: null,\n };\n }\n }\n\n private _renderBarCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this.fillLeftLineAtCell(x, y);\n this._ctx.restore();\n }\n\n private _renderBlockCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this.fillCells(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1);\n this._ctx.fillStyle = this._colors.cursorAccent;\n this.fillCharTrueColor(terminal, charData, x, y);\n this._ctx.restore();\n }\n\n private _renderUnderlineCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this.fillBottomLineAtCells(x, y);\n this._ctx.restore();\n }\n\n private _renderBlurCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.strokeStyle = this._colors.cursor;\n this.strokeRectAtCell(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1);\n this._ctx.restore();\n }\n}\n\nclass CursorBlinkStateManager {\n public isCursorVisible: boolean;\n\n private _animationFrame: number;\n private _blinkStartTimeout: number;\n private _blinkInterval: number;\n\n /**\n * The time at which the animation frame was restarted, this is used on the\n * next render to restart the timers so they don't need to restart the timers\n * multiple times over a short period.\n */\n private _animationTimeRestarted: number;\n\n constructor(\n terminal: ITerminal,\n private renderCallback: () => void\n ) {\n this.isCursorVisible = true;\n if (terminal.isFocused) {\n this._restartInterval();\n }\n }\n\n public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); }\n\n public dispose(): void {\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n this._blinkInterval = null;\n }\n if (this._blinkStartTimeout) {\n window.clearTimeout(this._blinkStartTimeout);\n this._blinkStartTimeout = null;\n }\n if (this._animationFrame) {\n window.cancelAnimationFrame(this._animationFrame);\n this._animationFrame = null;\n }\n }\n\n public restartBlinkAnimation(terminal: ITerminal): void {\n if (this.isPaused) {\n return;\n }\n // Save a timestamp so that the restart can be done on the next interval\n this._animationTimeRestarted = Date.now();\n // Force a cursor render to ensure it's visible and in the correct position\n this.isCursorVisible = true;\n if (!this._animationFrame) {\n this._animationFrame = window.requestAnimationFrame(() => {\n this.renderCallback();\n this._animationFrame = null;\n });\n }\n }\n\n private _restartInterval(timeToStart: number = BLINK_INTERVAL): void {\n // Clear any existing interval\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n }\n\n // Setup the initial timeout which will hide the cursor, this is done before\n // the regular interval is setup in order to support restarting the blink\n // animation in a lightweight way (without thrashing clearInterval and\n // setInterval).\n this._blinkStartTimeout = setTimeout(() => {\n // Check if another animation restart was requested while this was being\n // started\n if (this._animationTimeRestarted) {\n const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);\n this._animationTimeRestarted = null;\n if (time > 0) {\n this._restartInterval(time);\n return;\n }\n }\n\n // Hide the cursor\n this.isCursorVisible = false;\n this._animationFrame = window.requestAnimationFrame(() => {\n this.renderCallback();\n this._animationFrame = null;\n });\n\n // Setup the blink interval\n this._blinkInterval = setInterval(() => {\n // Adjust the animation time if it was restarted\n if (this._animationTimeRestarted) {\n // calc time diff\n // Make restart interval do a setTimeout initially?\n const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);\n this._animationTimeRestarted = null;\n this._restartInterval(time);\n return;\n }\n\n // Invert visibility and render\n this.isCursorVisible = !this.isCursorVisible;\n this._animationFrame = window.requestAnimationFrame(() => {\n this.renderCallback();\n this._animationFrame = null;\n });\n }, BLINK_INTERVAL);\n }, timeToStart);\n }\n\n public pause(): void {\n this.isCursorVisible = true;\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n this._blinkInterval = null;\n }\n if (this._blinkStartTimeout) {\n window.clearTimeout(this._blinkStartTimeout);\n this._blinkStartTimeout = null;\n }\n if (this._animationFrame) {\n window.cancelAnimationFrame(this._animationFrame);\n this._animationFrame = null;\n }\n }\n\n public resume(terminal: ITerminal): void {\n this._animationTimeRestarted = null;\n this._restartInterval();\n this.restartBlinkAnimation(terminal);\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IColorManager } from './Interfaces';\nimport { ITheme } from '../Interfaces';\n\nconst DEFAULT_FOREGROUND = '#ffffff';\nconst DEFAULT_BACKGROUND = '#000000';\nconst DEFAULT_CURSOR = '#ffffff';\nconst DEFAULT_CURSOR_ACCENT = '#000000';\nconst DEFAULT_SELECTION = 'rgba(255, 255, 255, 0.3)';\nexport const DEFAULT_ANSI_COLORS = [\n // dark:\n '#2e3436',\n '#cc0000',\n '#4e9a06',\n '#c4a000',\n '#3465a4',\n '#75507b',\n '#06989a',\n '#d3d7cf',\n // bright:\n '#555753',\n '#ef2929',\n '#8ae234',\n '#fce94f',\n '#729fcf',\n '#ad7fa8',\n '#34e2e2',\n '#eeeeec'\n];\n\n/**\n * Fills an existing 16 length string with the remaining 240 ANSI colors.\n * @param first16Colors The first 16 ANSI colors.\n */\nfunction generate256Colors(first16Colors: string[]): string[] {\n let colors = first16Colors.slice();\n\n // Generate colors (16-231)\n let v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];\n for (let i = 0; i < 216; i++) {\n const r = toPaddedHex(v[(i / 36) % 6 | 0]);\n const g = toPaddedHex(v[(i / 6) % 6 | 0]);\n const b = toPaddedHex(v[i % 6]);\n colors.push(`#${r}${g}${b}`);\n }\n\n // Generate greys (232-255)\n for (let i = 0; i < 24; i++) {\n const c = toPaddedHex(8 + i * 10);\n colors.push(`#${c}${c}${c}`);\n }\n\n return colors;\n}\n\nfunction toPaddedHex(c: number): string {\n let s = c.toString(16);\n return s.length < 2 ? '0' + s : s;\n}\n\n/**\n * Manages the source of truth for a terminal's colors.\n */\nexport class ColorManager implements IColorManager {\n public colors: IColorSet;\n\n constructor() {\n this.colors = {\n foreground: DEFAULT_FOREGROUND,\n background: DEFAULT_BACKGROUND,\n cursor: DEFAULT_CURSOR,\n cursorAccent: DEFAULT_CURSOR_ACCENT,\n selection: DEFAULT_SELECTION,\n ansi: generate256Colors(DEFAULT_ANSI_COLORS)\n };\n }\n\n /**\n * Sets the terminal's theme.\n * @param theme The theme to use. If a partial theme is provided then default\n * colors will be used where colors are not defined.\n */\n public setTheme(theme: ITheme): void {\n this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND;\n this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND);\n this.colors.cursor = theme.cursor || DEFAULT_CURSOR;\n this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT;\n this.colors.selection = theme.selection || DEFAULT_SELECTION;\n this.colors.ansi[0] = theme.black || DEFAULT_ANSI_COLORS[0];\n this.colors.ansi[1] = theme.red || DEFAULT_ANSI_COLORS[1];\n this.colors.ansi[2] = theme.green || DEFAULT_ANSI_COLORS[2];\n this.colors.ansi[3] = theme.yellow || DEFAULT_ANSI_COLORS[3];\n this.colors.ansi[4] = theme.blue || DEFAULT_ANSI_COLORS[4];\n this.colors.ansi[5] = theme.magenta || DEFAULT_ANSI_COLORS[5];\n this.colors.ansi[6] = theme.cyan || DEFAULT_ANSI_COLORS[6];\n this.colors.ansi[7] = theme.white || DEFAULT_ANSI_COLORS[7];\n this.colors.ansi[8] = theme.brightBlack || DEFAULT_ANSI_COLORS[8];\n this.colors.ansi[9] = theme.brightRed || DEFAULT_ANSI_COLORS[9];\n this.colors.ansi[10] = theme.brightGreen || DEFAULT_ANSI_COLORS[10];\n this.colors.ansi[11] = theme.brightYellow || DEFAULT_ANSI_COLORS[11];\n this.colors.ansi[12] = theme.brightBlue || DEFAULT_ANSI_COLORS[12];\n this.colors.ansi[13] = theme.brightMagenta || DEFAULT_ANSI_COLORS[13];\n this.colors.ansi[14] = theme.brightCyan || DEFAULT_ANSI_COLORS[14];\n this.colors.ansi[15] = theme.brightWhite || DEFAULT_ANSI_COLORS[15];\n }\n\n private _validateColor(color: string, fallback: string): string {\n if (!color) {\n return fallback;\n }\n if (color.length === 7 && color.charAt(0) === '#') {\n return color;\n }\n if (color.length === 4 && color.charAt(0) === '#') {\n const r = color.charAt(1);\n const g = color.charAt(2);\n const b = color.charAt(3);\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return fallback;\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, ITheme } from '../Interfaces';\nimport { IColorSet } from '../renderer/Interfaces';\nimport { isFirefox } from '../utils/Browser';\n\nexport const CHAR_ATLAS_CELL_SPACING = 1;\n\ninterface ICharAtlasConfig {\n fontSize: number;\n fontFamily: string;\n scaledCharWidth: number;\n scaledCharHeight: number;\n colors: IColorSet;\n}\n\ninterface ICharAtlasCacheEntry {\n bitmap: HTMLCanvasElement | Promise;\n config: ICharAtlasConfig;\n ownedBy: ITerminal[];\n}\n\nlet charAtlasCache: ICharAtlasCacheEntry[] = [];\n\n/**\n * Acquires a char atlas, either generating a new one or returning an existing\n * one that is in use by another terminal.\n * @param terminal The terminal.\n * @param colors The colors to use.\n */\nexport function acquireCharAtlas(terminal: ITerminal, colors: IColorSet, scaledCharWidth: number, scaledCharHeight: number): HTMLCanvasElement | Promise {\n const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors);\n\n // Check to see if the terminal already owns this config\n for (let i = 0; i < charAtlasCache.length; i++) {\n const entry = charAtlasCache[i];\n const ownedByIndex = entry.ownedBy.indexOf(terminal);\n if (ownedByIndex >= 0) {\n if (configEquals(entry.config, newConfig)) {\n return entry.bitmap;\n } else {\n // The configs differ, release the terminal from the entry\n if (entry.ownedBy.length === 1) {\n charAtlasCache.splice(i, 1);\n } else {\n entry.ownedBy.splice(ownedByIndex, 1);\n }\n break;\n }\n }\n }\n\n // Try match a char atlas from the cache\n for (let i = 0; i < charAtlasCache.length; i++) {\n const entry = charAtlasCache[i];\n if (configEquals(entry.config, newConfig)) {\n // Add the terminal to the cache entry and return\n entry.ownedBy.push(terminal);\n return entry.bitmap;\n }\n }\n\n const newEntry: ICharAtlasCacheEntry = {\n bitmap: generator.generate(scaledCharWidth, scaledCharHeight, terminal.options.fontSize, terminal.options.fontFamily, colors.background, colors.foreground, colors.ansi),\n config: newConfig,\n ownedBy: [terminal]\n };\n charAtlasCache.push(newEntry);\n return newEntry.bitmap;\n}\n\nfunction generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig {\n const clonedColors = {\n foreground: colors.foreground,\n background: colors.background,\n cursor: null,\n cursorAccent: null,\n selection: null,\n ansi: colors.ansi.slice(0, 16)\n };\n return {\n scaledCharWidth,\n scaledCharHeight,\n fontFamily: terminal.options.fontFamily,\n fontSize: terminal.options.fontSize,\n colors: clonedColors\n };\n}\n\nfunction configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean {\n for (let i = 0; i < a.colors.ansi.length; i++) {\n if (a.colors.ansi[i] !== b.colors.ansi[i]) {\n return false;\n }\n }\n return a.fontFamily === b.fontFamily &&\n a.fontSize === b.fontSize &&\n a.scaledCharWidth === b.scaledCharWidth &&\n a.scaledCharHeight === b.scaledCharHeight &&\n a.colors.foreground === b.colors.foreground &&\n a.colors.background === b.colors.background;\n}\n\nlet generator: CharAtlasGenerator;\n\n/**\n * Initializes the char atlas generator.\n * @param document The document.\n */\nexport function initialize(document: Document): void {\n if (!generator) {\n generator = new CharAtlasGenerator(document);\n }\n}\n\nclass CharAtlasGenerator {\n private _canvas: HTMLCanvasElement;\n private _ctx: CanvasRenderingContext2D;\n\n constructor(private _document: Document) {\n this._canvas = this._document.createElement('canvas');\n this._ctx = this._canvas.getContext('2d', {alpha: false});\n this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n }\n\n public generate(scaledCharWidth: number, scaledCharHeight: number, fontSize: number, fontFamily: string, background: string, foreground: string, ansiColors: string[]): HTMLCanvasElement | Promise {\n const cellWidth = scaledCharWidth + CHAR_ATLAS_CELL_SPACING;\n const cellHeight = scaledCharHeight + CHAR_ATLAS_CELL_SPACING;\n this._canvas.width = 255 * cellWidth;\n this._canvas.height = (/*default+default bold*/2 + /*0-15*/16) * cellHeight;\n\n this._ctx.fillStyle = background;\n this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);\n\n this._ctx.save();\n this._ctx.fillStyle = foreground;\n this._ctx.font = `${fontSize * window.devicePixelRatio}px ${fontFamily}`;\n this._ctx.textBaseline = 'top';\n\n // Default color\n for (let i = 0; i < 256; i++) {\n this._ctx.save();\n this._ctx.beginPath();\n this._ctx.rect(i * cellWidth, 0, cellWidth, cellHeight);\n this._ctx.clip();\n this._ctx.fillText(String.fromCharCode(i), i * cellWidth, 0);\n this._ctx.restore();\n }\n // Default color bold\n this._ctx.save();\n this._ctx.font = `bold ${this._ctx.font}`;\n for (let i = 0; i < 256; i++) {\n this._ctx.save();\n this._ctx.beginPath();\n this._ctx.rect(i * cellWidth, cellHeight, cellWidth, cellHeight);\n this._ctx.clip();\n this._ctx.fillText(String.fromCharCode(i), i * cellWidth, cellHeight);\n this._ctx.restore();\n }\n this._ctx.restore();\n\n // Colors 0-15\n this._ctx.font = `${fontSize * window.devicePixelRatio}px ${fontFamily}`;\n for (let colorIndex = 0; colorIndex < 16; colorIndex++) {\n // colors 8-15 are bold\n if (colorIndex === 8) {\n this._ctx.font = `bold ${this._ctx.font}`;\n }\n const y = (colorIndex + 2) * cellHeight;\n // Draw ascii characters\n for (let i = 0; i < 256; i++) {\n this._ctx.save();\n this._ctx.beginPath();\n this._ctx.rect(i * cellWidth, y, cellWidth, cellHeight);\n this._ctx.clip();\n this._ctx.fillStyle = ansiColors[colorIndex];\n this._ctx.fillText(String.fromCharCode(i), i * cellWidth, y);\n this._ctx.restore();\n }\n }\n this._ctx.restore();\n\n // Support is patchy for createImageBitmap at the moment, pass a canvas back\n // if support is lacking as drawImage works there too. Firefox is also\n // included here as ImageBitmap appears both buggy and has horrible\n // performance (tested on v55).\n if (!('createImageBitmap' in window) || isFirefox) {\n // Regenerate canvas and context as they are now owned by the char atlas\n const result = this._canvas;\n this._canvas = this._document.createElement('canvas');\n this._ctx = this._canvas.getContext('2d');\n this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n return result;\n }\n\n const charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);\n\n // Remove the background color from the image so characters may overlap\n const r = parseInt(background.substr(1, 2), 16);\n const g = parseInt(background.substr(3, 2), 16);\n const b = parseInt(background.substr(5, 2), 16);\n this._clearColor(charAtlasImageData, r, g, b);\n\n const promise = window.createImageBitmap(charAtlasImageData);\n // Clear the rect while the promise is in progress\n this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n return promise;\n }\n\n private _clearColor(imageData: ImageData, r: number, g: number, b: number): void {\n for (let offset = 0; offset < imageData.data.length; offset += 4) {\n if (imageData.data[offset] === r &&\n imageData.data[offset + 1] === g &&\n imageData.data[offset + 2] === b) {\n imageData.data[offset + 3] = 0;\n }\n }\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderLayer, IColorSet, IRenderDimensions } from './Interfaces';\nimport { ITerminal, ITerminalOptions } from '../Interfaces';\nimport { acquireCharAtlas, CHAR_ATLAS_CELL_SPACING } from './CharAtlas';\nimport { CharData } from '../Types';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer';\n\nexport const INVERTED_DEFAULT_COLOR = -1;\nconst DIM_OPACITY = 0.5;\n\nexport abstract class BaseRenderLayer implements IRenderLayer {\n private _canvas: HTMLCanvasElement;\n protected _ctx: CanvasRenderingContext2D;\n private _scaledCharWidth: number = 0;\n private _scaledCharHeight: number = 0;\n private _scaledCellWidth: number = 0;\n private _scaledCellHeight: number = 0;\n private _scaledCharLeft: number = 0;\n private _scaledCharTop: number = 0;\n\n private _charAtlas: HTMLCanvasElement | ImageBitmap;\n\n constructor(\n container: HTMLElement,\n id: string,\n zIndex: number,\n private _alpha: boolean,\n protected _colors: IColorSet\n ) {\n this._canvas = document.createElement('canvas');\n this._canvas.id = `xterm-${id}-layer`;\n this._canvas.style.zIndex = zIndex.toString();\n this._ctx = this._canvas.getContext('2d', {alpha: _alpha});\n this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n // Draw the background if this is an opaque layer\n if (!_alpha) {\n this.clearAll();\n }\n container.appendChild(this._canvas);\n }\n\n public onOptionsChanged(terminal: ITerminal): void {}\n public onBlur(terminal: ITerminal): void {}\n public onFocus(terminal: ITerminal): void {}\n public onCursorMove(terminal: ITerminal): void {}\n public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {}\n public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {}\n\n public onThemeChanged(terminal: ITerminal, colorSet: IColorSet): void {\n this._refreshCharAtlas(terminal, colorSet);\n }\n\n /**\n * Refreshes the char atlas, aquiring a new one if necessary.\n * @param terminal The terminal.\n * @param colorSet The color set to use for the char atlas.\n */\n private _refreshCharAtlas(terminal: ITerminal, colorSet: IColorSet): void {\n if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {\n return;\n }\n this._charAtlas = null;\n const result = acquireCharAtlas(terminal, this._colors, this._scaledCharWidth, this._scaledCharHeight);\n if (result instanceof HTMLCanvasElement) {\n this._charAtlas = result;\n } else {\n result.then(bitmap => this._charAtlas = bitmap);\n }\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n this._scaledCellWidth = dim.scaledCellWidth;\n this._scaledCellHeight = dim.scaledCellHeight;\n this._scaledCharWidth = dim.scaledCharWidth;\n this._scaledCharHeight = dim.scaledCharHeight;\n this._scaledCharLeft = dim.scaledCharLeft;\n this._scaledCharTop = dim.scaledCharTop;\n this._canvas.width = dim.scaledCanvasWidth;\n this._canvas.height = dim.scaledCanvasHeight;\n this._canvas.style.width = `${dim.canvasWidth}px`;\n this._canvas.style.height = `${dim.canvasHeight}px`;\n\n // Draw the background if this is an opaque layer\n if (!this._alpha) {\n this.clearAll();\n }\n\n if (charSizeChanged) {\n this._refreshCharAtlas(terminal, this._colors);\n }\n }\n\n public abstract reset(terminal: ITerminal): void;\n\n /**\n * Fills 1+ cells completely. This uses the existing fillStyle on the context.\n * @param x The column to start at.\n * @param y The row to start at\n * @param width The number of columns to fill.\n * @param height The number of rows to fill.\n */\n protected fillCells(x: number, y: number, width: number, height: number): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n }\n\n /**\n * Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the\n * existing fillStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected fillBottomLineAtCells(x: number, y: number, width: number = 1): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,\n width * this._scaledCellWidth,\n window.devicePixelRatio);\n }\n\n /**\n * Fills a 1px line (2px on HDPI) at the left of the cell. This uses the\n * existing fillStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected fillLeftLineAtCell(x: number, y: number): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n window.devicePixelRatio,\n this._scaledCellHeight);\n }\n\n /**\n * Strokes a 1px rectangle (2px on HDPI) around a cell. This uses the existing\n * strokeStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected strokeRectAtCell(x: number, y: number, width: number, height: number): void {\n this._ctx.lineWidth = window.devicePixelRatio;\n this._ctx.strokeRect(\n x * this._scaledCellWidth + window.devicePixelRatio / 2,\n y * this._scaledCellHeight + (window.devicePixelRatio / 2),\n width * this._scaledCellWidth - window.devicePixelRatio,\n (height * this._scaledCellHeight) - window.devicePixelRatio);\n }\n\n /**\n * Clears the entire canvas.\n */\n protected clearAll(): void {\n if (this._alpha) {\n this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n } else {\n this._ctx.fillStyle = this._colors.background;\n this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);\n }\n }\n\n /**\n * Clears 1+ cells completely.\n * @param x The column to start at.\n * @param y The row to start at.\n * @param width The number of columns to clear.\n * @param height The number of rows to clear.\n */\n protected clearCells(x: number, y: number, width: number, height: number): void {\n if (this._alpha) {\n this._ctx.clearRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n } else {\n this._ctx.fillStyle = this._colors.background;\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n }\n }\n\n /**\n * Draws a truecolor character at the cell. The character will be clipped to\n * ensure that it fits with the cell, including the cell to the right if it's\n * a wide character. This uses the existing fillStyle on the context.\n * @param terminal The terminal.\n * @param charData The char data for the character to draw.\n * @param x The column to draw at.\n * @param y The row to draw at.\n * @param color The color of the character.\n */\n protected fillCharTrueColor(terminal: ITerminal, charData: CharData, x: number, y: number): void {\n this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;\n this._ctx.textBaseline = 'top';\n this._clipRow(terminal, y);\n this._ctx.fillText(\n charData[CHAR_DATA_CHAR_INDEX],\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop);\n }\n\n /**\n * Draws a character at a cell. If possible this will draw using the character\n * atlas to reduce draw time.\n * @param terminal The terminal.\n * @param char The character.\n * @param code The character code.\n * @param width The width of the character.\n * @param x The column to draw at.\n * @param y The row to draw at.\n * @param fg The foreground color, in the format stored within the attributes.\n * @param bg The background color, in the format stored within the attributes.\n * This is used to validate whether a cached image can be used.\n * @param bold Whether the text is bold.\n */\n protected drawChar(terminal: ITerminal, char: string, code: number, width: number, x: number, y: number, fg: number, bg: number, bold: boolean, dim: boolean): void {\n let colorIndex = 0;\n if (fg < 256) {\n colorIndex = fg + 2;\n } else {\n // If default color and bold\n if (bold && terminal.options.enableBold) {\n colorIndex = 1;\n }\n }\n const isAscii = code < 256;\n // A color is basic if it is one of the standard normal or bold weight\n // colors of the characters held in the char atlas. Note that this excludes\n // the normal weight _light_ color characters.\n const isBasicColor = (colorIndex > 1 && fg < 16) && (fg < 8 || bold);\n const isDefaultColor = fg >= 256;\n const isDefaultBackground = bg >= 256;\n if (this._charAtlas && isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground) {\n // ImageBitmap's draw about twice as fast as from a canvas\n const charAtlasCellWidth = this._scaledCharWidth + CHAR_ATLAS_CELL_SPACING;\n const charAtlasCellHeight = this._scaledCharHeight + CHAR_ATLAS_CELL_SPACING;\n\n // Apply alpha to dim the character\n if (dim) {\n this._ctx.globalAlpha = DIM_OPACITY;\n }\n\n // Draw the non-bold version of the same color if bold is not enabled\n if (bold && !terminal.options.enableBold) {\n // Ignore default color as it's not touched above\n if (colorIndex > 1) {\n colorIndex -= 8;\n }\n }\n\n this._ctx.drawImage(this._charAtlas,\n code * charAtlasCellWidth,\n colorIndex * charAtlasCellHeight,\n charAtlasCellWidth,\n this._scaledCharHeight,\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop,\n charAtlasCellWidth,\n this._scaledCharHeight);\n } else {\n this._drawUncachedChar(terminal, char, width, fg, x, y, bold, dim);\n }\n // This draws the atlas (for debugging purposes)\n // this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n // this._ctx.drawImage(this._charAtlas, 0, 0);\n }\n\n /**\n * Draws a character at a cell. The character will be clipped to\n * ensure that it fits with the cell, including the cell to the right if it's\n * a wide character.\n * @param terminal The terminal.\n * @param char The character.\n * @param width The width of the character.\n * @param fg The foreground color, in the format stored within the attributes.\n * @param x The column to draw at.\n * @param y The row to draw at.\n */\n private _drawUncachedChar(terminal: ITerminal, char: string, width: number, fg: number, x: number, y: number, bold: boolean, dim: boolean): void {\n this._ctx.save();\n this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;\n if (bold && terminal.options.enableBold) {\n this._ctx.font = `bold ${this._ctx.font}`;\n }\n this._ctx.textBaseline = 'top';\n\n if (fg === INVERTED_DEFAULT_COLOR) {\n this._ctx.fillStyle = this._colors.background;\n } else if (fg < 256) {\n // 256 color support\n this._ctx.fillStyle = this._colors.ansi[fg];\n } else {\n this._ctx.fillStyle = this._colors.foreground;\n }\n\n this._clipRow(terminal, y);\n\n // Apply alpha to dim the character\n if (dim) {\n this._ctx.globalAlpha = DIM_OPACITY;\n }\n // Draw the character\n this._ctx.fillText(\n char,\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop);\n this._ctx.restore();\n }\n\n /**\n * Clips a row to ensure no pixels will be drawn outside the cells in the row.\n * @param terminal The terminal.\n * @param y The row to clip.\n */\n private _clipRow(terminal: ITerminal, y: number): void {\n this._ctx.beginPath();\n this._ctx.rect(\n 0,\n y * this._scaledCellHeight,\n terminal.cols * this._scaledCellWidth,\n this._scaledCellHeight);\n this._ctx.clip();\n }\n}\n\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IMouseZoneManager, IMouseZone } from './Interfaces';\nimport { ITerminal } from '../Interfaces';\n\nconst HOVER_DURATION = 500;\n\n/**\n * The MouseZoneManager allows components to register zones within the terminal\n * that trigger hover and click callbacks.\n *\n * This class was intentionally made not so robust initially as the only case it\n * needed to support was single-line links which never overlap. Improvements can\n * be made in the future.\n */\nexport class MouseZoneManager implements IMouseZoneManager {\n private _zones: IMouseZone[] = [];\n\n private _areZonesActive: boolean = false;\n private _mouseMoveListener: (e: MouseEvent) => any;\n private _clickListener: (e: MouseEvent) => any;\n\n private _tooltipTimeout: number = null;\n private _currentZone: IMouseZone = null;\n private _lastHoverCoords: [number, number] = [null, null];\n\n constructor(\n private _terminal: ITerminal\n ) {\n this._terminal.element.addEventListener('mousedown', e => this._onMouseDown(e));\n\n // These events are expensive, only listen to it when mouse zones are active\n this._mouseMoveListener = e => this._onMouseMove(e);\n this._clickListener = e => this._onClick(e);\n }\n\n public add(zone: IMouseZone): void {\n this._zones.push(zone);\n if (this._zones.length === 1) {\n this._activate();\n }\n }\n\n public clearAll(start?: number, end?: number): void {\n // Exit if there's nothing to clear\n if (this._zones.length === 0) {\n return;\n }\n\n // Clear all if start/end weren't set\n if (!end) {\n start = 0;\n end = this._terminal.rows - 1;\n }\n\n // Iterate through zones and clear them out if they're within the range\n for (let i = 0; i < this._zones.length; i++) {\n const zone = this._zones[i];\n if (zone.y > start && zone.y <= end + 1) {\n if (this._currentZone && this._currentZone === zone) {\n this._currentZone.leaveCallback();\n this._currentZone = null;\n }\n this._zones.splice(i--, 1);\n }\n }\n\n // Deactivate the mouse zone manager if all the zones have been removed\n if (this._zones.length === 0) {\n this._deactivate();\n }\n }\n\n private _activate(): void {\n if (!this._areZonesActive) {\n this._areZonesActive = true;\n this._terminal.element.addEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.addEventListener('click', this._clickListener);\n }\n }\n\n private _deactivate(): void {\n if (this._areZonesActive) {\n this._areZonesActive = false;\n this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.removeEventListener('click', this._clickListener);\n }\n }\n\n private _onMouseMove(e: MouseEvent): void {\n // TODO: Ideally this would only clear the hover state when the mouse moves\n // outside of the mouse zone\n if (this._lastHoverCoords[0] !== e.pageX || this._lastHoverCoords[1] !== e.pageY) {\n this._onHover(e);\n // Record the current coordinates\n this._lastHoverCoords = [e.pageX, e.pageY];\n }\n }\n\n private _onHover(e: MouseEvent): void {\n const zone = this._findZoneEventAt(e);\n\n // Do nothing if the zone is the same\n if (zone === this._currentZone) {\n return;\n }\n\n // Fire the hover end callback and cancel any existing timer if a new zone\n // is being hovered\n if (this._currentZone) {\n this._currentZone.leaveCallback();\n this._currentZone = null;\n if (this._tooltipTimeout) {\n clearTimeout(this._tooltipTimeout);\n }\n }\n\n // Exit if there is not zone\n if (!zone) {\n return;\n }\n this._currentZone = zone;\n\n // Trigger the hover callback\n if (zone.hoverCallback) {\n zone.hoverCallback(e);\n }\n\n // Restart the tooltip timeout\n this._tooltipTimeout = setTimeout(() => this._onTooltip(e), HOVER_DURATION);\n }\n\n private _onTooltip(e: MouseEvent): void {\n this._tooltipTimeout = null;\n const zone = this._findZoneEventAt(e);\n if (zone && zone.tooltipCallback) {\n zone.tooltipCallback(e);\n }\n }\n\n private _onMouseDown(e: MouseEvent): void {\n // Ignore the event if there are no zones active\n if (!this._areZonesActive) {\n return;\n }\n\n // Find the active zone, prevent event propagation if found to prevent other\n // components from handling the mouse event.\n const zone = this._findZoneEventAt(e);\n if (zone) {\n // TODO: When link modifier support is added, the event should only be\n // cancelled when the modifier is held (see #1021)\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n private _onClick(e: MouseEvent): void {\n // Find the active zone and click it if found\n const zone = this._findZoneEventAt(e);\n if (zone) {\n zone.clickCallback(e);\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n private _findZoneEventAt(e: MouseEvent): IMouseZone {\n const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.element, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows);\n if (!coords) {\n return null;\n }\n for (let i = 0; i < this._zones.length; i++) {\n const zone = this._zones[i];\n if (zone.y === coords[1] && zone.x1 <= coords[0] && zone.x2 > coords[0]) {\n return zone;\n }\n };\n return null;\n }\n}\n\nexport class MouseZone implements IMouseZone {\n constructor(\n public x1: number,\n public x2: number,\n public y: number,\n public clickCallback: (e: MouseEvent) => any,\n public hoverCallback?: (e: MouseEvent) => any,\n public tooltipCallback?: (e: MouseEvent) => any,\n public leaveCallback?: () => void\n ) {\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, ISelectionManager } from '../Interfaces';\n\ninterface IWindow extends Window {\n clipboardData?: {\n getData(format: string): string;\n setData(format: string, data: string): void;\n };\n}\n\ndeclare var window: IWindow;\n\n/**\n * Prepares text to be pasted into the terminal by normalizing the line endings\n * @param text The pasted text that needs processing before inserting into the terminal\n */\nexport function prepareTextForTerminal(text: string, isMSWindows: boolean): string {\n if (isMSWindows) {\n return text.replace(/\\r?\\n/g, '\\r');\n }\n return text;\n}\n\n/**\n * Bracket text for paste, if necessary, as per https://cirw.in/blog/bracketed-paste\n * @param text The pasted text to bracket\n */\nexport function bracketTextForPaste(text: string, bracketedPasteMode: boolean): string {\n if (bracketedPasteMode) {\n return '\\x1b[200~' + text + '\\x1b[201~';\n }\n return text;\n}\n\n/**\n * Binds copy functionality to the given terminal.\n * @param {ClipboardEvent} ev The original copy event to be handled\n */\nexport function copyHandler(ev: ClipboardEvent, term: ITerminal, selectionManager: ISelectionManager): void {\n if (term.browser.isMSIE) {\n window.clipboardData.setData('Text', selectionManager.selectionText);\n } else {\n ev.clipboardData.setData('text/plain', selectionManager.selectionText);\n }\n\n // Prevent or the original text will be copied.\n ev.preventDefault();\n}\n\n/**\n * Redirect the clipboard's data to the terminal's input handler.\n * @param {ClipboardEvent} ev The original paste event to be handled\n * @param {Terminal} term The terminal on which to apply the handled paste event\n */\nexport function pasteHandler(ev: ClipboardEvent, term: ITerminal): void {\n ev.stopPropagation();\n\n let text: string;\n\n let dispatchPaste = function(text: string): void {\n text = prepareTextForTerminal(text, term.browser.isMSWindows);\n text = bracketTextForPaste(text, term.bracketedPasteMode);\n term.handler(text);\n term.textarea.value = '';\n term.emit('paste', text);\n term.cancel(ev);\n };\n\n if (term.browser.isMSIE) {\n if (window.clipboardData) {\n text = window.clipboardData.getData('Text');\n dispatchPaste(text);\n }\n } else {\n if (ev.clipboardData) {\n text = ev.clipboardData.getData('text/plain');\n dispatchPaste(text);\n }\n }\n}\n\n/**\n * Moves the textarea under the mouse cursor and focuses it.\n * @param ev The original right click event to be handled.\n * @param textarea The terminal's textarea.\n */\nexport function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement): void {\n // Bring textarea at the cursor position\n textarea.style.position = 'fixed';\n textarea.style.width = '20px';\n textarea.style.height = '20px';\n textarea.style.left = (ev.clientX - 10) + 'px';\n textarea.style.top = (ev.clientY - 10) + 'px';\n textarea.style.zIndex = '1000';\n\n textarea.focus();\n\n // Reset the terminal textarea's styling\n setTimeout(() => {\n textarea.style.position = null;\n textarea.style.width = null;\n textarea.style.height = null;\n textarea.style.left = null;\n textarea.style.top = null;\n textarea.style.zIndex = null;\n }, 4);\n}\n\n/**\n * Bind to right-click event and allow right-click copy and paste.\n * @param ev The original right click event to be handled.\n * @param textarea The terminal's textarea.\n * @param selectionManager The terminal's selection manager.\n */\nexport function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager): void {\n moveTextAreaUnderMouseCursor(ev, textarea);\n\n // Get textarea ready to copy from the context menu\n textarea.value = selectionManager.selectionText;\n textarea.select();\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, IViewport } from './Interfaces';\nimport { CharMeasure } from './utils/CharMeasure';\nimport { IColorSet } from './renderer/Interfaces';\n\n/**\n * Represents the viewport of a terminal, the visible area within the larger buffer of output.\n * Logic for the virtual scroll bar is included in this object.\n */\nexport class Viewport implements IViewport {\n private currentRowHeight: number = 0;\n private lastRecordedBufferLength: number = 0;\n private lastRecordedViewportHeight: number = 0;\n private lastRecordedBufferHeight: number = 0;\n private lastTouchY: number;\n\n /**\n * Creates a new Viewport.\n * @param terminal The terminal this viewport belongs to.\n * @param viewportElement The DOM element acting as the viewport.\n * @param scrollArea The DOM element acting as the scroll area.\n * @param charMeasure A DOM element used to measure the character size of. the terminal.\n */\n constructor(\n private terminal: ITerminal,\n private viewportElement: HTMLElement,\n private scrollArea: HTMLElement,\n private charMeasure: CharMeasure\n ) {\n this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));\n\n // Perform this async to ensure the CharMeasure is ready.\n setTimeout(() => this.syncScrollArea(), 0);\n }\n\n public onThemeChanged(colors: IColorSet): void {\n this.viewportElement.style.backgroundColor = colors.background;\n }\n\n /**\n * Refreshes row height, setting line-height, viewport height and scroll area height if\n * necessary.\n */\n private refresh(): void {\n if (this.charMeasure.height > 0) {\n this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio;\n\n if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) {\n this.lastRecordedViewportHeight = this.terminal.renderer.dimensions.canvasHeight;\n this.viewportElement.style.height = this.lastRecordedViewportHeight + 'px';\n }\n\n const newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength);\n if (this.lastRecordedBufferHeight !== newBufferHeight) {\n this.lastRecordedBufferHeight = newBufferHeight;\n this.scrollArea.style.height = this.lastRecordedBufferHeight + 'px';\n }\n }\n }\n\n /**\n * Updates dimensions and synchronizes the scroll area if necessary.\n */\n public syncScrollArea(): void {\n if (this.lastRecordedBufferLength !== this.terminal.buffer.lines.length) {\n // If buffer height changed\n this.lastRecordedBufferLength = this.terminal.buffer.lines.length;\n this.refresh();\n } else if (this.lastRecordedViewportHeight !== (this.terminal).renderer.dimensions.canvasHeight) {\n // If viewport height changed\n this.refresh();\n } else {\n // If size has changed, refresh viewport\n if (this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this.currentRowHeight) {\n this.refresh();\n }\n }\n\n // Sync scrollTop\n const scrollTop = this.terminal.buffer.ydisp * this.currentRowHeight;\n if (this.viewportElement.scrollTop !== scrollTop) {\n this.viewportElement.scrollTop = scrollTop;\n }\n }\n\n /**\n * Handles scroll events on the viewport, calculating the new viewport and requesting the\n * terminal to scroll to it.\n * @param ev The scroll event.\n */\n private onScroll(ev: Event): void {\n const newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);\n const diff = newRow - this.terminal.buffer.ydisp;\n this.terminal.scrollLines(diff, true);\n }\n\n /**\n * Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual\n * scrolling to `onScroll`, this event needs to be attached manually by the consumer of\n * `Viewport`.\n * @param ev The mouse wheel event.\n */\n public onWheel(ev: WheelEvent): void {\n if (ev.deltaY === 0) {\n // Do nothing if it's not a vertical scroll event\n return;\n }\n // Fallback to WheelEvent.DOM_DELTA_PIXEL\n let multiplier = 1;\n if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {\n multiplier = this.currentRowHeight;\n } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {\n multiplier = this.currentRowHeight * this.terminal.rows;\n }\n this.viewportElement.scrollTop += ev.deltaY * multiplier;\n // Prevent the page from scrolling when the terminal scrolls\n ev.preventDefault();\n };\n\n /**\n * Handles the touchstart event, recording the touch occurred.\n * @param ev The touch event.\n */\n public onTouchStart(ev: TouchEvent): void {\n this.lastTouchY = ev.touches[0].pageY;\n };\n\n /**\n * Handles the touchmove event, scrolling the viewport if the position shifted.\n * @param ev The touch event.\n */\n public onTouchMove(ev: TouchEvent): void {\n let deltaY = this.lastTouchY - ev.touches[0].pageY;\n this.lastTouchY = ev.touches[0].pageY;\n if (deltaY === 0) {\n return;\n }\n this.viewportElement.scrollTop += deltaY;\n ev.preventDefault();\n };\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport type LinkMatcher = {\n id: number,\n regex: RegExp,\n handler: LinkMatcherHandler,\n hoverTooltipCallback?: LinkMatcherHandler,\n hoverLeaveCallback?: () => void,\n matchIndex?: number,\n validationCallback?: LinkMatcherValidationCallback,\n priority?: number\n};\nexport type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void;\nexport type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;\n\nexport type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;\nexport type Charset = {[key: string]: string};\n\nexport type CharData = [number, string, number, number];\nexport type LineData = CharData[];\n\nexport type LinkHoverEvent = {\n x: number,\n y: number,\n length: number\n};\n\nexport enum LinkHoverEventTypes {\n HOVER = 'linkhover',\n TOOLTIP = 'linktooltip',\n LEAVE = 'linkleave'\n};\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n *\n * Originally forked from (with the author's permission):\n * Fabrice Bellard's javascript vt100 for jslinux:\n * http://bellard.org/jslinux/\n * Copyright (c) 2011 Fabrice Bellard\n * The original design remains. The terminal itself\n * has been extended to include xterm CSI codes, among\n * other features.\n *\n * Terminal Emulation References:\n * http://vt100.net/\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n * http://invisible-island.net/vttest/\n * http://www.inwap.com/pdp10/ansicode.txt\n * http://linux.die.net/man/4/console_codes\n * http://linux.die.net/man/7/urxvt\n */\n\nimport { BufferSet } from './BufferSet';\nimport { Buffer, MAX_BUFFER_SIZE } from './Buffer';\nimport { CompositionHelper } from './CompositionHelper';\nimport { EventEmitter } from './EventEmitter';\nimport { Viewport } from './Viewport';\nimport { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard';\nimport { CircularList } from './utils/CircularList';\nimport { C0 } from './EscapeSequences';\nimport { InputHandler } from './InputHandler';\nimport { Parser } from './Parser';\nimport { Renderer } from './renderer/Renderer';\nimport { Linkifier } from './Linkifier';\nimport { SelectionManager } from './SelectionManager';\nimport { CharMeasure } from './utils/CharMeasure';\nimport * as Browser from './utils/Browser';\nimport { MouseHelper } from './utils/MouseHelper';\nimport { CHARSETS } from './Charsets';\nimport { CustomKeyEventHandler, Charset, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData } from './Types';\nimport { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions, IViewport, ICompositionHelper, ITheme, ILinkifier } from './Interfaces';\nimport { BellSound } from './utils/Sounds';\nimport { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';\nimport { IMouseZoneManager } from './input/Interfaces';\nimport { MouseZoneManager } from './input/MouseZoneManager';\nimport { initialize as initializeCharAtlas } from './renderer/CharAtlas';\nimport { IRenderer } from './renderer/Interfaces';\n\n// Declares required for loadAddon\ndeclare var exports: any;\ndeclare var module: any;\ndeclare var define: any;\ndeclare var require: any;\n\n// Let it work inside Node.js for automated testing purposes.\nconst document = (typeof window !== 'undefined') ? window.document : null;\n\n/**\n * The amount of write requests to queue before sending an XOFF signal to the\n * pty process. This number must be small in order for ^C and similar sequences\n * to be responsive.\n */\nconst WRITE_BUFFER_PAUSE_THRESHOLD = 5;\n\n/**\n * The number of writes to perform in a single batch before allowing the\n * renderer to catch up with a 0ms setTimeout.\n */\nconst WRITE_BATCH_SIZE = 300;\n\nconst DEFAULT_OPTIONS: ITerminalOptions = {\n convertEol: false,\n termName: 'xterm',\n geometry: [80, 24],\n cursorBlink: false,\n cursorStyle: 'block',\n bellSound: BellSound,\n bellStyle: 'none',\n enableBold: true,\n fontFamily: 'courier-new, courier, monospace',\n fontSize: 15,\n lineHeight: 1.0,\n letterSpacing: 0,\n scrollback: 1000,\n screenKeys: false,\n debug: false,\n cancelEvents: false,\n disableStdin: false,\n useFlowControl: false,\n tabStopWidth: 8,\n theme: null\n // programFeatures: false,\n // focusKeys: false,\n};\n\nexport class Terminal extends EventEmitter implements ITerminal, IInputHandlingTerminal {\n public textarea: HTMLTextAreaElement;\n public element: HTMLElement;\n\n /**\n * The HTMLElement that the terminal is created in, set by Terminal.open.\n */\n private parent: HTMLElement;\n private context: Window;\n private document: Document;\n private body: HTMLBodyElement;\n private viewportScrollArea: HTMLElement;\n private viewportElement: HTMLElement;\n private helperContainer: HTMLElement;\n private compositionView: HTMLElement;\n private charSizeStyleElement: HTMLStyleElement;\n private bellAudioElement: HTMLAudioElement;\n private visualBellTimer: number;\n\n public browser: IBrowser = Browser;\n\n public options: ITerminalOptions;\n private colors: any;\n\n // TODO: This can be changed to an enum or boolean, 0 and 1 seem to be the only options\n public cursorState: number;\n public cursorHidden: boolean;\n public convertEol: boolean;\n\n private sendDataQueue: string;\n private customKeyEventHandler: CustomKeyEventHandler;\n\n // modes\n public applicationKeypad: boolean;\n public applicationCursor: boolean;\n public originMode: boolean;\n public insertMode: boolean;\n public wraparoundMode: boolean; // defaults: xterm - true, vt100 - false\n public bracketedPasteMode: boolean;\n\n // charset\n // The current charset\n public charset: Charset;\n public gcharset: number;\n public glevel: number;\n public charsets: Charset[];\n\n // mouse properties\n private decLocator: boolean; // This is unstable and never set\n public x10Mouse: boolean;\n public vt200Mouse: boolean;\n private vt300Mouse: boolean; // This is unstable and never set\n public normalMouse: boolean;\n public mouseEvents: boolean;\n public sendFocus: boolean;\n public utfMouse: boolean;\n public sgrMouse: boolean;\n public urxvtMouse: boolean;\n\n // misc\n private refreshStart: number;\n private refreshEnd: number;\n public savedCols: number;\n\n // stream\n private readable: boolean;\n private writable: boolean;\n\n public defAttr: number;\n public curAttr: number;\n\n public params: (string | number)[];\n public currentParam: string | number;\n public prefix: string;\n public postfix: string;\n\n // user input states\n public writeBuffer: string[];\n private writeInProgress: boolean;\n\n /**\n * Whether _xterm.js_ sent XOFF in order to catch up with the pty process.\n * This is a distinct state from writeStopped so that if the user requested\n * XOFF via ^S that it will not automatically resume when the writeBuffer goes\n * below threshold.\n */\n private xoffSentToCatchUp: boolean;\n\n /** Whether writing has been stopped as a result of XOFF */\n private writeStopped: boolean;\n\n // leftover surrogate high from previous write invocation\n private surrogate_high: string;\n\n // Store if user went browsing history in scrollback\n private userScrolling: boolean;\n\n private inputHandler: InputHandler;\n private parser: Parser;\n public renderer: IRenderer;\n public selectionManager: SelectionManager;\n public linkifier: ILinkifier;\n public buffers: BufferSet;\n public buffer: Buffer;\n public viewport: IViewport;\n private compositionHelper: ICompositionHelper;\n public charMeasure: CharMeasure;\n private _mouseZoneManager: IMouseZoneManager;\n public mouseHelper: MouseHelper;\n\n public cols: number;\n public rows: number;\n public geometry: [/*cols*/number, /*rows*/number];\n\n /**\n * Creates a new `Terminal` object.\n *\n * @param {object} options An object containing a set of options, the available options are:\n * - `cursorBlink` (boolean): Whether the terminal cursor blinks\n * - `cols` (number): The number of columns of the terminal (horizontal size)\n * - `rows` (number): The number of rows of the terminal (vertical size)\n *\n * @public\n * @class Xterm Xterm\n * @alias module:xterm/src/xterm\n */\n constructor(\n options: ITerminalOptions = {}\n ) {\n super();\n this.options = options;\n this.setup();\n }\n\n private setup(): void {\n Object.keys(DEFAULT_OPTIONS).forEach((key) => {\n if (this.options[key] == null) {\n this.options[key] = DEFAULT_OPTIONS[key];\n }\n // TODO: We should move away from duplicate options on the Terminal object\n this[key] = this.options[key];\n });\n\n // this.context = options.context || window;\n // this.document = options.document || document;\n // TODO: WHy not document.body?\n this.parent = document ? document.body : null;\n\n this.cols = this.options.cols || this.options.geometry[0];\n this.rows = this.options.rows || this.options.geometry[1];\n this.geometry = [this.cols, this.rows];\n\n if (this.options.handler) {\n this.on('data', this.options.handler);\n }\n\n this.cursorState = 0;\n this.cursorHidden = false;\n this.sendDataQueue = '';\n this.customKeyEventHandler = null;\n\n // modes\n this.applicationKeypad = false;\n this.applicationCursor = false;\n this.originMode = false;\n this.insertMode = false;\n this.wraparoundMode = true; // defaults: xterm - true, vt100 - false\n this.bracketedPasteMode = false;\n\n // charset\n this.charset = null;\n this.gcharset = null;\n this.glevel = 0;\n // TODO: Can this be just []?\n this.charsets = [null];\n\n this.readable = true;\n this.writable = true;\n\n this.defAttr = (0 << 18) | (257 << 9) | (256 << 0);\n this.curAttr = (0 << 18) | (257 << 9) | (256 << 0);\n\n this.params = [];\n this.currentParam = 0;\n this.prefix = '';\n this.postfix = '';\n\n // user input states\n this.writeBuffer = [];\n this.writeInProgress = false;\n\n this.xoffSentToCatchUp = false;\n this.writeStopped = false;\n this.surrogate_high = '';\n this.userScrolling = false;\n\n this.inputHandler = new InputHandler(this);\n this.parser = new Parser(this.inputHandler, this);\n // Reuse renderer if the Terminal is being recreated via a reset call.\n this.renderer = this.renderer || null;\n this.selectionManager = this.selectionManager || null;\n this.linkifier = this.linkifier || new Linkifier(this);\n this._mouseZoneManager = this._mouseZoneManager || null;\n\n // Create the terminal's buffers and set the current buffer\n this.buffers = new BufferSet(this);\n this.buffer = this.buffers.active; // Convenience shortcut;\n this.buffers.on('activate', (buffer: Buffer) => {\n this.buffer = buffer;\n });\n\n // Ensure the selection manager has the correct buffer\n if (this.selectionManager) {\n this.selectionManager.setBuffer(this.buffer);\n }\n }\n\n /**\n * back_color_erase feature for xterm.\n */\n public eraseAttr(): number {\n // if (this.is('screen')) return this.defAttr;\n return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);\n }\n\n /**\n * Focus the terminal. Delegates focus handling to the terminal's DOM element.\n */\n public focus(): void {\n this.textarea.focus();\n }\n\n public get isFocused(): boolean {\n return document.activeElement === this.textarea;\n }\n\n /**\n * Retrieves an option's value from the terminal.\n * @param {string} key The option key.\n */\n public getOption(key: string): any {\n if (!(key in DEFAULT_OPTIONS)) {\n throw new Error('No option with key \"' + key + '\"');\n }\n\n if (typeof this.options[key] !== 'undefined') {\n return this.options[key];\n }\n\n return this[key];\n }\n\n /**\n * Sets an option on the terminal.\n * @param {string} key The option key.\n * @param {any} value The option value.\n */\n public setOption(key: string, value: any): void {\n if (!(key in DEFAULT_OPTIONS)) {\n throw new Error('No option with key \"' + key + '\"');\n }\n switch (key) {\n case 'bellStyle':\n if (!value) {\n value = 'none';\n }\n break;\n case 'cursorStyle':\n if (!value) {\n value = 'block';\n }\n break;\n case 'lineHeight':\n if (value < 1) {\n console.warn(`${key} cannot be less than 1, value: ${value}`);\n return;\n }\n case 'tabStopWidth':\n if (value < 1) {\n console.warn(`${key} cannot be less than 1, value: ${value}`);\n return;\n }\n break;\n case 'theme':\n // If open has been called we do not want to set options.theme as the\n // source of truth is owned by the renderer.\n if (this.renderer) {\n this._setTheme(value);\n return;\n }\n break;\n case 'scrollback':\n value = Math.min(value, MAX_BUFFER_SIZE);\n\n if (value < 0) {\n console.warn(`${key} cannot be less than 0, value: ${value}`);\n return;\n }\n if (this.options[key] !== value) {\n const newBufferLength = this.rows + value;\n if (this.buffer.lines.length > newBufferLength) {\n const amountToTrim = this.buffer.lines.length - newBufferLength;\n const needsRefresh = (this.buffer.ydisp - amountToTrim < 0);\n this.buffer.lines.trimStart(amountToTrim);\n this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0);\n this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0);\n if (needsRefresh) {\n this.refresh(0, this.rows - 1);\n }\n }\n }\n break;\n }\n this[key] = value;\n this.options[key] = value;\n switch (key) {\n case 'fontFamily':\n case 'fontSize':\n // When the font changes the size of the cells may change which requires a renderer clear\n this.renderer.clear();\n this.charMeasure.measure(this.options);\n break;\n case 'enableBold':\n case 'letterSpacing':\n case 'lineHeight':\n // When the font changes the size of the cells may change which requires a renderer clear\n this.renderer.clear();\n this.renderer.onResize(this.cols, this.rows, false);\n this.refresh(0, this.rows - 1);\n // this.charMeasure.measure(this.options);\n case 'scrollback':\n this.buffers.resize(this.cols, this.rows);\n this.viewport.syncScrollArea();\n break;\n case 'tabStopWidth': this.buffers.setupTabStops(); break;\n case 'bellSound':\n case 'bellStyle': this.syncBellSound(); break;\n }\n // Inform renderer of changes\n if (this.renderer) {\n this.renderer.onOptionsChanged();\n }\n }\n\n /**\n * Binds the desired focus behavior on a given terminal object.\n */\n private _onTextAreaFocus(): void {\n if (this.sendFocus) {\n this.send(C0.ESC + '[I');\n }\n this.element.classList.add('focus');\n this.showCursor();\n this.emit('focus');\n };\n\n /**\n * Blur the terminal, calling the blur function on the terminal's underlying\n * textarea.\n */\n public blur(): void {\n return this.textarea.blur();\n }\n\n /**\n * Binds the desired blur behavior on a given terminal object.\n */\n private _onTextAreaBlur(): void {\n this.refresh(this.buffer.y, this.buffer.y);\n if (this.sendFocus) {\n this.send(C0.ESC + '[O');\n }\n this.element.classList.remove('focus');\n this.emit('blur');\n }\n\n /**\n * Initialize default behavior\n */\n private initGlobal(): void {\n this.bindKeys();\n\n // Bind clipboard functionality\n on(this.element, 'copy', (event: ClipboardEvent) => {\n // If mouse events are active it means the selection manager is disabled and\n // copy should be handled by the host program.\n if (!this.hasSelection()) {\n return;\n }\n copyHandler(event, this, this.selectionManager);\n });\n const pasteHandlerWrapper = event => pasteHandler(event, this);\n on(this.textarea, 'paste', pasteHandlerWrapper);\n on(this.element, 'paste', pasteHandlerWrapper);\n\n // Handle right click context menus\n if (Browser.isFirefox) {\n // Firefox doesn't appear to fire the contextmenu event on right click\n on(this.element, 'mousedown', (event: MouseEvent) => {\n if (event.button === 2) {\n rightClickHandler(event, this.textarea, this.selectionManager);\n }\n });\n } else {\n on(this.element, 'contextmenu', (event: MouseEvent) => {\n rightClickHandler(event, this.textarea, this.selectionManager);\n });\n }\n\n // Move the textarea under the cursor when middle clicking on Linux to ensure\n // middle click to paste selection works. This only appears to work in Chrome\n // at the time is writing.\n if (Browser.isLinux) {\n // Use auxclick event over mousedown the latter doesn't seem to work. Note\n // that the regular click event doesn't fire for the middle mouse button.\n on(this.element, 'auxclick', (event: MouseEvent) => {\n if (event.button === 1) {\n moveTextAreaUnderMouseCursor(event, this.textarea);\n }\n });\n }\n }\n\n /**\n * Apply key handling to the terminal\n */\n private bindKeys(): void {\n const self = this;\n on(this.element, 'keydown', function (ev: KeyboardEvent): void {\n if (document.activeElement !== this) {\n return;\n }\n self._keyDown(ev);\n }, true);\n\n on(this.element, 'keypress', function (ev: KeyboardEvent): void {\n if (document.activeElement !== this) {\n return;\n }\n self._keyPress(ev);\n }, true);\n\n on(this.element, 'keyup', (ev: KeyboardEvent) => {\n if (!wasMondifierKeyOnlyEvent(ev)) {\n this.focus();\n }\n }, true);\n\n on(this.textarea, 'keydown', (ev: KeyboardEvent) => {\n this._keyDown(ev);\n }, true);\n\n on(this.textarea, 'keypress', (ev: KeyboardEvent) => {\n this._keyPress(ev);\n // Truncate the textarea's value, since it is not needed\n this.textarea.value = '';\n }, true);\n\n on(this.textarea, 'compositionstart', () => this.compositionHelper.compositionstart());\n on(this.textarea, 'compositionupdate', (e: CompositionEvent) => this.compositionHelper.compositionupdate(e));\n on(this.textarea, 'compositionend', () => this.compositionHelper.compositionend());\n this.on('refresh', () => this.compositionHelper.updateCompositionElements());\n this.on('refresh', (data) => this.queueLinkification(data.start, data.end));\n }\n\n /**\n * Opens the terminal within an element.\n *\n * @param {HTMLElement} parent The element to create the terminal within.\n */\n public open(parent: HTMLElement): void {\n let i = 0;\n let div;\n\n this.parent = parent || this.parent;\n\n if (!this.parent) {\n throw new Error('Terminal requires a parent element.');\n }\n\n // Grab global elements\n this.context = this.parent.ownerDocument.defaultView;\n this.document = this.parent.ownerDocument;\n this.body = this.document.body;\n\n initializeCharAtlas(this.document);\n\n // Create main element container\n this.element = this.document.createElement('div');\n this.element.classList.add('terminal');\n this.element.classList.add('xterm');\n\n this.element.setAttribute('tabindex', '0');\n\n this.viewportElement = document.createElement('div');\n this.viewportElement.classList.add('xterm-viewport');\n this.element.appendChild(this.viewportElement);\n this.viewportScrollArea = document.createElement('div');\n this.viewportScrollArea.classList.add('xterm-scroll-area');\n this.viewportElement.appendChild(this.viewportScrollArea);\n\n // preload audio\n this.syncBellSound();\n\n this._mouseZoneManager = new MouseZoneManager(this);\n this.on('scroll', () => this._mouseZoneManager.clearAll());\n this.linkifier.attachToDom(this._mouseZoneManager);\n\n // Create the container that will hold helpers like the textarea for\n // capturing DOM Events. Then produce the helpers.\n this.helperContainer = document.createElement('div');\n this.helperContainer.classList.add('xterm-helpers');\n // TODO: This should probably be inserted once it's filled to prevent an additional layout\n this.element.appendChild(this.helperContainer);\n this.textarea = document.createElement('textarea');\n this.textarea.classList.add('xterm-helper-textarea');\n this.textarea.setAttribute('autocorrect', 'off');\n this.textarea.setAttribute('autocapitalize', 'off');\n this.textarea.setAttribute('spellcheck', 'false');\n this.textarea.tabIndex = 0;\n this.textarea.addEventListener('focus', () => this._onTextAreaFocus());\n this.textarea.addEventListener('blur', () => this._onTextAreaBlur());\n this.helperContainer.appendChild(this.textarea);\n\n this.compositionView = document.createElement('div');\n this.compositionView.classList.add('composition-view');\n this.compositionHelper = new CompositionHelper(this.textarea, this.compositionView, this);\n this.helperContainer.appendChild(this.compositionView);\n\n this.charSizeStyleElement = document.createElement('style');\n this.helperContainer.appendChild(this.charSizeStyleElement);\n\n this.parent.appendChild(this.element);\n\n this.charMeasure = new CharMeasure(document, this.helperContainer);\n\n this.renderer = new Renderer(this, this.options.theme);\n this.options.theme = null;\n this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);\n this.viewport.onThemeChanged(this.renderer.colorManager.colors);\n\n this.on('cursormove', () => this.renderer.onCursorMove());\n this.on('resize', () => this.renderer.onResize(this.cols, this.rows, false));\n this.on('blur', () => this.renderer.onBlur());\n this.on('focus', () => this.renderer.onFocus());\n window.addEventListener('resize', () => this.renderer.onWindowResize(window.devicePixelRatio));\n this.charMeasure.on('charsizechanged', () => this.renderer.onResize(this.cols, this.rows, true));\n this.renderer.on('resize', (dimensions) => this.viewport.syncScrollArea());\n\n this.selectionManager = new SelectionManager(this, this.buffer, this.charMeasure);\n this.element.addEventListener('mousedown', (e: MouseEvent) => this.selectionManager.onMouseDown(e));\n this.selectionManager.on('refresh', data => this.renderer.onSelectionChanged(data.start, data.end));\n this.selectionManager.on('newselection', text => {\n // If there's a new selection, put it into the textarea, focus and select it\n // in order to register it as a selection on the OS. This event is fired\n // only on Linux to enable middle click to paste selection.\n this.textarea.value = text;\n this.textarea.focus();\n this.textarea.select();\n });\n this.on('scroll', () => {\n this.viewport.syncScrollArea();\n this.selectionManager.refresh();\n });\n this.viewportElement.addEventListener('scroll', () => this.selectionManager.refresh());\n\n this.mouseHelper = new MouseHelper(this.renderer);\n\n // Measure the character size\n this.charMeasure.measure(this.options);\n\n // Setup loop that draws to screen\n this.refresh(0, this.rows - 1);\n\n // Initialize global actions that need to be taken on the document.\n this.initGlobal();\n\n // Listen for mouse events and translate\n // them into terminal mouse protocols.\n this.bindMouse();\n }\n\n /**\n * Sets the theme on the renderer. The renderer must have been initialized.\n * @param theme The theme to ste.\n */\n private _setTheme(theme: ITheme): void {\n const colors = this.renderer.setTheme(theme);\n if (this.viewport) {\n this.viewport.onThemeChanged(colors);\n }\n }\n\n /**\n * Attempts to load an add-on using CommonJS or RequireJS (whichever is available).\n * @param {string} addon The name of the addon to load\n * @static\n */\n public static loadAddon(addon: string, callback?: Function): boolean | any {\n // TODO: Improve return type and documentation\n if (typeof exports === 'object' && typeof module === 'object') {\n // CommonJS\n return require('./addons/' + addon + '/' + addon);\n } else if (typeof define === 'function') {\n // RequireJS\n return (require)(['./addons/' + addon + '/' + addon], callback);\n } else {\n console.error('Cannot load a module without a CommonJS or RequireJS environment.');\n return false;\n }\n }\n\n /**\n * XTerm mouse events\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking\n * To better understand these\n * the xterm code is very helpful:\n * Relevant files:\n * button.c, charproc.c, misc.c\n * Relevant functions in xterm/button.c:\n * BtnCode, EmitButtonCode, EditorButton, SendMousePosition\n */\n public bindMouse(): void {\n const el = this.element;\n const self = this;\n let pressed = 32;\n\n // mouseup, mousedown, wheel\n // left click: ^[[M 3<^[[M#3<\n // wheel up: ^[[M`3>\n function sendButton(ev: MouseEvent | WheelEvent): void {\n let button;\n let pos;\n\n // get the xterm-style button\n button = getButton(ev);\n\n // get mouse coordinates\n pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows);\n if (!pos) return;\n\n sendEvent(button, pos);\n\n switch ((ev).overrideType || ev.type) {\n case 'mousedown':\n pressed = button;\n break;\n case 'mouseup':\n // keep it at the left\n // button, just in case.\n pressed = 32;\n break;\n case 'wheel':\n // nothing. don't\n // interfere with\n // `pressed`.\n break;\n }\n }\n\n // motion example of a left click:\n // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<\n function sendMove(ev: MouseEvent): void {\n let button = pressed;\n let pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows);\n if (!pos) return;\n\n // buttons marked as motions\n // are incremented by 32\n button += 32;\n\n sendEvent(button, pos);\n }\n\n // encode button and\n // position to characters\n function encode(data: number[], ch: number): void {\n if (!self.utfMouse) {\n if (ch === 255) {\n data.push(0);\n return;\n }\n if (ch > 127) ch = 127;\n data.push(ch);\n } else {\n if (ch === 2047) {\n data.push(0);\n return;\n }\n if (ch < 127) {\n data.push(ch);\n } else {\n if (ch > 2047) ch = 2047;\n data.push(0xC0 | (ch >> 6));\n data.push(0x80 | (ch & 0x3F));\n }\n }\n }\n\n // send a mouse event:\n // regular/utf8: ^[[M Cb Cx Cy\n // urxvt: ^[[ Cb ; Cx ; Cy M\n // sgr: ^[[ Cb ; Cx ; Cy M/m\n // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \\r\n // locator: CSI P e ; P b ; P r ; P c ; P p & w\n function sendEvent(button: number, pos: {x: number, y: number}): void {\n // self.emit('mouse', {\n // x: pos.x - 32,\n // y: pos.x - 32,\n // button: button\n // });\n\n if (self.vt300Mouse) {\n // NOTE: Unstable.\n // http://www.vt100.net/docs/vt3xx-gp/chapter15.html\n button &= 3;\n pos.x -= 32;\n pos.y -= 32;\n let data = C0.ESC + '[24';\n if (button === 0) data += '1';\n else if (button === 1) data += '3';\n else if (button === 2) data += '5';\n else if (button === 3) return;\n else data += '0';\n data += '~[' + pos.x + ',' + pos.y + ']\\r';\n self.send(data);\n return;\n }\n\n if (self.decLocator) {\n // NOTE: Unstable.\n button &= 3;\n pos.x -= 32;\n pos.y -= 32;\n if (button === 0) button = 2;\n else if (button === 1) button = 4;\n else if (button === 2) button = 6;\n else if (button === 3) button = 3;\n self.send(C0.ESC + '['\n + button\n + ';'\n + (button === 3 ? 4 : 0)\n + ';'\n + pos.y\n + ';'\n + pos.x\n + ';'\n // Not sure what page is meant to be\n + (pos).page || 0\n + '&w');\n return;\n }\n\n if (self.urxvtMouse) {\n pos.x -= 32;\n pos.y -= 32;\n pos.x++;\n pos.y++;\n self.send(C0.ESC + '[' + button + ';' + pos.x + ';' + pos.y + 'M');\n return;\n }\n\n if (self.sgrMouse) {\n pos.x -= 32;\n pos.y -= 32;\n self.send(C0.ESC + '[<'\n + (((button & 3) === 3 ? button & ~3 : button) - 32)\n + ';'\n + pos.x\n + ';'\n + pos.y\n + ((button & 3) === 3 ? 'm' : 'M'));\n return;\n }\n\n let data: number[] = [];\n\n encode(data, button);\n encode(data, pos.x);\n encode(data, pos.y);\n\n self.send(C0.ESC + '[M' + String.fromCharCode.apply(String, data));\n }\n\n function getButton(ev: MouseEvent): number {\n let button;\n let shift;\n let meta;\n let ctrl;\n let mod;\n\n // two low bits:\n // 0 = left\n // 1 = middle\n // 2 = right\n // 3 = release\n // wheel up/down:\n // 1, and 2 - with 64 added\n switch ((ev).overrideType || ev.type) {\n case 'mousedown':\n button = ev.button != null\n ? +ev.button\n : ev.which != null\n ? ev.which - 1\n : null;\n\n if (Browser.isMSIE) {\n button = button === 1 ? 0 : button === 4 ? 1 : button;\n }\n break;\n case 'mouseup':\n button = 3;\n break;\n case 'DOMMouseScroll':\n button = ev.detail < 0\n ? 64\n : 65;\n break;\n case 'wheel':\n button = (ev).wheelDeltaY > 0\n ? 64\n : 65;\n break;\n }\n\n // next three bits are the modifiers:\n // 4 = shift, 8 = meta, 16 = control\n shift = ev.shiftKey ? 4 : 0;\n meta = ev.metaKey ? 8 : 0;\n ctrl = ev.ctrlKey ? 16 : 0;\n mod = shift | meta | ctrl;\n\n // no mods\n if (self.vt200Mouse) {\n // ctrl only\n mod &= ctrl;\n } else if (!self.normalMouse) {\n mod = 0;\n }\n\n // increment to SP\n button = (32 + (mod << 2)) + button;\n\n return button;\n }\n\n on(el, 'mousedown', (ev: MouseEvent) => {\n\n // Prevent the focus on the textarea from getting lost\n // and make sure we get focused on mousedown\n ev.preventDefault();\n this.focus();\n\n // Don't send the mouse button to the pty if mouse events are disabled or\n // if the selection manager is having selection forced (ie. a modifier is\n // held).\n if (!this.mouseEvents || this.selectionManager.shouldForceSelection(ev)) {\n return;\n }\n\n // send the button\n sendButton(ev);\n\n // fix for odd bug\n // if (this.vt200Mouse && !this.normalMouse) {\n if (this.vt200Mouse) {\n (ev).overrideType = 'mouseup';\n sendButton(ev);\n return this.cancel(ev);\n }\n\n // bind events\n if (this.normalMouse) on(this.document, 'mousemove', sendMove);\n\n // x10 compatibility mode can't send button releases\n if (!this.x10Mouse) {\n const handler = (ev: MouseEvent) => {\n sendButton(ev);\n // TODO: Seems dangerous calling this on document?\n if (this.normalMouse) off(this.document, 'mousemove', sendMove);\n off(this.document, 'mouseup', handler);\n return this.cancel(ev);\n };\n // TODO: Seems dangerous calling this on document?\n on(this.document, 'mouseup', handler);\n }\n\n return this.cancel(ev);\n });\n\n // if (this.normalMouse) {\n // on(this.document, 'mousemove', sendMove);\n // }\n\n on(el, 'wheel', (ev: WheelEvent) => {\n if (!this.mouseEvents) return;\n if (this.x10Mouse || this.vt300Mouse || this.decLocator) return;\n sendButton(ev);\n ev.preventDefault();\n });\n\n // allow wheel scrolling in\n // the shell for example\n on(el, 'wheel', (ev: WheelEvent) => {\n if (this.mouseEvents) return;\n this.viewport.onWheel(ev);\n return this.cancel(ev);\n });\n\n on(el, 'touchstart', (ev: TouchEvent) => {\n if (this.mouseEvents) return;\n this.viewport.onTouchStart(ev);\n return this.cancel(ev);\n });\n\n on(el, 'touchmove', (ev: TouchEvent) => {\n if (this.mouseEvents) return;\n this.viewport.onTouchMove(ev);\n return this.cancel(ev);\n });\n }\n\n /**\n * Destroys the terminal.\n */\n public destroy(): void {\n super.destroy();\n this.readable = false;\n this.writable = false;\n this.handler = () => {};\n this.write = () => {};\n if (this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n // this.emit('close');\n }\n\n /**\n * Tells the renderer to refresh terminal content between two rows (inclusive) at the next\n * opportunity.\n * @param {number} start The row to start from (between 0 and this.rows - 1).\n * @param {number} end The row to end at (between start and this.rows - 1).\n */\n public refresh(start: number, end: number): void {\n if (this.renderer) {\n this.renderer.queueRefresh(start, end);\n }\n }\n\n /**\n * Queues linkification for the specified rows.\n * @param {number} start The row to start from (between 0 and this.rows - 1).\n * @param {number} end The row to end at (between start and this.rows - 1).\n */\n private queueLinkification(start: number, end: number): void {\n if (this.linkifier) {\n this.linkifier.linkifyRows(start, end);\n }\n }\n\n /**\n * Display the cursor element\n */\n public showCursor(): void {\n if (!this.cursorState) {\n this.cursorState = 1;\n this.refresh(this.buffer.y, this.buffer.y);\n }\n }\n\n /**\n * Scroll the terminal down 1 row, creating a blank line.\n * @param isWrapped Whether the new line is wrapped from the previous line.\n */\n public scroll(isWrapped?: boolean): void {\n const newLine = this.blankLine(undefined, isWrapped);\n const topRow = this.buffer.ybase + this.buffer.scrollTop;\n let bottomRow = this.buffer.ybase + this.buffer.scrollBottom;\n\n if (this.buffer.scrollTop === 0) {\n // Determine whether the buffer is going to be trimmed after insertion.\n const willBufferBeTrimmed = this.buffer.lines.length === this.buffer.lines.maxLength;\n\n // Insert the line using the fastest method\n if (bottomRow === this.buffer.lines.length - 1) {\n this.buffer.lines.push(newLine);\n } else {\n this.buffer.lines.splice(bottomRow + 1, 0, newLine);\n }\n\n // Only adjust ybase and ydisp when the buffer is not trimmed\n if (!willBufferBeTrimmed) {\n this.buffer.ybase++;\n // Only scroll the ydisp with ybase if the user has not scrolled up\n if (!this.userScrolling) {\n this.buffer.ydisp++;\n }\n } else {\n // When the buffer is full and the user has scrolled up, keep the text\n // stable unless ydisp is right at the top\n if (this.userScrolling) {\n this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0);\n }\n }\n } else {\n // scrollTop is non-zero which means no line will be going to the\n // scrollback, instead we can just shift them in-place.\n const scrollRegionHeight = bottomRow - topRow + 1/*as it's zero-based*/;\n this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);\n this.buffer.lines.set(bottomRow, newLine);\n }\n\n // Move the viewport to the bottom of the buffer unless the user is\n // scrolling.\n if (!this.userScrolling) {\n this.buffer.ydisp = this.buffer.ybase;\n }\n\n // Flag rows that need updating\n this.updateRange(this.buffer.scrollTop);\n this.updateRange(this.buffer.scrollBottom);\n\n /**\n * This event is emitted whenever the terminal is scrolled.\n * The one parameter passed is the new y display position.\n *\n * @event scroll\n */\n this.emit('scroll', this.buffer.ydisp);\n }\n\n /**\n * Scroll the display of the terminal\n * @param {number} disp The number of lines to scroll down (negative scroll up).\n * @param {boolean} suppressScrollEvent Don't emit the scroll event as scrollLines. This is used\n * to avoid unwanted events being handled by the viewport when the event was triggered from the\n * viewport originally.\n */\n public scrollLines(disp: number, suppressScrollEvent?: boolean): void {\n if (disp < 0) {\n if (this.buffer.ydisp === 0) {\n return;\n }\n this.userScrolling = true;\n } else if (disp + this.buffer.ydisp >= this.buffer.ybase) {\n this.userScrolling = false;\n }\n\n const oldYdisp = this.buffer.ydisp;\n this.buffer.ydisp = Math.max(Math.min(this.buffer.ydisp + disp, this.buffer.ybase), 0);\n\n // No change occurred, don't trigger scroll/refresh\n if (oldYdisp === this.buffer.ydisp) {\n return;\n }\n\n if (!suppressScrollEvent) {\n this.emit('scroll', this.buffer.ydisp);\n }\n\n this.refresh(0, this.rows - 1);\n }\n\n /**\n * Scroll the display of the terminal by a number of pages.\n * @param {number} pageCount The number of pages to scroll (negative scrolls up).\n */\n public scrollPages(pageCount: number): void {\n this.scrollLines(pageCount * (this.rows - 1));\n }\n\n /**\n * Scrolls the display of the terminal to the top.\n */\n public scrollToTop(): void {\n this.scrollLines(-this.buffer.ydisp);\n }\n\n /**\n * Scrolls the display of the terminal to the bottom.\n */\n public scrollToBottom(): void {\n this.scrollLines(this.buffer.ybase - this.buffer.ydisp);\n }\n\n /**\n * Writes text to the terminal.\n * @param {string} data The text to write to the terminal.\n */\n public write(data: string): void {\n this.writeBuffer.push(data);\n\n // Send XOFF to pause the pty process if the write buffer becomes too large so\n // xterm.js can catch up before more data is sent. This is necessary in order\n // to keep signals such as ^C responsive.\n if (this.options.useFlowControl && !this.xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {\n // XOFF - stop pty pipe\n // XON will be triggered by emulator before processing data chunk\n this.send(C0.DC3);\n this.xoffSentToCatchUp = true;\n }\n\n if (!this.writeInProgress && this.writeBuffer.length > 0) {\n // Kick off a write which will write all data in sequence recursively\n this.writeInProgress = true;\n // Kick off an async innerWrite so more writes can come in while processing data\n setTimeout(() => {\n this.innerWrite();\n });\n }\n }\n\n private innerWrite(): void {\n const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE);\n while (writeBatch.length > 0) {\n const data = writeBatch.shift();\n\n // If XOFF was sent in order to catch up with the pty process, resume it if\n // the writeBuffer is empty to allow more data to come in.\n if (this.xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) {\n this.send(C0.DC1);\n this.xoffSentToCatchUp = false;\n }\n\n this.refreshStart = this.buffer.y;\n this.refreshEnd = this.buffer.y;\n\n // HACK: Set the parser state based on it's state at the time of return.\n // This works around the bug #662 which saw the parser state reset in the\n // middle of parsing escape sequence in two chunks. For some reason the\n // state of the parser resets to 0 after exiting parser.parse. This change\n // just sets the state back based on the correct return statement.\n const state = this.parser.parse(data);\n this.parser.setState(state);\n\n this.updateRange(this.buffer.y);\n this.refresh(this.refreshStart, this.refreshEnd);\n }\n if (this.writeBuffer.length > 0) {\n // Allow renderer to catch up before processing the next batch\n setTimeout(() => this.innerWrite(), 0);\n } else {\n this.writeInProgress = false;\n }\n }\n\n /**\n * Writes text to the terminal, followed by a break line character (\\n).\n * @param {string} data The text to write to the terminal.\n */\n public writeln(data: string): void {\n this.write(data + '\\r\\n');\n }\n\n /**\n * Attaches a custom key event handler which is run before keys are processed,\n * giving consumers of xterm.js ultimate control as to what keys should be\n * processed by the terminal and what keys should not.\n * @param customKeyEventHandler The custom KeyboardEvent handler to attach.\n * This is a function that takes a KeyboardEvent, allowing consumers to stop\n * propogation and/or prevent the default action. The function returns whether\n * the event should be processed by xterm.js.\n */\n public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void {\n this.customKeyEventHandler = customKeyEventHandler;\n }\n\n /**\n * Attaches a http(s) link handler, forcing web links to behave differently to\n * regular tags. This will trigger a refresh as links potentially need to be\n * reconstructed. Calling this with null will remove the handler.\n * @param handler The handler callback function.\n */\n public setHypertextLinkHandler(handler: LinkMatcherHandler): void {\n if (!this.linkifier) {\n throw new Error('Cannot attach a hypertext link handler before Terminal.open is called');\n }\n this.linkifier.setHypertextLinkHandler(handler);\n // Refresh to force links to refresh\n this.refresh(0, this.rows - 1);\n }\n\n /**\n * Attaches a validation callback for hypertext links. This is useful to use\n * validation logic or to do something with the link's element and url.\n * @param callback The callback to use, this can\n * be cleared with null.\n */\n public setHypertextValidationCallback(callback: LinkMatcherValidationCallback): void {\n if (!this.linkifier) {\n throw new Error('Cannot attach a hypertext validation callback before Terminal.open is called');\n }\n this.linkifier.setHypertextValidationCallback(callback);\n // // Refresh to force links to refresh\n this.refresh(0, this.rows - 1);\n }\n\n /**\n * Registers a link matcher, allowing custom link patterns to be matched and\n * handled.\n * @param regex The regular expression to search for, specifically\n * this searches the textContent of the rows. You will want to use \\s to match\n * a space ' ' character for example.\n * @param handler The callback when the link is called.\n * @param options Options for the link matcher.\n * @return The ID of the new matcher, this can be used to deregister.\n */\n public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number {\n if (this.linkifier) {\n const matcherId = this.linkifier.registerLinkMatcher(regex, handler, options);\n this.refresh(0, this.rows - 1);\n return matcherId;\n }\n return 0;\n }\n\n /**\n * Deregisters a link matcher if it has been registered.\n * @param matcherId The link matcher's ID (returned after register)\n */\n public deregisterLinkMatcher(matcherId: number): void {\n if (this.linkifier) {\n if (this.linkifier.deregisterLinkMatcher(matcherId)) {\n this.refresh(0, this.rows - 1);\n }\n }\n }\n\n /**\n * Gets whether the terminal has an active selection.\n */\n public hasSelection(): boolean {\n return this.selectionManager ? this.selectionManager.hasSelection : false;\n }\n\n /**\n * Gets the terminal's current selection, this is useful for implementing copy\n * behavior outside of xterm.js.\n */\n public getSelection(): string {\n return this.selectionManager ? this.selectionManager.selectionText : '';\n }\n\n /**\n * Clears the current terminal selection.\n */\n public clearSelection(): void {\n if (this.selectionManager) {\n this.selectionManager.clearSelection();\n }\n }\n\n /**\n * Selects all text within the terminal.\n */\n public selectAll(): void {\n if (this.selectionManager) {\n this.selectionManager.selectAll();\n }\n }\n\n /**\n * Handle a keydown event\n * Key Resources:\n * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent\n * @param {KeyboardEvent} ev The keydown event to be handled.\n */\n protected _keyDown(ev: KeyboardEvent): boolean {\n if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {\n return false;\n }\n\n if (!this.compositionHelper.keydown(ev)) {\n if (this.buffer.ybase !== this.buffer.ydisp) {\n this.scrollToBottom();\n }\n return false;\n }\n\n const result = this._evaluateKeyEscapeSequence(ev);\n\n if (result.key === C0.DC3) { // XOFF\n this.writeStopped = true;\n } else if (result.key === C0.DC1) { // XON\n this.writeStopped = false;\n }\n\n if (result.scrollLines) {\n this.scrollLines(result.scrollLines);\n return this.cancel(ev, true);\n }\n\n if (isThirdLevelShift(this.browser, ev)) {\n return true;\n }\n\n if (result.cancel) {\n // The event is canceled at the end already, is this necessary?\n this.cancel(ev, true);\n }\n\n if (!result.key) {\n return true;\n }\n\n this.emit('keydown', ev);\n this.emit('key', result.key, ev);\n this.showCursor();\n this.handler(result.key);\n\n return this.cancel(ev, true);\n }\n\n /**\n * Returns an object that determines how a KeyboardEvent should be handled. The key of the\n * returned value is the new key code to pass to the PTY.\n *\n * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n * @param ev The keyboard event to be translated to key escape sequence.\n */\n protected _evaluateKeyEscapeSequence(ev: KeyboardEvent): {cancel: boolean, key: string, scrollLines: number} {\n const result: {cancel: boolean, key: string, scrollLines: number} = {\n // Whether to cancel event propogation (NOTE: this may not be needed since the event is\n // canceled at the end of keyDown\n cancel: false,\n // The new key even to emit\n key: undefined,\n // The number of characters to scroll, if this is defined it will cancel the event\n scrollLines: undefined\n };\n const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0);\n switch (ev.keyCode) {\n case 0:\n if (ev.key === 'UIKeyInputUpArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OA';\n } else {\n result.key = C0.ESC + '[A';\n }\n }\n else if (ev.key === 'UIKeyInputLeftArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OD';\n } else {\n result.key = C0.ESC + '[D';\n }\n }\n else if (ev.key === 'UIKeyInputRightArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OC';\n } else {\n result.key = C0.ESC + '[C';\n }\n }\n else if (ev.key === 'UIKeyInputDownArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OB';\n } else {\n result.key = C0.ESC + '[B';\n }\n }\n break;\n case 8:\n // backspace\n if (ev.shiftKey) {\n result.key = C0.BS; // ^H\n break;\n }\n result.key = C0.DEL; // ^?\n break;\n case 9:\n // tab\n if (ev.shiftKey) {\n result.key = C0.ESC + '[Z';\n break;\n }\n result.key = C0.HT;\n result.cancel = true;\n break;\n case 13:\n // return/enter\n result.key = C0.CR;\n result.cancel = true;\n break;\n case 27:\n // escape\n result.key = C0.ESC;\n result.cancel = true;\n break;\n case 37:\n // left-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D';\n // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards\n // http://unix.stackexchange.com/a/108106\n // macOS uses different escape sequences than linux\n if (result.key === C0.ESC + '[1;3D') {\n result.key = (this.browser.isMac) ? C0.ESC + 'b' : C0.ESC + '[1;5D';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OD';\n } else {\n result.key = C0.ESC + '[D';\n }\n break;\n case 39:\n // right-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C';\n // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward\n // http://unix.stackexchange.com/a/108106\n // macOS uses different escape sequences than linux\n if (result.key === C0.ESC + '[1;3C') {\n result.key = (this.browser.isMac) ? C0.ESC + 'f' : C0.ESC + '[1;5C';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OC';\n } else {\n result.key = C0.ESC + '[C';\n }\n break;\n case 38:\n // up-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';\n // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow\n // http://unix.stackexchange.com/a/108106\n if (result.key === C0.ESC + '[1;3A') {\n result.key = C0.ESC + '[1;5A';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OA';\n } else {\n result.key = C0.ESC + '[A';\n }\n break;\n case 40:\n // down-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';\n // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow\n // http://unix.stackexchange.com/a/108106\n if (result.key === C0.ESC + '[1;3B') {\n result.key = C0.ESC + '[1;5B';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OB';\n } else {\n result.key = C0.ESC + '[B';\n }\n break;\n case 45:\n // insert\n if (!ev.shiftKey && !ev.ctrlKey) {\n // or + are used to\n // copy-paste on some systems.\n result.key = C0.ESC + '[2~';\n }\n break;\n case 46:\n // delete\n if (modifiers) {\n result.key = C0.ESC + '[3;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[3~';\n }\n break;\n case 36:\n // home\n if (modifiers)\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'H';\n else if (this.applicationCursor)\n result.key = C0.ESC + 'OH';\n else\n result.key = C0.ESC + '[H';\n break;\n case 35:\n // end\n if (modifiers)\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'F';\n else if (this.applicationCursor)\n result.key = C0.ESC + 'OF';\n else\n result.key = C0.ESC + '[F';\n break;\n case 33:\n // page up\n if (ev.shiftKey) {\n result.scrollLines = -(this.rows - 1);\n } else {\n result.key = C0.ESC + '[5~';\n }\n break;\n case 34:\n // page down\n if (ev.shiftKey) {\n result.scrollLines = this.rows - 1;\n } else {\n result.key = C0.ESC + '[6~';\n }\n break;\n case 112:\n // F1-F12\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'P';\n } else {\n result.key = C0.ESC + 'OP';\n }\n break;\n case 113:\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'Q';\n } else {\n result.key = C0.ESC + 'OQ';\n }\n break;\n case 114:\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'R';\n } else {\n result.key = C0.ESC + 'OR';\n }\n break;\n case 115:\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'S';\n } else {\n result.key = C0.ESC + 'OS';\n }\n break;\n case 116:\n if (modifiers) {\n result.key = C0.ESC + '[15;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[15~';\n }\n break;\n case 117:\n if (modifiers) {\n result.key = C0.ESC + '[17;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[17~';\n }\n break;\n case 118:\n if (modifiers) {\n result.key = C0.ESC + '[18;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[18~';\n }\n break;\n case 119:\n if (modifiers) {\n result.key = C0.ESC + '[19;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[19~';\n }\n break;\n case 120:\n if (modifiers) {\n result.key = C0.ESC + '[20;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[20~';\n }\n break;\n case 121:\n if (modifiers) {\n result.key = C0.ESC + '[21;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[21~';\n }\n break;\n case 122:\n if (modifiers) {\n result.key = C0.ESC + '[23;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[23~';\n }\n break;\n case 123:\n if (modifiers) {\n result.key = C0.ESC + '[24;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[24~';\n }\n break;\n default:\n // a-z and space\n if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {\n if (ev.keyCode >= 65 && ev.keyCode <= 90) {\n result.key = String.fromCharCode(ev.keyCode - 64);\n } else if (ev.keyCode === 32) {\n // NUL\n result.key = String.fromCharCode(0);\n } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {\n // escape, file sep, group sep, record sep, unit sep\n result.key = String.fromCharCode(ev.keyCode - 51 + 27);\n } else if (ev.keyCode === 56) {\n // delete\n result.key = String.fromCharCode(127);\n } else if (ev.keyCode === 219) {\n // ^[ - Control Sequence Introducer (CSI)\n result.key = String.fromCharCode(27);\n } else if (ev.keyCode === 220) {\n // ^\\ - String Terminator (ST)\n result.key = String.fromCharCode(28);\n } else if (ev.keyCode === 221) {\n // ^] - Operating System Command (OSC)\n result.key = String.fromCharCode(29);\n }\n } else if (!this.browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) {\n // On Mac this is a third level shift. Use instead.\n if (ev.keyCode >= 65 && ev.keyCode <= 90) {\n result.key = C0.ESC + String.fromCharCode(ev.keyCode + 32);\n } else if (ev.keyCode === 192) {\n result.key = C0.ESC + '`';\n } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {\n result.key = C0.ESC + (ev.keyCode - 48);\n }\n } else if (this.browser.isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) {\n if (ev.keyCode === 65) { // cmd + a\n this.selectAll();\n }\n }\n break;\n }\n\n return result;\n }\n\n /**\n * Set the G level of the terminal\n * @param g\n */\n public setgLevel(g: number): void {\n this.glevel = g;\n this.charset = this.charsets[g];\n }\n\n /**\n * Set the charset for the given G level of the terminal\n * @param g\n * @param charset\n */\n public setgCharset(g: number, charset: Charset): void {\n this.charsets[g] = charset;\n if (this.glevel === g) {\n this.charset = charset;\n }\n }\n\n /**\n * Handle a keypress event.\n * Key Resources:\n * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent\n * @param {KeyboardEvent} ev The keypress event to be handled.\n */\n protected _keyPress(ev: KeyboardEvent): boolean {\n let key;\n\n if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {\n return false;\n }\n\n this.cancel(ev);\n\n if (ev.charCode) {\n key = ev.charCode;\n } else if (ev.which == null) {\n key = ev.keyCode;\n } else if (ev.which !== 0 && ev.charCode !== 0) {\n key = ev.which;\n } else {\n return false;\n }\n\n if (!key || (\n (ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this.browser, ev)\n )) {\n return false;\n }\n\n key = String.fromCharCode(key);\n\n this.emit('keypress', key, ev);\n this.emit('key', key, ev);\n this.showCursor();\n this.handler(key);\n\n return true;\n }\n\n /**\n * Send data for handling to the terminal\n * @param {string} data\n */\n public send(data: string): void {\n if (!this.sendDataQueue) {\n setTimeout(() => {\n this.handler(this.sendDataQueue);\n this.sendDataQueue = '';\n }, 1);\n }\n\n this.sendDataQueue += data;\n }\n\n /**\n * Ring the bell.\n * Note: We could do sweet things with webaudio here\n */\n public bell(): void {\n this.emit('bell');\n if (this.soundBell()) this.bellAudioElement.play();\n\n if (this.visualBell()) {\n this.element.classList.add('visual-bell-active');\n clearTimeout(this.visualBellTimer);\n this.visualBellTimer = window.setTimeout(() => {\n this.element.classList.remove('visual-bell-active');\n }, 200);\n }\n }\n\n /**\n * Log the current state to the console.\n */\n public log(text: string, data?: any): void {\n if (!this.options.debug) return;\n if (!this.context.console || !this.context.console.log) return;\n this.context.console.log(text, data);\n }\n\n /**\n * Log the current state as error to the console.\n */\n public error(text: string, data?: any): void {\n if (!this.options.debug) return;\n if (!this.context.console || !this.context.console.error) return;\n this.context.console.error(text, data);\n }\n\n /**\n * Resizes the terminal.\n *\n * @param {number} x The number of columns to resize to.\n * @param {number} y The number of rows to resize to.\n */\n public resize(x: number, y: number): void {\n if (isNaN(x) || isNaN(y)) {\n return;\n }\n\n if (x === this.cols && y === this.rows) {\n // Check if we still need to measure the char size (fixes #785).\n if (!this.charMeasure.width || !this.charMeasure.height) {\n this.charMeasure.measure(this.options);\n }\n return;\n }\n\n if (x < 1) x = 1;\n if (y < 1) y = 1;\n\n this.buffers.resize(x, y);\n\n this.cols = x;\n this.rows = y;\n this.buffers.setupTabStops(this.cols);\n\n this.charMeasure.measure(this.options);\n\n this.refresh(0, this.rows - 1);\n\n this.geometry = [this.cols, this.rows];\n this.emit('resize', {cols: x, rows: y});\n }\n\n /**\n * Updates the range of rows to refresh\n * @param {number} y The number of rows to refresh next.\n */\n public updateRange(y: number): void {\n if (y < this.refreshStart) this.refreshStart = y;\n if (y > this.refreshEnd) this.refreshEnd = y;\n // if (y > this.refreshEnd) {\n // this.refreshEnd = y;\n // if (y > this.rows - 1) {\n // this.refreshEnd = this.rows - 1;\n // }\n // }\n }\n\n /**\n * Set the range of refreshing to the maximum value\n */\n public maxRange(): void {\n this.refreshStart = 0;\n this.refreshEnd = this.rows - 1;\n }\n\n /**\n * Erase in the identified line everything from \"x\" to the end of the line (right).\n * @param {number} x The column from which to start erasing to the end of the line.\n * @param {number} y The line in which to operate.\n */\n public eraseRight(x: number, y: number): void {\n const line = this.buffer.lines.get(this.buffer.ybase + y);\n if (!line) {\n return;\n }\n const ch: CharData = [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */]; // xterm\n for (; x < this.cols; x++) {\n line[x] = ch;\n }\n this.updateRange(y);\n }\n\n /**\n * Erase in the identified line everything from \"x\" to the start of the line (left).\n * @param {number} x The column from which to start erasing to the start of the line.\n * @param {number} y The line in which to operate.\n */\n public eraseLeft(x: number, y: number): void {\n const line = this.buffer.lines.get(this.buffer.ybase + y);\n if (!line) {\n return;\n }\n const ch: CharData = [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */]; // xterm\n x++;\n while (x--) {\n line[x] = ch;\n }\n this.updateRange(y);\n }\n\n /**\n * Clear the entire buffer, making the prompt line the new first line.\n */\n public clear(): void {\n if (this.buffer.ybase === 0 && this.buffer.y === 0) {\n // Don't clear if it's already clear\n return;\n }\n this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y));\n this.buffer.lines.length = 1;\n this.buffer.ydisp = 0;\n this.buffer.ybase = 0;\n this.buffer.y = 0;\n for (let i = 1; i < this.rows; i++) {\n this.buffer.lines.push(this.blankLine());\n }\n this.refresh(0, this.rows - 1);\n this.emit('scroll', this.buffer.ydisp);\n }\n\n /**\n * Erase all content in the given line\n * @param {number} y The line to erase all of its contents.\n */\n public eraseLine(y: number): void {\n this.eraseRight(0, y);\n }\n\n /**\n * Return the data array of a blank line\n * @param {boolean} cur First bunch of data for each \"blank\" character.\n * @param {boolean} isWrapped Whether the new line is wrapped from the previous line.\n * @param {boolean} cols The number of columns in the terminal, if this is not\n * set, the terminal's current column count would be used.\n */\n public blankLine(cur?: boolean, isWrapped?: boolean, cols?: number): LineData {\n const attr = cur ? this.eraseAttr() : this.defAttr;\n\n const ch: CharData = [attr, ' ', 1, 32 /* ' '.charCodeAt(0) */]; // width defaults to 1 halfwidth character\n const line: LineData = [];\n\n // TODO: It is not ideal that this is a property on an array, a buffer line\n // class should be added that will hold this data and other useful functions.\n if (isWrapped) {\n (line).isWrapped = isWrapped;\n }\n\n cols = cols || this.cols;\n for (let i = 0; i < cols; i++) {\n line[i] = ch;\n }\n\n return line;\n }\n\n /**\n * If cur return the back color xterm feature attribute. Else return defAttr.\n * @param cur\n */\n public ch(cur?: boolean): CharData {\n if (cur) {\n return [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */];\n }\n return [this.defAttr, ' ', 1, 32 /* ' '.charCodeAt(0) */];\n }\n\n /**\n * Evaluate if the current terminal is the given argument.\n * @param term The terminal name to evaluate\n */\n public is(term: string): boolean {\n return (this.options.termName + '').indexOf(term) === 0;\n }\n\n /**\n * Emit the 'data' event and populate the given data.\n * @param {string} data The data to populate in the event.\n */\n public handler(data: string): void {\n // Prevents all events to pty process if stdin is disabled\n if (this.options.disableStdin) {\n return;\n }\n\n // Clear the selection if the selection manager is available and has an active selection\n if (this.selectionManager && this.selectionManager.hasSelection) {\n this.selectionManager.clearSelection();\n }\n\n // Input is being sent to the terminal, the terminal should focus the prompt.\n if (this.buffer.ybase !== this.buffer.ydisp) {\n this.scrollToBottom();\n }\n this.emit('data', data);\n }\n\n /**\n * Emit the 'title' event and populate the given title.\n * @param {string} title The title to populate in the event.\n */\n private handleTitle(title: string): void {\n /**\n * This event is emitted when the title of the terminal is changed\n * from inside the terminal. The parameter is the new title.\n *\n * @event title\n */\n this.emit('title', title);\n }\n\n /**\n * ESC\n */\n\n /**\n * ESC D Index (IND is 0x84).\n */\n public index(): void {\n this.buffer.y++;\n if (this.buffer.y > this.buffer.scrollBottom) {\n this.buffer.y--;\n this.scroll();\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this.buffer.x >= this.cols) {\n this.buffer.x--;\n }\n }\n\n /**\n * ESC M Reverse Index (RI is 0x8d).\n *\n * Move the cursor up one row, inserting a new blank line if necessary.\n */\n public reverseIndex(): void {\n if (this.buffer.y === this.buffer.scrollTop) {\n // possibly move the code below to term.reverseScroll();\n // test: echo -ne '\\e[1;1H\\e[44m\\eM\\e[0m'\n // blankLine(true) is xterm/linux behavior\n const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop;\n this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1);\n this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.blankLine(true));\n this.updateRange(this.buffer.scrollTop);\n this.updateRange(this.buffer.scrollBottom);\n } else {\n this.buffer.y--;\n }\n }\n\n /**\n * ESC c Full Reset (RIS).\n */\n public reset(): void {\n this.options.rows = this.rows;\n this.options.cols = this.cols;\n const customKeyEventHandler = this.customKeyEventHandler;\n const inputHandler = this.inputHandler;\n const buffers = this.buffers;\n this.setup();\n this.customKeyEventHandler = customKeyEventHandler;\n this.inputHandler = inputHandler;\n this.buffers = buffers;\n this.refresh(0, this.rows - 1);\n this.viewport.syncScrollArea();\n }\n\n\n /**\n * ESC H Tab Set (HTS is 0x88).\n */\n private tabSet(): void {\n this.buffer.tabs[this.buffer.x] = true;\n }\n\n // TODO: Remove cancel function and cancelEvents option\n public cancel(ev: Event, force?: boolean): boolean {\n if (!this.options.cancelEvents && !force) {\n return;\n }\n ev.preventDefault();\n ev.stopPropagation();\n return false;\n }\n\n // TODO: Remove when true color is implemented\n public matchColor(r1: number, g1: number, b1: number): number {\n return matchColor_(r1, g1, b1);\n }\n\n private visualBell(): boolean {\n return this.options.bellStyle === 'visual' ||\n this.options.bellStyle === 'both';\n }\n\n private soundBell(): boolean {\n return this.options.bellStyle === 'sound' ||\n this.options.bellStyle === 'both';\n }\n\n private syncBellSound(): void {\n if (this.soundBell() && this.bellAudioElement) {\n this.bellAudioElement.setAttribute('src', this.options.bellSound);\n } else if (this.soundBell()) {\n this.bellAudioElement = document.createElement('audio');\n this.bellAudioElement.setAttribute('preload', 'auto');\n this.bellAudioElement.setAttribute('src', this.options.bellSound);\n this.helperContainer.appendChild(this.bellAudioElement);\n } else if (this.bellAudioElement) {\n this.helperContainer.removeChild(this.bellAudioElement);\n }\n }\n}\n\n/**\n * Helpers\n */\n\nfunction globalOn(el: any, type: string, handler: (event: Event) => any, capture?: boolean): void {\n if (!Array.isArray(el)) {\n el = [el];\n }\n el.forEach((element: HTMLElement) => {\n element.addEventListener(type, handler, capture || false);\n });\n}\n// TODO: Remove once everything is typed\nconst on = globalOn;\n\nfunction off(el: any, type: string, handler: (event: Event) => any, capture: boolean = false): void {\n el.removeEventListener(type, handler, capture);\n}\n\nfunction isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean {\n const thirdLevelKey =\n (browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||\n (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);\n\n if (ev.type === 'keypress') {\n return thirdLevelKey;\n }\n\n // Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events)\n return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47);\n}\n\nfunction wasMondifierKeyOnlyEvent(ev: KeyboardEvent): boolean {\n return ev.keyCode === 16 || // Shift\n ev.keyCode === 17 || // Ctrl\n ev.keyCode === 18; // Alt\n}\n\n/**\n * TODO:\n * The below color-related code can be removed when true color is implemented.\n * It's only purpose is to match true color requests with the closest matching\n * ANSI color code.\n */\n\n// Colors 0-15 + 16-255\n// Much thanks to TooTallNate for writing this.\nconst vcolors: number[][] = (function(): number[][] {\n const result = DEFAULT_ANSI_COLORS.map(c => {\n c = c.substring(1);\n return [\n parseInt(c.substring(0, 2), 16),\n parseInt(c.substring(2, 4), 16),\n parseInt(c.substring(4, 6), 16)\n ];\n });\n const r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];\n\n // 16-231\n for (let i = 0; i < 216; i++) {\n result.push([\n r[(i / 36) % 6 | 0],\n r[(i / 6) % 6 | 0],\n r[i % 6]\n ]);\n }\n\n // 232-255 (grey)\n let c: number;\n for (let i = 0; i < 24; i++) {\n c = 8 + i * 10;\n result.push([c, c, c]);\n }\n\n return result;\n})();\n\nconst matchColorCache: {[colorRGBHash: number]: number} = {};\n\n// http://stackoverflow.com/questions/1633828\nfunction matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number {\n return Math.pow(30 * (r1 - r2), 2)\n + Math.pow(59 * (g1 - g2), 2)\n + Math.pow(11 * (b1 - b2), 2);\n};\n\n\nfunction matchColor_(r1: number, g1: number, b1: number): number {\n const hash = (r1 << 16) | (g1 << 8) | b1;\n\n if (matchColorCache[hash] != null) {\n return matchColorCache[hash];\n }\n\n let ldiff = Infinity;\n let li = -1;\n let i = 0;\n let c: number[];\n let r2: number;\n let g2: number;\n let b2: number;\n let diff: number;\n\n for (; i < vcolors.length; i++) {\n c = vcolors[i];\n r2 = c[0];\n g2 = c[1];\n b2 = c[2];\n\n diff = matchColorDistance(r1, g1, b1, r2, g2, b2);\n\n if (diff === 0) {\n li = i;\n break;\n }\n\n if (diff < ldiff) {\n ldiff = diff;\n li = i;\n }\n }\n\n return matchColorCache[hash] = li;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal } from './Interfaces';\n\n/**\n * Represents a selection within the buffer. This model only cares about column\n * and row coordinates, not wide characters.\n */\nexport class SelectionModel {\n /**\n * Whether select all is currently active.\n */\n public isSelectAllActive: boolean;\n\n /**\n * The [x, y] position the selection starts at.\n */\n public selectionStart: [number, number];\n\n /**\n * The minimal length of the selection from the start position. When double\n * clicking on a word, the word will be selected which makes the selection\n * start at the start of the word and makes this variable the length.\n */\n public selectionStartLength: number;\n\n /**\n * The [x, y] position the selection ends at.\n */\n public selectionEnd: [number, number];\n\n constructor(\n private _terminal: ITerminal\n ) {\n this.clearSelection();\n }\n\n /**\n * Clears the current selection.\n */\n public clearSelection(): void {\n this.selectionStart = null;\n this.selectionEnd = null;\n this.isSelectAllActive = false;\n this.selectionStartLength = 0;\n }\n\n /**\n * The final selection start, taking into consideration select all.\n */\n public get finalSelectionStart(): [number, number] {\n if (this.isSelectAllActive) {\n return [0, 0];\n }\n\n if (!this.selectionEnd || !this.selectionStart) {\n return this.selectionStart;\n }\n\n return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart;\n }\n\n /**\n * The final selection end, taking into consideration select all, double click\n * word selection and triple click line selection.\n */\n public get finalSelectionEnd(): [number, number] {\n if (this.isSelectAllActive) {\n return [this._terminal.cols, this._terminal.buffer.ybase + this._terminal.rows - 1];\n }\n\n if (!this.selectionStart) {\n return null;\n }\n\n // Use the selection start if the end doesn't exist or they're reversed\n if (!this.selectionEnd || this.areSelectionValuesReversed()) {\n return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]];\n }\n\n // Ensure the the word/line is selected after a double/triple click\n if (this.selectionStartLength) {\n // Select the larger of the two when start and end are on the same line\n if (this.selectionEnd[1] === this.selectionStart[1]) {\n return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]];\n }\n }\n return this.selectionEnd;\n }\n\n /**\n * Returns whether the selection start and end are reversed.\n */\n public areSelectionValuesReversed(): boolean {\n const start = this.selectionStart;\n const end = this.selectionEnd;\n if (!start || !end) {\n return false;\n }\n return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]);\n }\n\n /**\n * Handle the buffer being trimmed, adjust the selection position.\n * @param amount The amount the buffer is being trimmed.\n * @return Whether a refresh is necessary.\n */\n public onTrim(amount: number): boolean {\n // Adjust the selection position based on the trimmed amount.\n if (this.selectionStart) {\n this.selectionStart[1] -= amount;\n }\n if (this.selectionEnd) {\n this.selectionEnd[1] -= amount;\n }\n\n // The selection has moved off the buffer, clear it.\n if (this.selectionEnd && this.selectionEnd[1] < 0) {\n this.clearSelection();\n return true;\n }\n\n // If the selection start is trimmed, ensure the start column is 0.\n if (this.selectionStart && this.selectionStart[1] < 0) {\n this.selectionStart[1] = 0;\n }\n return false;\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { MouseHelper } from './utils/MouseHelper';\nimport * as Browser from './utils/Browser';\nimport { CharMeasure } from './utils/CharMeasure';\nimport { CircularList } from './utils/CircularList';\nimport { EventEmitter } from './EventEmitter';\nimport { ITerminal, ICircularList, ISelectionManager, IBuffer } from './Interfaces';\nimport { SelectionModel } from './SelectionModel';\nimport { LineData, CharData } from './Types';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer';\n\n/**\n * The number of pixels the mouse needs to be above or below the viewport in\n * order to scroll at the maximum speed.\n */\nconst DRAG_SCROLL_MAX_THRESHOLD = 50;\n\n/**\n * The maximum scrolling speed\n */\nconst DRAG_SCROLL_MAX_SPEED = 15;\n\n/**\n * The number of milliseconds between drag scroll updates.\n */\nconst DRAG_SCROLL_INTERVAL = 50;\n\n/**\n * A string containing all characters that are considered word separated by the\n * double click to select work logic.\n */\nconst WORD_SEPARATORS = ' ()[]{}\\'\"';\n\nconst NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);\nconst ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');\n\n/**\n * Represents a position of a word on a line.\n */\ninterface IWordPosition {\n start: number;\n length: number;\n}\n\n/**\n * A selection mode, this drives how the selection behaves on mouse move.\n */\nenum SelectionMode {\n NORMAL,\n WORD,\n LINE\n}\n\n/**\n * A class that manages the selection of the terminal. With help from\n * SelectionModel, SelectionManager handles with all logic associated with\n * dealing with the selection, including handling mouse interaction, wide\n * characters and fetching the actual text within the selection. Rendering is\n * not handled by the SelectionManager but a 'refresh' event is fired when the\n * selection is ready to be redrawn.\n */\nexport class SelectionManager extends EventEmitter implements ISelectionManager {\n protected _model: SelectionModel;\n\n /**\n * The amount to scroll every drag scroll update (depends on how far the mouse\n * drag is above or below the terminal).\n */\n private _dragScrollAmount: number;\n\n /**\n * The current selection mode.\n */\n private _activeSelectionMode: SelectionMode;\n\n /**\n * A setInterval timer that is active while the mouse is down whose callback\n * scrolls the viewport when necessary.\n */\n private _dragScrollIntervalTimer: NodeJS.Timer;\n\n /**\n * The animation frame ID used for refreshing the selection.\n */\n private _refreshAnimationFrame: number;\n\n /**\n * Whether selection is enabled.\n */\n private _enabled = true;\n\n private _mouseMoveListener: EventListener;\n private _mouseUpListener: EventListener;\n\n constructor(\n private _terminal: ITerminal,\n private _buffer: IBuffer,\n private _charMeasure: CharMeasure\n ) {\n super();\n this._initListeners();\n this.enable();\n\n this._model = new SelectionModel(_terminal);\n this._activeSelectionMode = SelectionMode.NORMAL;\n }\n\n /**\n * Initializes listener variables.\n */\n private _initListeners(): void {\n this._mouseMoveListener = event => this._onMouseMove(event);\n this._mouseUpListener = event => this._onMouseUp(event);\n\n // Only adjust the selection on trim, shiftElements is rarely used (only in\n // reverseIndex) and delete in a splice is only ever used when the same\n // number of elements was just added. Given this is could actually be\n // beneficial to leave the selection as is for these cases.\n this._buffer.lines.on('trim', (amount: number) => this._onTrim(amount));\n }\n\n /**\n * Disables the selection manager. This is useful for when terminal mouse\n * are enabled.\n */\n public disable(): void {\n this.clearSelection();\n this._enabled = false;\n }\n\n /**\n * Enable the selection manager.\n */\n public enable(): void {\n this._enabled = true;\n }\n\n /**\n * Sets the active buffer, this should be called when the alt buffer is\n * switched in or out.\n * @param buffer The active buffer.\n */\n public setBuffer(buffer: IBuffer): void {\n this._buffer = buffer;\n this.clearSelection();\n }\n\n public get selectionStart(): [number, number] { return this._model.finalSelectionStart; }\n public get selectionEnd(): [number, number] { return this._model.finalSelectionEnd; }\n\n /**\n * Gets whether there is an active text selection.\n */\n public get hasSelection(): boolean {\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n if (!start || !end) {\n return false;\n }\n return start[0] !== end[0] || start[1] !== end[1];\n }\n\n /**\n * Gets the text currently selected.\n */\n public get selectionText(): string {\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n if (!start || !end) {\n return '';\n }\n\n // Get first row\n const startRowEndCol = start[1] === end[1] ? end[0] : null;\n let result: string[] = [];\n result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));\n\n // Get middle rows\n for (let i = start[1] + 1; i <= end[1] - 1; i++) {\n const bufferLine = this._buffer.lines.get(i);\n const lineText = this._buffer.translateBufferLineToString(i, true);\n if ((bufferLine).isWrapped) {\n result[result.length - 1] += lineText;\n } else {\n result.push(lineText);\n }\n }\n\n // Get final row\n if (start[1] !== end[1]) {\n const bufferLine = this._buffer.lines.get(end[1]);\n const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]);\n if ((bufferLine).isWrapped) {\n result[result.length - 1] += lineText;\n } else {\n result.push(lineText);\n }\n }\n\n // Format string by replacing non-breaking space chars with regular spaces\n // and joining the array into a multi-line string.\n const formattedResult = result.map(line => {\n return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' ');\n }).join(Browser.isMSWindows ? '\\r\\n' : '\\n');\n\n return formattedResult;\n }\n\n /**\n * Clears the current terminal selection.\n */\n public clearSelection(): void {\n this._model.clearSelection();\n this._removeMouseDownListeners();\n this.refresh();\n }\n\n /**\n * Queues a refresh, redrawing the selection on the next opportunity.\n * @param isNewSelection Whether the selection should be registered as a new\n * selection on Linux.\n */\n public refresh(isNewSelection?: boolean): void {\n // Queue the refresh for the renderer\n if (!this._refreshAnimationFrame) {\n this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());\n }\n\n // If the platform is Linux and the refresh call comes from a mouse event,\n // we need to update the selection for middle click to paste selection.\n if (Browser.isLinux && isNewSelection) {\n const selectionText = this.selectionText;\n if (selectionText.length) {\n this.emit('newselection', this.selectionText);\n }\n }\n }\n\n /**\n * Fires the refresh event, causing consumers to pick it up and redraw the\n * selection state.\n */\n private _refresh(): void {\n this._refreshAnimationFrame = null;\n this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd });\n }\n\n /**\n * Selects all text within the terminal.\n */\n public selectAll(): void {\n this._model.isSelectAllActive = true;\n this.refresh();\n this.emit('selection');\n }\n\n /**\n * Handle the buffer being trimmed, adjust the selection position.\n * @param amount The amount the buffer is being trimmed.\n */\n private _onTrim(amount: number): void {\n const needsRefresh = this._model.onTrim(amount);\n if (needsRefresh) {\n this.refresh();\n }\n }\n\n /**\n * Gets the 0-based [x, y] buffer coordinates of the current mouse event.\n * @param event The mouse event.\n */\n private _getMouseBufferCoords(event: MouseEvent): [number, number] {\n const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.element, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true);\n if (!coords) {\n return null;\n }\n\n // Convert to 0-based\n coords[0]--;\n coords[1]--;\n\n // Convert viewport coords to buffer coords\n coords[1] += this._terminal.buffer.ydisp;\n return coords;\n }\n\n /**\n * Gets the amount the viewport should be scrolled based on how far out of the\n * terminal the mouse is.\n * @param event The mouse event.\n */\n private _getMouseEventScrollAmount(event: MouseEvent): number {\n let offset = MouseHelper.getCoordsRelativeToElement(event, this._terminal.element)[1];\n const terminalHeight = this._terminal.rows * Math.ceil(this._charMeasure.height * this._terminal.options.lineHeight);\n if (offset >= 0 && offset <= terminalHeight) {\n return 0;\n }\n if (offset > terminalHeight) {\n offset -= terminalHeight;\n }\n\n offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD);\n offset /= DRAG_SCROLL_MAX_THRESHOLD;\n return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1));\n }\n\n /**\n * Returns whether the selection manager should force selection, regardless of\n * whether the terminal is in mouse events mode.\n * @param event The mouse event.\n */\n public shouldForceSelection(event: MouseEvent): boolean {\n return Browser.isMac ? event.altKey : event.shiftKey;\n }\n\n /**\n * Handles te mousedown event, setting up for a new selection.\n * @param event The mousedown event.\n */\n public onMouseDown(event: MouseEvent): void {\n // If we have selection, we want the context menu on right click even if the\n // terminal is in mouse mode.\n if (event.button === 2 && this.hasSelection) {\n return;\n }\n\n // Only action the primary button\n if (event.button !== 0) {\n return;\n }\n\n // Allow selection when using a specific modifier key, even when disabled\n if (!this._enabled) {\n if (!this.shouldForceSelection(event)) {\n return;\n }\n\n // Don't send the mouse down event to the current process, we want to select\n event.stopPropagation();\n }\n\n // Tell the browser not to start a regular selection\n event.preventDefault();\n\n // Reset drag scroll state\n this._dragScrollAmount = 0;\n\n if (this._enabled && event.shiftKey) {\n this._onIncrementalClick(event);\n } else {\n if (event.detail === 1) {\n this._onSingleClick(event);\n } else if (event.detail === 2) {\n this._onDoubleClick(event);\n } else if (event.detail === 3) {\n this._onTripleClick(event);\n }\n }\n\n this._addMouseDownListeners();\n this.refresh(true);\n }\n\n /**\n * Adds listeners when mousedown is triggered.\n */\n private _addMouseDownListeners(): void {\n // Listen on the document so that dragging outside of viewport works\n this._terminal.element.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.ownerDocument.addEventListener('mouseup', this._mouseUpListener);\n this._dragScrollIntervalTimer = setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);\n }\n\n /**\n * Removes the listeners that are registered when mousedown is triggered.\n */\n private _removeMouseDownListeners(): void {\n this._terminal.element.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);\n clearInterval(this._dragScrollIntervalTimer);\n this._dragScrollIntervalTimer = null;\n }\n\n /**\n * Performs an incremental click, setting the selection end position to the mouse\n * position.\n * @param event The mouse event.\n */\n private _onIncrementalClick(event: MouseEvent): void {\n if (this._model.selectionStart) {\n this._model.selectionEnd = this._getMouseBufferCoords(event);\n }\n }\n\n /**\n * Performs a single click, resetting relevant state and setting the selection\n * start position.\n * @param event The mouse event.\n */\n private _onSingleClick(event: MouseEvent): void {\n this._model.selectionStartLength = 0;\n this._model.isSelectAllActive = false;\n this._activeSelectionMode = SelectionMode.NORMAL;\n\n // Initialize the new selection\n this._model.selectionStart = this._getMouseBufferCoords(event);\n if (!this._model.selectionStart) {\n return;\n }\n this._model.selectionEnd = null;\n\n // Ensure the line exists\n const line = this._buffer.lines.get(this._model.selectionStart[1]);\n if (!line) {\n return;\n }\n\n // Return early if the click event is not in the buffer (eg. in scroll bar)\n if (line.length >= this._model.selectionStart[0]) {\n return;\n }\n\n // If the mouse is over the second half of a wide character, adjust the\n // selection to cover the whole character\n const char = line[this._model.selectionStart[0]];\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n this._model.selectionStart[0]++;\n }\n }\n\n /**\n * Performs a double click, selecting the current work.\n * @param event The mouse event.\n */\n private _onDoubleClick(event: MouseEvent): void {\n const coords = this._getMouseBufferCoords(event);\n if (coords) {\n this._activeSelectionMode = SelectionMode.WORD;\n this._selectWordAt(coords);\n }\n }\n\n /**\n * Performs a triple click, selecting the current line and activating line\n * select mode.\n * @param event The mouse event.\n */\n private _onTripleClick(event: MouseEvent): void {\n const coords = this._getMouseBufferCoords(event);\n if (coords) {\n this._activeSelectionMode = SelectionMode.LINE;\n this._selectLineAt(coords[1]);\n }\n }\n\n /**\n * Handles the mousemove event when the mouse button is down, recording the\n * end of the selection and refreshing the selection.\n * @param event The mousemove event.\n */\n private _onMouseMove(event: MouseEvent): void {\n // If the mousemove listener is active it means that a selection is\n // currently being made, we should stop propogation to prevent mouse events\n // to be sent to the pty.\n event.stopImmediatePropagation();\n\n // Record the previous position so we know whether to redraw the selection\n // at the end.\n const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;\n\n // Set the initial selection end based on the mouse coordinates\n this._model.selectionEnd = this._getMouseBufferCoords(event);\n if (!this._model.selectionEnd) {\n this.refresh(true);\n return;\n }\n\n // Select the entire line if line select mode is active.\n if (this._activeSelectionMode === SelectionMode.LINE) {\n if (this._model.selectionEnd[1] < this._model.selectionStart[1]) {\n this._model.selectionEnd[0] = 0;\n } else {\n this._model.selectionEnd[0] = this._terminal.cols;\n }\n } else if (this._activeSelectionMode === SelectionMode.WORD) {\n this._selectToWordAt(this._model.selectionEnd);\n }\n\n // Determine the amount of scrolling that will happen.\n this._dragScrollAmount = this._getMouseEventScrollAmount(event);\n\n // If the cursor was above or below the viewport, make sure it's at the\n // start or end of the viewport respectively.\n if (this._dragScrollAmount > 0) {\n this._model.selectionEnd[0] = this._terminal.cols;\n } else if (this._dragScrollAmount < 0) {\n this._model.selectionEnd[0] = 0;\n }\n\n // If the character is a wide character include the cell to the right in the\n // selection. Note that selections at the very end of the line will never\n // have a character.\n if (this._model.selectionEnd[1] < this._buffer.lines.length) {\n const char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]];\n if (char && char[CHAR_DATA_WIDTH_INDEX] === 0) {\n this._model.selectionEnd[0]++;\n }\n }\n\n // Only draw here if the selection changes.\n if (!previousSelectionEnd ||\n previousSelectionEnd[0] !== this._model.selectionEnd[0] ||\n previousSelectionEnd[1] !== this._model.selectionEnd[1]) {\n this.refresh(true);\n }\n }\n\n /**\n * The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the\n * scrolling of the viewport.\n */\n private _dragScroll(): void {\n if (this._dragScrollAmount) {\n this._terminal.scrollLines(this._dragScrollAmount, false);\n // Re-evaluate selection\n if (this._dragScrollAmount > 0) {\n this._model.selectionEnd = [this._terminal.cols - 1, this._terminal.buffer.ydisp + this._terminal.rows];\n } else {\n this._model.selectionEnd = [0, this._terminal.buffer.ydisp];\n }\n this.refresh();\n }\n }\n\n /**\n * Handles the mouseup event, removing the mousedown listeners.\n * @param event The mouseup event.\n */\n private _onMouseUp(event: MouseEvent): void {\n this._removeMouseDownListeners();\n\n if (this.hasSelection)\n this.emit('selection');\n }\n\n /**\n * Converts a viewport column to the character index on the buffer line, the\n * latter takes into account wide characters.\n * @param coords The coordinates to find the 2 index for.\n */\n private _convertViewportColToCharacterIndex(bufferLine: any, coords: [number, number]): number {\n let charIndex = coords[0];\n for (let i = 0; coords[0] >= i; i++) {\n const char = bufferLine[i];\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n // Wide characters aren't included in the line string so decrement the\n // index so the index is back on the wide character.\n charIndex--;\n } else if (char[CHAR_DATA_CHAR_INDEX].length > 1 && coords[0] !== i) {\n // Emojis take up multiple characters, so adjust accordingly. For these\n // we don't want ot include the character at the column as we're\n // returning the start index in the string, not the end index.\n charIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n }\n return charIndex;\n }\n\n public setSelection(col: number, row: number, length: number): void {\n this._model.clearSelection();\n this._removeMouseDownListeners();\n this._model.selectionStart = [col, row];\n this._model.selectionStartLength = length;\n this.refresh();\n }\n\n /**\n * Gets positional information for the word at the coordinated specified.\n * @param coords The coordinates to get the word at.\n */\n private _getWordAt(coords: [number, number]): IWordPosition {\n const bufferLine = this._buffer.lines.get(coords[1]);\n if (!bufferLine) {\n return null;\n }\n\n const line = this._buffer.translateBufferLineToString(coords[1], false);\n\n // Get actual index, taking into consideration wide characters\n let startIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);\n let endIndex = startIndex;\n\n // Record offset to be used later\n const charOffset = coords[0] - startIndex;\n let leftWideCharCount = 0;\n let rightWideCharCount = 0;\n let leftLongCharOffset = 0;\n let rightLongCharOffset = 0;\n\n if (line.charAt(startIndex) === ' ') {\n // Expand until non-whitespace is hit\n while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') {\n startIndex--;\n }\n while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') {\n endIndex++;\n }\n } else {\n // Expand until whitespace is hit. This algorithm works by scanning left\n // and right from the starting position, keeping both the index format\n // (line) and the column format (bufferLine) in sync. When a wide\n // character is hit, it is recorded and the column index is adjusted.\n let startCol = coords[0];\n let endCol = coords[0];\n\n // Consider the initial position, skip it and increment the wide char\n // variable\n if (bufferLine[startCol][CHAR_DATA_WIDTH_INDEX] === 0) {\n leftWideCharCount++;\n startCol--;\n }\n if (bufferLine[endCol][CHAR_DATA_WIDTH_INDEX] === 2) {\n rightWideCharCount++;\n endCol++;\n }\n\n // Adjust the end index for characters whose length are > 1 (emojis)\n if (bufferLine[endCol][CHAR_DATA_CHAR_INDEX].length > 1) {\n rightLongCharOffset += bufferLine[endCol][CHAR_DATA_CHAR_INDEX].length - 1;\n endIndex += bufferLine[endCol][CHAR_DATA_CHAR_INDEX].length - 1;\n }\n\n // Expand the string in both directions until a space is hit\n while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine[startCol - 1])) {\n const char = bufferLine[startCol - 1];\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n // If the next character is a wide char, record it and skip the column\n leftWideCharCount++;\n startCol--;\n } else if (char[CHAR_DATA_CHAR_INDEX].length > 1) {\n // If the next character's string is longer than 1 char (eg. emoji),\n // adjust the index\n leftLongCharOffset += char[CHAR_DATA_CHAR_INDEX].length - 1;\n startIndex -= char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n startIndex--;\n startCol--;\n }\n while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine[endCol + 1])) {\n const char = bufferLine[endCol + 1];\n if (char[CHAR_DATA_WIDTH_INDEX] === 2) {\n // If the next character is a wide char, record it and skip the column\n rightWideCharCount++;\n endCol++;\n } else if (char[CHAR_DATA_CHAR_INDEX].length > 1) {\n // If the next character's string is longer than 1 char (eg. emoji),\n // adjust the index\n rightLongCharOffset += char[CHAR_DATA_CHAR_INDEX].length - 1;\n endIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n endIndex++;\n endCol++;\n }\n }\n\n // Incremenet the end index so it is at the start of the next character\n endIndex++;\n\n // Calculate the start _column_, converting the the string indexes back to\n // column coordinates.\n const start =\n startIndex // The index of the selection's start char in the line string\n + charOffset // The difference between the initial char's column and index\n - leftWideCharCount // The number of wide chars left of the initial char\n + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)\n\n // Calculate the length in _columns_, converting the the string indexes back\n // to column coordinates.\n const length = Math.min(this._terminal.cols, // Disallow lengths larger than the terminal cols\n endIndex // The index of the selection's end char in the line string\n - startIndex // The index of the selection's start char in the line string\n + leftWideCharCount // The number of wide chars left of the initial char\n + rightWideCharCount // The number of wide chars right of the initial char (inclusive)\n - leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)\n - rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis)\n\n return { start, length };\n }\n\n /**\n * Selects the word at the coordinates specified.\n * @param coords The coordinates to get the word at.\n */\n protected _selectWordAt(coords: [number, number]): void {\n const wordPosition = this._getWordAt(coords);\n if (wordPosition) {\n this._model.selectionStart = [wordPosition.start, coords[1]];\n this._model.selectionStartLength = wordPosition.length;\n }\n }\n\n /**\n * Sets the selection end to the word at the coordinated specified.\n * @param coords The coordinates to get the word at.\n */\n private _selectToWordAt(coords: [number, number]): void {\n const wordPosition = this._getWordAt(coords);\n if (wordPosition) {\n this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]];\n }\n }\n\n /**\n * Gets whether the character is considered a word separator by the select\n * word logic.\n * @param char The character to check.\n */\n private _isCharWordSeparator(charData: CharData): boolean {\n // Zero width characters are never separators as they are always to the\n // right of wide characters\n if (charData[CHAR_DATA_WIDTH_INDEX] === 0) {\n return false;\n }\n return WORD_SEPARATORS.indexOf(charData[CHAR_DATA_CHAR_INDEX]) >= 0;\n }\n\n /**\n * Selects the line specified.\n * @param line The line index.\n */\n protected _selectLineAt(line: number): void {\n this._model.selectionStart = [0, line];\n this._model.selectionStartLength = this._terminal.cols;\n }\n}\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n */\n\nimport { C0 } from './EscapeSequences';\nimport { IInputHandler } from './Interfaces';\nimport { CHARSETS, DEFAULT_CHARSET } from './Charsets';\n\nconst normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {};\nnormalStateHandler[C0.BEL] = (parser, handler) => handler.bell();\nnormalStateHandler[C0.LF] = (parser, handler) => handler.lineFeed();\nnormalStateHandler[C0.VT] = normalStateHandler[C0.LF];\nnormalStateHandler[C0.FF] = normalStateHandler[C0.LF];\nnormalStateHandler[C0.CR] = (parser, handler) => handler.carriageReturn();\nnormalStateHandler[C0.BS] = (parser, handler) => handler.backspace();\nnormalStateHandler[C0.HT] = (parser, handler) => handler.tab();\nnormalStateHandler[C0.SO] = (parser, handler) => handler.shiftOut();\nnormalStateHandler[C0.SI] = (parser, handler) => handler.shiftIn();\nnormalStateHandler[C0.ESC] = (parser, handler) => parser.setState(ParserState.ESCAPED);\n\n// TODO: Remove terminal when parser owns params and currentParam\nconst escapedStateHandler: {[key: string]: (parser: Parser, terminal: any) => void} = {};\nescapedStateHandler['['] = (parser, terminal) => {\n // ESC [ Control Sequence Introducer (CSI is 0x9b)\n terminal.params = [];\n terminal.currentParam = 0;\n parser.setState(ParserState.CSI_PARAM);\n};\nescapedStateHandler[']'] = (parser, terminal) => {\n // ESC ] Operating System Command (OSC is 0x9d)\n terminal.params = [];\n terminal.currentParam = 0;\n parser.setState(ParserState.OSC);\n};\nescapedStateHandler['P'] = (parser, terminal) => {\n // ESC P Device Control String (DCS is 0x90)\n terminal.params = [];\n terminal.currentParam = 0;\n parser.setState(ParserState.DCS);\n};\nescapedStateHandler['_'] = (parser, terminal) => {\n // ESC _ Application Program Command ( APC is 0x9f).\n parser.setState(ParserState.IGNORE);\n};\nescapedStateHandler['^'] = (parser, terminal) => {\n // ESC ^ Privacy Message ( PM is 0x9e).\n parser.setState(ParserState.IGNORE);\n};\nescapedStateHandler['c'] = (parser, terminal) => {\n // ESC c Full Reset (RIS).\n terminal.reset();\n};\nescapedStateHandler['E'] = (parser, terminal) => {\n // ESC E Next Line ( NEL is 0x85).\n terminal.buffer.x = 0;\n terminal.index();\n parser.setState(ParserState.NORMAL);\n};\nescapedStateHandler['D'] = (parser, terminal) => {\n // ESC D Index ( IND is 0x84).\n terminal.index();\n parser.setState(ParserState.NORMAL);\n};\nescapedStateHandler['M'] = (parser, terminal) => {\n // ESC M Reverse Index ( RI is 0x8d).\n terminal.reverseIndex();\n parser.setState(ParserState.NORMAL);\n};\nescapedStateHandler['%'] = (parser, terminal) => {\n // ESC % Select default/utf-8 character set.\n // @ = default, G = utf-8\n terminal.setgLevel(0);\n terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)\n parser.setState(ParserState.NORMAL);\n parser.skipNextChar();\n};\nescapedStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);\n\nconst csiParamStateHandler: {[key: string]: (parser: Parser) => void} = {};\ncsiParamStateHandler['?'] = (parser) => parser.setPrefix('?');\ncsiParamStateHandler['>'] = (parser) => parser.setPrefix('>');\ncsiParamStateHandler['!'] = (parser) => parser.setPrefix('!');\ncsiParamStateHandler['0'] = (parser) => parser.setParam(parser.getParam() * 10);\ncsiParamStateHandler['1'] = (parser) => parser.setParam(parser.getParam() * 10 + 1);\ncsiParamStateHandler['2'] = (parser) => parser.setParam(parser.getParam() * 10 + 2);\ncsiParamStateHandler['3'] = (parser) => parser.setParam(parser.getParam() * 10 + 3);\ncsiParamStateHandler['4'] = (parser) => parser.setParam(parser.getParam() * 10 + 4);\ncsiParamStateHandler['5'] = (parser) => parser.setParam(parser.getParam() * 10 + 5);\ncsiParamStateHandler['6'] = (parser) => parser.setParam(parser.getParam() * 10 + 6);\ncsiParamStateHandler['7'] = (parser) => parser.setParam(parser.getParam() * 10 + 7);\ncsiParamStateHandler['8'] = (parser) => parser.setParam(parser.getParam() * 10 + 8);\ncsiParamStateHandler['9'] = (parser) => parser.setParam(parser.getParam() * 10 + 9);\ncsiParamStateHandler['$'] = (parser) => parser.setPostfix('$');\ncsiParamStateHandler['\"'] = (parser) => parser.setPostfix('\"');\ncsiParamStateHandler[' '] = (parser) => parser.setPostfix(' ');\ncsiParamStateHandler['\\''] = (parser) => parser.setPostfix('\\'');\ncsiParamStateHandler[';'] = (parser) => parser.finalizeParam();\ncsiParamStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);\n\nconst csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string, postfix: string, parser: Parser) => void} = {};\ncsiStateHandler['@'] = (handler, params, prefix) => handler.insertChars(params);\ncsiStateHandler['A'] = (handler, params, prefix) => handler.cursorUp(params);\ncsiStateHandler['B'] = (handler, params, prefix) => handler.cursorDown(params);\ncsiStateHandler['C'] = (handler, params, prefix) => handler.cursorForward(params);\ncsiStateHandler['D'] = (handler, params, prefix) => handler.cursorBackward(params);\ncsiStateHandler['E'] = (handler, params, prefix) => handler.cursorNextLine(params);\ncsiStateHandler['F'] = (handler, params, prefix) => handler.cursorPrecedingLine(params);\ncsiStateHandler['G'] = (handler, params, prefix) => handler.cursorCharAbsolute(params);\ncsiStateHandler['H'] = (handler, params, prefix) => handler.cursorPosition(params);\ncsiStateHandler['I'] = (handler, params, prefix) => handler.cursorForwardTab(params);\ncsiStateHandler['J'] = (handler, params, prefix) => handler.eraseInDisplay(params);\ncsiStateHandler['K'] = (handler, params, prefix) => handler.eraseInLine(params);\ncsiStateHandler['L'] = (handler, params, prefix) => handler.insertLines(params);\ncsiStateHandler['M'] = (handler, params, prefix) => handler.deleteLines(params);\ncsiStateHandler['P'] = (handler, params, prefix) => handler.deleteChars(params);\ncsiStateHandler['S'] = (handler, params, prefix) => handler.scrollUp(params);\ncsiStateHandler['T'] = (handler, params, prefix) => {\n if (params.length < 2 && !prefix) {\n handler.scrollDown(params);\n }\n};\ncsiStateHandler['X'] = (handler, params, prefix) => handler.eraseChars(params);\ncsiStateHandler['Z'] = (handler, params, prefix) => handler.cursorBackwardTab(params);\ncsiStateHandler['`'] = (handler, params, prefix) => handler.charPosAbsolute(params);\ncsiStateHandler['a'] = (handler, params, prefix) => handler.HPositionRelative(params);\ncsiStateHandler['b'] = (handler, params, prefix) => handler.repeatPrecedingCharacter(params);\ncsiStateHandler['c'] = (handler, params, prefix) => handler.sendDeviceAttributes(params);\ncsiStateHandler['d'] = (handler, params, prefix) => handler.linePosAbsolute(params);\ncsiStateHandler['e'] = (handler, params, prefix) => handler.VPositionRelative(params);\ncsiStateHandler['f'] = (handler, params, prefix) => handler.HVPosition(params);\ncsiStateHandler['g'] = (handler, params, prefix) => handler.tabClear(params);\ncsiStateHandler['h'] = (handler, params, prefix) => handler.setMode(params);\ncsiStateHandler['l'] = (handler, params, prefix) => handler.resetMode(params);\ncsiStateHandler['m'] = (handler, params, prefix) => handler.charAttributes(params);\ncsiStateHandler['n'] = (handler, params, prefix) => handler.deviceStatus(params);\ncsiStateHandler['p'] = (handler, params, prefix) => {\n switch (prefix) {\n case '!': handler.softReset(params); break;\n }\n};\ncsiStateHandler['q'] = (handler, params, prefix, postfix) => {\n if (postfix === ' ') {\n handler.setCursorStyle(params);\n }\n};\ncsiStateHandler['r'] = (handler, params) => handler.setScrollRegion(params);\ncsiStateHandler['s'] = (handler, params) => handler.saveCursor(params);\ncsiStateHandler['u'] = (handler, params) => handler.restoreCursor(params);\ncsiStateHandler[C0.CAN] = (handler, params, prefix, postfix, parser) => parser.setState(ParserState.NORMAL);\n\nexport enum ParserState {\n NORMAL = 0,\n ESCAPED = 1,\n CSI_PARAM = 2,\n CSI = 3,\n OSC = 4,\n CHARSET = 5,\n DCS = 6,\n IGNORE = 7\n}\n\n/**\n * The terminal's parser, all input into the terminal goes through the parser\n * which parses and defers the actual input handling the the IInputHandler\n * specified in the constructor.\n */\nexport class Parser {\n private _state: ParserState;\n private _position: number;\n\n // TODO: Remove terminal when handler can do everything\n constructor(\n private _inputHandler: IInputHandler,\n private _terminal: any\n ) {\n this._state = ParserState.NORMAL;\n }\n\n /**\n * Parse and handle data.\n *\n * @param data The data to parse.\n */\n public parse(data: string): ParserState {\n const l = data.length;\n let j;\n let cs;\n let ch;\n let code;\n let low;\n\n const cursorStartX = this._terminal.buffer.x;\n const cursorStartY = this._terminal.buffer.y;\n\n if (this._terminal.debug) {\n this._terminal.log('data: ' + data);\n }\n\n this._position = 0;\n // apply leftover surrogate high from last write\n if (this._terminal.surrogate_high) {\n data = this._terminal.surrogate_high + data;\n this._terminal.surrogate_high = '';\n }\n\n for (; this._position < l; this._position++) {\n ch = data[this._position];\n\n // FIXME: higher chars than 0xa0 are not allowed in escape sequences\n // --> maybe move to default\n code = data.charCodeAt(this._position);\n if (0xD800 <= code && code <= 0xDBFF) {\n // we got a surrogate high\n // get surrogate low (next 2 bytes)\n low = data.charCodeAt(this._position + 1);\n if (isNaN(low)) {\n // end of data stream, save surrogate high\n this._terminal.surrogate_high = ch;\n continue;\n }\n code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;\n ch += data.charAt(this._position + 1);\n }\n // surrogate low - already handled above\n if (0xDC00 <= code && code <= 0xDFFF)\n continue;\n\n switch (this._state) {\n case ParserState.NORMAL:\n if (ch in normalStateHandler) {\n normalStateHandler[ch](this, this._inputHandler);\n } else {\n this._inputHandler.addChar(ch, code);\n }\n break;\n case ParserState.ESCAPED:\n if (ch in escapedStateHandler) {\n escapedStateHandler[ch](this, this._terminal);\n // Skip switch as it was just handled\n break;\n }\n switch (ch) {\n\n // ESC (,),*,+,-,. Designate G0-G2 Character Set.\n case '(': // <-- this seems to get all the attention\n case ')':\n case '*':\n case '+':\n case '-':\n case '.':\n switch (ch) {\n case '(':\n this._terminal.gcharset = 0;\n break;\n case ')':\n this._terminal.gcharset = 1;\n break;\n case '*':\n this._terminal.gcharset = 2;\n break;\n case '+':\n this._terminal.gcharset = 3;\n break;\n case '-':\n this._terminal.gcharset = 1;\n break;\n case '.':\n this._terminal.gcharset = 2;\n break;\n }\n this._state = ParserState.CHARSET;\n break;\n\n // Designate G3 Character Set (VT300).\n // A = ISO Latin-1 Supplemental.\n // Not implemented.\n case '/':\n this._terminal.gcharset = 3;\n this._state = ParserState.CHARSET;\n this._position--;\n break;\n\n // ESC N\n // Single Shift Select of G2 Character Set\n // ( SS2 is 0x8e). This affects next character only.\n case 'N':\n break;\n // ESC O\n // Single Shift Select of G3 Character Set\n // ( SS3 is 0x8f). This affects next character only.\n case 'O':\n break;\n // ESC n\n // Invoke the G2 Character Set as GL (LS2).\n case 'n':\n this._terminal.setgLevel(2);\n break;\n // ESC o\n // Invoke the G3 Character Set as GL (LS3).\n case 'o':\n this._terminal.setgLevel(3);\n break;\n // ESC |\n // Invoke the G3 Character Set as GR (LS3R).\n case '|':\n this._terminal.setgLevel(3);\n break;\n // ESC }\n // Invoke the G2 Character Set as GR (LS2R).\n case '}':\n this._terminal.setgLevel(2);\n break;\n // ESC ~\n // Invoke the G1 Character Set as GR (LS1R).\n case '~':\n this._terminal.setgLevel(1);\n break;\n\n // ESC 7 Save Cursor (DECSC).\n case '7':\n this._inputHandler.saveCursor();\n this._state = ParserState.NORMAL;\n break;\n\n // ESC 8 Restore Cursor (DECRC).\n case '8':\n this._inputHandler.restoreCursor();\n this._state = ParserState.NORMAL;\n break;\n\n // ESC # 3 DEC line height/width\n case '#':\n this._state = ParserState.NORMAL;\n this._position++;\n break;\n\n // ESC H Tab Set (HTS is 0x88).\n case 'H':\n this._terminal.tabSet();\n this._state = ParserState.NORMAL;\n break;\n\n // ESC = Application Keypad (DECKPAM).\n case '=':\n this._terminal.log('Serial port requested application keypad.');\n this._terminal.applicationKeypad = true;\n if (this._terminal.viewport) {\n this._terminal.viewport.syncScrollArea();\n }\n this._state = ParserState.NORMAL;\n break;\n\n // ESC > Normal Keypad (DECKPNM).\n case '>':\n this._terminal.log('Switching back to normal keypad.');\n this._terminal.applicationKeypad = false;\n if (this._terminal.viewport) {\n this._terminal.viewport.syncScrollArea();\n }\n this._state = ParserState.NORMAL;\n break;\n\n default:\n this._state = ParserState.NORMAL;\n this._terminal.error('Unknown ESC control: %s.', ch);\n break;\n }\n break;\n\n case ParserState.CHARSET:\n if (ch in CHARSETS) {\n cs = CHARSETS[ch];\n if (ch === '/') { // ISOLatin is actually /A\n this.skipNextChar();\n }\n } else {\n cs = DEFAULT_CHARSET;\n }\n this._terminal.setgCharset(this._terminal.gcharset, cs);\n this._terminal.gcharset = null;\n this._state = ParserState.NORMAL;\n break;\n\n case ParserState.OSC:\n // OSC Ps ; Pt ST\n // OSC Ps ; Pt BEL\n // Set Text Parameters.\n if (ch === C0.ESC || ch === C0.BEL) {\n if (ch === C0.ESC) this._position++;\n\n this._terminal.params.push(this._terminal.currentParam);\n\n switch (this._terminal.params[0]) {\n case 0:\n case 1:\n case 2:\n if (this._terminal.params[1]) {\n this._terminal.title = this._terminal.params[1];\n this._terminal.handleTitle(this._terminal.title);\n }\n break;\n case 3:\n // set X property\n break;\n case 4:\n case 5:\n // change dynamic colors\n break;\n case 10:\n case 11:\n case 12:\n case 13:\n case 14:\n case 15:\n case 16:\n case 17:\n case 18:\n case 19:\n // change dynamic ui colors\n break;\n case 46:\n // change log file\n break;\n case 50:\n // dynamic font\n break;\n case 51:\n // emacs shell\n break;\n case 52:\n // manipulate selection data\n break;\n case 104:\n case 105:\n case 110:\n case 111:\n case 112:\n case 113:\n case 114:\n case 115:\n case 116:\n case 117:\n case 118:\n // reset colors\n break;\n }\n\n this._terminal.params = [];\n this._terminal.currentParam = 0;\n this._state = ParserState.NORMAL;\n } else {\n if (!this._terminal.params.length) {\n if (ch >= '0' && ch <= '9') {\n this._terminal.currentParam =\n this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48;\n } else if (ch === ';') {\n this._terminal.params.push(this._terminal.currentParam);\n this._terminal.currentParam = '';\n }\n } else {\n this._terminal.currentParam += ch;\n }\n }\n break;\n\n case ParserState.CSI_PARAM:\n if (ch in csiParamStateHandler) {\n csiParamStateHandler[ch](this);\n break;\n }\n this.finalizeParam();\n // Fall through the CSI as this character should be the CSI code.\n this._state = ParserState.CSI;\n\n case ParserState.CSI:\n if (ch in csiStateHandler) {\n if (this._terminal.debug) {\n this._terminal.log(`CSI ${this._terminal.prefix ? this._terminal.prefix : ''} ${this._terminal.params ? this._terminal.params.join(';') : ''} ${this._terminal.postfix ? this._terminal.postfix : ''} ${ch}`);\n }\n csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this);\n } else {\n this._terminal.error('Unknown CSI code: %s.', ch);\n }\n\n this._state = ParserState.NORMAL;\n this._terminal.prefix = '';\n this._terminal.postfix = '';\n break;\n\n case ParserState.DCS:\n if (ch === C0.ESC || ch === C0.BEL) {\n if (ch === C0.ESC) this._position++;\n let pt;\n let valid: boolean;\n\n switch (this._terminal.prefix) {\n // User-Defined Keys (DECUDK).\n case '':\n break;\n\n // Request Status String (DECRQSS).\n // test: echo -e '\\eP$q\"p\\e\\\\'\n case '$q':\n pt = this._terminal.currentParam;\n valid = false;\n\n switch (pt) {\n // DECSCA\n case '\"q':\n pt = '0\"q';\n break;\n\n // DECSCL\n case '\"p':\n pt = '61\"p';\n break;\n\n // DECSTBM\n case 'r':\n pt = ''\n + (this._terminal.buffer.scrollTop + 1)\n + ';'\n + (this._terminal.buffer.scrollBottom + 1)\n + 'r';\n break;\n\n // SGR\n case 'm':\n pt = '0m';\n break;\n\n default:\n this._terminal.error('Unknown DCS Pt: %s.', pt);\n pt = '';\n break;\n }\n\n this._terminal.send(C0.ESC + 'P' + +valid + '$r' + pt + C0.ESC + '\\\\');\n break;\n\n // Set Termcap/Terminfo Data (xterm, experimental).\n case '+p':\n break;\n\n // Request Termcap/Terminfo String (xterm, experimental)\n // Regular xterm does not even respond to this sequence.\n // This can cause a small glitch in vim.\n // test: echo -ne '\\eP+q6b64\\e\\\\'\n case '+q':\n pt = this._terminal.currentParam;\n valid = false;\n\n this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\\\');\n break;\n\n default:\n this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix);\n break;\n }\n\n this._terminal.currentParam = 0;\n this._terminal.prefix = '';\n this._state = ParserState.NORMAL;\n } else if (!this._terminal.currentParam) {\n if (!this._terminal.prefix && ch !== '$' && ch !== '+') {\n this._terminal.currentParam = ch;\n } else if (this._terminal.prefix.length === 2) {\n this._terminal.currentParam = ch;\n } else {\n this._terminal.prefix += ch;\n }\n } else {\n this._terminal.currentParam += ch;\n }\n break;\n\n case ParserState.IGNORE:\n // For PM and APC.\n if (ch === C0.ESC || ch === C0.BEL) {\n if (ch === C0.ESC) this._position++;\n this._state = ParserState.NORMAL;\n }\n break;\n }\n }\n\n // Fire the cursormove event if it's moved. This is done inside the parser\n // as a render cannot happen in the middle of a parsing round.\n if (this._terminal.buffer.x !== cursorStartX || this._terminal.buffer.y !== cursorStartY) {\n this._terminal.emit('cursormove');\n }\n\n return this._state;\n }\n\n /**\n * Set the parser's current parsing state.\n *\n * @param state The new state.\n */\n public setState(state: ParserState): void {\n this._state = state;\n }\n\n /**\n * Sets the parsier's current prefix. CSI codes can have prefixes of '?', '>'\n * or '!'.\n *\n * @param prefix The prefix.\n */\n public setPrefix(prefix: string): void {\n this._terminal.prefix = prefix;\n }\n\n /**\n * Sets the parsier's current prefix. CSI codes can have postfixes of '$',\n * '\"', ' ', '\\''.\n *\n * @param postfix The postfix.\n */\n public setPostfix(postfix: string): void {\n this._terminal.postfix = postfix;\n }\n\n /**\n * Sets the parser's current parameter.\n *\n * @param param the parameter.\n */\n public setParam(param: number): void {\n this._terminal.currentParam = param;\n }\n\n /**\n * Gets the parser's current parameter.\n */\n public getParam(): number {\n return this._terminal.currentParam;\n }\n\n /**\n * Finalizes the parser's current parameter, adding it to the list of\n * parameters and setting the new current parameter to 0.\n */\n public finalizeParam(): void {\n this._terminal.params.push(this._terminal.currentParam);\n this._terminal.currentParam = 0;\n }\n\n /**\n * Tell the parser to skip the next character.\n */\n public skipNextChar(): void {\n this._position++;\n }\n\n /**\n * Tell the parser to repeat parsing the current character (for example if it\n * needs parsing using a different state.\n */\n // public repeatChar(): void {\n // this._position--;\n // }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ILinkMatcherOptions, ITerminal, IBufferAccessor, ILinkifier, IElementAccessor } from './Interfaces';\nimport { LinkMatcher, LinkMatcherHandler, LinkMatcherValidationCallback, LineData, LinkHoverEvent, LinkHoverEventTypes } from './Types';\nimport { IMouseZoneManager } from './input/Interfaces';\nimport { MouseZone } from './input/MouseZoneManager';\nimport { EventEmitter } from './EventEmitter';\n\nconst protocolClause = '(https?:\\\\/\\\\/)';\nconst domainCharacterSet = '[\\\\da-z\\\\.-]+';\nconst negatedDomainCharacterSet = '[^\\\\da-z\\\\.-]+';\nconst domainBodyClause = '(' + domainCharacterSet + ')';\nconst tldClause = '([a-z\\\\.]{2,6})';\nconst ipClause = '((\\\\d{1,3}\\\\.){3}\\\\d{1,3})';\nconst localHostClause = '(localhost)';\nconst portClause = '(:\\\\d{1,5})';\nconst hostClause = '((' + domainBodyClause + '\\\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';\nconst pathClause = '(\\\\/[\\\\/\\\\w\\\\.\\\\-%~]*)*';\nconst queryStringHashFragmentCharacterSet = '[0-9\\\\w\\\\[\\\\]\\\\(\\\\)\\\\/\\\\?\\\\!#@$%&\\'*+,:;~\\\\=\\\\.\\\\-]*';\nconst queryStringClause = '(\\\\?' + queryStringHashFragmentCharacterSet + ')?';\nconst hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';\nconst negatedPathCharacterSet = '[^\\\\/\\\\w\\\\.\\\\-%]+';\nconst bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;\nconst start = '(?:^|' + negatedDomainCharacterSet + ')(';\nconst end = ')($|' + negatedPathCharacterSet + ')';\nconst strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);\n\n/**\n * The ID of the built in http(s) link matcher.\n */\nconst HYPERTEXT_LINK_MATCHER_ID = 0;\n\n/**\n * The Linkifier applies links to rows shortly after they have been refreshed.\n */\nexport class Linkifier extends EventEmitter implements ILinkifier {\n /**\n * The time to wait after a row is changed before it is linkified. This prevents\n * the costly operation of searching every row multiple times, potentially a\n * huge amount of times.\n */\n protected static TIME_BEFORE_LINKIFY = 200;\n\n protected _linkMatchers: LinkMatcher[] = [];\n\n private _mouseZoneManager: IMouseZoneManager;\n private _rowsTimeoutId: number;\n private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;\n private _rowsToLinkify: {start: number, end: number};\n\n constructor(\n protected _terminal: IBufferAccessor & IElementAccessor\n ) {\n super();\n this._rowsToLinkify = {\n start: null,\n end: null\n };\n this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 });\n }\n\n /**\n * Attaches the linkifier to the DOM, enabling linkification.\n * @param mouseZoneManager The mouse zone manager to register link zones with.\n */\n public attachToDom(mouseZoneManager: IMouseZoneManager): void {\n this._mouseZoneManager = mouseZoneManager;\n }\n\n /**\n * Queue linkification on a set of rows.\n * @param start The row to linkify from (inclusive).\n * @param end The row to linkify to (inclusive).\n */\n public linkifyRows(start: number, end: number): void {\n // Don't attempt linkify if not yet attached to DOM\n if (!this._mouseZoneManager) {\n return;\n }\n\n // Increase range to linkify\n if (!this._rowsToLinkify.start) {\n this._rowsToLinkify.start = start;\n this._rowsToLinkify.end = end;\n } else {\n this._rowsToLinkify.start = this._rowsToLinkify.start < start ? this._rowsToLinkify.start : start;\n this._rowsToLinkify.end = this._rowsToLinkify.end > end ? this._rowsToLinkify.end : end;\n }\n\n // Clear out any existing links on this row range\n this._mouseZoneManager.clearAll(start, end);\n\n // Restart timer\n if (this._rowsTimeoutId) {\n clearTimeout(this._rowsTimeoutId);\n }\n this._rowsTimeoutId = setTimeout(() => this._linkifyRows(), Linkifier.TIME_BEFORE_LINKIFY);\n }\n\n /**\n * Linkifies the rows requested.\n */\n private _linkifyRows(): void {\n this._rowsTimeoutId = null;\n for (let i = this._rowsToLinkify.start; i <= this._rowsToLinkify.end; i++) {\n this._linkifyRow(i);\n }\n this._rowsToLinkify.start = null;\n this._rowsToLinkify.end = null;\n }\n\n /**\n * Attaches a handler for hypertext links, overriding default behavior for\n * tandard http(s) links.\n * @param handler The handler to use, this can be cleared with null.\n */\n public setHypertextLinkHandler(handler: LinkMatcherHandler): void {\n this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler;\n }\n\n /**\n * Attaches a validation callback for hypertext links.\n * @param callback The callback to use, this can be cleared with null.\n */\n public setHypertextValidationCallback(callback: LinkMatcherValidationCallback): void {\n this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].validationCallback = callback;\n }\n\n /**\n * Registers a link matcher, allowing custom link patterns to be matched and\n * handled.\n * @param regex The regular expression to search for. Specifically, this\n * searches the textContent of the rows. You will want to use \\s to match a\n * space ' ' character for example.\n * @param handler The callback when the link is called.\n * @param options Options for the link matcher.\n * @return The ID of the new matcher, this can be used to deregister.\n */\n public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: ILinkMatcherOptions = {}): number {\n if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) {\n throw new Error('handler must be defined');\n }\n const matcher: LinkMatcher = {\n id: this._nextLinkMatcherId++,\n regex,\n handler,\n matchIndex: options.matchIndex,\n validationCallback: options.validationCallback,\n hoverTooltipCallback: options.tooltipCallback,\n hoverLeaveCallback: options.leaveCallback,\n priority: options.priority || 0\n };\n this._addLinkMatcherToList(matcher);\n return matcher.id;\n }\n\n /**\n * Inserts a link matcher to the list in the correct position based on the\n * priority of each link matcher. New link matchers of equal priority are\n * considered after older link matchers.\n * @param matcher The link matcher to be added.\n */\n private _addLinkMatcherToList(matcher: LinkMatcher): void {\n if (this._linkMatchers.length === 0) {\n this._linkMatchers.push(matcher);\n return;\n }\n\n for (let i = this._linkMatchers.length - 1; i >= 0; i--) {\n if (matcher.priority <= this._linkMatchers[i].priority) {\n this._linkMatchers.splice(i + 1, 0, matcher);\n return;\n }\n }\n\n this._linkMatchers.splice(0, 0, matcher);\n }\n\n /**\n * Deregisters a link matcher if it has been registered.\n * @param matcherId The link matcher's ID (returned after register)\n * @return Whether a link matcher was found and deregistered.\n */\n public deregisterLinkMatcher(matcherId: number): boolean {\n // ID 0 is the hypertext link matcher which cannot be deregistered\n for (let i = 1; i < this._linkMatchers.length; i++) {\n if (this._linkMatchers[i].id === matcherId) {\n this._linkMatchers.splice(i, 1);\n return true;\n }\n }\n return false;\n }\n\n /**\n * Linkifies a row.\n * @param rowIndex The index of the row to linkify.\n */\n private _linkifyRow(rowIndex: number): void {\n const absoluteRowIndex = this._terminal.buffer.ydisp + rowIndex;\n if (absoluteRowIndex >= this._terminal.buffer.lines.length) {\n return;\n }\n const text = this._terminal.buffer.translateBufferLineToString(absoluteRowIndex, false);\n for (let i = 0; i < this._linkMatchers.length; i++) {\n this._doLinkifyRow(rowIndex, text, this._linkMatchers[i]);\n }\n }\n\n /**\n * Linkifies a row given a specific handler.\n * @param rowIndex The row index to linkify.\n * @param text The text of the row (excludes text in the row that's already\n * linkified).\n * @param matcher The link matcher for this line.\n * @param offset The how much of the row has already been linkified.\n * @return The link element(s) that were added.\n */\n private _doLinkifyRow(rowIndex: number, text: string, matcher: LinkMatcher, offset: number = 0): void {\n // Iterate over nodes as we want to consider text nodes\n let result = [];\n const isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID;\n\n // Find the first match\n let match = text.match(matcher.regex);\n if (!match || match.length === 0) {\n return;\n }\n let uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];\n\n // Get index, match.index is for the outer match which includes negated chars\n const index = text.indexOf(uri);\n\n // Ensure the link is valid before registering\n if (matcher.validationCallback) {\n matcher.validationCallback(uri, isValid => {\n // Discard link if the line has already changed\n if (this._rowsTimeoutId) {\n return;\n }\n if (isValid) {\n this._addLink(offset + index, rowIndex, uri, matcher);\n }\n });\n } else {\n this._addLink(offset + index, rowIndex, uri, matcher);\n }\n\n // Recursively check for links in the rest of the text\n const remainingStartIndex = index + uri.length;\n const remainingText = text.substr(remainingStartIndex);\n if (remainingText.length > 0) {\n this._doLinkifyRow(rowIndex, remainingText, matcher, offset + remainingStartIndex);\n }\n }\n\n /**\n * Registers a link to the mouse zone manager.\n * @param x The column the link starts.\n * @param y The row the link is on.\n * @param uri The URI of the link.\n * @param matcher The link matcher for the link.\n */\n private _addLink(x: number, y: number, uri: string, matcher: LinkMatcher): void {\n this._mouseZoneManager.add(new MouseZone(\n x + 1,\n x + 1 + uri.length,\n y + 1,\n e => {\n if (matcher.handler) {\n return matcher.handler(e, uri);\n }\n window.open(uri, '_blank');\n },\n e => {\n this.emit(LinkHoverEventTypes.HOVER, { x, y, length: uri.length});\n this._terminal.element.style.cursor = 'pointer';\n },\n e => {\n this.emit(LinkHoverEventTypes.TOOLTIP, { x, y, length: uri.length});\n if (matcher.hoverTooltipCallback) {\n matcher.hoverTooltipCallback(e, uri);\n }\n },\n () => {\n this.emit(LinkHoverEventTypes.LEAVE, { x, y, length: uri.length});\n this._terminal.element.style.cursor = '';\n if (matcher.hoverLeaveCallback) {\n matcher.hoverLeaveCallback();\n }\n }\n ));\n }\n}\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n */\n\nimport { IInputHandler, ITerminal, IInputHandlingTerminal } from './Interfaces';\nimport { C0 } from './EscapeSequences';\nimport { DEFAULT_CHARSET } from './Charsets';\nimport { CharData } from './Types';\nimport { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer';\nimport { FLAGS } from './renderer/Types';\nimport { wcwidth } from './CharWidth';\n\n/**\n * The terminal's standard implementation of IInputHandler, this handles all\n * input from the Parser.\n *\n * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand\n * each function's header comment.\n */\nexport class InputHandler implements IInputHandler {\n constructor(private _terminal: IInputHandlingTerminal) { }\n\n public addChar(char: string, code: number): void {\n if (char >= ' ') {\n // calculate print space\n // expensive call, therefore we save width in line buffer\n const ch_width = wcwidth(code);\n\n if (this._terminal.charset && this._terminal.charset[char]) {\n char = this._terminal.charset[char];\n }\n\n let row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n // insert combining char in last cell\n // FIXME: needs handling after cursor jumps\n if (!ch_width && this._terminal.buffer.x) {\n // dont overflow left\n if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) {\n if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_WIDTH_INDEX]) {\n // found empty cell after fullwidth, need to go 2 cells back\n if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2]) {\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][CHAR_DATA_CHAR_INDEX] += char;\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][3] = char.charCodeAt(0);\n }\n } else {\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_CHAR_INDEX] += char;\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][3] = char.charCodeAt(0);\n }\n this._terminal.updateRange(this._terminal.buffer.y);\n }\n return;\n }\n\n // goto next line if ch would overflow\n // TODO: needs a global min terminal width of 2\n if (this._terminal.buffer.x + ch_width - 1 >= this._terminal.cols) {\n // autowrap - DECAWM\n if (this._terminal.wraparoundMode) {\n this._terminal.buffer.x = 0;\n this._terminal.buffer.y++;\n if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) {\n this._terminal.buffer.y--;\n this._terminal.scroll(true);\n } else {\n // The line already exists (eg. the initial viewport), mark it as a\n // wrapped line\n (this._terminal.buffer.lines.get(this._terminal.buffer.y)).isWrapped = true;\n }\n } else {\n if (ch_width === 2) // FIXME: check for xterm behavior\n return;\n }\n }\n row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n // insert mode: move characters to right\n if (this._terminal.insertMode) {\n // do this twice for a fullwidth char\n for (let moves = 0; moves < ch_width; ++moves) {\n // remove last cell, if it's width is 0\n // we have to adjust the second last cell as well\n const removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop();\n if (removed[CHAR_DATA_WIDTH_INDEX] === 0\n && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2]\n && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][CHAR_DATA_WIDTH_INDEX] === 2) {\n this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)];\n }\n\n // insert empty cell at cursor\n this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 0, [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)]);\n }\n }\n\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, char, ch_width, char.charCodeAt(0)];\n this._terminal.buffer.x++;\n this._terminal.updateRange(this._terminal.buffer.y);\n\n // fullwidth char - set next cell width to zero and advance cursor\n if (ch_width === 2) {\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, '', 0, undefined];\n this._terminal.buffer.x++;\n }\n }\n }\n\n /**\n * BEL\n * Bell (Ctrl-G).\n */\n public bell(): void {\n this._terminal.bell();\n }\n\n /**\n * LF\n * Line Feed or New Line (NL). (LF is Ctrl-J).\n */\n public lineFeed(): void {\n if (this._terminal.convertEol) {\n this._terminal.buffer.x = 0;\n }\n this._terminal.buffer.y++;\n if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) {\n this._terminal.buffer.y--;\n this._terminal.scroll();\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n /**\n * This event is emitted whenever the terminal outputs a LF or NL.\n *\n * @event lineFeed\n */\n this._terminal.emit('lineFeed');\n }\n\n /**\n * CR\n * Carriage Return (Ctrl-M).\n */\n public carriageReturn(): void {\n this._terminal.buffer.x = 0;\n }\n\n /**\n * BS\n * Backspace (Ctrl-H).\n */\n public backspace(): void {\n if (this._terminal.buffer.x > 0) {\n this._terminal.buffer.x--;\n }\n }\n\n /**\n * TAB\n * Horizontal Tab (HT) (Ctrl-I).\n */\n public tab(): void {\n this._terminal.buffer.x = this._terminal.buffer.nextStop();\n }\n\n /**\n * SO\n * Shift Out (Ctrl-N) -> Switch to Alternate Character Set. This invokes the\n * G1 character set.\n */\n public shiftOut(): void {\n this._terminal.setgLevel(1);\n }\n\n /**\n * SI\n * Shift In (Ctrl-O) -> Switch to Standard Character Set. This invokes the G0\n * character set (the default).\n */\n public shiftIn(): void {\n this._terminal.setgLevel(0);\n }\n\n /**\n * CSI Ps @\n * Insert Ps (Blank) Character(s) (default = 1) (ICH).\n */\n public insertChars(params: number[]): void {\n let param = params[0];\n if (param < 1) param = 1;\n\n const row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n let j = this._terminal.buffer.x;\n const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm\n\n while (param-- && j < this._terminal.cols) {\n this._terminal.buffer.lines.get(row).splice(j++, 0, ch);\n this._terminal.buffer.lines.get(row).pop();\n }\n }\n\n /**\n * CSI Ps A\n * Cursor Up Ps Times (default = 1) (CUU).\n */\n public cursorUp(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y -= param;\n if (this._terminal.buffer.y < 0) {\n this._terminal.buffer.y = 0;\n }\n }\n\n /**\n * CSI Ps B\n * Cursor Down Ps Times (default = 1) (CUD).\n */\n public cursorDown(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y += param;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n }\n\n /**\n * CSI Ps C\n * Cursor Forward Ps Times (default = 1) (CUF).\n */\n public cursorForward(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x += param;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Ps D\n * Cursor Backward Ps Times (default = 1) (CUB).\n */\n public cursorBackward(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n this._terminal.buffer.x -= param;\n if (this._terminal.buffer.x < 0) {\n this._terminal.buffer.x = 0;\n }\n }\n\n /**\n * CSI Ps E\n * Cursor Next Line Ps Times (default = 1) (CNL).\n * same as CSI Ps B ?\n */\n public cursorNextLine(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y += param;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n this._terminal.buffer.x = 0;\n }\n\n\n /**\n * CSI Ps F\n * Cursor Preceding Line Ps Times (default = 1) (CNL).\n * reuse CSI Ps A ?\n */\n public cursorPrecedingLine(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y -= param;\n if (this._terminal.buffer.y < 0) {\n this._terminal.buffer.y = 0;\n }\n this._terminal.buffer.x = 0;\n }\n\n\n /**\n * CSI Ps G\n * Cursor Character Absolute [column] (default = [row,1]) (CHA).\n */\n public cursorCharAbsolute(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x = param - 1;\n }\n\n /**\n * CSI Ps ; Ps H\n * Cursor Position [row;column] (default = [1,1]) (CUP).\n */\n public cursorPosition(params: number[]): void {\n let col: number;\n let row: number = params[0] - 1;\n\n if (params.length >= 2) {\n col = params[1] - 1;\n } else {\n col = 0;\n }\n\n if (row < 0) {\n row = 0;\n } else if (row >= this._terminal.rows) {\n row = this._terminal.rows - 1;\n }\n\n if (col < 0) {\n col = 0;\n } else if (col >= this._terminal.cols) {\n col = this._terminal.cols - 1;\n }\n\n this._terminal.buffer.x = col;\n this._terminal.buffer.y = row;\n }\n\n /**\n * CSI Ps I\n * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).\n */\n public cursorForwardTab(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.x = this._terminal.buffer.nextStop();\n }\n }\n\n /**\n * CSI Ps J Erase in Display (ED).\n * Ps = 0 -> Erase Below (default).\n * Ps = 1 -> Erase Above.\n * Ps = 2 -> Erase All.\n * Ps = 3 -> Erase Saved Lines (xterm).\n * CSI ? Ps J\n * Erase in Display (DECSED).\n * Ps = 0 -> Selective Erase Below (default).\n * Ps = 1 -> Selective Erase Above.\n * Ps = 2 -> Selective Erase All.\n */\n public eraseInDisplay(params: number[]): void {\n let j;\n switch (params[0]) {\n case 0:\n this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y);\n j = this._terminal.buffer.y + 1;\n for (; j < this._terminal.rows; j++) {\n this._terminal.eraseLine(j);\n }\n break;\n case 1:\n this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y);\n j = this._terminal.buffer.y;\n while (j--) {\n this._terminal.eraseLine(j);\n }\n break;\n case 2:\n j = this._terminal.rows;\n while (j--) this._terminal.eraseLine(j);\n break;\n case 3:\n // Clear scrollback (everything not in viewport)\n const scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows;\n if (scrollBackSize > 0) {\n this._terminal.buffer.lines.trimStart(scrollBackSize);\n this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0);\n this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0);\n // Force a scroll event to refresh viewport\n this._terminal.emit('scroll', 0);\n }\n break;\n }\n }\n\n /**\n * CSI Ps K Erase in Line (EL).\n * Ps = 0 -> Erase to Right (default).\n * Ps = 1 -> Erase to Left.\n * Ps = 2 -> Erase All.\n * CSI ? Ps K\n * Erase in Line (DECSEL).\n * Ps = 0 -> Selective Erase to Right (default).\n * Ps = 1 -> Selective Erase to Left.\n * Ps = 2 -> Selective Erase All.\n */\n public eraseInLine(params: number[]): void {\n switch (params[0]) {\n case 0:\n this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y);\n break;\n case 1:\n this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y);\n break;\n case 2:\n this._terminal.eraseLine(this._terminal.buffer.y);\n break;\n }\n }\n\n /**\n * CSI Ps L\n * Insert Ps Line(s) (default = 1) (IL).\n */\n public insertLines(params: number[]): void {\n let param: number = params[0];\n if (param < 1) {\n param = 1;\n }\n let row: number = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n let scrollBottomRowsOffset = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom;\n let scrollBottomAbsolute = this._terminal.rows - 1 + this._terminal.buffer.ybase - scrollBottomRowsOffset + 1;\n while (param--) {\n // test: echo -e '\\e[44m\\e[1L\\e[0m'\n // blankLine(true) - xterm/linux behavior\n this._terminal.buffer.lines.splice(scrollBottomAbsolute - 1, 1);\n this._terminal.buffer.lines.splice(row, 0, this._terminal.blankLine(true));\n }\n\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.y);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps M\n * Delete Ps Line(s) (default = 1) (DL).\n */\n public deleteLines(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n const row: number = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n let j: number;\n j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom;\n j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j;\n while (param--) {\n // test: echo -e '\\e[44m\\e[1M\\e[0m'\n // blankLine(true) - xterm/linux behavior\n this._terminal.buffer.lines.splice(row, 1);\n this._terminal.buffer.lines.splice(j, 0, this._terminal.blankLine(true));\n }\n\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.y);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps P\n * Delete Ps Character(s) (default = 1) (DCH).\n */\n public deleteChars(params: number[]): void {\n let param: number = params[0];\n if (param < 1) {\n param = 1;\n }\n\n const row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm\n\n while (param--) {\n this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 1);\n this._terminal.buffer.lines.get(row).push(ch);\n }\n this._terminal.updateRange(this._terminal.buffer.y);\n }\n\n /**\n * CSI Ps S Scroll up Ps lines (default = 1) (SU).\n */\n public scrollUp(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 1);\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 0, this._terminal.blankLine());\n }\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.scrollTop);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps T Scroll down Ps lines (default = 1) (SD).\n */\n public scrollDown(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 1);\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 0, this._terminal.blankLine());\n }\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.scrollTop);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps X\n * Erase Ps Character(s) (default = 1) (ECH).\n */\n public eraseChars(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n\n const row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n let j = this._terminal.buffer.x;\n const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm\n\n while (param-- && j < this._terminal.cols) {\n this._terminal.buffer.lines.get(row)[j++] = ch;\n }\n }\n\n /**\n * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).\n */\n public cursorBackwardTab(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.x = this._terminal.buffer.prevStop();\n }\n }\n\n /**\n * CSI Pm ` Character Position Absolute\n * [column] (default = [row,1]) (HPA).\n */\n public charPosAbsolute(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x = param - 1;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Pm a Character Position Relative\n * [columns] (default = [row,col+1]) (HPR)\n * reuse CSI Ps C ?\n */\n public HPositionRelative(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x += param;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Ps b Repeat the preceding graphic character Ps times (REP).\n */\n public repeatPrecedingCharacter(params: number[]): void {\n let param = params[0] || 1;\n const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y);\n const ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1, 32];\n\n while (param--) {\n line[this._terminal.buffer.x++] = ch;\n }\n }\n\n /**\n * CSI Ps c Send Device Attributes (Primary DA).\n * Ps = 0 or omitted -> request attributes from terminal. The\n * response depends on the decTerminalID resource setting.\n * -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')\n * -> CSI ? 1 ; 0 c (``VT101 with No Options'')\n * -> CSI ? 6 c (``VT102'')\n * -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')\n * The VT100-style response parameters do not mean anything by\n * themselves. VT220 parameters do, telling the host what fea-\n * tures the terminal supports:\n * Ps = 1 -> 132-columns.\n * Ps = 2 -> Printer.\n * Ps = 6 -> Selective erase.\n * Ps = 8 -> User-defined keys.\n * Ps = 9 -> National replacement character sets.\n * Ps = 1 5 -> Technical characters.\n * Ps = 2 2 -> ANSI color, e.g., VT525.\n * Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).\n * CSI > Ps c\n * Send Device Attributes (Secondary DA).\n * Ps = 0 or omitted -> request the terminal's identification\n * code. The response depends on the decTerminalID resource set-\n * ting. It should apply only to VT220 and up, but xterm extends\n * this to VT100.\n * -> CSI > Pp ; Pv ; Pc c\n * where Pp denotes the terminal type\n * Pp = 0 -> ``VT100''.\n * Pp = 1 -> ``VT220''.\n * and Pv is the firmware version (for xterm, this was originally\n * the XFree86 patch number, starting with 95). In a DEC termi-\n * nal, Pc indicates the ROM cartridge registration number and is\n * always zero.\n * More information:\n * xterm/charproc.c - line 2012, for more information.\n * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)\n */\n public sendDeviceAttributes(params: number[]): void {\n if (params[0] > 0) {\n return;\n }\n\n if (!this._terminal.prefix) {\n if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) {\n this._terminal.send(C0.ESC + '[?1;2c');\n } else if (this._terminal.is('linux')) {\n this._terminal.send(C0.ESC + '[?6c');\n }\n } else if (this._terminal.prefix === '>') {\n // xterm and urxvt\n // seem to spit this\n // out around ~370 times (?).\n if (this._terminal.is('xterm')) {\n this._terminal.send(C0.ESC + '[>0;276;0c');\n } else if (this._terminal.is('rxvt-unicode')) {\n this._terminal.send(C0.ESC + '[>85;95;0c');\n } else if (this._terminal.is('linux')) {\n // not supported by linux console.\n // linux console echoes parameters.\n this._terminal.send(params[0] + 'c');\n } else if (this._terminal.is('screen')) {\n this._terminal.send(C0.ESC + '[>83;40003;0c');\n }\n }\n }\n\n /**\n * CSI Pm d Vertical Position Absolute (VPA)\n * [row] (default = [1,column])\n */\n public linePosAbsolute(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y = param - 1;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n }\n\n /**\n * CSI Pm e Vertical Position Relative (VPR)\n * [rows] (default = [row+1,column])\n * reuse CSI Ps B ?\n */\n public VPositionRelative(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y += param;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n }\n\n /**\n * CSI Ps ; Ps f\n * Horizontal and Vertical Position [row;column] (default =\n * [1,1]) (HVP).\n */\n public HVPosition(params: number[]): void {\n if (params[0] < 1) params[0] = 1;\n if (params[1] < 1) params[1] = 1;\n\n this._terminal.buffer.y = params[0] - 1;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n\n this._terminal.buffer.x = params[1] - 1;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Ps g Tab Clear (TBC).\n * Ps = 0 -> Clear Current Column (default).\n * Ps = 3 -> Clear All.\n * Potentially:\n * Ps = 2 -> Clear Stops on Line.\n * http://vt100.net/annarbor/aaa-ug/section6.html\n */\n public tabClear(params: number[]): void {\n let param = params[0];\n if (param <= 0) {\n delete this._terminal.buffer.tabs[this._terminal.buffer.x];\n } else if (param === 3) {\n this._terminal.buffer.tabs = {};\n }\n }\n\n /**\n * CSI Pm h Set Mode (SM).\n * Ps = 2 -> Keyboard Action Mode (AM).\n * Ps = 4 -> Insert Mode (IRM).\n * Ps = 1 2 -> Send/receive (SRM).\n * Ps = 2 0 -> Automatic Newline (LNM).\n * CSI ? Pm h\n * DEC Private Mode Set (DECSET).\n * Ps = 1 -> Application Cursor Keys (DECCKM).\n * Ps = 2 -> Designate USASCII for character sets G0-G3\n * (DECANM), and set VT100 mode.\n * Ps = 3 -> 132 Column Mode (DECCOLM).\n * Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).\n * Ps = 5 -> Reverse Video (DECSCNM).\n * Ps = 6 -> Origin Mode (DECOM).\n * Ps = 7 -> Wraparound Mode (DECAWM).\n * Ps = 8 -> Auto-repeat Keys (DECARM).\n * Ps = 9 -> Send Mouse X & Y on button press. See the sec-\n * tion Mouse Tracking.\n * Ps = 1 0 -> Show toolbar (rxvt).\n * Ps = 1 2 -> Start Blinking Cursor (att610).\n * Ps = 1 8 -> Print form feed (DECPFF).\n * Ps = 1 9 -> Set print extent to full screen (DECPEX).\n * Ps = 2 5 -> Show Cursor (DECTCEM).\n * Ps = 3 0 -> Show scrollbar (rxvt).\n * Ps = 3 5 -> Enable font-shifting functions (rxvt).\n * Ps = 3 8 -> Enter Tektronix Mode (DECTEK).\n * Ps = 4 0 -> Allow 80 -> 132 Mode.\n * Ps = 4 1 -> more(1) fix (see curses resource).\n * Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-\n * RCM).\n * Ps = 4 4 -> Turn On Margin Bell.\n * Ps = 4 5 -> Reverse-wraparound Mode.\n * Ps = 4 6 -> Start Logging. This is normally disabled by a\n * compile-time option.\n * Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-\n * abled by the titeInhibit resource).\n * Ps = 6 6 -> Application keypad (DECNKM).\n * Ps = 6 7 -> Backarrow key sends backspace (DECBKM).\n * Ps = 1 0 0 0 -> Send Mouse X & Y on button press and\n * release. See the section Mouse Tracking.\n * Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.\n * Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.\n * Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.\n * Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.\n * Ps = 1 0 0 5 -> Enable Extended Mouse Mode.\n * Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).\n * Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).\n * Ps = 1 0 3 4 -> Interpret \"meta\" key, sets eighth bit.\n * (enables the eightBitInput resource).\n * Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-\n * Lock keys. (This enables the numLock resource).\n * Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This\n * enables the metaSendsEscape resource).\n * Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete\n * key.\n * Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This\n * enables the altSendsEscape resource).\n * Ps = 1 0 4 0 -> Keep selection even if not highlighted.\n * (This enables the keepSelection resource).\n * Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables\n * the selectToClipboard resource).\n * Ps = 1 0 4 2 -> Enable Urgency window manager hint when\n * Control-G is received. (This enables the bellIsUrgent\n * resource).\n * Ps = 1 0 4 3 -> Enable raising of the window when Control-G\n * is received. (enables the popOnBell resource).\n * Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be\n * disabled by the titeInhibit resource).\n * Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-\n * abled by the titeInhibit resource).\n * Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate\n * Screen Buffer, clearing it first. (This may be disabled by\n * the titeInhibit resource). This combines the effects of the 1\n * 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based\n * applications rather than the 4 7 mode.\n * Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.\n * Ps = 1 0 5 1 -> Set Sun function-key mode.\n * Ps = 1 0 5 2 -> Set HP function-key mode.\n * Ps = 1 0 5 3 -> Set SCO function-key mode.\n * Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).\n * Ps = 1 0 6 1 -> Set VT220 keyboard emulation.\n * Ps = 2 0 0 4 -> Set bracketed paste mode.\n * Modes:\n * http: *vt100.net/docs/vt220-rm/chapter4.html\n */\n public setMode(params: number[]): void {\n if (params.length > 1) {\n for (let i = 0; i < params.length; i++) {\n this.setMode([params[i]]);\n }\n\n return;\n }\n\n if (!this._terminal.prefix) {\n switch (params[0]) {\n case 4:\n this._terminal.insertMode = true;\n break;\n case 20:\n // this._t.convertEol = true;\n break;\n }\n } else if (this._terminal.prefix === '?') {\n switch (params[0]) {\n case 1:\n this._terminal.applicationCursor = true;\n break;\n case 2:\n this._terminal.setgCharset(0, DEFAULT_CHARSET);\n this._terminal.setgCharset(1, DEFAULT_CHARSET);\n this._terminal.setgCharset(2, DEFAULT_CHARSET);\n this._terminal.setgCharset(3, DEFAULT_CHARSET);\n // set VT100 mode here\n break;\n case 3: // 132 col mode\n this._terminal.savedCols = this._terminal.cols;\n this._terminal.resize(132, this._terminal.rows);\n break;\n case 6:\n this._terminal.originMode = true;\n break;\n case 7:\n this._terminal.wraparoundMode = true;\n break;\n case 12:\n // this.cursorBlink = true;\n break;\n case 66:\n this._terminal.log('Serial port requested application keypad.');\n this._terminal.applicationKeypad = true;\n this._terminal.viewport.syncScrollArea();\n break;\n case 9: // X10 Mouse\n // no release, no motion, no wheel, no modifiers.\n case 1000: // vt200 mouse\n // no motion.\n // no modifiers, except control on the wheel.\n case 1002: // button event mouse\n case 1003: // any event mouse\n // any event - sends motion events,\n // even if there is no button held down.\n\n // TODO: Why are params[0] compares nested within a switch for params[0]?\n\n this._terminal.x10Mouse = params[0] === 9;\n this._terminal.vt200Mouse = params[0] === 1000;\n this._terminal.normalMouse = params[0] > 1000;\n this._terminal.mouseEvents = true;\n this._terminal.element.classList.add('enable-mouse-events');\n this._terminal.selectionManager.disable();\n this._terminal.log('Binding to mouse events.');\n break;\n case 1004: // send focusin/focusout events\n // focusin: ^[[I\n // focusout: ^[[O\n this._terminal.sendFocus = true;\n break;\n case 1005: // utf8 ext mode mouse\n this._terminal.utfMouse = true;\n // for wide terminals\n // simply encodes large values as utf8 characters\n break;\n case 1006: // sgr ext mode mouse\n this._terminal.sgrMouse = true;\n // for wide terminals\n // does not add 32 to fields\n // press: ^[[ Keyboard Action Mode (AM).\n * Ps = 4 -> Replace Mode (IRM).\n * Ps = 1 2 -> Send/receive (SRM).\n * Ps = 2 0 -> Normal Linefeed (LNM).\n * CSI ? Pm l\n * DEC Private Mode Reset (DECRST).\n * Ps = 1 -> Normal Cursor Keys (DECCKM).\n * Ps = 2 -> Designate VT52 mode (DECANM).\n * Ps = 3 -> 80 Column Mode (DECCOLM).\n * Ps = 4 -> Jump (Fast) Scroll (DECSCLM).\n * Ps = 5 -> Normal Video (DECSCNM).\n * Ps = 6 -> Normal Cursor Mode (DECOM).\n * Ps = 7 -> No Wraparound Mode (DECAWM).\n * Ps = 8 -> No Auto-repeat Keys (DECARM).\n * Ps = 9 -> Don't send Mouse X & Y on button press.\n * Ps = 1 0 -> Hide toolbar (rxvt).\n * Ps = 1 2 -> Stop Blinking Cursor (att610).\n * Ps = 1 8 -> Don't print form feed (DECPFF).\n * Ps = 1 9 -> Limit print to scrolling region (DECPEX).\n * Ps = 2 5 -> Hide Cursor (DECTCEM).\n * Ps = 3 0 -> Don't show scrollbar (rxvt).\n * Ps = 3 5 -> Disable font-shifting functions (rxvt).\n * Ps = 4 0 -> Disallow 80 -> 132 Mode.\n * Ps = 4 1 -> No more(1) fix (see curses resource).\n * Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-\n * NRCM).\n * Ps = 4 4 -> Turn Off Margin Bell.\n * Ps = 4 5 -> No Reverse-wraparound Mode.\n * Ps = 4 6 -> Stop Logging. (This is normally disabled by a\n * compile-time option).\n * Ps = 4 7 -> Use Normal Screen Buffer.\n * Ps = 6 6 -> Numeric keypad (DECNKM).\n * Ps = 6 7 -> Backarrow key sends delete (DECBKM).\n * Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and\n * release. See the section Mouse Tracking.\n * Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.\n * Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.\n * Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.\n * Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.\n * Ps = 1 0 0 5 -> Disable Extended Mouse Mode.\n * Ps = 1 0 1 0 -> Don't scroll to bottom on tty output\n * (rxvt).\n * Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).\n * Ps = 1 0 3 4 -> Don't interpret \"meta\" key. (This disables\n * the eightBitInput resource).\n * Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-\n * Lock keys. (This disables the numLock resource).\n * Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.\n * (This disables the metaSendsEscape resource).\n * Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad\n * Delete key.\n * Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.\n * (This disables the altSendsEscape resource).\n * Ps = 1 0 4 0 -> Do not keep selection when not highlighted.\n * (This disables the keepSelection resource).\n * Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables\n * the selectToClipboard resource).\n * Ps = 1 0 4 2 -> Disable Urgency window manager hint when\n * Control-G is received. (This disables the bellIsUrgent\n * resource).\n * Ps = 1 0 4 3 -> Disable raising of the window when Control-\n * G is received. (This disables the popOnBell resource).\n * Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen\n * first if in the Alternate Screen. (This may be disabled by\n * the titeInhibit resource).\n * Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be\n * disabled by the titeInhibit resource).\n * Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor\n * as in DECRC. (This may be disabled by the titeInhibit\n * resource). This combines the effects of the 1 0 4 7 and 1 0\n * 4 8 modes. Use this with terminfo-based applications rather\n * than the 4 7 mode.\n * Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.\n * Ps = 1 0 5 1 -> Reset Sun function-key mode.\n * Ps = 1 0 5 2 -> Reset HP function-key mode.\n * Ps = 1 0 5 3 -> Reset SCO function-key mode.\n * Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).\n * Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.\n * Ps = 2 0 0 4 -> Reset bracketed paste mode.\n */\n public resetMode(params: number[]): void {\n if (params.length > 1) {\n for (let i = 0; i < params.length; i++) {\n this.resetMode([params[i]]);\n }\n\n return;\n }\n\n if (!this._terminal.prefix) {\n switch (params[0]) {\n case 4:\n this._terminal.insertMode = false;\n break;\n case 20:\n // this._t.convertEol = false;\n break;\n }\n } else if (this._terminal.prefix === '?') {\n switch (params[0]) {\n case 1:\n this._terminal.applicationCursor = false;\n break;\n case 3:\n if (this._terminal.cols === 132 && this._terminal.savedCols) {\n this._terminal.resize(this._terminal.savedCols, this._terminal.rows);\n }\n delete this._terminal.savedCols;\n break;\n case 6:\n this._terminal.originMode = false;\n break;\n case 7:\n this._terminal.wraparoundMode = false;\n break;\n case 12:\n // this.cursorBlink = false;\n break;\n case 66:\n this._terminal.log('Switching back to normal keypad.');\n this._terminal.applicationKeypad = false;\n this._terminal.viewport.syncScrollArea();\n break;\n case 9: // X10 Mouse\n case 1000: // vt200 mouse\n case 1002: // button event mouse\n case 1003: // any event mouse\n this._terminal.x10Mouse = false;\n this._terminal.vt200Mouse = false;\n this._terminal.normalMouse = false;\n this._terminal.mouseEvents = false;\n this._terminal.element.classList.remove('enable-mouse-events');\n this._terminal.selectionManager.enable();\n break;\n case 1004: // send focusin/focusout events\n this._terminal.sendFocus = false;\n break;\n case 1005: // utf8 ext mode mouse\n this._terminal.utfMouse = false;\n break;\n case 1006: // sgr ext mode mouse\n this._terminal.sgrMouse = false;\n break;\n case 1015: // urxvt ext mode mouse\n this._terminal.urxvtMouse = false;\n break;\n case 25: // hide cursor\n this._terminal.cursorHidden = true;\n break;\n case 1049: // alt screen buffer cursor\n // FALL-THROUGH\n case 47: // normal screen buffer\n case 1047: // normal screen buffer - clearing it first\n // Ensure the selection manager has the correct buffer\n this._terminal.buffers.activateNormalBuffer();\n // TODO: Not sure if we need to save/restore after switching the buffer\n // if (params[0] === 1049) {\n // this.restoreCursor(params);\n // }\n this._terminal.selectionManager.setBuffer(this._terminal.buffer);\n this._terminal.refresh(0, this._terminal.rows - 1);\n this._terminal.viewport.syncScrollArea();\n this._terminal.showCursor();\n break;\n case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)\n this._terminal.bracketedPasteMode = false;\n break;\n }\n }\n }\n\n /**\n * CSI Pm m Character Attributes (SGR).\n * Ps = 0 -> Normal (default).\n * Ps = 1 -> Bold.\n * Ps = 2 -> Faint, decreased intensity (ISO 6429).\n * Ps = 4 -> Underlined.\n * Ps = 5 -> Blink (appears as Bold).\n * Ps = 7 -> Inverse.\n * Ps = 8 -> Invisible, i.e., hidden (VT300).\n * Ps = 2 2 -> Normal (neither bold nor faint).\n * Ps = 2 4 -> Not underlined.\n * Ps = 2 5 -> Steady (not blinking).\n * Ps = 2 7 -> Positive (not inverse).\n * Ps = 2 8 -> Visible, i.e., not hidden (VT300).\n * Ps = 3 0 -> Set foreground color to Black.\n * Ps = 3 1 -> Set foreground color to Red.\n * Ps = 3 2 -> Set foreground color to Green.\n * Ps = 3 3 -> Set foreground color to Yellow.\n * Ps = 3 4 -> Set foreground color to Blue.\n * Ps = 3 5 -> Set foreground color to Magenta.\n * Ps = 3 6 -> Set foreground color to Cyan.\n * Ps = 3 7 -> Set foreground color to White.\n * Ps = 3 9 -> Set foreground color to default (original).\n * Ps = 4 0 -> Set background color to Black.\n * Ps = 4 1 -> Set background color to Red.\n * Ps = 4 2 -> Set background color to Green.\n * Ps = 4 3 -> Set background color to Yellow.\n * Ps = 4 4 -> Set background color to Blue.\n * Ps = 4 5 -> Set background color to Magenta.\n * Ps = 4 6 -> Set background color to Cyan.\n * Ps = 4 7 -> Set background color to White.\n * Ps = 4 9 -> Set background color to default (original).\n *\n * If 16-color support is compiled, the following apply. Assume\n * that xterm's resources are set so that the ISO color codes are\n * the first 8 of a set of 16. Then the aixterm colors are the\n * bright versions of the ISO colors:\n * Ps = 9 0 -> Set foreground color to Black.\n * Ps = 9 1 -> Set foreground color to Red.\n * Ps = 9 2 -> Set foreground color to Green.\n * Ps = 9 3 -> Set foreground color to Yellow.\n * Ps = 9 4 -> Set foreground color to Blue.\n * Ps = 9 5 -> Set foreground color to Magenta.\n * Ps = 9 6 -> Set foreground color to Cyan.\n * Ps = 9 7 -> Set foreground color to White.\n * Ps = 1 0 0 -> Set background color to Black.\n * Ps = 1 0 1 -> Set background color to Red.\n * Ps = 1 0 2 -> Set background color to Green.\n * Ps = 1 0 3 -> Set background color to Yellow.\n * Ps = 1 0 4 -> Set background color to Blue.\n * Ps = 1 0 5 -> Set background color to Magenta.\n * Ps = 1 0 6 -> Set background color to Cyan.\n * Ps = 1 0 7 -> Set background color to White.\n *\n * If xterm is compiled with the 16-color support disabled, it\n * supports the following, from rxvt:\n * Ps = 1 0 0 -> Set foreground and background color to\n * default.\n *\n * If 88- or 256-color support is compiled, the following apply.\n * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second\n * Ps.\n * Ps = 4 8 ; 5 ; Ps -> Set background color to the second\n * Ps.\n */\n public charAttributes(params: number[]): void {\n // Optimize a single SGR0.\n if (params.length === 1 && params[0] === 0) {\n this._terminal.curAttr = this._terminal.defAttr;\n return;\n }\n\n const l = params.length;\n let flags = this._terminal.curAttr >> 18;\n let fg = (this._terminal.curAttr >> 9) & 0x1ff;\n let bg = this._terminal.curAttr & 0x1ff;\n let p;\n\n for (let i = 0; i < l; i++) {\n p = params[i];\n if (p >= 30 && p <= 37) {\n // fg color 8\n fg = p - 30;\n } else if (p >= 40 && p <= 47) {\n // bg color 8\n bg = p - 40;\n } else if (p >= 90 && p <= 97) {\n // fg color 16\n p += 8;\n fg = p - 90;\n } else if (p >= 100 && p <= 107) {\n // bg color 16\n p += 8;\n bg = p - 100;\n } else if (p === 0) {\n // default\n flags = this._terminal.defAttr >> 18;\n fg = (this._terminal.defAttr >> 9) & 0x1ff;\n bg = this._terminal.defAttr & 0x1ff;\n // flags = 0;\n // fg = 0x1ff;\n // bg = 0x1ff;\n } else if (p === 1) {\n // bold text\n flags |= FLAGS.BOLD;\n } else if (p === 4) {\n // underlined text\n flags |= FLAGS.UNDERLINE;\n } else if (p === 5) {\n // blink\n flags |= FLAGS.BLINK;\n } else if (p === 7) {\n // inverse and positive\n // test with: echo -e '\\e[31m\\e[42mhello\\e[7mworld\\e[27mhi\\e[m'\n flags |= FLAGS.INVERSE;\n } else if (p === 8) {\n // invisible\n flags |= FLAGS.INVISIBLE;\n } else if (p === 2) {\n // dimmed text\n flags |= FLAGS.DIM;\n } else if (p === 22) {\n // not bold nor faint\n flags &= ~FLAGS.BOLD;\n flags &= ~FLAGS.DIM;\n } else if (p === 24) {\n // not underlined\n flags &= ~FLAGS.UNDERLINE;\n } else if (p === 25) {\n // not blink\n flags &= ~FLAGS.BLINK;\n } else if (p === 27) {\n // not inverse\n flags &= ~FLAGS.INVERSE;\n } else if (p === 28) {\n // not invisible\n flags &= ~FLAGS.INVISIBLE;\n } else if (p === 39) {\n // reset fg\n fg = (this._terminal.defAttr >> 9) & 0x1ff;\n } else if (p === 49) {\n // reset bg\n bg = this._terminal.defAttr & 0x1ff;\n } else if (p === 38) {\n // fg color 256\n if (params[i + 1] === 2) {\n i += 2;\n fg = this._terminal.matchColor(\n params[i] & 0xff,\n params[i + 1] & 0xff,\n params[i + 2] & 0xff);\n if (fg === -1) fg = 0x1ff;\n i += 2;\n } else if (params[i + 1] === 5) {\n i += 2;\n p = params[i] & 0xff;\n fg = p;\n }\n } else if (p === 48) {\n // bg color 256\n if (params[i + 1] === 2) {\n i += 2;\n bg = this._terminal.matchColor(\n params[i] & 0xff,\n params[i + 1] & 0xff,\n params[i + 2] & 0xff);\n if (bg === -1) bg = 0x1ff;\n i += 2;\n } else if (params[i + 1] === 5) {\n i += 2;\n p = params[i] & 0xff;\n bg = p;\n }\n } else if (p === 100) {\n // reset fg/bg\n fg = (this._terminal.defAttr >> 9) & 0x1ff;\n bg = this._terminal.defAttr & 0x1ff;\n } else {\n this._terminal.error('Unknown SGR attribute: %d.', p);\n }\n }\n\n this._terminal.curAttr = (flags << 18) | (fg << 9) | bg;\n }\n\n /**\n * CSI Ps n Device Status Report (DSR).\n * Ps = 5 -> Status Report. Result (``OK'') is\n * CSI 0 n\n * Ps = 6 -> Report Cursor Position (CPR) [row;column].\n * Result is\n * CSI r ; c R\n * CSI ? Ps n\n * Device Status Report (DSR, DEC-specific).\n * Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI\n * ? r ; c R (assumes page is zero).\n * Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).\n * or CSI ? 1 1 n (not ready).\n * Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)\n * or CSI ? 2 1 n (locked).\n * Ps = 2 6 -> Report Keyboard status as\n * CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).\n * The last two parameters apply to VT400 & up, and denote key-\n * board ready and LK01 respectively.\n * Ps = 5 3 -> Report Locator status as\n * CSI ? 5 3 n Locator available, if compiled-in, or\n * CSI ? 5 0 n No Locator, if not.\n */\n public deviceStatus(params: number[]): void {\n if (!this._terminal.prefix) {\n switch (params[0]) {\n case 5:\n // status report\n this._terminal.send(C0.ESC + '[0n');\n break;\n case 6:\n // cursor position\n this._terminal.send(C0.ESC + '['\n + (this._terminal.buffer.y + 1)\n + ';'\n + (this._terminal.buffer.x + 1)\n + 'R');\n break;\n }\n } else if (this._terminal.prefix === '?') {\n // modern xterm doesnt seem to\n // respond to any of these except ?6, 6, and 5\n switch (params[0]) {\n case 6:\n // cursor position\n this._terminal.send(C0.ESC + '[?'\n + (this._terminal.buffer.y + 1)\n + ';'\n + (this._terminal.buffer.x + 1)\n + 'R');\n break;\n case 15:\n // no printer\n // this.send(C0.ESC + '[?11n');\n break;\n case 25:\n // dont support user defined keys\n // this.send(C0.ESC + '[?21n');\n break;\n case 26:\n // north american keyboard\n // this.send(C0.ESC + '[?27;1;0;0n');\n break;\n case 53:\n // no dec locator/mouse\n // this.send(C0.ESC + '[?50n');\n break;\n }\n }\n }\n\n /**\n * CSI ! p Soft terminal reset (DECSTR).\n * http://vt100.net/docs/vt220-rm/table4-10.html\n */\n public softReset(params: number[]): void {\n this._terminal.cursorHidden = false;\n this._terminal.insertMode = false;\n this._terminal.originMode = false;\n this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false\n this._terminal.applicationKeypad = false; // ?\n this._terminal.viewport.syncScrollArea();\n this._terminal.applicationCursor = false;\n this._terminal.buffer.scrollTop = 0;\n this._terminal.buffer.scrollBottom = this._terminal.rows - 1;\n this._terminal.curAttr = this._terminal.defAttr;\n this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?\n this._terminal.charset = null;\n this._terminal.glevel = 0; // ??\n this._terminal.charsets = [null]; // ??\n }\n\n /**\n * CSI Ps SP q Set cursor style (DECSCUSR, VT520).\n * Ps = 0 -> blinking block.\n * Ps = 1 -> blinking block (default).\n * Ps = 2 -> steady block.\n * Ps = 3 -> blinking underline.\n * Ps = 4 -> steady underline.\n * Ps = 5 -> blinking bar (xterm).\n * Ps = 6 -> steady bar (xterm).\n */\n public setCursorStyle(params?: number[]): void {\n const param = params[0] < 1 ? 1 : params[0];\n switch (param) {\n case 1:\n case 2:\n this._terminal.setOption('cursorStyle', 'block');\n break;\n case 3:\n case 4:\n this._terminal.setOption('cursorStyle', 'underline');\n break;\n case 5:\n case 6:\n this._terminal.setOption('cursorStyle', 'bar');\n break;\n }\n const isBlinking = param % 2 === 1;\n this._terminal.setOption('cursorBlink', isBlinking);\n }\n\n /**\n * CSI Ps ; Ps r\n * Set Scrolling Region [top;bottom] (default = full size of win-\n * dow) (DECSTBM).\n * CSI ? Pm r\n */\n public setScrollRegion(params: number[]): void {\n if (this._terminal.prefix) return;\n this._terminal.buffer.scrollTop = (params[0] || 1) - 1;\n this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1;\n this._terminal.buffer.x = 0;\n this._terminal.buffer.y = 0;\n }\n\n\n /**\n * CSI s\n * Save cursor (ANSI.SYS).\n */\n public saveCursor(params: number[]): void {\n this._terminal.buffer.savedX = this._terminal.buffer.x;\n this._terminal.buffer.savedY = this._terminal.buffer.y;\n }\n\n\n /**\n * CSI u\n * Restore cursor (ANSI.SYS).\n */\n public restoreCursor(params: number[]): void {\n this._terminal.buffer.x = this._terminal.buffer.savedX || 0;\n this._terminal.buffer.y = this._terminal.buffer.savedY || 0;\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IEventEmitter, IListenerType } from './Interfaces';\n\nexport class EventEmitter implements IEventEmitter {\n private _events: {[type: string]: IListenerType[]};\n\n constructor() {\n // Restore the previous events if available, this will happen if the\n // constructor is called multiple times on the same object (terminal reset).\n this._events = this._events || {};\n }\n\n public on(type: string, listener: IListenerType): void {\n this._events[type] = this._events[type] || [];\n this._events[type].push(listener);\n }\n\n public off(type: string, listener: IListenerType): void {\n if (!this._events[type]) {\n return;\n }\n\n let obj = this._events[type];\n let i = obj.length;\n\n while (i--) {\n if (obj[i] === listener || obj[i].listener === listener) {\n obj.splice(i, 1);\n return;\n }\n }\n }\n\n public removeAllListeners(type: string): void {\n if (this._events[type]) {\n delete this._events[type];\n }\n }\n\n public once(type: string, listener: IListenerType): void {\n function on(): void {\n let args = Array.prototype.slice.call(arguments);\n this.off(type, on);\n listener.apply(this, args);\n }\n (on).listener = listener;\n this.on(type, on);\n }\n\n public emit(type: string, ...args: any[]): void {\n if (!this._events[type]) {\n return;\n }\n let obj = this._events[type];\n for (let i = 0; i < obj.length; i++) {\n obj[i].apply(this, args);\n }\n }\n\n public listeners(type: string): IListenerType[] {\n return this._events[type] || [];\n }\n\n protected destroy(): void {\n this._events = {};\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n/**\n * C0 control codes\n * See = https://en.wikipedia.org/wiki/C0_and_C1_control_codes\n */\nexport namespace C0 {\n /** Null (Caret = ^@, C = \\0) */\n export const NUL = '\\x00';\n /** Start of Heading (Caret = ^A) */\n export const SOH = '\\x01';\n /** Start of Text (Caret = ^B) */\n export const STX = '\\x02';\n /** End of Text (Caret = ^C) */\n export const ETX = '\\x03';\n /** End of Transmission (Caret = ^D) */\n export const EOT = '\\x04';\n /** Enquiry (Caret = ^E) */\n export const ENQ = '\\x05';\n /** Acknowledge (Caret = ^F) */\n export const ACK = '\\x06';\n /** Bell (Caret = ^G, C = \\a) */\n export const BEL = '\\x07';\n /** Backspace (Caret = ^H, C = \\b) */\n export const BS = '\\x08';\n /** Character Tabulation, Horizontal Tabulation (Caret = ^I, C = \\t) */\n export const HT = '\\x09';\n /** Line Feed (Caret = ^J, C = \\n) */\n export const LF = '\\x0a';\n /** Line Tabulation, Vertical Tabulation (Caret = ^K, C = \\v) */\n export const VT = '\\x0b';\n /** Form Feed (Caret = ^L, C = \\f) */\n export const FF = '\\x0c';\n /** Carriage Return (Caret = ^M, C = \\r) */\n export const CR = '\\x0d';\n /** Shift Out (Caret = ^N) */\n export const SO = '\\x0e';\n /** Shift In (Caret = ^O) */\n export const SI = '\\x0f';\n /** Data Link Escape (Caret = ^P) */\n export const DLE = '\\x10';\n /** Device Control One (XON) (Caret = ^Q) */\n export const DC1 = '\\x11';\n /** Device Control Two (Caret = ^R) */\n export const DC2 = '\\x12';\n /** Device Control Three (XOFF) (Caret = ^S) */\n export const DC3 = '\\x13';\n /** Device Control Four (Caret = ^T) */\n export const DC4 = '\\x14';\n /** Negative Acknowledge (Caret = ^U) */\n export const NAK = '\\x15';\n /** Synchronous Idle (Caret = ^V) */\n export const SYN = '\\x16';\n /** End of Transmission Block (Caret = ^W) */\n export const ETB = '\\x17';\n /** Cancel (Caret = ^X) */\n export const CAN = '\\x18';\n /** End of Medium (Caret = ^Y) */\n export const EM = '\\x19';\n /** Substitute (Caret = ^Z) */\n export const SUB = '\\x1a';\n /** Escape (Caret = ^[, C = \\e) */\n export const ESC = '\\x1b';\n /** File Separator (Caret = ^\\) */\n export const FS = '\\x1c';\n /** Group Separator (Caret = ^]) */\n export const GS = '\\x1d';\n /** Record Separator (Caret = ^^) */\n export const RS = '\\x1e';\n /** Unit Separator (Caret = ^_) */\n export const US = '\\x1f';\n /** Space */\n export const SP = '\\x20';\n /** Delete (Caret = ^?) */\n export const DEL = '\\x7f';\n};\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal } from './Interfaces';\n\ninterface IPosition {\n start: number;\n end: number;\n}\n\n/**\n * Encapsulates the logic for handling compositionstart, compositionupdate and compositionend\n * events, displaying the in-progress composition to the UI and forwarding the final composition\n * to the handler.\n */\nexport class CompositionHelper {\n /**\n * Whether input composition is currently happening, eg. via a mobile keyboard, speech input or\n * IME. This variable determines whether the compositionText should be displayed on the UI.\n */\n private isComposing: boolean;\n\n /**\n * The position within the input textarea's value of the current composition.\n */\n private compositionPosition: IPosition;\n\n /**\n * Whether a composition is in the process of being sent, setting this to false will cancel any\n * in-progress composition.\n */\n private isSendingComposition: boolean;\n\n /**\n * Creates a new CompositionHelper.\n * @param textarea The textarea that xterm uses for input.\n * @param compositionView The element to display the in-progress composition in.\n * @param terminal The Terminal to forward the finished composition to.\n */\n constructor(\n private textarea: HTMLTextAreaElement,\n private compositionView: HTMLElement,\n private terminal: ITerminal\n ) {\n this.isComposing = false;\n this.isSendingComposition = false;\n this.compositionPosition = { start: null, end: null };\n }\n\n /**\n * Handles the compositionstart event, activating the composition view.\n */\n public compositionstart(): void {\n this.isComposing = true;\n this.compositionPosition.start = this.textarea.value.length;\n this.compositionView.textContent = '';\n this.compositionView.classList.add('active');\n }\n\n /**\n * Handles the compositionupdate event, updating the composition view.\n * @param {CompositionEvent} ev The event.\n */\n public compositionupdate(ev: CompositionEvent): void {\n this.compositionView.textContent = ev.data;\n this.updateCompositionElements();\n setTimeout(() => {\n this.compositionPosition.end = this.textarea.value.length;\n }, 0);\n }\n\n /**\n * Handles the compositionend event, hiding the composition view and sending the composition to\n * the handler.\n */\n public compositionend(): void {\n this.finalizeComposition(true);\n }\n\n /**\n * Handles the keydown event, routing any necessary events to the CompositionHelper functions.\n * @param ev The keydown event.\n * @return Whether the Terminal should continue processing the keydown event.\n */\n public keydown(ev: KeyboardEvent): boolean {\n if (this.isComposing || this.isSendingComposition) {\n if (ev.keyCode === 229) {\n // Continue composing if the keyCode is the \"composition character\"\n return false;\n } else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {\n // Continue composing if the keyCode is a modifier key\n return false;\n } else {\n // Finish composition immediately. This is mainly here for the case where enter is\n // pressed and the handler needs to be triggered before the command is executed.\n this.finalizeComposition(false);\n }\n }\n\n if (ev.keyCode === 229) {\n // If the \"composition character\" is used but gets to this point it means a non-composition\n // character (eg. numbers and punctuation) was pressed when the IME was active.\n this.handleAnyTextareaChanges();\n return false;\n }\n\n return true;\n }\n\n /**\n * Finalizes the composition, resuming regular input actions. This is called when a composition\n * is ending.\n * @param waitForPropogation Whether to wait for events to propogate before sending\n * the input. This should be false if a non-composition keystroke is entered before the\n * compositionend event is triggered, such as enter, so that the composition is send before\n * the command is executed.\n */\n private finalizeComposition(waitForPropogation: boolean): void {\n this.compositionView.classList.remove('active');\n this.isComposing = false;\n this.clearTextareaPosition();\n\n if (!waitForPropogation) {\n // Cancel any delayed composition send requests and send the input immediately.\n this.isSendingComposition = false;\n const input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end);\n this.terminal.handler(input);\n } else {\n // Make a deep copy of the composition position here as a new compositionstart event may\n // fire before the setTimeout executes.\n const currentCompositionPosition = {\n start: this.compositionPosition.start,\n end: this.compositionPosition.end,\n };\n\n // Since composition* events happen before the changes take place in the textarea on most\n // browsers, use a setTimeout with 0ms time to allow the native compositionend event to\n // complete. This ensures the correct character is retrieved, this solution was used\n // because:\n // - The compositionend event's data property is unreliable, at least on Chromium\n // - The last compositionupdate event's data property does not always accurately describe\n // the character, a counter example being Korean where an ending consonsant can move to\n // the following character if the following input is a vowel.\n this.isSendingComposition = true;\n setTimeout(() => {\n // Ensure that the input has not already been sent\n if (this.isSendingComposition) {\n this.isSendingComposition = false;\n let input;\n if (this.isComposing) {\n // Use the end position to get the string if a new composition has started.\n input = this.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);\n } else {\n // Don't use the end position here in order to pick up any characters after the\n // composition has finished, for example when typing a non-composition character\n // (eg. 2) after a composition character.\n input = this.textarea.value.substring(currentCompositionPosition.start);\n }\n this.terminal.handler(input);\n }\n }, 0);\n }\n }\n\n /**\n * Apply any changes made to the textarea after the current event chain is allowed to complete.\n * This should be called when not currently composing but a keydown event with the \"composition\n * character\" (229) is triggered, in order to allow non-composition text to be entered when an\n * IME is active.\n */\n private handleAnyTextareaChanges(): void {\n const oldValue = this.textarea.value;\n setTimeout(() => {\n // Ignore if a composition has started since the timeout\n if (!this.isComposing) {\n const newValue = this.textarea.value;\n const diff = newValue.replace(oldValue, '');\n if (diff.length > 0) {\n this.terminal.handler(diff);\n }\n }\n }, 0);\n }\n\n /**\n * Positions the composition view on top of the cursor and the textarea just below it (so the\n * IME helper dialog is positioned correctly).\n * @param dontRecurse Whether to use setTimeout to recursively trigger another update, this is\n * necessary as the IME events across browsers are not consistently triggered.\n */\n public updateCompositionElements(dontRecurse?: boolean): void {\n if (!this.isComposing) {\n return;\n }\n\n if (this.terminal.buffer.isCursorInViewport) {\n const cellHeight = Math.ceil(this.terminal.charMeasure.height * this.terminal.options.lineHeight);\n const cursorTop = this.terminal.buffer.y * cellHeight;\n const cursorLeft = this.terminal.buffer.x * this.terminal.charMeasure.width;\n\n this.compositionView.style.left = cursorLeft + 'px';\n this.compositionView.style.top = cursorTop + 'px';\n this.compositionView.style.height = cellHeight + 'px';\n this.compositionView.style.lineHeight = cellHeight + 'px';\n // Sync the textarea to the exact position of the composition view so the IME knows where the\n // text is.\n const compositionViewBounds = this.compositionView.getBoundingClientRect();\n this.textarea.style.left = cursorLeft + 'px';\n this.textarea.style.top = cursorTop + 'px';\n this.textarea.style.width = compositionViewBounds.width + 'px';\n this.textarea.style.height = compositionViewBounds.height + 'px';\n this.textarea.style.lineHeight = compositionViewBounds.height + 'px';\n }\n\n if (!dontRecurse) {\n setTimeout(() => this.updateCompositionElements(true), 0);\n }\n };\n\n /**\n * Clears the textarea's position so that the cursor does not blink on IE.\n * @private\n */\n private clearTextareaPosition(): void {\n this.textarea.style.left = '';\n this.textarea.style.top = '';\n };\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Charset } from './Types';\n\n/**\n * The character sets supported by the terminal. These enable several languages\n * to be represented within the terminal with only 8-bit encoding. See ISO 2022\n * for a discussion on character sets. Only VT100 character sets are supported.\n */\nexport const CHARSETS: { [key: string]: Charset } = {};\n\n/**\n * The default character set, US.\n */\nexport const DEFAULT_CHARSET: Charset = CHARSETS['B'];\n\n/**\n * DEC Special Character and Line Drawing Set.\n * Reference: http://vt100.net/docs/vt102-ug/table5-13.html\n * A lot of curses apps use this if they see TERM=xterm.\n * testing: echo -e '\\e(0a\\e(B'\n * The xterm output sometimes seems to conflict with the\n * reference above. xterm seems in line with the reference\n * when running vttest however.\n * The table below now uses xterm's output from vttest.\n */\nCHARSETS['0'] = {\n '`': '\\u25c6', // '◆'\n 'a': '\\u2592', // '▒'\n 'b': '\\u0009', // '\\t'\n 'c': '\\u000c', // '\\f'\n 'd': '\\u000d', // '\\r'\n 'e': '\\u000a', // '\\n'\n 'f': '\\u00b0', // '°'\n 'g': '\\u00b1', // '±'\n 'h': '\\u2424', // '\\u2424' (NL)\n 'i': '\\u000b', // '\\v'\n 'j': '\\u2518', // '┘'\n 'k': '\\u2510', // '┐'\n 'l': '\\u250c', // '┌'\n 'm': '\\u2514', // '└'\n 'n': '\\u253c', // '┼'\n 'o': '\\u23ba', // '⎺'\n 'p': '\\u23bb', // '⎻'\n 'q': '\\u2500', // '─'\n 'r': '\\u23bc', // '⎼'\n 's': '\\u23bd', // '⎽'\n 't': '\\u251c', // '├'\n 'u': '\\u2524', // '┤'\n 'v': '\\u2534', // '┴'\n 'w': '\\u252c', // '┬'\n 'x': '\\u2502', // '│'\n 'y': '\\u2264', // '≤'\n 'z': '\\u2265', // '≥'\n '{': '\\u03c0', // 'π'\n '|': '\\u2260', // '≠'\n '}': '\\u00a3', // '£'\n '~': '\\u00b7' // '·'\n};\n\n/**\n * British character set\n * ESC (A\n * Reference: http://vt100.net/docs/vt220-rm/table2-5.html\n */\nCHARSETS['A'] = {\n '#': '£'\n};\n\n/**\n * United States character set\n * ESC (B\n */\nCHARSETS['B'] = null;\n\n/**\n * Dutch character set\n * ESC (4\n * Reference: http://vt100.net/docs/vt220-rm/table2-6.html\n */\nCHARSETS['4'] = {\n '#': '£',\n '@': '¾',\n '[': 'ij',\n '\\\\': '½',\n ']': '|',\n '{': '¨',\n '|': 'f',\n '}': '¼',\n '~': '´'\n};\n\n/**\n * Finnish character set\n * ESC (C or ESC (5\n * Reference: http://vt100.net/docs/vt220-rm/table2-7.html\n */\nCHARSETS['C'] =\nCHARSETS['5'] = {\n '[': 'Ä',\n '\\\\': 'Ö',\n ']': 'Å',\n '^': 'Ü',\n '`': 'é',\n '{': 'ä',\n '|': 'ö',\n '}': 'å',\n '~': 'ü'\n};\n\n/**\n * French character set\n * ESC (R\n * Reference: http://vt100.net/docs/vt220-rm/table2-8.html\n */\nCHARSETS['R'] = {\n '#': '£',\n '@': 'à',\n '[': '°',\n '\\\\': 'ç',\n ']': '§',\n '{': 'é',\n '|': 'ù',\n '}': 'è',\n '~': '¨'\n};\n\n/**\n * French Canadian character set\n * ESC (Q\n * Reference: http://vt100.net/docs/vt220-rm/table2-9.html\n */\nCHARSETS['Q'] = {\n '@': 'à',\n '[': 'â',\n '\\\\': 'ç',\n ']': 'ê',\n '^': 'î',\n '`': 'ô',\n '{': 'é',\n '|': 'ù',\n '}': 'è',\n '~': 'û'\n};\n\n/**\n * German character set\n * ESC (K\n * Reference: http://vt100.net/docs/vt220-rm/table2-10.html\n */\nCHARSETS['K'] = {\n '@': '§',\n '[': 'Ä',\n '\\\\': 'Ö',\n ']': 'Ü',\n '{': 'ä',\n '|': 'ö',\n '}': 'ü',\n '~': 'ß'\n};\n\n/**\n * Italian character set\n * ESC (Y\n * Reference: http://vt100.net/docs/vt220-rm/table2-11.html\n */\nCHARSETS['Y'] = {\n '#': '£',\n '@': '§',\n '[': '°',\n '\\\\': 'ç',\n ']': 'é',\n '`': 'ù',\n '{': 'à',\n '|': 'ò',\n '}': 'è',\n '~': 'ì'\n};\n\n/**\n * Norwegian/Danish character set\n * ESC (E or ESC (6\n * Reference: http://vt100.net/docs/vt220-rm/table2-12.html\n */\nCHARSETS['E'] =\nCHARSETS['6'] = {\n '@': 'Ä',\n '[': 'Æ',\n '\\\\': 'Ø',\n ']': 'Å',\n '^': 'Ü',\n '`': 'ä',\n '{': 'æ',\n '|': 'ø',\n '}': 'å',\n '~': 'ü'\n};\n\n/**\n * Spanish character set\n * ESC (Z\n * Reference: http://vt100.net/docs/vt220-rm/table2-13.html\n */\nCHARSETS['Z'] = {\n '#': '£',\n '@': '§',\n '[': '¡',\n '\\\\': 'Ñ',\n ']': '¿',\n '{': '°',\n '|': 'ñ',\n '}': 'ç'\n};\n\n/**\n * Swedish character set\n * ESC (H or ESC (7\n * Reference: http://vt100.net/docs/vt220-rm/table2-14.html\n */\nCHARSETS['H'] =\nCHARSETS['7'] = {\n '@': 'É',\n '[': 'Ä',\n '\\\\': 'Ö',\n ']': 'Å',\n '^': 'Ü',\n '`': 'é',\n '{': 'ä',\n '|': 'ö',\n '}': 'å',\n '~': 'ü'\n};\n\n/**\n * Swiss character set\n * ESC (=\n * Reference: http://vt100.net/docs/vt220-rm/table2-15.html\n */\nCHARSETS['='] = {\n '#': 'ù',\n '@': 'à',\n '[': 'é',\n '\\\\': 'ç',\n ']': 'ê',\n '^': 'î',\n '_': 'è',\n '`': 'ô',\n '{': 'ä',\n '|': 'ö',\n '}': 'ü',\n '~': 'û'\n};\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number {\n // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c\n // combining characters\n const COMBINING_BMP = [\n [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],\n [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],\n [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603],\n [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670],\n [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED],\n [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A],\n [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902],\n [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D],\n [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981],\n [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD],\n [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],\n [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],\n [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC],\n [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD],\n [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],\n [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D],\n [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0],\n [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],\n [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC],\n [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD],\n [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D],\n [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6],\n [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E],\n [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC],\n [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35],\n [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E],\n [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97],\n [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030],\n [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039],\n [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F],\n [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753],\n [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD],\n [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD],\n [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922],\n [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B],\n [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34],\n [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42],\n [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF],\n [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063],\n [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],\n [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],\n [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],\n [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB],\n ];\n const COMBINING_HIGH = [\n [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],\n [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169],\n [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],\n [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F],\n [0xE0100, 0xE01EF]\n ];\n // binary search\n function bisearch(ucs: number, data: number[][]): boolean {\n let min = 0;\n let max = data.length - 1;\n let mid;\n if (ucs < data[0][0] || ucs > data[max][1])\n return false;\n while (max >= min) {\n mid = (min + max) >> 1;\n if (ucs > data[mid][1])\n min = mid + 1;\n else if (ucs < data[mid][0])\n max = mid - 1;\n else\n return true;\n }\n return false;\n }\n function wcwidthBMP(ucs: number): number {\n // test for 8-bit control characters\n if (ucs === 0)\n return opts.nul;\n if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))\n return opts.control;\n // binary search in table of non-spacing characters\n if (bisearch(ucs, COMBINING_BMP))\n return 0;\n // if we arrive here, ucs is not a combining or C0/C1 control character\n if (isWideBMP(ucs)) {\n return 2;\n }\n return 1;\n }\n function isWideBMP(ucs: number): boolean {\n return (\n ucs >= 0x1100 && (\n ucs <= 0x115f || // Hangul Jamo init. consonants\n ucs === 0x2329 ||\n ucs === 0x232a ||\n (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi\n (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables\n (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs\n (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms\n (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms\n (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms\n (ucs >= 0xffe0 && ucs <= 0xffe6)));\n }\n function wcwidthHigh(ucs: number): 0 | 1 | 2 {\n if (bisearch(ucs, COMBINING_HIGH))\n return 0;\n if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) {\n return 2;\n }\n return 1;\n }\n const control = opts.control | 0;\n let table: number[] | Uint32Array = null;\n function init_table(): number[] | Uint32Array {\n // lookup table for BMP\n const CODEPOINTS = 65536; // BMP holds 65536 codepoints\n const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2\n const ITEMSIZE = 32; // using uint32_t\n const CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE;\n const CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH;\n table = (typeof Uint32Array === 'undefined')\n ? new Array(CONTAINERSIZE)\n : new Uint32Array(CONTAINERSIZE);\n for (let i = 0; i < CONTAINERSIZE; ++i) {\n let num = 0;\n let pos = CODEPOINTS_PER_ITEM;\n while (pos--)\n num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos);\n table[i] = num;\n }\n return table;\n }\n // get width from lookup table\n // position in container : num / CODEPOINTS_PER_ITEM\n // ==> n = table[Math.floor(num / 16)]\n // ==> n = table[num >> 4]\n // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100\n // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH\n // ==> m = (n % 16) * 2\n // ==> m = (num & 15) << 1\n // right shift to position m\n // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766\n // we are only interested in 2 LSBs, cut off higher bits\n // ==> n = n & 3 e.g. 000000000000000000000000000000XX\n return function (num: number): number {\n num = num | 0; // get asm.js like optimization under V8\n if (num < 32)\n return control | 0;\n if (num < 127)\n return 1;\n let t = table || init_table();\n if (num < 65536)\n return t[num >> 4] >> ((num & 15) << 1) & 3;\n // do a full search for high codepoints\n return wcwidthHigh(num);\n };\n})({nul: 0, control: 0}); // configurable options\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, IBufferSet } from './Interfaces';\nimport { Buffer } from './Buffer';\nimport { EventEmitter } from './EventEmitter';\n\n/**\n * The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and\n * provides also utilities for working with them.\n */\nexport class BufferSet extends EventEmitter implements IBufferSet {\n private _normal: Buffer;\n private _alt: Buffer;\n private _activeBuffer: Buffer;\n\n /**\n * Create a new BufferSet for the given terminal.\n * @param {Terminal} terminal - The terminal the BufferSet will belong to\n */\n constructor(private _terminal: ITerminal) {\n super();\n this._normal = new Buffer(this._terminal, true);\n this._normal.fillViewportRows();\n\n // The alt buffer should never have scrollback.\n // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer\n this._alt = new Buffer(this._terminal, false);\n this._activeBuffer = this._normal;\n\n this.setupTabStops();\n }\n\n /**\n * Returns the alt Buffer of the BufferSet\n * @returns {Buffer}\n */\n public get alt(): Buffer {\n return this._alt;\n }\n\n /**\n * Returns the normal Buffer of the BufferSet\n * @returns {Buffer}\n */\n public get active(): Buffer {\n return this._activeBuffer;\n }\n\n /**\n * Returns the currently active Buffer of the BufferSet\n * @returns {Buffer}\n */\n public get normal(): Buffer {\n return this._normal;\n }\n\n /**\n * Sets the normal Buffer of the BufferSet as its currently active Buffer\n */\n public activateNormalBuffer(): void {\n // The alt buffer should always be cleared when we switch to the normal\n // buffer. This frees up memory since the alt buffer should always be new\n // when activated.\n this._alt.clear();\n\n this._activeBuffer = this._normal;\n this.emit('activate', this._normal);\n }\n\n /**\n * Sets the alt Buffer of the BufferSet as its currently active Buffer\n */\n public activateAltBuffer(): void {\n // Since the alt buffer is always cleared when the normal buffer is\n // activated, we want to fill it when switching to it.\n this._alt.fillViewportRows();\n this._activeBuffer = this._alt;\n this.emit('activate', this._alt);\n }\n\n /**\n * Resizes both normal and alt buffers, adjusting their data accordingly.\n * @param newCols The new number of columns.\n * @param newRows The new number of rows.\n */\n public resize(newCols: number, newRows: number): void {\n this._normal.resize(newCols, newRows);\n this._alt.resize(newCols, newRows);\n }\n\n /**\n * Setup the tab stops.\n * @param i The index to start setting up tab stops from.\n */\n public setupTabStops(i?: number): void {\n this._normal.setupTabStops(i);\n this._alt.setupTabStops(i);\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, IBuffer } from './Interfaces';\nimport { CircularList } from './utils/CircularList';\nimport { LineData, CharData } from './Types';\n\nexport const CHAR_DATA_ATTR_INDEX = 0;\nexport const CHAR_DATA_CHAR_INDEX = 1;\nexport const CHAR_DATA_WIDTH_INDEX = 2;\nexport const CHAR_DATA_CODE_INDEX = 3;\nexport const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1\n\n/**\n * This class represents a terminal buffer (an internal state of the terminal), where the\n * following information is stored (in high-level):\n * - text content of this particular buffer\n * - cursor position\n * - scroll position\n */\nexport class Buffer implements IBuffer {\n private _lines: CircularList;\n\n public ydisp: number;\n public ybase: number;\n public y: number;\n public x: number;\n public scrollBottom: number;\n public scrollTop: number;\n public tabs: any;\n public savedY: number;\n public savedX: number;\n\n /**\n * Create a new Buffer.\n * @param _terminal The terminal the Buffer will belong to.\n * @param _hasScrollback Whether the buffer should respect the scrollback of\n * the terminal.\n */\n constructor(\n private _terminal: ITerminal,\n private _hasScrollback: boolean\n ) {\n this.clear();\n }\n\n public get lines(): CircularList {\n return this._lines;\n }\n\n public get hasScrollback(): boolean {\n return this._hasScrollback && this.lines.maxLength > this._terminal.rows;\n }\n\n public get isCursorInViewport(): boolean {\n const absoluteY = this.ybase + this.y;\n const relativeY = absoluteY - this.ydisp;\n return (relativeY >= 0 && relativeY < this._terminal.rows);\n }\n\n /**\n * Gets the correct buffer length based on the rows provided, the terminal's\n * scrollback and whether this buffer is flagged to have scrollback or not.\n * @param rows The terminal rows to use in the calculation.\n */\n private _getCorrectBufferLength(rows: number): number {\n if (!this._hasScrollback) {\n return rows;\n }\n\n const correctBufferLength = rows + this._terminal.options.scrollback;\n\n return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength;\n }\n\n /**\n * Fills the buffer's viewport with blank lines.\n */\n public fillViewportRows(): void {\n if (this._lines.length === 0) {\n let i = this._terminal.rows;\n while (i--) {\n this.lines.push(this._terminal.blankLine());\n }\n }\n }\n\n /**\n * Clears the buffer to it's initial state, discarding all previous data.\n */\n public clear(): void {\n this.ydisp = 0;\n this.ybase = 0;\n this.y = 0;\n this.x = 0;\n this._lines = new CircularList(this._getCorrectBufferLength(this._terminal.rows));\n this.scrollTop = 0;\n this.scrollBottom = this._terminal.rows - 1;\n this.setupTabStops();\n }\n\n /**\n * Resizes the buffer, adjusting its data accordingly.\n * @param newCols The new number of columns.\n * @param newRows The new number of rows.\n */\n public resize(newCols: number, newRows: number): void {\n // Increase max length if needed before adjustments to allow space to fill\n // as required.\n const newMaxLength = this._getCorrectBufferLength(newRows);\n if (newMaxLength > this._lines.maxLength) {\n this._lines.maxLength = newMaxLength;\n }\n\n // The following adjustments should only happen if the buffer has been\n // initialized/filled.\n if (this._lines.length > 0) {\n // Deal with columns increasing (we don't do anything when columns reduce)\n if (this._terminal.cols < newCols) {\n const ch: CharData = [this._terminal.defAttr, ' ', 1, 32]; // does xterm use the default attr?\n for (let i = 0; i < this._lines.length; i++) {\n // TODO: This should be removed, with tests setup for the case that was\n // causing the underlying bug, see https://github.com/sourcelair/xterm.js/issues/824\n if (this._lines.get(i) === undefined) {\n this._lines.set(i, this._terminal.blankLine(undefined, undefined, newCols));\n }\n while (this._lines.get(i).length < newCols) {\n this._lines.get(i).push(ch);\n }\n }\n }\n\n // Resize rows in both directions as needed\n let addToY = 0;\n if (this._terminal.rows < newRows) {\n for (let y = this._terminal.rows; y < newRows; y++) {\n if (this._lines.length < newRows + this.ybase) {\n if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) {\n // There is room above the buffer and there are no empty elements below the line,\n // scroll up\n this.ybase--;\n addToY++;\n if (this.ydisp > 0) {\n // Viewport is at the top of the buffer, must increase downwards\n this.ydisp--;\n }\n } else {\n // Add a blank line if there is no buffer left at the top to scroll to, or if there\n // are blank lines after the cursor\n this._lines.push(this._terminal.blankLine(undefined, undefined, newCols));\n }\n }\n }\n } else { // (this._terminal.rows >= newRows)\n for (let y = this._terminal.rows; y > newRows; y--) {\n if (this._lines.length > newRows + this.ybase) {\n if (this._lines.length > this.ybase + this.y + 1) {\n // The line is a blank line below the cursor, remove it\n this._lines.pop();\n } else {\n // The line is the cursor, scroll down\n this.ybase++;\n this.ydisp++;\n }\n }\n }\n }\n\n // Reduce max length if needed after adjustments, this is done after as it\n // would otherwise cut data from the bottom of the buffer.\n if (newMaxLength < this._lines.maxLength) {\n // Trim from the top of the buffer and adjust ybase and ydisp.\n const amountToTrim = this._lines.length - newMaxLength;\n if (amountToTrim > 0) {\n this._lines.trimStart(amountToTrim);\n this.ybase = Math.max(this.ybase - amountToTrim, 0);\n this.ydisp = Math.max(this.ydisp - amountToTrim, 0);\n }\n this._lines.maxLength = newMaxLength;\n }\n\n // Make sure that the cursor stays on screen\n if (this.y >= newRows) {\n this.y = newRows - 1;\n }\n if (addToY) {\n this.y += addToY;\n }\n\n if (this.x >= newCols) {\n this.x = newCols - 1;\n }\n\n this.scrollTop = 0;\n }\n\n this.scrollBottom = newRows - 1;\n }\n\n /**\n * Translates a buffer line to a string, with optional start and end columns.\n * Wide characters will count as two columns in the resulting string. This\n * function is useful for getting the actual text underneath the raw selection\n * position.\n * @param line The line being translated.\n * @param trimRight Whether to trim whitespace to the right.\n * @param startCol The column to start at.\n * @param endCol The column to end at.\n */\n public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string {\n // Get full line\n let lineString = '';\n const line = this.lines.get(lineIndex);\n if (!line) {\n return '';\n }\n\n // Initialize column and index values. Column values represent the actual\n // cell column, indexes represent the index in the string. Indexes are\n // needed here because some chars are 0 characters long (eg. after wide\n // chars) and some chars are longer than 1 characters long (eg. emojis).\n let startIndex = startCol;\n endCol = endCol || line.length;\n let endIndex = endCol;\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n lineString += char[CHAR_DATA_CHAR_INDEX];\n // Adjust start and end cols for wide characters if they affect their\n // column indexes\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n if (startCol >= i) {\n startIndex--;\n }\n if (endCol >= i) {\n endIndex--;\n }\n } else {\n // Adjust the columns to take glyphs that are represented by multiple\n // code points into account.\n if (char[CHAR_DATA_CHAR_INDEX].length > 1) {\n if (startCol > i) {\n startIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n if (endCol > i) {\n endIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n }\n }\n }\n\n // Calculate the final end col by trimming whitespace on the right of the\n // line if needed.\n if (trimRight) {\n const rightWhitespaceIndex = lineString.search(/\\s+$/);\n if (rightWhitespaceIndex !== -1) {\n endIndex = Math.min(endIndex, rightWhitespaceIndex);\n }\n // Return the empty string if only trimmed whitespace is selected\n if (endIndex <= startIndex) {\n return '';\n }\n }\n\n return lineString.substring(startIndex, endIndex);\n }\n\n /**\n * Setup the tab stops.\n * @param i The index to start setting up tab stops from.\n */\n public setupTabStops(i?: number): void {\n if (i != null) {\n if (!this.tabs[i]) {\n i = this.prevStop(i);\n }\n } else {\n this.tabs = {};\n i = 0;\n }\n\n for (; i < this._terminal.cols; i += this._terminal.options.tabStopWidth) {\n this.tabs[i] = true;\n }\n }\n\n /**\n * Move the cursor to the previous tab stop from the given position (default is current).\n * @param x The position to move the cursor to the previous tab stop.\n */\n public prevStop(x?: number): number {\n if (x == null) {\n x = this.x;\n }\n while (!this.tabs[--x] && x > 0);\n return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x;\n }\n\n /**\n * Move the cursor one tab stop forward from the given position (default is current).\n * @param x The position to move the cursor one tab stop forward.\n */\n public nextStop(x?: number): number {\n if (x == null) {\n x = this.x;\n }\n while (!this.tabs[++x] && x < this._terminal.cols);\n return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x;\n }\n}\n",null],"names":[],"mappings":"AkCAA;;;ADMA;AAGa;AACA;AACA;AACA;AACA;AASb;AAmBA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;AACA;AACA;;;AAAA;AAOA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAGA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAYA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAjSa;;;;;;;;;;;;;;;;;ADhBb;AACA;AAMA;AAAA;AASA;AAAA;AAAA;AAEA;AACA;AAIA;AACA;AAEA;;AACA;AAMA;AAAA;AACA;AACA;;;AAAA;AAMA;AAAA;AACA;AACA;;;AAAA;AAMA;AAAA;AACA;AACA;;;AAAA;AAKA;AAIA;AAEA;AACA;AACA;AAKA;AAGA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AAxFa;;;;;;;ADRA;AAGb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;ADpJa;AAKA;AAYb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AAMA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AD7OA;AAwBA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAMA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAOA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAGA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AACA;AAUA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AAUA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAMA;AACA;AACA;AACA;AAAA;AACA;AAAA;AApNa;;;;;;;ADRb;AAAA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAAC;;;;;;;ADvED;AAGA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AA/Da;;;;;;;ADAb;AACA;AAEA;AACA;AACA;AASA;AACA;AAAA;AAAA;AAEA;AACA;AAGA;AAEA;AACA;AACA;AAEA;AAIA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAMA;AACA;AAMA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAOA;AACA;AACA;AAOA;AACA;AACA;AAMA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAuCA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAIA;AACA;AACA;AAAA;AACA;AACA;AAAA;AAGA;AACA;AAAA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAOA;AACA;AAAA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAUA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAwFA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAKA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAoFA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAmEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAIA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAGA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAIA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAIA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAyBA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAGA;AACA;AAGA;AACA;AAGA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAAA;AAz5Ca;;;;;;;;;;;;;;;;;ADfb;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAKA;AAAA;AAeA;AAAA;AACA;AARA;AAIA;AAOA;AACA;AACA;AACA;AACA;;AACA;AAMA;AACA;AACA;AAOA;AAAA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAGA;AAGA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AAMA;AACA;AACA;AAYA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAWA;AAAA;AAAA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AASA;AAAA;AACA;AAKA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AA3PA;AA4PA;AAlQA;AAAa;;;;;;;ADhCb;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAKA;AACA;AACA;AAEA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAKA;AACA;AAIA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAIA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AAMA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAEA;AACA;AAOA;AACA;AACA;AAQA;AACA;AACA;AAQA;AACA;AACA;AAOA;AACA;AACA;AAKA;AACA;AACA;AAMA;AACA;AACA;AACA;AAKA;AACA;AACA;AASA;AAAA;AAjfa;;;;;;;;;;;;;;;;;ADnKb;AACA;AAGA;AAEA;AAEA;AAMA;AAKA;AAKA;AAMA;AAEA;AACA;AAaA;AAAA;AACA;AACA;AACA;AACA;AAUA;AAAA;AAiCA;AAAA;AACA;AACA;AACA;AARA;AAWA;AACA;AAEA;AACA;;AACA;AAKA;AAAA;AACA;AACA;AAMA;AACA;AAMA;AACA;AACA;AACA;AAKA;AACA;AACA;AAOA;AACA;AACA;AACA;AAEA;AAAA;;;AAAA;AACA;AAAA;;;AAAA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAAA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAIA;AACA;AACA;AAEA;AACA;;;AAAA;AAKA;AACA;AACA;AACA;AACA;AAOA;AAAA;AAEA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAGA;AACA;AAGA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAOA;AACA;AACA;AAMA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AAGA;AAGA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAKA;AAAA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAIA;AAIA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAGA;AAIA;AACA;AACA;AAAA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AAEA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAGA;AACA;AAAA;AAIA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAKA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAIA;AAEA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAOA;AAGA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AAjqBa;;;;;;;ADtDb;AAuBA;AACA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;;AAAA;AAMA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;;;AAAA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAxHa;;;;;;;;;;;;;;;;;ADYb;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AAEA;AACA;AAUA;AAOA;AAMA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAAA;AA8HA;AACA;AADA;AA3GA;AA+GA;AACA;;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAKA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAKA;AAEA;AACA;AAKA;AACA;AACA;AAEA;AAAA;AACA;AACA;;;AAAA;AAMA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAMA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AAGA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAKA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAOA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AAIA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAGA;AAGA;AAIA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAOA;AAEA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAYA;AAAA;AACA;AACA;AACA;AAKA;AACA;AACA;AAGA;AAGA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAIA;AACA;AACA;AAIA;AACA;AACA;AACA;AAAA;AAIA;AAEA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAQA;AAOA;AAGA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAGA;AAEA;AACA;AAAA;AACA;AACA;AAGA;AAEA;AACA;AAEA;AAIA;AACA;AAKA;AACA;AACA;AAGA;AAIA;AACA;AACA;AACA;AACA;AAGA;AAAA;AAGA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAMA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAIA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAQA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AAEA;AAEA;AAGA;AACA;AACA;AAAA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AAIA;AACA;AACA;AAGA;AACA;AAQA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AAKA;AACA;AACA;AAKA;AACA;AACA;AAMA;AAAA;AACA;AAKA;AAGA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAEA;AACA;AAOA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AAMA;AACA;AACA;AAWA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AACA;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AAMA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AASA;AACA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAEA;AAGA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAMA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAKA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAQA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAMA;AACA;AAAA;AACA;AAAA;AAOA;AAKA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AASA;AACA;AAEA;AACA;AAIA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAMA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAMA;AAOA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAOA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AA//Da;AAqgEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAAC;AAGD;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;;;;;;AD9rEA;AAAA;AACA;AACA;AACA;AACA;AAAC;;;;;;;ADrBD;AAcA;AAAA;AACA;AACA;AACA;AACA;AAjBA;AACA;AACA;AACA;AAgBA;AAGA;AACA;AAEA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAQA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAMA;AACA;AACA;AAAA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAnIa;;;;;;;ADOb;AACA;AACA;AACA;AACA;AACA;AALA;AAWA;AACA;AACA;AACA;AACA;AACA;AALA;AAWA;AACA;AACA;AACA;AAAA;AACA;AACA;AAGA;AACA;AATA;AAgBA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAzBA;AAgCA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AApBA;AA4BA;AACA;AAGA;AACA;AACA;AANA;;;;;;;AD9GA;AAUA;AAWA;AAAA;AACA;AAXA;AAEA;AAIA;AACA;AACA;AAKA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AAGA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AACA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AArKa;AAuKb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAXa;;;;;;;ADlLb;AAEA;AAEa;AACb;AAEA;AAYA;AAIA;AACA;AAdA;AACA;AACA;AACA;AACA;AACA;AAWA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAOA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAWA;AACA;AAKA;AAQA;AAAA;AACA;AAKA;AAQA;AACA;AAKA;AAQA;AACA;AACA;AAKA;AAKA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AASA;AACA;AACA;AAKA;AAAA;AACA;AACA;AAKA;AACA;AAYA;AACA;AACA;AACA;AACA;AAIA;AAgBA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AAEA;AASA;AAAA;AACA;AACA;AAIA;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AAIA;AACA;AAOA;AACA;AACA;AAKA;AACA;AACA;AAAA;AAhUsB;;;;;;;ADPtB;AAEa;AAgBb;AAQA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAvCA;AAyCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AACA;AACA;AACA;AACA;AAJA;AAMA;AAIA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;;;;;;;ADrNA;AACA;AACA;AACA;AACA;AACa;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAKA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AA1Da;;;;;;;;;;;;;;;;;AD5Db;AAGA;AAcA;AAEA;AAAA;AAMA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AApMa;AAsMb;AAcA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;;;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAMA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;;;;;;;AD5VA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AA3Ba;;;;;;;;;;;;;;;;;ADKb;AACA;AAEA;AAAA;AAGA;AAAA;AAFA;AAIA;AACA;;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAnCa;;;;;;;;;;;;;;;;;ADNb;AACA;AACA;AACA;AAGA;AACA;AAEA;AAAA;AAWA;AAAA;AAAA;AATA;AACA;AAUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAMA;AAKA;AAMA;AAIA;AAGA;AAIA;AAIA;AACA;AAOA;AACA;AAQA;AACA;AAGA;AAGA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AAMA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AA5Ma;;;;;;;;;;;;;;;;;ADNb;AAEA;AAAA;AAGA;AAAA;AAEA;AACA;AACA;AACA;;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AAGA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AAAA;AA5Ea;;;;;;;;;;;;;;;;;ADLb;AACA;AACA;AAEA;AAOA;AAEA;AAAA;AAMA;AAAA;AAFA;AAIA;;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAKA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAiBA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAIA;AAQA;AACA;AAMA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAKA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AAKA;AAGA;AAGA;AACA;AACA;AAOA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAnOa;;;;;;;ADZb;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAC;;;;;;;ADVD;AAEA;AACA;AACA;AAEa;AACA;AAKA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ADhBb;AAQA;AAAA;AAOA;AAAA;AAEA;AACA;;AACA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAtDa;;;;;;;;;;;;;;;;;ADRb;AAOA;AAAA;AAKA;AAAA;AACA;AAGA;AACA;AACA;;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAjBA;AAmBA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AATA;AAWA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAAA;AAUA;AACA;AACA;AAUA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAMA;AACA;AACA;AAWA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AAAA;AAxMa;;;;;;;ADFb;AACA;AACA;AAFA;AAEC;;;;;;;ADJD;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAeA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAKA;AACA;AAEA;AACA;AAYA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AAAA;AAtFa;;;;;;;ADCA;;;;;;;ADFb;AAEA;;;"} \ No newline at end of file diff --git a/route/static/codemirror/addon/dialog/dialog.css b/route/static/codemirror/addon/dialog/dialog.css deleted file mode 100755 index 494690b40..000000000 --- a/route/static/codemirror/addon/dialog/dialog.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.4em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:0;outline:0;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}.Dialog-close{color:#111;float:right;font-family:Arial;font-size:16px;height:30px;line-height:30px;text-align:center;width:30px;cursor:pointer} \ No newline at end of file diff --git a/route/static/codemirror/addon/dialog/dialog.js b/route/static/codemirror/addon/dialog/dialog.js deleted file mode 100755 index ef64aa000..000000000 --- a/route/static/codemirror/addon/dialog/dialog.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){function c(d,h,e){var g=d.getWrapperElement();var f;f=g.appendChild(document.createElement("div"));if(e){f.className="CodeMirror-dialog CodeMirror-dialog-bottom"}else{f.className="CodeMirror-dialog CodeMirror-dialog-top"}if(typeof h=="string"){f.innerHTML=h}else{f.appendChild(h)}return f}function a(d,e){if(d.state.currentNotificationClose){d.state.currentNotificationClose()}d.state.currentNotificationClose=e}b.defineExtension("openDialog",function(i,j,l){if(!l){l={}}a(this,null);var f=c(this,i,l.bottom);var e=false,g=this;function k(m){if(typeof m=="string"){h.value=m}else{if(e){return}e=true;f.parentNode.removeChild(f);g.focus();if(l.onClose){l.onClose(f)}}}var h=f.getElementsByTagName("input")[0],d;if(h){h.focus();if(l.value){h.value=l.value;if(l.selectValueOnOpen!==false){h.select()}}if(l.onInput){b.on(h,"input",function(m){l.onInput(m,h.value,k)})}if(l.onKeyUp){b.on(h,"keyup",function(m){l.onKeyUp(m,h.value,k)})}b.on(h,"keydown",function(m){if(l&&l.onKeyDown&&l.onKeyDown(m,h.value,k)){return}if(m.keyCode==27||(l.closeOnEnter!==false&&m.keyCode==13)){h.blur();b.e_stop(m);k()}if(m.keyCode==13){j(h.value,m)}});if(l.closeOnBlur!==false){b.on(h,"blur",k)}}else{if(d=f.getElementsByTagName("button")[0]){b.on(d,"click",function(){k();g.focus()});if(l.closeOnBlur!==false){b.on(d,"blur",k)}d.focus()}}return k});b.defineExtension("openConfirm",function(m,g,o){a(this,null);var h=c(this,m,o&&o.bottom);var j=h.getElementsByTagName("button");var f=false,k=this,d=1;function n(){if(f){return}f=true;h.parentNode.removeChild(h);k.focus()}j[0].focus();for(var e=0;e -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    - \ No newline at end of file diff --git a/route/static/codemirror/addon/display/autorefresh.js b/route/static/codemirror/addon/display/autorefresh.js deleted file mode 100755 index 5a7a32968..000000000 --- a/route/static/codemirror/addon/display/autorefresh.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("autoRefresh",false,function(d,e){if(d.state.autoRefresh){a(d,d.state.autoRefresh);d.state.autoRefresh=null}if(e&&d.display.wrapper.offsetHeight==0){c(d,d.state.autoRefresh={delay:e.delay||250})}});function c(d,f){function e(){if(d.display.wrapper.offsetHeight){a(d,f);if(d.display.lastWrapHeight!=d.display.wrapper.clientHeight){d.refresh()}}else{f.timeout=setTimeout(e,f.delay)}}f.timeout=setTimeout(e,f.delay);f.hurry=function(){clearTimeout(f.timeout);f.timeout=setTimeout(e,50)};b.on(window,"mouseup",f.hurry);b.on(window,"keyup",f.hurry)}function a(d,e){clearTimeout(e.timeout);b.off(window,"mouseup",e.hurry);b.off(window,"keyup",e.hurry)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/fullscreen.css b/route/static/codemirror/addon/display/fullscreen.css deleted file mode 100755 index a414b0220..000000000 --- a/route/static/codemirror/addon/display/fullscreen.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-fullscreen{position:fixed;top:0;left:0;right:0;bottom:0;height:auto;z-index:9} \ No newline at end of file diff --git a/route/static/codemirror/addon/display/fullscreen.js b/route/static/codemirror/addon/display/fullscreen.js deleted file mode 100755 index a2329a4dd..000000000 --- a/route/static/codemirror/addon/display/fullscreen.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){a.defineOption("fullScreen",false,function(d,f,e){if(e==a.Init){e=false}if(!e==!f){return}if(f){b(d)}else{c(d)}});function b(d){var e=d.getWrapperElement();d.state.fullScreenRestore={scrollTop:window.pageYOffset,scrollLeft:window.pageXOffset,width:e.style.width,height:e.style.height};e.style.width="";e.style.height="auto";e.className+=" CodeMirror-fullscreen";document.documentElement.style.overflow="hidden";d.refresh()}function c(d){var e=d.getWrapperElement();e.className=e.className.replace(/\s*CodeMirror-fullscreen\b/,"");document.documentElement.style.overflow="";var f=d.state.fullScreenRestore;e.style.width=f.width;e.style.height=f.height;window.scrollTo(f.scrollLeft,f.scrollTop);d.refresh()}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/index.html b/route/static/codemirror/addon/display/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/display/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/codemirror/addon/display/panel.js b/route/static/codemirror/addon/display/panel.js deleted file mode 100755 index d3b0e63f1..000000000 --- a/route/static/codemirror/addon/display/panel.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){a.defineExtension("addPanel",function(g,f){f=f||{};if(!this.state.panels){b(this)}var h=this.state.panels;var j=h.wrapper;var i=this.getWrapperElement();if(f.after instanceof d&&!f.after.cleared){j.insertBefore(g,f.before.node.nextSibling)}else{if(f.before instanceof d&&!f.before.cleared){j.insertBefore(g,f.before.node)}else{if(f.replace instanceof d&&!f.replace.cleared){j.insertBefore(g,f.replace.node);f.replace.clear()}else{if(f.position=="bottom"){j.appendChild(g)}else{if(f.position=="before-bottom"){j.insertBefore(g,i.nextSibling)}else{if(f.position=="after-top"){j.insertBefore(g,i)}else{j.insertBefore(g,j.firstChild)}}}}}}var e=(f&&f.height)||g.offsetHeight;this._setSize(null,h.heightLeft-=e);h.panels++;return new d(this,g,f,e)});function d(f,h,g,e){this.cm=f;this.node=h;this.options=g;this.height=e;this.cleared=false}d.prototype.clear=function(){if(this.cleared){return}this.cleared=true;var e=this.cm.state.panels;this.cm._setSize(null,e.heightLeft+=this.height);e.wrapper.removeChild(this.node);if(--e.panels==0){c(this.cm)}};d.prototype.changed=function(e){var f=e==null?this.node.offsetHeight:e;var g=this.cm.state.panels;this.cm._setSize(null,g.height+=(f-this.height));this.height=f};function b(f){var h=f.getWrapperElement();var g=window.getComputedStyle?window.getComputedStyle(h):h.currentStyle;var e=parseInt(g.height);var i=f.state.panels={setHeight:h.style.height,heightLeft:e,panels:0,wrapper:document.createElement("div")};h.parentNode.insertBefore(i.wrapper,h);var j=f.hasFocus();i.wrapper.appendChild(h);if(j){f.focus()}f._setSize=f.setSize;if(e!=null){f.setSize=function(m,k){if(k==null){return this._setSize(m,k)}i.setHeight=k;if(typeof k!="number"){var l=/^(\d+\.?\d*)px$/.exec(k);if(l){k=Number(l[1])}else{i.wrapper.style.height=k;k=i.wrapper.offsetHeight;i.wrapper.style.height=""}}f._setSize(m,i.heightLeft+=(k-e));e=k}}}function c(e){var g=e.state.panels;e.state.panels=null;var f=e.getWrapperElement();g.wrapper.parentNode.replaceChild(f,g.wrapper);f.style.height=g.setHeight;e.setSize=e._setSize;e.setSize()}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/placeholder.js b/route/static/codemirror/addon/display/placeholder.js deleted file mode 100755 index acb9db7ec..000000000 --- a/route/static/codemirror/addon/display/placeholder.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("placeholder","",function(g,j,h){var i=h&&h!=b.Init;if(j&&!i){g.on("blur",e);g.on("change",a);g.on("swapDoc",a);a(g)}else{if(!j&&i){g.off("blur",e);g.off("change",a);g.off("swapDoc",a);c(g);var k=g.getWrapperElement();k.className=k.className.replace(" CodeMirror-empty","")}}if(j&&!g.hasFocus()){e(g)}});function c(g){if(g.state.placeholder){g.state.placeholder.parentNode.removeChild(g.state.placeholder);g.state.placeholder=null}}function d(g){c(g);var h=g.state.placeholder=document.createElement("pre");h.style.cssText="height: 0; overflow: visible";h.className="CodeMirror-placeholder";var i=g.getOption("placeholder");if(typeof i=="string"){i=document.createTextNode(i)}h.appendChild(i);g.display.lineSpace.insertBefore(h,g.display.lineSpace.firstChild)}function e(g){if(f(g)){d(g)}}function a(g){var i=g.getWrapperElement(),h=f(g);i.className=i.className.replace(" CodeMirror-empty","")+(h?" CodeMirror-empty":"");if(h){d(g)}else{c(g)}}function f(g){return(g.lineCount()===1)&&(g.getLine(0)==="")}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/rulers.js b/route/static/codemirror/addon/display/rulers.js deleted file mode 100755 index cfbd155da..000000000 --- a/route/static/codemirror/addon/display/rulers.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("rulers",false,function(c,d){if(c.state.rulerDiv){c.state.rulerDiv.parentElement.removeChild(c.state.rulerDiv);c.state.rulerDiv=null;c.off("refresh",a)}if(d&&d.length){c.state.rulerDiv=c.display.lineSpace.parentElement.insertBefore(document.createElement("div"),c.display.lineSpace);c.state.rulerDiv.className="CodeMirror-rulers";a(c);c.on("refresh",a)}});function a(c){c.state.rulerDiv.textContent="";var k=c.getOption("rulers");var d=c.defaultCharWidth();var j=c.charCoords(b.Pos(c.firstLine(),0),"div").left;c.state.rulerDiv.style.minHeight=(c.display.scroller.offsetHeight+30)+"px";for(var h=0;h=0;v--){var y=t[v].head;s.replaceRange("",p(y.line,y.ch-1),p(y.line,y.ch+1),"+delete")}}function b(s){var u=o(s);var w=u&&a(u,"explode");if(!w||s.getOption("disableInput")){return f.Pass}var t=s.listSelections();for(var v=0;v0;return{anchor:new p(s.anchor.line,s.anchor.ch+(i?-1:1)),head:new p(s.head.line,s.head.ch+(i?1:-1))}}function q(F,s){var A=o(F);if(!A||F.getOption("disableInput")){return f.Pass}var u=a(A,"pairs");var D=u.indexOf(s);if(D==-1){return f.Pass}var I=a(A,"triples");var E=u.charAt(D+1)==s;var t=F.listSelections();var v=D%2==0;var B;for(var x=0;x=0&&F.getRange(H,p(H.line,H.ch+3))==s+s+s){C="skipThree"}else{C="skip"}}}else{if(E&&H.ch>1&&I.indexOf(s)>=0&&F.getRange(p(H.line,H.ch-2),H)==s+s&&(H.ch<=2||F.getRange(p(H.line,H.ch-3),p(H.line,H.ch-2))!=s)){C="addFour"}else{if(E){if(!f.isWordChar(y)&&n(F,H,s)){C="both"}else{return f.Pass}}else{if(v&&(F.getLine(H.line).length==H.ch||c(y,u)||/\s/.test(y))){C="both"}else{return f.Pass}}}}}if(!B){B=C}else{if(B!=C){return f.Pass}}}var w=D%2?u.charAt(D-1):s;var G=D%2?s:u.charAt(D+1);F.operation(function(){if(B=="skip"){F.execCommand("goCharRight")}else{if(B=="skipThree"){for(var K=0;K<3;K++){F.execCommand("goCharRight")}}else{if(B=="surround"){var J=F.getSelections();for(var K=0;K-1&&t%2==1}function h(i,t){var s=i.getRange(p(t.line,t.ch-1),p(t.line,t.ch+1));return s.length==2?s:null}function n(i,x,v){var s=i.getLine(x.line);var u=i.getTokenAt(x);if(/\bstring2?\b/.test(u.type)){return false}var w=new f.StringStream(s.slice(0,x.ch)+v+s.slice(x.ch),4);w.pos=w.start=u.start;for(;;){var t=i.getMode().token(w,u.state);if(w.pos>=x.ch+1){return/\bstring2?\b/.test(t)}w.start=w.pos}}function k(i,t){var s=i.getTokenAt(p(t.line,t.ch+1));return/\bstring/.test(s.type)&&s.start==t.ch}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/edit/closetag.js b/route/static/codemirror/addon/edit/closetag.js deleted file mode 100755 index 3fda5544b..000000000 --- a/route/static/codemirror/addon/edit/closetag.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../fold/xml-fold"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../fold/xml-fold"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("autoCloseTags",false,function(i,l,j){if(j!=b.Init&&j){i.removeKeyMap("autoCloseTags")}if(!l){return}var k={name:"autoCloseTags"};if(typeof l!="object"||l.whenClosing){k["'/'"]=function(m){return e(m)}}if(typeof l!="object"||l.whenOpening){k["'>'"]=function(m){return a(m)}}i.addKeyMap(k)});var d=["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"];var c=["applet","blockquote","body","button","div","dl","fieldset","form","frameset","h1","h2","h3","h4","h5","h6","head","html","iframe","layer","legend","object","ol","p","select","table","ul"];function a(x){if(x.getOption("disableInput")){return b.Pass}var k=x.listSelections(),r=[];for(var s=0;sw.ch){q=q.slice(0,q.length-y.end+w.ch)}var u=q.toLowerCase();if(!q||y.type=="string"&&(y.end!=w.ch||!/[\"\']/.test(y.string.charAt(y.string.length-1))||y.string.length==1)||y.type=="tag"&&j.type=="closeTag"||y.string.indexOf("/")==(y.string.length-1)||n&&g(n,u)>-1||f(x,q,w,j,true)){return b.Pass}var p=v&&g(v,u)>-1;r[s]={indent:p,text:">"+(p?"\n\n":"")+"",newPos:p?b.Pos(w.line+1,0):b.Pos(w.line,w.ch+1)}}for(var s=k.length-1;s>=0;s--){var o=r[s];x.replaceRange(o.text,k[s].head,k[s].anchor,"+insert");var m=x.listSelections().slice(0);m[s]={head:o.newPos,anchor:o.newPos};x.setSelections(m);if(o.indent){x.indentLine(o.newPos.line,null,true);x.indentLine(o.newPos.line+1,null,true)}}}function h(r,n){var k=r.listSelections(),m=[];var q=n?"/":""){l+=">"}m[o]=l}r.replaceSelections(m);k=r.listSelections();for(var o=0;o[> ]*|- \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,c=/^(\s*)(>[> ]*|- \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,a=/[*+-]\s/;b.commands.newlineAndIndentContinueMarkdownList=function(q){if(q.getOption("disableInput")){return b.Pass}var g=q.listSelections(),k=[];for(var m=0;m")>=0?n[2].replace("x"," "):(parseInt(n[3],10)+1)+n[4];k[m]="\n"+j+e+f}}q.replaceSelections(k)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/edit/editAll.js b/route/static/codemirror/addon/edit/editAll.js deleted file mode 100755 index 0426f0e93..000000000 --- a/route/static/codemirror/addon/edit/editAll.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../fold/xml-fold"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../fold/xml-fold"],a)}else{a(CodeMirror)}}})(function(c){c.defineOption("matchTags",false,function(e,g,f){if(f&&f!=c.Init){e.off("cursorActivity",b);e.off("viewportChange",d);a(e)}if(g){e.state.matchBothTags=typeof g=="object"&&g.bothTags;e.on("cursorActivity",b);e.on("viewportChange",d);b(e)}});function a(e){if(e.state.tagHit){e.state.tagHit.clear()}if(e.state.tagOther){e.state.tagOther.clear()}e.state.tagHit=e.state.tagOther=null}function b(e){e.state.failedTagMatch=false;e.operation(function(){a(e);if(e.somethingSelected()){return}var j=e.getCursor(),g=e.getViewport();g.from=Math.min(g.from,j.line);g.to=Math.max(j.line+1,g.to);var h=c.findMatchingTag(e,j,g);if(!h){return}if(e.state.matchBothTags){var i=h.at=="open"?h.open:h.close;if(i){e.state.tagHit=e.markText(i.from,i.to,{className:"CodeMirror-matchingtag"})}}var f=h.at=="close"?h.open:h.close;if(f){e.state.tagOther=e.markText(f.from,f.to,{className:"CodeMirror-matchingtag"})}else{e.state.failedTagMatch=true}})}function d(e){if(e.state.failedTagMatch){b(e)}}c.commands.toMatchingTag=function(f){var g=c.findMatchingTag(f,f.getCursor());if(g){var e=g.at=="close"?g.open:g.close;if(e){f.extendSelection(e.to,e.from)}}}});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(e){var d=/MSIE \d/.test(navigator.userAgent)&&(document.documentMode==null||document.documentMode<8);var i=e.Pos;var a={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<"};function f(q,m,p,k){var s=q.getLineHandle(m.line),o=m.ch-1;var n=(o>=0&&a[s.text.charAt(o)])||a[s.text.charAt(++o)];if(!n){return null}var l=n.charAt(1)==">"?1:-1;if(p&&(l>0)!=(o==m.ch)){return null}var j=q.getTokenTypeAt(i(m.line,o+1));var r=g(q,i(m.line,o+(l>0?1:0)),l,j||null,k);if(r==null){return null}return{from:i(m.line,o),to:r&&r.pos,match:r&&r.ch==n.charAt(0),forward:l>0}}function g(w,r,n,k,m){var l=(m&&m.maxScanLineLength)||10000;var t=(m&&m.maxScanLines)||1000;var v=[];var x=m&&m.bracketRegex?m.bracketRegex:/[(){}[\]]/;var q=n>0?Math.min(r.line+t,w.lastLine()+1):Math.max(w.firstLine()-1,r.line-t);for(var o=r.line;o!=q;o+=n){var y=w.getLine(o);if(!y){continue}var u=n>0?0:y.length-1,p=n>0?y.length:-1;if(y.length>l){continue}if(o==r.line){u=r.ch-(n<0?1:0)}for(;u!=p;u+=n){var j=y.charAt(u);if(x.test(j)&&(k===undefined||w.getTokenTypeAt(i(o,u+1))==k)){var s=a[j];if((s.charAt(1)==">")==(n>0)){v.push(j)}else{if(!v.length){return{pos:i(o,u),ch:j}}else{v.pop()}}}}}return o-n==(n>0?w.lastLine():w.firstLine())?false:null}function b(s,n,m){var k=s.state.matchBrackets.maxHighlightLineLength||1000;var r=[],l=s.listSelections();for(var o=0;o -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    - \ No newline at end of file diff --git a/route/static/codemirror/addon/edit/matchbrackets.js b/route/static/codemirror/addon/edit/matchbrackets.js deleted file mode 100755 index 7e6a31eb7..000000000 --- a/route/static/codemirror/addon/edit/matchbrackets.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(e){var d=/MSIE \d/.test(navigator.userAgent)&&(document.documentMode==null||document.documentMode<8);var i=e.Pos;var a={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<"};function f(q,m,p,k){var s=q.getLineHandle(m.line),o=m.ch-1;var n=(o>=0&&a[s.text.charAt(o)])||a[s.text.charAt(++o)];if(!n){return null}var l=n.charAt(1)==">"?1:-1;if(p&&(l>0)!=(o==m.ch)){return null}var j=q.getTokenTypeAt(i(m.line,o+1));var r=g(q,i(m.line,o+(l>0?1:0)),l,j||null,k);if(r==null){return null}return{from:i(m.line,o),to:r&&r.pos,match:r&&r.ch==n.charAt(0),forward:l>0}}function g(w,r,n,k,m){var l=(m&&m.maxScanLineLength)||10000;var t=(m&&m.maxScanLines)||1000;var v=[];var x=m&&m.bracketRegex?m.bracketRegex:/[(){}[\]]/;var q=n>0?Math.min(r.line+t,w.lastLine()+1):Math.max(w.firstLine()-1,r.line-t);for(var o=r.line;o!=q;o+=n){var y=w.getLine(o);if(!y){continue}var u=n>0?0:y.length-1,p=n>0?y.length:-1;if(y.length>l){continue}if(o==r.line){u=r.ch-(n<0?1:0)}for(;u!=p;u+=n){var j=y.charAt(u);if(x.test(j)&&(k===undefined||w.getTokenTypeAt(i(o,u+1))==k)){var s=a[j];if((s.charAt(1)==">")==(n>0)){v.push(j)}else{if(!v.length){return{pos:i(o,u),ch:j}}else{v.pop()}}}}}return o-n==(n>0?w.lastLine():w.firstLine())?false:null}function b(s,n,m){var k=s.state.matchBrackets.maxHighlightLineLength||1000;var r=[],l=s.listSelections();for(var o=0;og.pos){g.pos=f;return null}g.pos=e;return"trailingspace"},name:"trailingspace"})}}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/anyword-hint.js b/route/static/codemirror/addon/hint/anyword-hint.js deleted file mode 100755 index 2b27ad172..000000000 --- a/route/static/codemirror/addon/hint/anyword-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(c){var b=/[\w$]+/,a=500;c.registerHelper("hint","anyword",function(o,u){var f=u&&u.word||b;var n=u&&u.range||a;var q=o.getCursor(),e=o.getLine(q.line);var l=q.ch,h=l;while(h&&f.test(e.charAt(h-1))){--h}var g=h!=l&&e.slice(h,l);var p=u&&u.list||[],d={};var s=new RegExp(f.source,"g");for(var k=-1;k<=1;k+=2){var t=q.line,j=Math.min(Math.max(t+k*n,o.firstLine()),o.lastLine())+k;for(;t!=j;t+=k){var r=o.getLine(t),i;while(i=s.exec(r)){if(t==q.line&&i[0]===g){continue}if((!g||i[0].lastIndexOf(g,0)==0)&&!Object.prototype.hasOwnProperty.call(d,i[0])){d[i[0]]=true;p.push(i[0])}}}}return{list:p,from:c.Pos(q.line,h),to:c.Pos(q.line,l)}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/css-hint.js b/route/static/codemirror/addon/hint/css-hint.js deleted file mode 100755 index 1086c2a59..000000000 --- a/route/static/codemirror/addon/hint/css-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../../mode/css/css"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../../mode/css/css"],a)}else{a(CodeMirror)}}})(function(a){var b={link:1,visited:1,active:1,hover:1,focus:1,"first-letter":1,"first-line":1,"first-child":1,before:1,after:1,lang:1};a.registerHelper("hint","css",function(g){var h=g.getCursor(),e=g.getTokenAt(h);var m=a.innerMode(g.getMode(),e.state);if(m.mode.name!="css"){return}if(e.type=="keyword"&&"!important".indexOf(e.string)==0){return{list:["!important"],from:a.Pos(h.line,e.start),to:a.Pos(h.line,e.end)}}var d=e.start,f=h.ch,c=e.string.slice(0,f-d);if(/[^\w$_-]/.test(c)){c="";d=f=h.ch}var i=a.resolveMode("text/css");var l=[];function k(o){for(var n in o){if(!c||n.lastIndexOf(c,0)==0){l.push(n)}}}var j=m.state.state;if(j=="pseudo"||e.type=="variable-3"){k(b)}else{if(j=="block"||j=="maybeprop"){k(i.propertyKeywords)}else{if(j=="prop"||j=="parens"||j=="at"||j=="params"){k(i.valueKeywords);k(i.colorKeywords)}else{if(j=="media"||j=="media_parens"){k(i.mediaTypes);k(i.mediaFeatures)}}}}if(l.length){return{list:l,from:a.Pos(h.line,d),to:a.Pos(h.line,f)}}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/html-hint.js b/route/static/codemirror/addon/hint/html-hint.js deleted file mode 100755 index 10e0ef6ea..000000000 --- a/route/static/codemirror/addon/hint/html-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("./xml-hint"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","./xml-hint"],a)}else{a(CodeMirror)}}})(function(f){var g="ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");var j=["_blank","_self","_top","_parent"];var a=["ascii","utf-8","utf-16","latin1","latin1"];var e=["get","post","put","delete"];var b=["application/x-www-form-urlencoded","multipart/form-data","text/plain"];var c=["all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses","resolution [>][<][=] [X]","device-aspect-ratio: X/Y","orientation:portrait","orientation:landscape","device-height: [X]","device-width: [X]"];var m={attrs:{}};var h={a:{attrs:{href:null,ping:null,type:null,media:c,target:j,hreflang:g}},abbr:m,acronym:m,address:m,applet:m,area:{attrs:{alt:null,coords:null,href:null,target:null,ping:null,media:c,hreflang:g,type:null,shape:["default","rect","circle","poly"]}},article:m,aside:m,audio:{attrs:{src:null,mediagroup:null,crossorigin:["anonymous","use-credentials"],preload:["none","metadata","auto"],autoplay:["","autoplay"],loop:["","loop"],controls:["","controls"]}},b:m,base:{attrs:{href:null,target:j}},basefont:m,bdi:m,bdo:m,big:m,blockquote:{attrs:{cite:null}},body:m,br:m,button:{attrs:{form:null,formaction:null,name:null,value:null,autofocus:["","autofocus"],disabled:["","autofocus"],formenctype:b,formmethod:e,formnovalidate:["","novalidate"],formtarget:j,type:["submit","reset","button"]}},canvas:{attrs:{width:null,height:null}},caption:m,center:m,cite:m,code:m,col:{attrs:{span:null}},colgroup:{attrs:{span:null}},command:{attrs:{type:["command","checkbox","radio"],label:null,icon:null,radiogroup:null,command:null,title:null,disabled:["","disabled"],checked:["","checked"]}},data:{attrs:{value:null}},datagrid:{attrs:{disabled:["","disabled"],multiple:["","multiple"]}},datalist:{attrs:{data:null}},dd:m,del:{attrs:{cite:null,datetime:null}},details:{attrs:{open:["","open"]}},dfn:m,dir:m,div:m,dl:m,dt:m,em:m,embed:{attrs:{src:null,type:null,width:null,height:null}},eventsource:{attrs:{src:null}},fieldset:{attrs:{disabled:["","disabled"],form:null,name:null}},figcaption:m,figure:m,font:m,footer:m,form:{attrs:{action:null,name:null,"accept-charset":a,autocomplete:["on","off"],enctype:b,method:e,novalidate:["","novalidate"],target:j}},frame:m,frameset:m,h1:m,h2:m,h3:m,h4:m,h5:m,h6:m,head:{attrs:{},children:["title","base","link","style","meta","script","noscript","command"]},header:m,hgroup:m,hr:m,html:{attrs:{manifest:null},children:["head","body"]},i:m,iframe:{attrs:{src:null,srcdoc:null,name:null,width:null,height:null,sandbox:["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"],seamless:["","seamless"]}},img:{attrs:{alt:null,src:null,ismap:null,usemap:null,width:null,height:null,crossorigin:["anonymous","use-credentials"]}},input:{attrs:{alt:null,dirname:null,form:null,formaction:null,height:null,list:null,max:null,maxlength:null,min:null,name:null,pattern:null,placeholder:null,size:null,src:null,step:null,value:null,width:null,accept:["audio/*","video/*","image/*"],autocomplete:["on","off"],autofocus:["","autofocus"],checked:["","checked"],disabled:["","disabled"],formenctype:b,formmethod:e,formnovalidate:["","novalidate"],formtarget:j,multiple:["","multiple"],readonly:["","readonly"],required:["","required"],type:["hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local","number","range","color","checkbox","radio","file","submit","image","reset","button"]}},ins:{attrs:{cite:null,datetime:null}},kbd:m,keygen:{attrs:{challenge:null,form:null,name:null,autofocus:["","autofocus"],disabled:["","disabled"],keytype:["RSA"]}},label:{attrs:{"for":null,form:null}},legend:m,li:{attrs:{value:null}},link:{attrs:{href:null,type:null,hreflang:g,media:c,sizes:["all","16x16","16x16 32x32","16x16 32x32 64x64"]}},map:{attrs:{name:null}},mark:m,menu:{attrs:{label:null,type:["list","context","toolbar"]}},meta:{attrs:{content:null,charset:a,name:["viewport","application-name","author","description","generator","keywords"],"http-equiv":["content-language","content-type","default-style","refresh"]}},meter:{attrs:{value:null,min:null,low:null,high:null,max:null,optimum:null}},nav:m,noframes:m,noscript:m,object:{attrs:{data:null,type:null,name:null,usemap:null,form:null,width:null,height:null,typemustmatch:["","typemustmatch"]}},ol:{attrs:{reversed:["","reversed"],start:null,type:["1","a","A","i","I"]}},optgroup:{attrs:{disabled:["","disabled"],label:null}},option:{attrs:{disabled:["","disabled"],label:null,selected:["","selected"],value:null}},output:{attrs:{"for":null,form:null,name:null}},p:m,param:{attrs:{name:null,value:null}},pre:m,progress:{attrs:{value:null,max:null}},q:{attrs:{cite:null}},rp:m,rt:m,ruby:m,s:m,samp:m,script:{attrs:{type:["text/javascript"],src:null,async:["","async"],defer:["","defer"],charset:a}},section:m,select:{attrs:{form:null,name:null,size:null,autofocus:["","autofocus"],disabled:["","disabled"],multiple:["","multiple"]}},small:m,source:{attrs:{src:null,type:null,media:null}},span:m,strike:m,strong:m,style:{attrs:{type:["text/css"],media:c,scoped:null}},sub:m,summary:m,sup:m,table:m,tbody:m,td:{attrs:{colspan:null,rowspan:null,headers:null}},textarea:{attrs:{dirname:null,form:null,maxlength:null,name:null,placeholder:null,rows:null,cols:null,autofocus:["","autofocus"],disabled:["","disabled"],readonly:["","readonly"],required:["","required"],wrap:["soft","hard"]}},tfoot:m,th:{attrs:{colspan:null,rowspan:null,headers:null,scope:["row","col","rowgroup","colgroup"]}},thead:m,time:{attrs:{datetime:null}},title:m,tr:m,track:{attrs:{src:null,label:null,"default":null,kind:["subtitles","captions","descriptions","chapters","metadata"],srclang:g}},tt:m,u:m,ul:m,"var":m,video:{attrs:{src:null,poster:null,width:null,height:null,crossorigin:["anonymous","use-credentials"],preload:["auto","metadata","none"],autoplay:["","autoplay"],mediagroup:["movie"],muted:["","muted"],controls:["","controls"]}},wbr:m};var d={accesskey:["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"],"class":null,contenteditable:["true","false"],contextmenu:null,dir:["ltr","rtl","auto"],draggable:["true","false","auto"],dropzone:["copy","move","link","string:","file:"],hidden:["hidden"],id:null,inert:["inert"],itemid:null,itemprop:null,itemref:null,itemscope:["itemscope"],itemtype:null,lang:["en","es"],spellcheck:["true","false"],style:null,tabindex:["1","2","3","4","5","6","7","8","9"],title:null,translate:["yes","no"],onclick:null,rel:["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]};function i(o){for(var n in d){if(d.hasOwnProperty(n)){o.attrs[n]=d[n]}}}i(m);for(var l in h){if(h.hasOwnProperty(l)&&h[l]!=m){i(h[l])}}f.htmlSchema=h;function k(n,o){var q={schemaInfo:h};if(o){for(var p in o){q[p]=o[p]}}return f.hint.xml(n,q)}f.registerHelper("hint","html",k)}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/index.html b/route/static/codemirror/addon/hint/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/hint/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/javascript-hint.js b/route/static/codemirror/addon/hint/javascript-hint.js deleted file mode 100755 index f9cfa09eb..000000000 --- a/route/static/codemirror/addon/hint/javascript-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(d){var l=d.Pos;function g(p,r){for(var q=0,s=p.length;qv.ch){s.end=v.ch;s.string=s.string.slice(0,v.ch-s.start)}}var p=s;while(p.type=="property"){p=w(u,l(v.line,p.start));if(p.string!="."){return}p=w(u,l(v.line,p.start));if(!r){var r=[]}r.push(p)}return{list:i(s,r,t,q),from:l(v.line,s.start),to:l(v.line,s.end)}}function a(q,p){return h(q,c,function(r,s){return r.getTokenAt(s)},p)}d.registerHelper("hint","javascript",a);function b(q,r){var p=q.getTokenAt(r);if(r.ch==p.start+1&&p.string.charAt(0)=="."){p.end=p.start;p.string=".";p.type="property"}else{if(/^\.[\w$_]*$/.test(p.string)){p.type="property";p.start++;p.string=p.string.replace(/\./,"")}}return p}function j(q,p){return h(q,n,b,p)}d.registerHelper("hint","coffeescript",j);var m=("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight toUpperCase toLowerCase split concat match replace search").split(" ");var o=("length concat join splice push pop shift unshift slice reverse sort indexOf lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");var f="prototype apply call bind".split(" ");var c=("break case catch continue debugger default delete do else false finally for function if in instanceof new null return switch throw true try typeof var void while with").split(" ");var n=("and break catch class continue delete do else extends false finally for if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");function k(q,s){if(!Object.getOwnPropertyNames||!Object.getPrototypeOf){for(var p in q){s(p)}}else{for(var r=q;r;r=Object.getPrototypeOf(r)){Object.getOwnPropertyNames(r).forEach(s)}}}function i(u,t,y,B){var A=[],r=u.string,s=B&&B.globalScope||window;function w(v){if(v.lastIndexOf(r,0)==0&&!e(A,v)){A.push(v)}}function q(v){if(typeof v=="string"){g(m,w)}else{if(v instanceof Array){g(o,w)}else{if(v instanceof Function){g(f,w)}}}k(v,w)}if(t&&t.length){var x=t.pop(),p;if(x.type&&x.type.indexOf("variable")===0){if(B&&B.additionalContext){p=B.additionalContext[x.string]}if(!B||B.useGlobalScope!==false){p=p||s[x.string]}}else{if(x.type=="string"){p=""}else{if(x.type=="atom"){p=1}else{if(x.type=="function"){if(s.jQuery!=null&&(x.string=="$"||x.string=="jQuery")&&(typeof s.jQuery=="function")){p=s.jQuery()}else{if(s._!=null&&(x.string=="_")&&(typeof s._=="function")){p=s._()}}}}}}while(p!=null&&t.length){p=p[t.pop().string]}if(p!=null){q(p)}}else{for(var z=u.state.localVars;z;z=z.next){w(z.name)}for(var z=u.state.globalVars;z;z=z.next){w(z.name)}if(!B||B.useGlobalScope!==false){q(s)}g(y,w)}return A}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/show-hint.css b/route/static/codemirror/addon/hint/show-hint.css deleted file mode 100755 index 4027ed60e..000000000 --- a/route/static/codemirror/addon/hint/show-hint.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:white;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:black;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:white} \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/show-hint.js b/route/static/codemirror/addon/hint/show-hint.js deleted file mode 100755 index 24ca4b4e7..000000000 --- a/route/static/codemirror/addon/hint/show-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(e){var i="CodeMirror-hint";var h="CodeMirror-hint-active";e.showHint=function(q,r,s){if(!r){return q.showHint(s)}if(s&&s.async){r.async=true}var t={hint:r};if(s){for(var u in s){t[u]=s[u]}}return q.showHint(t)};e.defineExtension("showHint",function(r){r=o(this,this.getCursor("start"),r);var t=this.listSelections();if(t.length>1){return}if(this.somethingSelected()){if(!r.hint.supportsSelection){return}for(var s=0;s0&&r.to.ch-r.from.ch!=q.to.ch-q.from.ch}function o(q,v,s){var t=q.options.hintOptions;var r={};for(var u in c){r[u]=c[u]}if(t){for(var u in t){if(t[u]!==undefined){r[u]=t[u]}}}if(s){for(var u in s){if(s[u]!==undefined){r[u]=s[u]}}}if(r.hint.resolve){r.hint=r.hint.resolve(q,v)}return r}function p(q){if(typeof q=="string"){return q}else{return q.text}}function d(u,x){var r={Up:function(){x.moveFocus(-1)},Down:function(){x.moveFocus(1)},PageUp:function(){x.moveFocus(-x.menuSize()+1,true)},PageDown:function(){x.moveFocus(x.menuSize()-1,true)},Home:function(){x.setFocus(0)},End:function(){x.setFocus(x.length-1)},Enter:x.pick,Tab:x.pick,Esc:x.close};var w=u.options.customKeys;var t=w?{}:r;function s(y,A){var z;if(typeof A!="string"){z=function(B){return A(B,x)}}else{if(r.hasOwnProperty(A)){z=r[A]}else{z=A}}t[y]=z}if(w){for(var v in w){if(w.hasOwnProperty(v)){s(v,w[v])}}}var q=u.options.extraKeys;if(q){for(var v in q){if(q.hasOwnProperty(v)){s(v,q[v])}}}return t}function m(r,q){while(q&&q!=r){if(q.nodeName.toUpperCase()==="LI"&&q.parentNode==r){return q}q=q.parentNode}}function n(E,P){this.completion=E;this.data=P;this.picked=false;var v=this,A=E.cm;var M=this.hints=document.createElement("ul");M.className="CodeMirror-hints";this.selectedHint=P.selectedHint||0;var z=P.list;for(var O=0;OM.clientHeight+1;var H=A.getScrollInfo();if(D>0){var J=C.bottom-C.top,q=y.top-(y.bottom-C.top);if(q-J>0){M.style.top=(I=y.top-J)+"px";G=false}else{if(J>N){M.style.height=(N-5)+"px";M.style.top=(I=y.bottom-C.top)+"px";var x=A.getCursor();if(P.from.ch!=x.ch){y=A.cursorCoords(x);M.style.left=(w=y.left)+"px";C=M.getBoundingClientRect()}}}}var F=C.right-B;if(F>0){if(C.right-C.left>B){M.style.width=(B-5)+"px";F-=(C.right-C.left)-B}M.style.left=(w=y.left-F)+"px"}if(r){for(var L=M.firstChild;L;L=L.nextSibling){L.style.paddingRight=A.display.nativeBarWidth+"px"}}A.addKeyMap(this.keyMap=d(E,{moveFocus:function(R,Q){v.changeActive(v.selectedHint+R,Q)},setFocus:function(Q){v.changeActive(Q)},menuSize:function(){return v.screenAmount()},length:z.length,close:function(){E.close()},pick:function(){v.pick()},data:P}));if(E.options.closeOnUnfocus){var K;A.on("blur",this.onBlur=function(){K=setTimeout(function(){E.close()},100)});A.on("focus",this.onFocus=function(){clearTimeout(K)})}A.on("scroll",this.onScroll=function(){var T=A.getScrollInfo(),S=A.getWrapperElement().getBoundingClientRect();var R=I+H.top-T.top;var Q=R-(window.pageYOffset||(document.documentElement||document.body).scrollTop);if(!G){Q+=M.offsetHeight}if(Q<=S.top||Q>=S.bottom){return E.close()}M.style.top=R+"px";M.style.left=(w+H.left-T.left)+"px"});e.on(M,"dblclick",function(R){var Q=m(M,R.target||R.srcElement);if(Q&&Q.hintId!=null){v.changeActive(Q.hintId);v.pick()}});e.on(M,"click",function(R){var Q=m(M,R.target||R.srcElement);if(Q&&Q.hintId!=null){v.changeActive(Q.hintId);if(E.options.completeOnSingleClick){v.pick()}}});e.on(M,"mousedown",function(){setTimeout(function(){A.focus()},20)});e.signal(P,"select",z[0],M.firstChild);return true}n.prototype={close:function(){if(this.completion.widget!=this){return}this.completion.widget=null;this.hints.parentNode.removeChild(this.hints);this.completion.cm.removeKeyMap(this.keyMap);var q=this.completion.cm;if(this.completion.options.closeOnUnfocus){q.off("blur",this.onBlur);q.off("focus",this.onFocus)}q.off("scroll",this.onScroll)},disable:function(){this.completion.cm.removeKeyMap(this.keyMap);var q=this;this.keyMap={Enter:function(){q.picked=true}};this.completion.cm.addKeyMap(this.keyMap)},pick:function(){this.completion.pick(this.data,this.selectedHint)},changeActive:function(q,s){if(q>=this.data.list.length){q=s?this.data.list.length-1:0}else{if(q<0){q=s?0:this.data.list.length-1}}if(this.selectedHint==q){return}var r=this.hints.childNodes[this.selectedHint];r.className=r.className.replace(" "+h,"");r=this.hints.childNodes[this.selectedHint=q];r.className+=" "+h;if(r.offsetTopthis.hints.scrollTop+this.hints.clientHeight){this.hints.scrollTop=r.offsetTop+r.offsetHeight-this.hints.clientHeight+3}}e.signal(this.data,"select",this.data.list[this.selectedHint],r)},screenAmount:function(){return Math.floor(this.hints.clientHeight/this.hints.firstChild.offsetHeight)||1}};function k(r,t){if(!r.somethingSelected()){return t}var q=[];for(var s=0;s0){z(B)}else{x(A+1)}})}x(0)};r.async=true;r.supportsSelection=true;return r}else{if(t=q.getHelper(q.getCursor(),"hintWords")){return function(v){return e.hint.fromList(v,{words:t})}}else{if(e.hint.anyword){return function(v,w){return e.hint.anyword(v,w)}}else{return function(){}}}}}e.registerHelper("hint","auto",{resolve:a});e.registerHelper("hint","fromList",function(w,z){var x=w.getCursor(),r=w.getTokenAt(x);var u=e.Pos(x.line,r.end);if(r.string&&/\w/.test(r.string[r.string.length-1])){var s=r.string,v=e.Pos(x.line,r.start)}else{var s="",v=u}var y=[];for(var t=0;t,]/,closeOnUnfocus:true,completeOnSingleClick:true,container:null,customKeys:null,extraKeys:null};e.defineOption("hintOptions",null)}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/sql-hint.js b/route/static/codemirror/addon/hint/sql-hint.js deleted file mode 100755 index cb9fd7e57..000000000 --- a/route/static/codemirror/addon/hint/sql-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../../mode/sql/sql"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../../mode/sql/sql"],a)}else{a(CodeMirror)}}})(function(p){var h;var s;var i;var j={QUERY_DIV:";",ALIAS_KEYWORD:"AS"};var n=p.Pos,o=p.cmpPos;function l(v){return Object.prototype.toString.call(v)=="[object Array]"}function d(v){var w=v.doc.modeOption;if(w==="sql"){w="text/x-sql"}return p.resolveMode(w).keywords}function a(v){return typeof v=="string"?v:v.text}function b(v,w){if(l(w)){w={columns:w}}if(!w.text){w.text=v}return w}function q(w){var v={};if(l(w)){for(var y=w.length-1;y>=0;y--){var z=w[y];v[a(z).toUpperCase()]=b(a(z),z)}}else{if(w){for(var x in w){v[x.toUpperCase()]=b(x,w[x])}}}return v}function c(v){return h[v.toUpperCase()]}function g(w){var v={};for(var x in w){if(w.hasOwnProperty(x)){v[x]=w[x]}}return v}function e(w,y){var v=w.length;var x=a(y).substr(0,v);return w.toUpperCase()===x.toUpperCase()}function f(v,y,z,x){if(l(z)){for(var w=0;w0)&&o(D,z[x])<=0){H={start:G,end:z[x]};break}G=z[x]}var E=I.getRange(H.start,H.end,false);for(var x=0;xB.ch){x.end=B.ch;x.string=x.string.slice(0,B.ch-x.start)}if(x.string.match(/^[.`\w@]\w*$/)){C=x.string;v=x.start;y=x.end}else{v=y=B.ch;C=""}if(C.charAt(0)=="."||C.charAt(0)=="`"){v=t(B,x,E,z)}else{f(E,C,h,function(F){return F});f(E,C,s,function(F){return F});if(!A){f(E,C,i,function(F){return F.toUpperCase()})}}return{list:E,from:n(B.line,v),to:n(B.line,y)}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/xml-hint.js b/route/static/codemirror/addon/hint/xml-hint.js deleted file mode 100755 index f2321c98c..000000000 --- a/route/static/codemirror/addon/hint/xml-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){var c=b.Pos;function a(o,h){var t=h&&h.schemaInfo;var E=(h&&h.quoteChar)||'"';if(!t){return}var g=o.getCursor(),m=o.getTokenAt(g);if(m.end>g.ch){m.end=g.ch;m.string=m.string.slice(0,g.ch-m.start)}var F=b.innerMode(o.getMode(),m.state);if(F.mode.name!="xml"){return}var p=[],z=false,A;var H=/\btag\b/.test(m.type)&&!/>$/.test(m.string);var e=H&&/^\w/.test(m.string),C;if(e){var s=o.getLine(g.line).slice(Math.max(0,m.start-2),m.start);var l=/<\/$/.test(s)?"close":/<$/.test(s)?"open":null;if(l){C=m.start-(l=="close"?2:1)}}else{if(H&&m.string=="<"){l="open"}else{if(H&&m.string=="")}}else{var u=t[F.state.tagName],x=u&&u.attrs;var j=t["!attrs"];if(!x&&!j){return}if(!x){x=j}else{if(j){var r={};for(var d in j){if(j.hasOwnProperty(d)){r[d]=j[d]}}for(var d in x){if(x.hasOwnProperty(d)){r[d]=x[d]}}x=r}}if(m.type=="string"||m.string=="="){var s=o.getRange(c(g.line,Math.max(0,g.ch-60)),c(g.line,m.type=="string"?m.start:m.end));var v=s.match(/([^\s\u00a0=<>\"\']+)=$/),q;if(!v||!x.hasOwnProperty(v[1])||!(q=x[v[1]])){return}if(typeof q=="function"){q=q.call(this,o)}if(m.type=="string"){A=m.string;var w=0;if(/['"]/.test(m.string.charAt(0))){E=m.string.charAt(0);A=m.string.slice(1);w++}var D=m.string.length;if(/['"]/.test(m.string.charAt(D-1))){E=m.string.charAt(D-1);A=m.string.substr(w,D-2)}z=true}for(var B=0;B -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    - \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/annotatescrollbar.js b/route/static/codemirror/addon/scroll/annotatescrollbar.js deleted file mode 100755 index 88f7f83b4..000000000 --- a/route/static/codemirror/addon/scroll/annotatescrollbar.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){a.defineExtension("annotateScrollbar",function(c){if(typeof c=="string"){c={className:c}}return new b(this,c)});a.defineOption("scrollButtonHeight",0);function b(c,e){this.cm=c;this.options=e;this.buttonHeight=e.scrollButtonHeight||c.getOption("scrollButtonHeight");this.annotations=[];this.doRedraw=this.doUpdate=null;this.div=c.getWrapperElement().appendChild(document.createElement("div"));this.div.style.cssText="position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";this.computeScale();function f(g){clearTimeout(d.doRedraw);d.doRedraw=setTimeout(function(){d.redraw()},g)}var d=this;c.on("refresh",this.resizeHandler=function(){clearTimeout(d.doUpdate);d.doUpdate=setTimeout(function(){if(d.computeScale()){f(20)}},100)});c.on("markerAdded",this.resizeHandler);c.on("markerCleared",this.resizeHandler);if(e.listenForChanges!==false){c.on("change",this.changeHandler=function(){f(250)})}}b.prototype.computeScale=function(){var c=this.cm;var d=(c.getWrapperElement().clientHeight-c.display.barHeight-this.buttonHeight*2)/c.getScrollerElement().scrollHeight;if(d!=this.hScale){this.hScale=d;return true}};b.prototype.update=function(c){this.annotations=c;this.redraw()};b.prototype.redraw=function(o){if(o!==false){this.computeScale()}var p=this.cm,t=this.hScale;var s=document.createDocumentFragment(),g=this.annotations;var k=p.getOption("lineWrapping");var j=k&&p.defaultTextHeight()*1.5;var e=null,n=null;function q(v,u){if(e!=v.line){e=v.line;n=p.getLineHandle(e)}if(k&&n.height>j){return p.charCoords(v,"local")[u?"top":"bottom"]}var i=p.heightAtLine(n,"local");return i+(u?0:n.height)}if(p.display.barWidth){for(var h=0,l;hc+0.9){break}d=g[++h];c=q(d.to,false)*t}if(c==m){continue}var r=Math.max(c-m,3);var f=s.appendChild(document.createElement("div"));f.style.cssText="position: absolute; right: 0px; width: "+Math.max(p.display.barWidth-1,2)+"px; top: "+(m+this.buttonHeight)+"px; height: "+r+"px";f.className=this.options.className;if(d.id){f.setAttribute("annotation-id",d.id)}}}this.div.textContent="";this.div.appendChild(s)};b.prototype.clear=function(){this.cm.off("refresh",this.resizeHandler);this.cm.off("markerAdded",this.resizeHandler);this.cm.off("markerCleared",this.resizeHandler);if(this.changeHandler){this.cm.off("change",this.changeHandler)}this.div.parentNode.removeChild(this.div)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/index.html b/route/static/codemirror/addon/scroll/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/scroll/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/scrollpastend.js b/route/static/codemirror/addon/scroll/scrollpastend.js deleted file mode 100755 index 9e2877c83..000000000 --- a/route/static/codemirror/addon/scroll/scrollpastend.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("scrollPastEnd",false,function(d,f,e){if(e&&e!=b.Init){d.off("change",a);d.off("refresh",c);d.display.lineSpace.parentNode.style.paddingBottom="";d.state.scrollPastEndPadding=null}if(f){d.on("change",a);d.on("refresh",c);c(d)}});function a(d,e){if(b.changeEnd(e).line==d.lastLine()){c(d)}}function c(d){var g="";if(d.lineCount()>1){var e=d.display.scroller.clientHeight-30,f=d.getLineHandle(d.lastLine()).height;g=(e-f)+"px"}if(d.state.scrollPastEndPadding!=g){d.state.scrollPastEndPadding=g;d.display.lineSpace.parentNode.style.paddingBottom=g;d.off("refresh",c);d.setSize();d.on("refresh",c)}}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/simplescrollbars.css b/route/static/codemirror/addon/scroll/simplescrollbars.css deleted file mode 100755 index b4b5245bc..000000000 --- a/route/static/codemirror/addon/scroll/simplescrollbars.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-simplescroll-horizontal div,.CodeMirror-simplescroll-vertical div{position:absolute;background:#ccc;-moz-box-sizing:border-box;box-sizing:border-box;border:1px solid #bbb;border-radius:2px}.CodeMirror-simplescroll-horizontal,.CodeMirror-simplescroll-vertical{position:absolute;z-index:6;background:#eee}.CodeMirror-simplescroll-horizontal{bottom:0;left:0;height:8px}.CodeMirror-simplescroll-horizontal div{bottom:0;height:100%}.CodeMirror-simplescroll-vertical{right:0;top:0;width:8px}.CodeMirror-simplescroll-vertical div{right:0;width:100%}.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler,.CodeMirror-overlayscroll .CodeMirror-gutter-filler{display:none}.CodeMirror-overlayscroll-horizontal div,.CodeMirror-overlayscroll-vertical div{position:absolute;background:#bcd;border-radius:3px}.CodeMirror-overlayscroll-horizontal,.CodeMirror-overlayscroll-vertical{position:absolute;z-index:6}.CodeMirror-overlayscroll-horizontal{bottom:0;left:0;height:6px}.CodeMirror-overlayscroll-horizontal div{bottom:0;height:100%}.CodeMirror-overlayscroll-vertical{right:0;top:0;width:6px}.CodeMirror-overlayscroll-vertical div{right:0;width:100%} \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/simplescrollbars.js b/route/static/codemirror/addon/scroll/simplescrollbars.js deleted file mode 100755 index 303b69a98..000000000 --- a/route/static/codemirror/addon/scroll/simplescrollbars.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){function a(f,h,e){this.orientation=h;this.scroll=e;this.screen=this.total=this.size=1;this.pos=0;this.node=document.createElement("div");this.node.className=f+"-"+h;this.inner=this.node.appendChild(document.createElement("div"));var g=this;b.on(this.inner,"mousedown",function(n){if(n.which!=1){return}b.e_preventDefault(n);var l=g.orientation=="horizontal"?"pageX":"pageY";var o=n[l],m=g.pos;function k(){b.off(document,"mousemove",j);b.off(document,"mouseup",k)}function j(p){if(p.which!=1){return k()}g.moveTo(m+(p[l]-o)*(g.total/g.size))}b.on(document,"mousemove",j);b.on(document,"mouseup",k)});b.on(this.node,"click",function(l){b.e_preventDefault(l);var j=g.inner.getBoundingClientRect(),k;if(g.orientation=="horizontal"){k=l.clientXj.right?1:0}else{k=l.clientYj.bottom?1:0}g.moveTo(g.pos+k*g.screen)});function i(l){var k=b.wheelEventPixels(l)[g.orientation=="horizontal"?"x":"y"];var j=g.pos;g.moveTo(g.pos+k);if(g.pos!=j){b.e_preventDefault(l)}}b.on(this.node,"mousewheel",i);b.on(this.node,"DOMMouseScroll",i)}a.prototype.setPos=function(f,e){if(f<0){f=0}if(f>this.total-this.screen){f=this.total-this.screen}if(!e&&f==this.pos){return false}this.pos=f;this.inner.style[this.orientation=="horizontal"?"left":"top"]=(f*(this.size/this.total))+"px";return true};a.prototype.moveTo=function(e){if(this.setPos(e)){this.scroll(e,this.orientation)}};var d=10;a.prototype.update=function(g,h,i){var f=this.screen!=h||this.total!=g||this.size!=i;if(f){this.screen=h;this.total=g;this.size=i}var e=this.screen*(this.size/this.total);if(eh.clientWidth+1;var e=h.scrollHeight>h.clientHeight+1;this.vert.node.style.display=e?"block":"none";this.horiz.node.style.display=i?"block":"none";if(e){this.vert.update(h.scrollHeight,h.clientHeight,h.viewHeight-(i?f:0));this.vert.node.style.bottom=i?f+"px":"0"}if(i){this.horiz.update(h.scrollWidth,h.clientWidth,h.viewWidth-(e?f:0)-h.barLeft);this.horiz.node.style.right=e?f+"px":"0";this.horiz.node.style.left=h.barLeft+"px"}return{right:e?f:0,bottom:i?f:0}};c.prototype.setScrollTop=function(e){this.vert.setPos(e)};c.prototype.setScrollLeft=function(e){this.horiz.setPos(e)};c.prototype.clear=function(){var e=this.horiz.node.parentNode;e.removeChild(this.horiz.node);e.removeChild(this.vert.node)};b.scrollbarModel.simple=function(f,e){return new c("CodeMirror-simplescroll",f,e)};b.scrollbarModel.overlay=function(f,e){return new c("CodeMirror-overlayscroll",f,e)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/search/index.html b/route/static/codemirror/addon/search/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/search/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/codemirror/addon/search/jump-to-line.js b/route/static/codemirror/addon/search/jump-to-line.js deleted file mode 100755 index 0d0a498dd..000000000 --- a/route/static/codemirror/addon/search/jump-to-line.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../dialog/dialog"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../dialog/dialog"],a)}else{a(CodeMirror)}}})(function(a){function b(e,i,g,j,h){if(e.openDialog){e.openDialog(i,h,{value:j,selectValueOnOpen:true})}else{h(prompt(g,j))}}var d='Jump to line: (Use line:column or scroll% syntax)';function c(e,g){var f=Number(g);if(/^[-+]/.test(g)){return e.getCursor().line+f}else{return f-1}}a.commands.jumpToLine=function(e){var f=e.getCursor();b(e,d,"Jump to line:",(f.line+1)+":"+f.ch,function(i){if(!i){return}var h;if(h=/^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(i)){e.setCursor(c(e,h[1]),Number(h[2]))}else{if(h=/^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(i)){var g=Math.round(e.lineCount()*Number(h[1])/100);if(/^[-+]/.test(h[1])){g=f.line+g+1}e.setCursor(g-1,f.ch)}else{if(h=/^\s*\:?\s*([\+\-]?\d+)\s*/.exec(i)){e.setCursor(c(e,h[1]),f.ch)}}}})};a.keyMap["default"]["Alt-G"]="jumpToLine"}); \ No newline at end of file diff --git a/route/static/codemirror/addon/search/match-highlighter.js b/route/static/codemirror/addon/search/match-highlighter.js deleted file mode 100755 index 5bc46f2ed..000000000 --- a/route/static/codemirror/addon/search/match-highlighter.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("./matchesonscrollbar"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","./matchesonscrollbar"],a)}else{a(CodeMirror)}}})(function(d){var c={style:"matchhighlight",minChars:2,delay:100,wordsOnly:false,annotateScrollbar:false,showToken:false,trim:true};function j(n){this.options={};for(var m in c){this.options[m]=(n&&n.hasOwnProperty(m)?n:c)[m]}this.overlay=this.timeout=null;this.matchesonscroll=null;this.active=false}d.defineOption("highlightSelectionMatches",false,function(m,p,n){if(n&&n!=d.Init){l(m);clearTimeout(m.state.matchHighlighter.timeout);m.state.matchHighlighter=null;m.off("cursorActivity",i);m.off("focus",h)}if(p){var o=m.state.matchHighlighter=new j(p);if(m.hasFocus()){o.active=true;f(m)}else{m.on("focus",h)}m.on("cursorActivity",i)}});function i(m){var n=m.state.matchHighlighter;if(n.active||m.hasFocus()){a(m,n)}}function h(m){var n=m.state.matchHighlighter;if(!n.active){n.active=true;a(m,n)}}function a(m,n){clearTimeout(n.timeout);n.timeout=setTimeout(function(){f(m)},n.options.delay)}function b(n,r,p,o){var q=n.state.matchHighlighter;n.addOverlay(q.overlay=e(r,p,o));if(q.options.annotateScrollbar&&n.showMatchesOnScrollbar){var m=p?new RegExp("\\b"+r+"\\b"):r;q.matchesonscroll=n.showMatchesOnScrollbar(m,false,{className:"CodeMirror-selection-highlight-scrollbar"})}}function l(m){var n=m.state.matchHighlighter;if(n.overlay){m.removeOverlay(n.overlay);n.overlay=null;if(n.matchesonscroll){n.matchesonscroll.clear();n.matchesonscroll=null}}}function f(m){m.operation(function(){var n=m.state.matchHighlighter;l(m);if(!m.somethingSelected()&&n.options.showToken){var u=n.options.showToken===true?/[\w$]/:n.options.showToken;var t=m.getCursor(),v=m.getLine(t.line),o=t.ch,p=o;while(o&&u.test(v.charAt(o-1))){--o}while(p=n.options.minChars){b(m,q,false,n.options.style)}})}function g(m,r,q){var o=m.getRange(r,q);if(o.match(/^\w+$/)!==null){if(r.ch>0){var p={line:r.line,ch:r.ch-1};var n=m.getRange(p,r);if(n.match(/\W/)===null){return false}}if(q.ch=this.gap.to){break}if(f.to.line>=this.gap.from){this.matches.splice(g--,1)}}var h=this.cm.getSearchCursor(this.query,b.Pos(this.gap.from,0),this.caseFold);var e=this.options&&this.options.maxMatches||d;while(h.findNext()){var f={from:h.from(),to:h.to()};if(f.from.line>=this.gap.to){break}this.matches.splice(g++,0,f);if(this.matches.length>e){break}}this.gap=null};function a(e,g,f){if(e<=g){return e}return Math.max(g,e+f)}c.prototype.onChange=function(k){var l=k.from.line;var e=b.changeEnd(k).line;var f=e-k.to.line;if(this.gap){this.gap.from=Math.min(a(this.gap.from,l,f),k.from.line);this.gap.to=Math.max(a(this.gap.to,l,f),k.from.line)}else{this.gap={from:k.from.line,to:e+1}}if(f){for(var h=0;hv.cursorCoords(H,"window").top){(w=G).style.opacity=0.4}})};d(v,u,B,A,function(E,G){var D=n.keyName(E);var F=n.keyMap[v.getOption("keyMap")][D];if(!F){F=v.getOption("extraKeys")[D]}if(F=="findNext"||F=="findPrev"||F=="findPersistentNext"||F=="findPersistentPrev"){n.e_stop(E);t(v,s(v),G);v.execCommand(F)}else{if(F=="find"||F=="findPersistent"){n.e_stop(E);A(G,E)}}});if(z&&B){t(v,C,B);f(v,y)}}else{p(v,u,"Search for:",B,function(D){if(D&&!C.query){v.operation(function(){t(v,C,D);C.posFrom=C.posTo=v.getCursor();f(v,y)})}})}}function f(v,w,x){v.operation(function(){var y=s(v);var z=b(v,y.query,w?y.posFrom:y.posTo);if(!z.find(w)){z=b(v,y.query,w?n.Pos(v.lastLine()):n.Pos(v.firstLine(),0));if(!z.find(w)){return}}v.setSelection(z.from(),z.to());v.scrollIntoView({from:z.from(),to:z.to()},20);y.posFrom=z.from();y.posTo=z.to();if(x){x(z.from(),z.to())}})}function q(v){v.operation(function(){var w=s(v);w.lastQuery=w.query;if(!w.query){return}w.query=w.queryText=null;v.removeOverlay(w.overlay);if(w.annotate){w.annotate.clear();w.annotate=null}})}var a=' (Use /re/ syntax for regexp search)';var g='到: ';var o="替换? ";function c(v,w,x){v.operation(function(){for(var z=b(v,w);z.findNext();){if(typeof w!="string"){var y=v.getRange(z.from(),z.to()).match(w);z.replace(x.replace(/\$(\d)/g,function(A,B){return y[B]}))}else{z.replace(x)}}})}function r(v,x){if(v.getOption("readOnly")){return}var y=v.getSelection()||s(v).lastQuery;var w=x?"全部替换:":"替换:";p(v,w+a,w,y,function(z){if(!z){return}z=h(z);p(v,g,"更换:","",function(D){D=i(D);if(x){c(v,z,D)}else{q(v);var C=b(v,z,v.getCursor("from"));var B=function(){var F=C.from(),E;if(!(E=C.findNext())){C=b(v,z);if(!(E=C.findNext())||(F&&C.from().line==F.line&&C.from().ch==F.ch)){return}}v.setSelection(C.from(),C.to());v.scrollIntoView({from:C.from(),to:C.to()});l(v,o,"替换?",[function(){A(E)},B,function(){c(v,z,D)}])};var A=function(E){C.replace(typeof z=="string"?D:D.replace(/\$(\d)/g,function(F,G){return E[G]}));B()};B()}})})}n.commands.find=function(v){q(v);m(v)};n.commands.findPersistent=function(v){q(v);m(v,false,true)};n.commands.findPersistentNext=function(v){m(v,false,true,true)};n.commands.findPersistentPrev=function(v){m(v,true,true,true)};n.commands.findNext=m;n.commands.findPrev=function(v){m(v,true)};n.commands.clearSearch=q;n.commands.replace=r;n.commands.replaceAll=function(v){r(v,true)}});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){var b=a.Pos;function c(k,i,l,g){this.atOccurrence=false;this.doc=k;if(g==null&&typeof i=="string"){g=false}l=l?k.clipPos(l):b(0,0);this.pos={from:l,to:l};if(typeof i!="string"){if(!i.global){i=new RegExp(i.source,i.ignoreCase?"ig":"g")}this.matches=function(p,t){if(p){i.lastIndex=0;var m=k.getLine(t.line).slice(0,t.ch),r=0,o,s;for(;;){i.lastIndex=r;var q=i.exec(m);if(!q){break}o=q;s=o.index;r=o.index+(o[0].length||1);if(r==m.length){break}}var n=(o&&o[0].length)||0;if(!n){if(s==0&&m.length==0){o=undefined}else{if(s!=k.getLine(t.line).length){n++}}}}else{i.lastIndex=t.ch;var m=k.getLine(t.line),o=i.exec(m);var n=(o&&o[0].length)||0;var s=o&&o.index;if(s+n!=m.length&&!n){n=1}}if(o&&n){return{from:b(t.line,s),to:b(t.line,s+n),match:o}}}}else{var e=i;if(g){i=i.toLowerCase()}var f=g?function(m){return m.toLowerCase()}:function(m){return m};var j=i.split("\n");if(j.length==1){if(!i.length){this.matches=function(){}}else{this.matches=function(o,q){if(o){var p=k.getLine(q.line).slice(0,q.ch),m=f(p);var n=m.lastIndexOf(i);if(n>-1){n=d(p,m,n);return{from:b(q.line,n),to:b(q.line,n+e.length)}}}else{var p=k.getLine(q.line).slice(q.ch),m=f(p);var n=m.indexOf(i);if(n>-1){n=d(p,m,n)+q.ch;return{from:b(q.line,n),to:b(q.line,n+e.length)}}}}}}else{var h=e.split("\n");this.matches=function(n,p){var t=j.length-1;if(n){if(p.line-(j.length-1)=1;--m,--o){if(j[m]!=f(k.getLine(o))){return}}var u=k.getLine(o),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}return{from:b(o,q),to:s}}else{if(p.line+(j.length-1)>k.lastLine()){return}var u=k.getLine(p.line),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}var r=b(p.line,q);for(var o=p.line+1,m=1;mh){--f}else{return f}}}}a.defineExtension("getSearchCursor",function(f,g,e){return new c(this.doc,f,g,e)});a.defineDocExtension("getSearchCursor",function(f,g,e){return new c(this,f,g,e)});a.defineExtension("selectMatches",function(g,f){var e=[];var h=this.getSearchCursor(g,this.getCursor("from"),f);while(h.findNext()){if(a.cmpPos(h.to(),this.getCursor("to"))>0){break}e.push({anchor:h.from(),head:h.to()})}if(e.length){this.setSelections(e,0)}})});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../dialog/dialog"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../dialog/dialog"],a)}else{a(CodeMirror)}}})(function(a){function b(e,i,g,j,h){if(e.openDialog){e.openDialog(i,h,{value:j,selectValueOnOpen:true})}else{h(prompt(g,j))}}var d='Jump to line: (Use line:column or scroll% syntax)';function c(e,g){var f=Number(g);if(/^[-+]/.test(g)){return e.getCursor().line+f}else{return f-1}}a.commands.jumpToLine=function(e){var f=e.getCursor();b(e,d,"Jump to line:",(f.line+1)+":"+f.ch,function(i){if(!i){return}var h;if(h=/^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(i)){e.setCursor(c(e,h[1]),Number(h[2]))}else{if(h=/^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(i)){var g=Math.round(e.lineCount()*Number(h[1])/100);if(/^[-+]/.test(h[1])){g=f.line+g+1}e.setCursor(g-1,f.ch)}else{if(h=/^\s*\:?\s*([\+\-]?\d+)\s*/.exec(i)){e.setCursor(c(e,h[1]),f.ch)}}}})};a.keyMap["default"]["Alt-G"]="jumpToLine"});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("./searchcursor"),require("../scroll/annotatescrollbar"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","./searchcursor","../scroll/annotatescrollbar"],a)}else{a(CodeMirror)}}})(function(b){b.defineExtension("showMatchesOnScrollbar",function(g,f,e){if(typeof e=="string"){e={className:e}}if(!e){e={}}return new c(this,g,f,e)});function c(e,j,i,h){this.cm=e;this.options=h;var f={listenForChanges:false};for(var k in h){f[k]=h[k]}if(!f.className){f.className="CodeMirror-search-match"}this.annotation=e.annotateScrollbar(f);this.query=j;this.caseFold=i;this.gap={from:e.firstLine(),to:e.lastLine()+1};this.matches=[];this.update=null;this.findMatches();this.annotation.update(this.matches);var g=this;e.on("change",this.changeHandler=function(l,m){g.onChange(m)})}var d=1000;c.prototype.findMatches=function(){if(!this.gap){return}for(var g=0;g=this.gap.to){break}if(f.to.line>=this.gap.from){this.matches.splice(g--,1)}}var h=this.cm.getSearchCursor(this.query,b.Pos(this.gap.from,0),this.caseFold);var e=this.options&&this.options.maxMatches||d;while(h.findNext()){var f={from:h.from(),to:h.to()};if(f.from.line>=this.gap.to){break}this.matches.splice(g++,0,f);if(this.matches.length>e){break}}this.gap=null};function a(e,g,f){if(e<=g){return e}return Math.max(g,e+f)}c.prototype.onChange=function(k){var l=k.from.line;var e=b.changeEnd(k).line;var f=e-k.to.line;if(this.gap){this.gap.from=Math.min(a(this.gap.from,l,f),k.from.line);this.gap.to=Math.max(a(this.gap.to,l,f),k.from.line)}else{this.gap={from:k.from.line,to:e+1}}if(f){for(var h=0;h=n.options.minChars){b(m,q,false,n.options.style)}})}function g(m,r,q){var o=m.getRange(r,q);if(o.match(/^\w+$/)!==null){if(r.ch>0){var p={line:r.line,ch:r.ch-1};var n=m.getRange(p,r);if(n.match(/\W/)===null){return false}}if(q.ch-1){n=d(p,m,n);return{from:b(q.line,n),to:b(q.line,n+e.length)}}}else{var p=k.getLine(q.line).slice(q.ch),m=f(p);var n=m.indexOf(i);if(n>-1){n=d(p,m,n)+q.ch;return{from:b(q.line,n),to:b(q.line,n+e.length)}}}}}}else{var h=e.split("\n");this.matches=function(n,p){var t=j.length-1;if(n){if(p.line-(j.length-1)=1;--m,--o){if(j[m]!=f(k.getLine(o))){return}}var u=k.getLine(o),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}return{from:b(o,q),to:s}}else{if(p.line+(j.length-1)>k.lastLine()){return}var u=k.getLine(p.line),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}var r=b(p.line,q);for(var o=p.line+1,m=1;mh){--f}else{return f}}}}a.defineExtension("getSearchCursor",function(f,g,e){return new c(this.doc,f,g,e)});a.defineDocExtension("getSearchCursor",function(f,g,e){return new c(this,f,g,e)});a.defineExtension("selectMatches",function(g,f){var e=[];var h=this.getSearchCursor(g,this.getCursor("from"),f);while(h.findNext()){if(a.cmpPos(h.to(),this.getCursor("to"))>0){break}e.push({anchor:h.from(),head:h.to()})}if(e.length){this.setSelections(e,0)}})}); \ No newline at end of file diff --git a/route/static/codemirror/index.html b/route/static/codemirror/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/codemirror/lib/codemirror.css b/route/static/codemirror/lib/codemirror.css deleted file mode 100755 index 48e2ac5a8..000000000 --- a/route/static/codemirror/lib/codemirror.css +++ /dev/null @@ -1,4 +0,0 @@ -.CodeMirror{font-family:monospace;height:auto;color:black;border:#ccc 1px solid}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:black}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid black;border-right:0;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:-20px;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:bold}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:#f00}.cm-invalidchar{color:#f00}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:white}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper{-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:none;font-variant-ligatures:none}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0} -.CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:white;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:black;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:white} -.CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.4em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:0;outline:0;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}.Dialog-close{color:#111;float:right;font-family:Arial;font-size:16px;height:30px;line-height:30px;text-align:center;width:30px;cursor:pointer} -.CodeMirror-search-match{background:gold;border-top:1px solid orange;border-bottom:1px solid orange;-moz-box-sizing:border-box;box-sizing:border-box;opacity:.5} \ No newline at end of file diff --git a/route/static/codemirror/lib/codemirror.js b/route/static/codemirror/lib/codemirror.js deleted file mode 100755 index b9cb291c8..000000000 --- a/route/static/codemirror/lib/codemirror.js +++ /dev/null @@ -1 +0,0 @@ -(function(c,d){typeof exports==="object"&&typeof module!=="undefined"?module.exports=d():typeof define==="function"&&define.amd?define(d):(c.CodeMirror=d())}(this,(function(){var iP=navigator.userAgent;var ir=navigator.platform;var kc=/gecko\/\d/i.test(iP);var kv=/MSIE \d/.test(iP);var nf=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(iP);var kj=kv||nf;var kH=kj&&(kv?document.documentMode||6:nf[1]);var mU=/WebKit\//.test(iP);var j6=mU&&/Qt\/\d+\.\d+/.test(iP);var ju=/Chrome\//.test(iP);var iL=/Opera\//.test(iP);var ob=/Apple Computer/.test(navigator.vendor);var jI=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(iP);var iq=/PhantomJS/.test(iP);var hE=/AppleWebKit/.test(iP)&&/Mobile\/\w+/.test(iP);var ie=hE||/Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(iP);var k9=hE||/Mac/.test(ir);var i6=/\bCrOS\b/.test(iP);var nW=/win/i.test(ir);var nE=iL&&iP.match(/Version\/(\d*\.\d*)/);if(nE){nE=Number(nE[1])}if(nE&&nE>=15){iL=false;mU=true}var mW=k9&&(j6||iL&&(nE==null||nE<12.11));var hT=kc||(kj&&kH>=9);function lO(a){return new RegExp("(^|\\s)"+a+"(?:$|\\s)\\s*")}var kS=function(d,c){var b=d.className;var e=lO(c).exec(b);if(e){var a=b.slice(e.index+e[0].length);d.className=b.slice(0,e.index)+(a?e[1]+a:"")}};function iN(b){for(var a=b.childNodes.length;a>0;--a){b.removeChild(b.firstChild)}return b}function mR(a,b){return iN(a).appendChild(b)}function gZ(f,b,c,d){var a=document.createElement(f);if(c){a.className=c}if(d){a.style.cssText=d}if(typeof b=="string"){a.appendChild(document.createTextNode(b))}else{if(b){for(var e=0;e=f){return h+(f-c)}h+=g-c;h+=b-(h%b);c=g+1}}function hA(){this.id=null}hA.prototype.set=function(a,b){clearTimeout(this.id);this.id=setTimeout(b,a)};function jb(a,c){for(var b=0;b=e){return a+Math.min(d,e-f)}f+=g-a;f+=b-(f%b);a=g+1;if(f>=e){return a}}}var gU=[""];function j7(a){while(gU.length<=a){gU.push(ji(gU)+" ")}return gU[a]}function ji(a){return a[a.length-1]}function mO(a,b){var d=[];for(var c=0;c"\x80"&&(a.toUpperCase()!=a.toLowerCase()||mu.test(a))}function mo(a,b){if(!b){return h0(a)}if(b.source.indexOf("\\w")>-1&&h0(a)){return true}return b.test(a)}function hS(a){for(var b in a){if(a.hasOwnProperty(b)&&a[b]){return false}}return true}var ky=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;function iB(a){return a.charCodeAt(0)>=768&&ky.test(a)}function iU(b,c,d){var a=this;this.input=d;a.scrollbarFiller=gZ("div",null,"CodeMirror-scrollbar-filler");a.scrollbarFiller.setAttribute("cm-not-content","true");a.gutterFiller=gZ("div",null,"CodeMirror-gutter-filler");a.gutterFiller.setAttribute("cm-not-content","true");a.lineDiv=gZ("div",null,"CodeMirror-code");a.selectionDiv=gZ("div",null,null,"position: relative; z-index: 1");a.cursorDiv=gZ("div",null,"CodeMirror-cursors");a.measure=gZ("div",null,"CodeMirror-measure");a.lineMeasure=gZ("div",null,"CodeMirror-measure");a.lineSpace=gZ("div",[a.measure,a.lineMeasure,a.selectionDiv,a.cursorDiv,a.lineDiv],null,"position: relative; outline: none");a.mover=gZ("div",[gZ("div",[a.lineSpace],"CodeMirror-lines")],null,"position: relative");a.sizer=gZ("div",[a.mover],"CodeMirror-sizer");a.sizerWidth=null;a.heightForcer=gZ("div",null,null,"position: absolute; height: "+ko+"px; width: 1px;");a.gutters=gZ("div",null,"CodeMirror-gutters");a.lineGutter=null;a.scroller=gZ("div",[a.sizer,a.heightForcer,a.gutters],"CodeMirror-scroll");a.scroller.setAttribute("tabIndex","-1");a.wrapper=gZ("div",[a.scrollbarFiller,a.gutterFiller,a.scroller],"CodeMirror");if(kj&&kH<8){a.gutters.style.zIndex=-1;a.scroller.style.paddingRight=0}if(!mU&&!(kc&&ie)){a.scroller.draggable=true}if(b){if(b.appendChild){b.appendChild(a.wrapper)}else{b(a.wrapper)}}a.viewFrom=a.viewTo=c.first;a.reportedViewFrom=a.reportedViewTo=c.first;a.view=[];a.renderedView=null;a.externalMeasured=null;a.viewOffset=0;a.lastWrapHeight=a.lastWrapWidth=0;a.updateLineNumbers=null;a.nativeBarWidth=a.barHeight=a.barWidth=0;a.scrollbarsClipped=false;a.lineNumWidth=a.lineNumInnerWidth=a.lineNumChars=null;a.alignWidgets=false;a.cachedCharWidth=a.cachedTextHeight=a.cachedPaddingH=null;a.maxLine=null;a.maxLineLength=0;a.maxLineChanged=false;a.wheelDX=a.wheelDY=a.wheelStartX=a.wheelStartY=null;a.shift=false;a.selForContextMenu=null;a.activeTouch=null;d.init(a)}function hm(c,a){a-=c.first;if(a<0||a>=c.size){throw new Error("There is no line "+(a+c.first)+" in the document.")}var f=c;while(!f.lines){for(var e=0;;++e){var b=f.children[e],d=b.chunkSize();if(a=b.first&&aa){return lA(a,hm(c,a).text.length)}return iu(b,hm(c,b.line).text.length)}function iu(b,c){var a=b.ch;if(a==null||a>c){return lA(b.line,c)}else{if(a<0){return lA(b.line,0)}else{return b}}}function lo(b,a){var d=[];for(var c=0;c=g:a.to>g);(b||(b=[])).push(new h7(d,a.from,f?null:a.to))}}}return b}function oc(h,f,c){var b;if(h){for(var e=0;e=f:a.to>f);if(g||a.from==f&&d.type=="bookmark"&&(!c||a.marker.insertLeft)){var j=a.from==null||(d.inclusiveLeft?a.from<=f:a.from0&&h){for(var l=0;l0){continue}var d=[k,1],n=kI(m.from,l.from),e=kI(m.to,l.to);if(n<0||!f.inclusiveLeft&&!n){d.push({from:m.from,to:l.from})}if(e>0||!f.inclusiveRight&&!e){d.push({from:l.to,to:m.to})}h.splice.apply(h,d);k+=d.length-1}}return h}function hV(c){var a=c.markedSpans;if(!a){return}for(var b=0;b=0&&g<=0||c<=0&&g>=0){continue}if(c<=0&&(k.marker.inclusiveRight&&h.inclusiveLeft?kI(b.to,f)>=0:kI(b.to,f)>0)||c>=0&&(k.marker.inclusiveRight&&h.inclusiveLeft?kI(b.from,e)<=0:kI(b.from,e)<0)){return true}}}}function jV(b){var a;while(a=kb(b)){b=a.find(-1,true).line}return b}function kW(b){var a,c;while(a=jy(b)){b=a.find(1,true).line;(c||(c=[])).push(b)}return c}function nJ(b,d){var a=hm(b,d),c=jV(a);if(a==c){return d}return m5(c)}function iJ(b,c){if(c>b.lastLine()){return c}var d=hm(b,c),a;if(!il(b,d)){return c}while(a=jy(d)){d=a.find(1,true).line}return m5(d)+1}function il(a,d){var e=gN&&d.markedSpans;if(e){for(var b=void 0,c=0;cb.maxLineLength){b.maxLineLength=e;b.maxLine=d}})}function iG(g,a,b,c){if(!g){return c(a,b,"ltr")}var d=false;for(var e=0;ea||a==b&&f.to==a){c(Math.max(f.from,a),Math.min(f.to,b),f.level==1?"rtl":"ltr");d=true}}if(!d){c(a,b,"ltr")}}function kT(a){return a.level%2?a.to:a.from}function hJ(a){return a.level%2?a.from:a.to}function md(b){var a=ld(b);return a?kT(a[0]):0}function lj(b){var a=ld(b);if(!a){return b.text.length}return hJ(ji(a))}function ne(d,c,b){var a=d[0].level;if(c==a){return true}if(b==a){return false}return ca){return d}if((b.from==a||b.to==a)){if(c==null){c=d}else{if(ne(e,b.level,e[c].level)){if(b.from!=b.to){hz=c}return d}else{if(b.from!=b.to){hz=d}return c}}}}return c}function hl(b,a,d,c){if(!c){return a+d}do{a+=d}while(a>0&&iB(b.text.charAt(a)));return a}function ka(f,a,g,e){var d=ld(f);if(!d){return nn(f,a,g,e)}var b=n6(d,a),h=d[b];var c=hl(f,a,h.level%2?-g:g,e);for(;;){if(c>h.from&&c0)==h.level%2?h.to:h.from}else{h=d[b+=g];if(!h){return null}if((g>0)==h.level%2){c=hl(f,h.to,-1,e)}else{c=hl(f,h.from,1,e)}}}}function nn(c,a,e,d){var b=a+e;if(d){while(b>0&&iB(c.text.charAt(b))){b+=e}}return b<0||b>c.text.length?null:b}var mc=(function(){var e="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";var g="rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";function h(l){if(l<=247){return e.charAt(l)}else{if(1424<=l&&l<=1524){return"R"}else{if(1536<=l&&l<=1773){return g.charAt(l-1536)}else{if(1774<=l&&l<=2220){return"r"}else{if(8192<=l&&l<=8203){return"w"}else{if(l==8204){return"b"}else{return"L"}}}}}}}var b=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;var a=/[stwN]/,j=/[LRr]/,k=/[Lb1n]/,f=/[1n]/;var c="L";function d(l,m,n){this.level=l;this.from=m;this.to=n}return function(s){if(!b.test(s)){return false}var R=s.length,F=[];for(var l=0;l-1){d[e]=c.slice(0,f).concat(c.slice(f+1))}}}}}function n9(a,b){var e=h4(a,b);if(!e.length){return}var d=Array.prototype.slice.call(arguments,2);for(var c=0;c0}function nA(a){a.prototype.on=function(c,b){nY(this,c,b)};a.prototype.off=function(c,b){ih(this,c,b)}}function l7(a){if(a.preventDefault){a.preventDefault()}else{a.returnValue=false}}function iT(a){if(a.stopPropagation){a.stopPropagation()}else{a.cancelBubble=true}}function na(a){return a.defaultPrevented!=null?a.defaultPrevented:a.returnValue==false}function jK(a){l7(a);iT(a)}function mb(a){return a.target||a.srcElement}function hi(b){var a=b.which;if(a==null){if(b.button&1){a=1}else{if(b.button&2){a=3}else{if(b.button&4){a=2}}}}if(k9&&b.ctrlKey&&a==1){a=3}return a}var kr=function(){if(kj&&kH<9){return false}var a=gZ("div");return"draggable" in a||"dragDrop" in a}();var hk;function lP(a){if(hk==null){var b=gZ("span","\u200b");mR(a,gZ("span",[b,document.createTextNode("x")]));if(a.firstChild.offsetHeight!=0){hk=b.offsetWidth<=1&&b.offsetHeight>2&&!(kj&&kH<8)}}var c=hk?gZ("span","\u200b"):gZ("span","\u00a0",null,"display: inline-block; width: 1px; margin-right: -1px");c.setAttribute("cm-text","");return c}var hj;function m2(b){if(hj!=null){return hj}var a=mR(b,document.createTextNode("A\u062eA"));var c=ki(a,0,1).getBoundingClientRect();var d=ki(a,1,2).getBoundingClientRect();iN(b);if(!c||c.left==c.right){return false}return hj=(d.right-c.right<3)}var hd="\n\nb".split(/\n/).length!=3?function(b){var a=0,e=[],c=b.length;while(a<=c){var d=b.indexOf("\n",a);if(d==-1){d=b.length}var f=b.slice(a,b.charAt(d-1)=="\r"?d-1:d);var g=f.indexOf("\r");if(g!=-1){e.push(f.slice(0,g));a+=g+1}else{e.push(f);a=d+1}}return e}:function(a){return a.split(/\r\n?|\n/)};var lv=window.getSelection?function(b){try{return b.selectionStart!=b.selectionEnd}catch(a){return false}}:function(b){var c;try{c=b.ownerDocument.selection.createRange()}catch(a){}if(!c||c.parentElement()!=b){return false}return c.compareEndPoints("StartToEnd",c)!=0};var jz=(function(){var a=gZ("div");if("oncopy" in a){return true}a.setAttribute("oncopy","return;");return typeof a.oncopy=="function"})();var hu=null;function n2(d){if(hu!=null){return hu}var c=mR(d,gZ("span","x"));var b=c.getBoundingClientRect();var a=ki(c,0,1).getBoundingClientRect();return hu=Math.abs(b.left-a.left)>1}var le={};var nQ={};function jE(a,b){if(arguments.length>2){b.dependencies=Array.prototype.slice.call(arguments,2)}le[a]=b}function lT(b,a){nQ[b]=a}function hP(a){if(typeof a=="string"&&nQ.hasOwnProperty(a)){a=nQ[a]}else{if(a&&typeof a.name=="string"&&nQ.hasOwnProperty(a.name)){var b=nQ[a.name];if(typeof b=="string"){b={name:b}}a=ks(b,a);a.name=b.name}else{if(typeof a=="string"&&/^[\w\-]+\/[\w\-]+\+xml$/.test(a)){return hP("application/xml")}else{if(typeof a=="string"&&/^[\w\-]+\/[\w\-]+\+json$/.test(a)){return hP("application/json")}}}}if(typeof a=="string"){return{name:a}}else{return a||{name:"null"}}}function h8(f,g){g=hP(g);var c=le[g.name];if(!c){return h8(f,"text/plain")}var b=c(f,g);if(iV.hasOwnProperty(g.name)){var e=iV[g.name];for(var a in e){if(!e.hasOwnProperty(a)){continue}if(b.hasOwnProperty(a)){b["_"+a]=b[a]}b[a]=e[a]}}b.name=g.name;if(g.helperType){b.helperType=g.helperType}if(g.modeProps){for(var d in g.modeProps){b[d]=g.modeProps[d]}}return b}var iV={};function jc(b,c){var a=iV.hasOwnProperty(b)?iV[b]:(iV[b]={});n0(c,a)}function nK(c,b){if(b===true){return b}if(c.copyState){return c.copyState(b)}var d={};for(var a in b){var e=b[a];if(e instanceof Array){e=e.concat([])}d[a]=e}return d}function hX(b,a){var c;while(b.innerMode){c=b.innerMode(a);if(!c||c.mode==b){break}a=c.state;b=c.mode}return c||{mode:b,state:a}}function nR(b,c,a){return b.startState?b.startState(c,a):true}var hU=function(a,b){this.pos=this.start=0;this.string=a;this.tabSize=b||8;this.lastColumnPos=this.lastColumnValue=0;this.lineStart=0};hU.prototype={eol:function(){return this.pos>=this.string.length},sol:function(){return this.pos==this.lineStart},peek:function(){return this.string.charAt(this.pos)||undefined},next:function(){if(this.posb},eatSpace:function(){var b=this;var a=this.pos;while(/[\s\u00a0]/.test(this.string.charAt(this.pos))){++b.pos}return this.pos>a},skipToEnd:function(){this.pos=this.string.length},skipTo:function(a){var b=this.string.indexOf(a,this.pos);if(b>-1){this.pos=b;return true}},backUp:function(a){this.pos-=a},column:function(){if(this.lastColumnPos0){return null}if(e&&f!==false){this.pos+=e[0].length}return e}},current:function(){return this.string.slice(this.start,this.pos)},hideFirstChars:function(b,a){this.lineStart+=b;try{return a()}finally{this.lineStart-=b}}};function ib(f,g,b,c){var d=[f.state.modeGen],e={};j2(f,g.text,f.doc.mode,b,function(k,j){return d.push(k,j)},e,c);var h=function(j){var l=f.state.overlays[j],k=1,m=0;j2(f,g.text,l.mode,true,function(r,p){var n=k;while(mr){d.splice(k,1,r,d[k+1],q)}k+=2;m=Math.min(r,q)}if(!p){return}if(l.opaque){d.splice(n,k-n,r,"overlay "+p);k=n+2}else{for(;ne.options.maxHighlightLength?nK(e.doc.mode,c):c);d.stateAfter=c;d.styles=b.styles;if(b.classes){d.styleClasses=b.classes}else{if(d.styleClasses){d.styleClasses=null}}if(a===e.doc.frontier){e.doc.frontier++}}return d.styles}function kM(e,a,g){var c=e.doc,d=e.display;if(!c.mode.startState){return true}var b=mv(e,a,g),f=b>c.first&&hm(c,b-1).stateAfter;if(!f){f=nR(c.mode)}else{f=nK(c.mode,f)}c.iter(b,a,function(h){k0(e,h.text,f);var j=b==a-1||b%5==0||b>=d.viewFrom&&bb.start){return d}}throw new Error("Mode "+a.name+" failed to advance stream.")}function j4(c,f,j,k){var d=function(n){return({start:b.start,end:b.pos,string:b.current(),type:l||null,state:n?nK(e.mode,m):m})};var e=c.doc,h=e.mode,l;f=i3(e,f);var a=hm(e,f.line),m=kM(c,f.line,j);var b=new hU(a.text,c.options.tabSize),g;if(k){g=[]}while((k||b.pose.options.maxHighlightLength){o=false;if(m){k0(e,c,p,d.pos)}d.pos=c.length;g=null}else{g=i8(jj(l,d,p,a),n)}if(a){var b=a[0].name;if(b){g="m-"+(g?b+" "+g:b)}}if(!o||j!=g){while(hj;--a){if(a<=f.first){return f.first}var c=hm(f,a-1);if(c.stateAfter&&(!h||a<=f.frontier)){return a}var d=mV(c.text,null,e.options.tabSize);if(g==null||k>d){g=a-1;k=d}}return g}function gW(b,c,a){this.text=b;mM(this,c);this.height=a?a(this):1}nA(gW);gW.prototype.lineNo=function(){return m5(this)};function hY(e,a,d,c){e.text=a;if(e.stateAfter){e.stateAfter=null}if(e.styles){e.styles=null}if(e.order!=null){e.order=null}hV(e);mM(e,d);var b=c?c(e):1;if(b!=e.height){gV(e,b)}}function nv(a){a.parent=null;hV(a)}var lE={};var nM={};function hO(b,c){if(!b||/^\s*$/.test(b)){return null}var a=c.addModeClass?nM:lE;return a[b]||(a[b]=b.replace(/\S+/g,"cm-$&"))}function j0(c,d){var e=gZ("span",null,null,mU?"padding-right: .1px":null);var f={pre:gZ("pre",[e],"CodeMirror-line"),content:e,col:0,pos:0,cm:c,trailingSpace:false,splitSpaces:(kj||mU)&&c.getOption("lineWrapping")};d.measure={};for(var g=0;g<=(d.rest?d.rest.length:0);g++){var a=g?d.rest[g-1]:d.line,h=void 0;f.pos=0;f.addToken=ke;if(m2(c.display.measure)&&(h=ld(a))){f.addToken=lF(f.addToken,h)}f.map=[];var j=d!=c.display.externalMeasured&&m5(a);lK(a,f,jG(c,a,j));if(a.styleClasses){if(a.styleClasses.bgClass){f.bgClass=g9(a.styleClasses.bgClass,f.bgClass||"")}if(a.styleClasses.textClass){f.textClass=g9(a.styleClasses.textClass,f.textClass||"")}}if(f.map.length==0){f.map.push(0,0,f.content.appendChild(lP(c.display.measure)))}if(g==0){d.measure.map=f.map;d.measure.cache={}}else{(d.measure.maps||(d.measure.maps=[])).push(f.map);(d.measure.caches||(d.measure.caches=[])).push({})}}if(mU){var b=f.content.lastChild;if(/\bcm-tab\b/.test(b.className)||(b.querySelector&&b.querySelector(".cm-tab"))){f.content.className="cm-tab-wrap-hack"}}n9(c,"renderLine",c,d.line,f.pre);if(f.pre.className){f.textClass=g9(f.pre.className,f.textClass||"")}return f}function hn(b){var a=gZ("span","\u2022","cm-invalidchar");a.title="\\u"+b.charCodeAt(0).toString(16);a.setAttribute("aria-label",a.title);return a}function ke(e,k,u,c,g,r,l){if(!k){return}var a=e.splitSpaces?ma(k,e.trailingSpace):k;var q=e.cm.state.specialChars,p=false;var b;if(!q.test(k)){e.col+=k.length;b=document.createTextNode(a);e.map.push(e.pos,e.pos+k.length,b);if(kj&&kH<9){p=true}e.pos+=k.length}else{b=document.createDocumentFragment();var n=0;while(true){q.lastIndex=n;var d=q.exec(k);var s=d?d.index-n:k.length-n;if(s){var h=document.createTextNode(a.slice(n,n+s));if(kj&&kH<9){b.appendChild(gZ("span",[h]))}else{b.appendChild(h)}e.map.push(e.pos,e.pos+s,h);e.col+=s;e.pos+=s}if(!d){break}n+=s+1;var t=void 0;if(d[0]=="\t"){var f=e.cm.options.tabSize,j=f-e.col%f;t=b.appendChild(gZ("span",j7(j),"cm-tab"));t.setAttribute("role","presentation");t.setAttribute("cm-text","\t");e.col+=j}else{if(d[0]=="\r"||d[0]=="\n"){t=b.appendChild(gZ("span",d[0]=="\r"?"\u240d":"\u2424","cm-invalidchar"));t.setAttribute("cm-text",d[0]);e.col+=1}else{t=e.cm.options.specialCharPlaceholder(d[0]);t.setAttribute("cm-text",d[0]);if(kj&&kH<9){b.appendChild(gZ("span",[t]))}else{b.appendChild(t)}e.col+=1}}e.map.push(e.pos,e.pos+1,t);e.pos++}}e.trailingSpace=a.charCodeAt(k.length-1)==32;if(u||c||g||p||l){var o=u||"";if(c){o+=c}if(g){o+=g}var m=gZ("span",[b],o,l);if(r){m.title=r}return e.content.appendChild(m)}e.content.appendChild(b)}function ma(a,b){if(a.length>1&&!/ /.test(a)){return a}var e=b,f="";for(var d=0;dm&&l.from<=m){break}}if(l.to>=k){return b(f,d,n,j,c,e,g)}b(f,d.slice(0,l.to-m),n,j,null,e,g);j=null;d=d.slice(l.to-m);m=l.to}}}function ny(e,c,b,d){var a=!d&&b.widgetNode;if(a){e.map.push(e.pos,e.pos+c,a)}if(!d&&e.cm.display.input.needsContentAttribute){if(!a){a=e.content.appendChild(document.createElement("span"))}a.setAttribute("cm-marker",b.id)}if(a){e.cm.display.input.setUneditable(a);e.content.appendChild(a)}e.pos+=c;e.trailingSpace=false}function lK(d,A,e){var o=d.markedSpans,h=d.text,C=0;if(!o){for(var l=1;lq||B.collapsed&&y.to==q&&y.from==q)){if(y.to!=null&&y.to!=q&&k>y.to){k=y.to;m=""}if(B.className){v+=" "+B.className}if(B.css){c=(c?c+";":"")+B.css}if(B.startStyle&&y.from==q){D+=" "+B.startStyle}if(B.endStyle&&y.to==k){(f||(f=[])).push(B.endStyle,y.to)}if(B.title&&!g){g=B.title}if(B.collapsed&&(!t||lW(t.marker,B)<0)){t=y}}else{if(y.from>q&&k>y.from){k=y.from}}}}if(f){for(var n=0;n=w){break}var b=Math.min(w,k);while(true){if(a){var u=q+a.length;if(!t){var r=u>b?a.slice(0,b-q):a;A.addToken(A,r,s?s+v:v,D,q+r.length==k?m:"",g,c)}if(u>=b){a=a.slice(b-q);q=b;break}q=u;D=""}a=h.slice(C,C=e[x++]);s=hO(e[x++],A.cm.options)}}}function lk(b,a,c){this.line=a;this.rest=kW(a);this.size=this.rest?m5(ji(this.rest))-c+1:1;this.node=this.text=null;this.hidden=il(b,a)}function hQ(e,a,b){var c=[],f;for(var d=a;d2){e.push((b.bottom+j.top)/2-d.top)}}}e.push(d.bottom-d.top)}}function jT(c,e,b){if(c.line==e){return{map:c.measure.map,cache:c.measure.cache}}for(var d=0;db){return{map:c.measure.maps[a],cache:c.measure.caches[a],before:true}}}}function mP(c,d){d=jV(d);var a=m5(d);var e=c.display.externalMeasured=new lk(c.doc,d,a);e.lineN=a;var b=e.built=j0(c,e);e.text=b.pre;mR(c.display.lineMeasure,b.pre);return e}function ic(b,d,a,c){return mB(b,gP(b,d),a,c)}function hp(a,b){if(b>=a.display.viewFrom&&b=c.lineN&&bk){f=b-g;j=f-1;if(k>=b){c="right"}}}}if(j!=null){h=a[e+2];if(g==b&&d==(h.insertLeft?"left":"right")){c=d}if(d=="left"&&j==0){while(e&&a[e-2]==a[e-3]&&a[e-1].insertLeft){h=a[(e-=3)+2];c="left"}}if(d=="right"&&j==b-g){while(e=0;a--){if((b=d[a]).left!=b.right){break}}}return b}function kL(g,s,d,k){var f=n3(s.map,d,k);var u=f.node,l=f.start,m=f.end,p=f.collapse;var o;if(u.nodeType==3){for(var j=0;j<4;j++){while(l&&iB(s.line.text.charAt(f.coverStart+l))){--l}while(f.coverStart+m0){p=k="right"}var n;if(g.options.lineWrapping&&(n=u.getClientRects()).length>1){o=n[k=="right"?n.length-1:0]}else{o=u.getBoundingClientRect()}}if(kj&&kH<9&&!l&&(!o||!o.left&&!o.right)){var h=u.parentNode.getClientRects()[0];if(h){o={left:h.left,right:h.left+kJ(g.display),top:h.top,bottom:h.bottom}}else{o=jk}}var a=o.top-s.rect.top,c=o.bottom-s.rect.top;var q=(a+c)/2;var r=s.view.measure.heights;var t=0;for(;tp.from){return j(n-1)}return j(n,o)}var h=ld(g),c=e.ch;if(!h){return j(c)}var m=n6(h,c);var k=b(c,m);if(hz!=null){k.other=b(c,hz)}return k}function kw(c,a){var b=0;a=i3(c.doc,a);if(!c.options.lineWrapping){b=kJ(c.display)*a.ch}var e=hm(c.doc,a.line);var d=m7(e)+hs(c.display);return{left:b,right:b,top:d,bottom:d+e.height}}function g1(c,e,d,a){var b=lA(c,e);b.xRel=a;if(d){b.outside=true}return b}function hg(d,g,h){var e=d.doc;h+=d.display.viewOffset;if(h<0){return g1(e.first,0,true,-1)}var j=nj(e,h),c=e.first+e.size-1;if(j>c){return g1(e.first+e.size-1,hm(e,c).text.length,true,1)}if(g<0){g=0}var k=hm(e,j);for(;;){var b=mX(d,k,j,g,h);var f=jy(k);var a=f&&f.find(0,true);if(f&&(b.ch>a.from.ch||b.ch==a.from.ch&&b.xRel>0)){j=m5(k=a.to.line)}else{return b}}}function mX(e,r,m,c,d){var f=d-m7(r);var k=false,y=2*e.display.wrapper.clientWidth;var B=gP(e,r);function q(C){var D=lL(e,lA(m,C),"line",r,B);k=true;if(f>D.bottom){return D.left-y}else{if(fs){return g1(m,p,l,1)}for(;;){if(a?p==x||p==ka(r,x,1):p-x<=1){var b=c0&&b1){var w=mB(e,B,b,"right");if(f<=w.bottom&&f>=w.top&&Math.abs(c-w.right)1?1:0);return g}var h=Math.ceil(z/2),j=x+h;if(a){j=x;for(var v=0;vc){p=j;s=n;if(l=k){s+=1000}z=h}else{x=j;A=n;t=k;z-=h}}}var n7;function nF(a){if(a.cachedTextHeight!=null){return a.cachedTextHeight}if(n7==null){n7=gZ("pre");for(var b=0;b<49;++b){n7.appendChild(document.createTextNode("x"));n7.appendChild(gZ("br"))}n7.appendChild(document.createTextNode("x"))}mR(a.measure,n7);var c=n7.offsetHeight/50;if(c>3){a.cachedTextHeight=c}iN(a.measure);return c||1}function kJ(a){if(a.cachedCharWidth!=null){return a.cachedCharWidth}var c=gZ("span","xxxxxxxxxx");var b=gZ("pre",[c]);mR(a.measure,b);var d=c.getBoundingClientRect(),e=(d.right-d.left)/10;if(e>2){a.cachedCharWidth=e}return e||10}function ho(g){var b=g.display,d={},e={};var c=b.gutters.clientLeft;for(var a=b.gutters.firstChild,f=0;a;a=a.nextSibling,++f){d[g.options.gutters[f]]=a.offsetLeft+a.clientLeft+c;e[g.options.gutters[f]]=a.clientWidth}return{fixedPos:lw(b),gutterTotalWidth:b.gutters.offsetWidth,gutterLeft:d,gutterWidth:e,wrapperWidth:b.wrapper.clientWidth}}function lw(a){return a.scroller.getBoundingClientRect().left-a.sizer.getBoundingClientRect().left}function mp(b){var c=nF(b.display),d=b.options.lineWrapping;var a=d&&Math.max(5,b.display.scroller.clientWidth/kJ(b.display)-3);return function(f){if(il(b.doc,f)){return 0}var g=0;if(f.widgets){for(var e=0;e=d.display.viewTo){return null}a-=d.display.viewFrom;if(a<0){return null}var c=d.display.view;for(var b=0;b=c.display.viewTo||b.to().line3){e(D,F.top,null,F.bottom);D=n;if(F.bottomz.bottom||E.bottom==z.bottom&&E.right>z.right){z=E}if(D0){b.blinker=setInterval(function(){return b.cursorDiv.style.visibility=(c=!c)?"":"hidden"},a.options.cursorBlinkRate)}else{if(a.options.cursorBlinkRate<0){b.cursorDiv.style.visibility="hidden"}}}function kh(a){if(!a.state.focused){a.display.input.focus();mm(a)}}function nk(a){a.state.delayingBlurEvent=true;setTimeout(function(){if(a.state.delayingBlurEvent){a.state.delayingBlurEvent=false;nL(a)}},100)}function mm(a,b){if(a.state.delayingBlurEvent){a.state.delayingBlurEvent=false}if(a.options.readOnly=="nocursor"){return}if(!a.state.focused){n9(a,"focus",a,b);a.state.focused=true;h6(a.display.wrapper,"CodeMirror-focused");if(!a.curOp&&a.display.selForContextMenu!=a.doc.sel){a.display.input.reset();if(mU){setTimeout(function(){return a.display.input.reset(true)},20)}}a.display.input.receivedFocus()}ku(a)}function nL(a,b){if(a.state.delayingBlurEvent){return}if(a.state.focused){n9(a,"blur",a,b);a.state.focused=false;kS(a.display.wrapper,"CodeMirror-focused")}clearInterval(a.display.blinker);setTimeout(function(){if(!a.state.focused){a.display.shift=false}},150)}function ja(a){var c=a.display,b=c.view;if(!c.alignWidgets&&(!c.gutters.firstChild||!a.options.fixedGutter)){return}var e=lw(c)-c.scroller.scrollLeft+a.doc.scrollLeft;var j=c.gutters.offsetWidth,h=e+"px";for(var f=0;f0.001||d<-0.001){gV(b.line,a);kX(b.line);if(b.rest){for(var k=0;k=d){e=nj(c,m7(hm(c,b))-g.wrapper.clientHeight);d=b}}}return{from:e,to:Math.max(d,e+1)}}function l4(a,b){if(Math.abs(a.doc.scrollTop-b)<2){return}a.doc.scrollTop=b;if(!kc){lN(a,{top:b})}if(a.display.scroller.scrollTop!=b){a.display.scroller.scrollTop=b}a.display.scrollbars.setScrollTop(b);if(kc){lN(a)}ij(a,100)}function nq(a,b,c){if(c?b==a.doc.scrollLeft:Math.abs(a.doc.scrollLeft-b)<2){return}b=Math.min(b,a.display.scroller.scrollWidth-a.display.scroller.clientWidth);a.doc.scrollLeft=b;ja(a);if(a.display.scroller.scrollLeft!=b){a.display.scroller.scrollLeft=b}a.display.scrollbars.setScrollLeft(b)}var iF=0;var kG=null;if(kj){kG=-0.53}else{if(kc){kG=15}else{if(ju){kG=-0.7}else{if(ob){kG=-1/3}}}}function ls(b){var c=b.wheelDeltaX,a=b.wheelDeltaY;if(c==null&&b.detail&&b.axis==b.HORIZONTAL_AXIS){c=b.detail}if(a==null&&b.detail&&b.axis==b.VERTICAL_AXIS){a=b.detail}else{if(a==null){a=b.wheelDelta}}return{x:c,y:a}}function mF(a){var b=ls(a);b.x*=kG;b.y*=kG;return b}function k8(e,l){var c=ls(l),a=c.x,b=c.y;var j=e.display,f=j.scroller;var m=f.scrollWidth>f.clientWidth;var n=f.scrollHeight>f.clientHeight;if(!(a&&m||b&&n)){return}if(b&&k9&&mU){outer:for(var d=l.target,g=j.view;d!=f;d=d.parentNode){for(var o=0;od.clientWidth+1;var e=d.scrollHeight>d.clientHeight+1;var a=d.nativeBarWidth;if(e){this.vert.style.display="block";this.vert.style.bottom=b?a+"px":"0";var f=d.viewHeight-(b?a:0);this.vert.firstChild.style.height=Math.max(0,d.scrollHeight-d.clientHeight+f)+"px"}else{this.vert.style.display="";this.vert.firstChild.style.height="0"}if(b){this.horiz.style.display="block";this.horiz.style.right=e?a+"px":"0";this.horiz.style.left=d.barLeft+"px";var c=d.viewWidth-d.barLeft-(e?a:0);this.horiz.firstChild.style.width=(d.scrollWidth-d.clientWidth+c)+"px"}else{this.horiz.style.display="";this.horiz.firstChild.style.width="0"}if(!this.checkedZeroWidth&&d.clientHeight>0){if(a==0){this.zeroWidthHack()}this.checkedZeroWidth=true}return{right:e?a:0,bottom:b?a:0}},setScrollLeft:function(a){if(this.horiz.scrollLeft!=a){this.horiz.scrollLeft=a}if(this.disableHoriz){this.enableZeroWidthBar(this.horiz,this.disableHoriz)}},setScrollTop:function(a){if(this.vert.scrollTop!=a){this.vert.scrollTop=a}if(this.disableVert){this.enableZeroWidthBar(this.vert,this.disableVert)}},zeroWidthHack:function(){var a=k9&&!jI?"12px":"18px";this.horiz.style.height=this.vert.style.width=a;this.horiz.style.pointerEvents=this.vert.style.pointerEvents="none";this.disableHoriz=new hA;this.disableVert=new hA},enableZeroWidthBar:function(c,a){c.style.pointerEvents="auto";function b(){var d=c.getBoundingClientRect();var e=document.elementFromPoint(d.left+1,d.bottom-1);if(e!=c){c.style.pointerEvents="none"}else{a.set(1000,b)}}a.set(1000,b)},clear:function(){var a=this.horiz.parentNode;a.removeChild(this.horiz);a.removeChild(this.vert)}},i7.prototype);function hy(){}hy.prototype=n0({update:function(){return{bottom:0,right:0}},setScrollLeft:function(){},setScrollTop:function(){},clear:function(){}},hy.prototype);function hK(c,a){if(!a){a=kR(c)}var d=c.display.barWidth,e=c.display.barHeight;nN(c,a);for(var b=0;b<4&&d!=c.display.barWidth||e!=c.display.barHeight;b++){if(d!=c.display.barWidth&&c.options.lineWrapping){i(c)}nN(c,kR(c));d=c.display.barWidth;e=c.display.barHeight}}function nN(b,d){var a=b.display;var c=a.scrollbars.update(d);a.sizer.style.paddingRight=(a.barWidth=c.right)+"px";a.sizer.style.paddingBottom=(a.barHeight=c.bottom)+"px";a.heightForcer.style.borderBottom=c.bottom+"px solid transparent";if(c.right&&c.bottom){a.scrollbarFiller.style.display="block";a.scrollbarFiller.style.height=c.bottom+"px";a.scrollbarFiller.style.width=c.right+"px"}else{a.scrollbarFiller.style.display=""}if(c.bottom&&b.options.coverGutterNextToScrollbar&&b.options.fixedGutter){a.gutterFiller.style.display="block";a.gutterFiller.style.height=c.bottom+"px";a.gutterFiller.style.width=d.gutterWidth+"px"}else{a.gutterFiller.style.display=""}}var lX={"native":i7,"null":hy};function oa(a){if(a.display.scrollbars){a.display.scrollbars.clear();if(a.display.scrollbars.addClass){kS(a.display.wrapper,a.display.scrollbars.addClass)}}a.display.scrollbars=new lX[a.options.scrollbarStyle](function(b){a.display.wrapper.insertBefore(b,a.display.scrollbarFiller);nY(b,"mousedown",function(){if(a.state.focused){setTimeout(function(){return a.display.input.focus()},0)}});b.setAttribute("cm-not-content","true")},function(b,c){if(c=="horizontal"){nq(a,b)}else{l4(a,b)}},a);if(a.display.scrollbars.addClass){h6(a.display.wrapper,a.display.scrollbars.addClass)}}function iD(f,b){if(nT(f,"scrollCursorIntoView")){return}var a=f.display,e=a.sizer.getBoundingClientRect(),d=null;if(b.top+e.top<0){d=true}else{if(b.bottom+e.top>(window.innerHeight||document.documentElement.clientHeight)){d=false}}if(d!=null&&!iq){var c=gZ("div","\u200b",null,("position: absolute;\n top: "+(b.top-a.viewOffset-hs(f.display))+"px;\n height: "+(b.bottom-b.top+lh(f)+a.barHeight)+"px;\n left: "+(b.left)+"px; width: 2px;"));f.display.lineSpace.appendChild(c);c.scrollIntoView(d);f.display.lineSpace.removeChild(c)}}function mx(a,d,h,j){if(j==null){j=0}var b;for(var g=0;g<5;g++){var f=false;b=lL(a,d);var c=!h||h==d?b:lL(a,h);var k=mn(a,Math.min(b.left,c.left),Math.min(b.top,c.top)-j,Math.max(b.left,c.left),Math.max(b.bottom,c.bottom)+j);var e=a.doc.scrollTop,l=a.doc.scrollLeft;if(k.scrollTop!=null){l4(a,k.scrollTop);if(Math.abs(a.doc.scrollTop-e)>1){f=true}}if(k.scrollLeft!=null){nq(a,k.scrollLeft);if(Math.abs(a.doc.scrollLeft-l)>1){f=true}}if(!f){break}}return b}function mt(d,e,b,f,c){var a=mn(d,e,b,f,c);if(a.scrollTop!=null){l4(d,a.scrollTop)}if(a.scrollLeft!=null){nq(d,a.scrollLeft)}}function mn(d,o,e,q,f){var j=d.display,l=nF(d.display);if(e<0){e=0}var n=d.curOp&&d.curOp.scrollTop!=null?d.curOp.scrollTop:j.scroller.scrollTop;var b=m8(d),r={};if(f-e>b){f=e+b}var p=d.doc.height+ni(j);var h=ep-l;if(en+b){var g=Math.min(e,(k?p:f)-b);if(g!=n){r.scrollTop=g}}}var a=d.curOp&&d.curOp.scrollLeft!=null?d.curOp.scrollLeft:j.scroller.scrollLeft;var c=i4(d)-(d.options.fixedGutter?j.gutters.offsetWidth:0);var m=q-o>c;if(m){q=o+c}if(o<10){r.scrollLeft=0}else{if(oc+a-3){r.scrollLeft=q+(m?0:10)-c}}}return r}function lM(a,b,c){if(b!=null||c!=null){h2(a)}if(b!=null){a.curOp.scrollLeft=(a.curOp.scrollLeft==null?a.doc.scrollLeft:a.curOp.scrollLeft)+b}if(c!=null){a.curOp.scrollTop=(a.curOp.scrollTop==null?a.doc.scrollTop:a.curOp.scrollTop)+c}}function jl(b){h2(b);var d=b.getCursor(),a=d,c=d;if(!b.options.lineWrapping){a=d.ch?lA(d.line,d.ch-1):d;c=lA(d.line,d.ch+1)}b.curOp.scrollToPos={from:a,to:c,margin:b.options.cursorScrollMargin,isCursor:true}}function h2(c){var d=c.curOp.scrollToPos;if(d){c.curOp.scrollToPos=null;var a=kw(c,d.from),b=kw(c,d.to);var e=mn(c,Math.min(a.left,b.left),Math.min(a.top,b.top)-d.margin,Math.max(a.right,b.right),Math.max(a.bottom,b.bottom)+d.margin);c.scrollTo(e.scrollLeft,e.scrollTop)}}var iv=0;function lY(a){a.curOp={cm:a,viewChanged:false,startHeight:a.doc.height,forceUpdate:false,updateInput:null,typing:false,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:false,updateMaxLine:false,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:false,id:++iv};js(a.curOp)}function ng(a){var b=a.curOp;nX(b,function(c){for(var d=0;d=c.viewTo)||c.maxLineChanged&&a.options.lineWrapping;b.update=b.mustUpdate&&new n4(a,b.mustUpdate&&{top:b.scrollTop,ensure:b.scrollToPos},b.forceUpdate)}function m6(a){a.updatedDisplay=a.mustUpdate&&mC(a.cm,a.update)}function nP(b){var a=b.cm,c=a.display;if(b.updatedDisplay){i(a)}b.barMeasure=kR(a);if(c.maxLineChanged&&!a.options.lineWrapping){b.adjustWidthTo=ic(a,c.maxLine,c.maxLine.text.length).left+3;a.display.sizerWidth=b.adjustWidthTo;b.barMeasure.scrollWidth=Math.max(c.scroller.clientWidth,c.sizer.offsetLeft+b.adjustWidthTo+lh(a)+a.display.barWidth);b.maxScrollLeft=Math.max(0,c.sizer.offsetLeft+b.adjustWidthTo-i4(a))}if(b.updatedDisplay||b.selectionChanged){b.preparedSelection=c.input.prepareSelection(b.focus)}}function m9(b){var a=b.cm;if(b.adjustWidthTo!=null){a.display.sizer.style.minWidth=b.adjustWidthTo+"px";if(b.maxScrollLefte)){h.updateLineNumbers=e}c.curOp.viewChanged=true;if(e>=h.viewTo){if(gN&&nJ(c.doc,e)h.viewFrom){jw(c)}else{h.viewFrom+=b;h.viewTo+=b}}else{if(e<=h.viewFrom&&d>=h.viewTo){jw(c)}else{if(e<=h.viewFrom){var f=jq(c,d,d+b,1);if(f){h.view=h.view.slice(f.index);h.viewFrom=f.lineN;h.viewTo+=b}else{jw(c)}}else{if(d>=h.viewTo){var k=jq(c,e,e,-1);if(k){h.view=h.view.slice(0,k.index);h.viewTo=k.lineN}else{jw(c)}}else{var g=jq(c,e,e,-1);var j=jq(c,d,d+b,1);if(g&&j){h.view=h.view.slice(0,g.index).concat(hQ(c,g.lineN,j.lineN)).concat(h.view.slice(j.index));h.viewTo+=b}else{jw(c)}}}}}}var a=h.externalMeasured;if(a){if(d=c.lineN&&f=a.viewTo){return}var e=a.view[iR(g,f)];if(e.node==null){return}var d=e.changes||(e.changes=[]);if(jb(d,b)==-1){d.push(b)}}function jw(a){a.display.viewFrom=a.display.viewTo=a.doc.first;a.display.view=[];a.display.viewOffset=0}function jq(a,g,e,h){var d=iR(a,g),b,c=a.display.view;if(!gN||e==a.doc.first+a.doc.size){return{index:d,lineN:e}}var j=a.display.viewFrom;for(var f=0;f0){if(d==c.length-1){return null}b=(j+c[d].size)-g;d++}else{b=j-g}g+=b;e+=b}while(nJ(a.doc,e)!=e){if(d==(h<0?0:c.length-1)){return null}e+=h*c[d-(h<0?1:0)].size;d+=h}return{index:d,lineN:e}}function lm(c,a,b){var d=c.display,e=d.view;if(e.length==0||a>=d.viewTo||b<=d.viewFrom){d.view=hQ(c,a,b);d.viewFrom=a}else{if(d.viewFrom>a){d.view=hQ(c,a,d.viewFrom).concat(d.view)}else{if(d.viewFromb){d.view=d.view.slice(0,iR(c,b))}}}d.viewTo=b}function jv(e){var d=e.display.view,a=0;for(var b=0;b=c.display.viewTo){return}var d=+new Date+c.options.workTime;var b=nK(a.mode,kM(c,a.frontier));var e=[];a.iter(a.frontier,Math.min(a.first+a.size,c.display.viewTo+500),function(h){if(a.frontier>=c.display.viewFrom){var n=h.styles,j=h.text.length>c.options.maxHighlightLength;var l=ib(c,h,j?nK(a.mode,b):b,true);h.styles=l.styles;var f=h.styleClasses,m=l.classes;if(m){h.styleClasses=m}else{if(f){h.styleClasses=null}}var k=!n||n.length!=h.styles.length||f!=m&&(!f||!m||f.bgClass!=m.bgClass||f.textClass!=m.textClass);for(var g=0;!k&&gd){ij(c,c.options.workDelay);return true}});if(e.length){lI(c,function(){for(var f=0;f=g.viewFrom&&h.visible.to<=g.viewTo&&(g.updateLineNumbers==null||g.updateLineNumbers>=g.viewTo)&&g.renderedView==g.view&&jv(b)==0){return false}if(iE(b)){jw(b);h.dims=ho(b)}var j=c.first+c.size;var e=Math.max(h.visible.from-b.options.viewportMargin,c.first);var d=Math.min(j,h.visible.to+b.options.viewportMargin);if(g.viewFromd&&g.viewTo-d<20){d=Math.min(j,g.viewTo)}if(gN){e=nJ(b.doc,e);d=iJ(b.doc,d)}var k=e!=g.viewFrom||d!=g.viewTo||g.lastWrapHeight!=h.wrapperHeight||g.lastWrapWidth!=h.wrapperWidth;lm(b,e,d);g.viewOffset=m7(hm(b.doc,g.viewFrom));b.display.mover.style.top=g.viewOffset+"px";var a=jv(b);if(!k&&a==0&&!h.force&&g.renderedView==g.view&&(g.updateLineNumbers==null||g.updateLineNumbers>=g.viewTo)){return false}var f=j3();if(a>4){g.lineDiv.style.display="none"}km(b,g.updateLineNumbers,h.dims);if(a>4){g.lineDiv.style.display=""}g.renderedView=g.view;if(f&&j3()!=f&&f.offsetHeight){f.focus()}iN(g.cursorDiv);iN(g.selectionDiv);g.gutters.style.height=g.sizer.style.minHeight=0;if(k){g.lastWrapHeight=h.wrapperHeight;g.lastWrapWidth=h.wrapperWidth;ij(b,400)}g.updateLineNumbers=null;return true}function kt(e,a){var c=a.viewport;for(var b=true;;b=false){if(!b||!e.options.lineWrapping||a.oldDisplayWidth==i4(e)){if(c&&c.top!=null){c={top:Math.min(e.doc.height+ni(e.display)-m8(e),c.top)}}a.visible=lc(e.display,e.doc,c);if(a.visible.from>=e.display.viewFrom&&a.visible.to<=e.display.viewTo){break}}if(!mC(e,a)){break}i(e);var d=kR(e);nu(e);hK(e,d);kV(e,d)}a.signal(e,"update",e);if(e.display.viewFrom!=e.display.reportedViewFrom||e.display.viewTo!=e.display.reportedViewTo){a.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo);e.display.reportedViewFrom=e.display.viewFrom;e.display.reportedViewTo=e.display.viewTo}}function lN(d,b){var a=new n4(d,b);if(mC(d,a)){i(d);kt(d,a);var c=kR(d);nu(d);hK(d,c);kV(d,c);a.finish()}}function km(c,m,d){var g=c.display,a=c.options.lineNumbers;var o=g.lineDiv,b=o.firstChild;function h(p){var q=p.nextSibling;if(mU&&k9&&c.display.currentWheelTarget==p){p.style.display="none"}else{p.parentNode.removeChild(p)}return q}var f=g.view,j=g.viewFrom;for(var l=0;l-1){e=false}nC(c,k,j,d)}if(e){iN(k.lineNumber);k.lineNumber.appendChild(document.createTextNode(jF(c.options,j)))}b=k.node.nextSibling}}j+=k.size}while(b){b=h(b)}}function jO(a){var b=a.display.gutters.offsetWidth;a.display.sizer.style.marginLeft=b+"px"}function kV(a,b){a.display.sizer.style.minHeight=b.docHeight+"px";a.display.heightForcer.style.top=b.docHeight+"px";a.display.gutters.style.height=(b.docHeight+a.display.barHeight+lh(a))+"px"}function io(f){var e=f.display.gutters,a=f.options.gutters;iN(e);var d=0;for(;d-1&&!a.lineNumbers){a.gutters=a.gutters.slice(0);a.gutters.splice(b,1)}}}function g0(a,b){this.ranges=a;this.primIndex=b}g0.prototype={primary:function(){return this.ranges[this.primIndex]},equals:function(e){var a=this;if(e==this){return true}if(e.primIndex!=this.primIndex||e.ranges.length!=this.ranges.length){return false}for(var c=0;c=0&&kI(a,d.to())<=0){return c}}return -1}};function lz(a,b){this.anchor=a;this.head=b}lz.prototype={from:function(){return m3(this.anchor,this.head)},to:function(){return nD(this.anchor,this.head)},empty:function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch}};function mA(j,b){var g=j[b];j.sort(function(l,k){return kI(l.from(),k.from())});b=jb(j,g);for(var e=1;e=0){var d=m3(h.from(),a.from()),c=nD(h.to(),a.to());var f=h.empty()?a.from()==a.head:h.from()==h.head;if(e<=b){--b}j.splice(--e,2,new lz(f?c:d,f?d:c))}}return new g0(j,b)}function j1(a,b){return new g0([new lz(a,b||a)],0)}function m1(a){if(!a.text){return a.to}return lA(a.from.line+a.text.length-1,ji(a.text).length+(a.text.length==1?a.from.ch:0))}function nS(b,c){if(kI(b,c.from)<0){return b}if(kI(b,c.to)<=0){return m1(c)}var a=b.line+c.text.length-(c.to.line-c.from.line)-1,d=b.ch;if(b.line==c.to.line){d+=m1(c).ch-c.to.ch}return lA(a,d)}function iK(b,a){var d=[];for(var c=0;c1){r.remove(a.line+1,c-1)}r.insert(a.line+1,l)}}}}nw(r,"change",r,f)}function iA(b,c,d){function a(j,e,g){if(j.linked){for(var f=0;f1&&!b.done[b.done.length-2].ranges){b.done.pop();return ji(b.done)}}}}function iX(d,f,a,g){var h=d.history;h.undone.length=0;var j=+new Date,c;var b;if((h.lastOp==g||h.lastOrigin==f.origin&&f.origin&&((f.origin.charAt(0)=="+"&&d.cm&&h.lastModTime>j-d.cm.options.historyEventDelay)||f.origin.charAt(0)=="*"))&&(c=kl(h,h.lastOp==g))){b=ji(c.changes);if(kI(f.from,f.to)==0&&kI(f.from,b.to)==0){b.to=m1(f)}else{c.changes.push(k6(d,f))}}else{var e=ji(h.done);if(!e||!e.ranges){lx(d.sel,h.done)}c={changes:[k6(d,f)],generation:h.generation};h.done.push(c);while(h.done.length>h.undoDepth){h.done.shift();if(!h.done[0].ranges){h.done.shift()}}}h.done.push(a);h.generation=++h.maxGeneration;h.lastModTime=h.lastSelTime=j;h.lastOp=h.lastSelOp=g;h.lastOrigin=h.lastSelOrigin=f.origin;if(!b){n9(d,"historyAdded")}}function nx(a,c,d,b){var e=c.charAt(0);return e=="*"||e=="+"&&d.ranges.length==b.ranges.length&&d.somethingSelected()==b.somethingSelected()&&new Date-a.history.lastSelTime<=(a.cm?a.cm.options.historyEventDelay:500)}function hN(a,d,c,e){var b=a.history,f=e&&e.origin;if(c==b.lastSelOp||(f&&b.lastSelOrigin==f&&(b.lastModTime==b.lastSelTime&&b.lastOrigin==f||nx(a,f,ji(b.done),d)))){b.done[b.done.length-1]=d}else{lx(d,b.done)}b.lastSelTime=+new Date;b.lastSelOrigin=f;b.lastSelOp=c;if(e&&e.clearRedo!==false){h3(b.undone)}}function lx(c,a){var b=ji(a);if(!(b&&b.ranges&&b.equals(c))){a.push(c)}}function nU(f,a,b,d){var c=a["spans_"+f.id],e=0;f.iter(Math.max(f.first,b),Math.min(f.first+f.size,d),function(g){if(g.markedSpans){(c||(c=a["spans_"+f.id]={}))[e]=g.markedSpans}++e})}function l3(a){if(!a){return null}var c;for(var b=0;b-1){ji(c)[m]=e[m];delete e[m]}}}}}}return l}function im(a,f,b,d){if(a.cm&&a.cm.display.shift||a.extend){var c=f.anchor;if(d){var e=kI(b,c)<0;if(e!=(kI(d,c)<0)){c=b;b=d}else{if(e!=(kI(b,d)<0)){b=d}}}return new lz(c,b)}else{return new lz(d||b,b)}}function g5(b,c,a,d){mL(b,new g0([im(b,b.sel.primary(),c,a)],0),d)}function mN(a,b,d){var e=[];for(var c=0;c=d.ch:m.to>d.ch))){if(f){n9(l,"beforeCursorEnter");if(l.explicitlyCleared){if(!a.markedSpans){break}else{--h;continue}}}if(!l.atomic){continue}if(j){var e=l.find(k<0?1:-1),c=void 0;if(k<0?l.inclusiveRight:l.inclusiveLeft){e=hF(b,e,-k,e&&e.line==d.line?a:null)}if(e&&e.line==d.line&&(c=kI(e,j))&&(k<0?c<0:c>0)){return iS(b,e,d,k,f)}}var g=l.find(k<0?-1:1);if(k<0?l.inclusiveLeft:l.inclusiveRight){g=hF(b,g,k,g.line==d.line?a:null)}return g?iS(b,g,d,k,f):null}}}return d}function mJ(b,a,e,g,d){var f=g||1;var c=iS(b,a,e,f,d)||(!d&&iS(b,a,e,f,true))||iS(b,a,e,-f,d)||(!d&&iS(b,a,e,-f,true));if(!c){b.cantEdit=true;return lA(b.first,0)}return c}function hF(c,b,d,a){if(d<0&&b.ch==0){if(b.line>c.first){return i3(c,lA(b.line-1))}else{return null}}else{if(d>0&&b.ch==(a||hm(c,b.line)).text.length){if(b.line=0;--e){mf(b,{from:d[e].from,to:d[e].to,text:e?[""]:a.text})}}else{mf(b,a)}}function mf(c,b){if(b.text.length==1&&b.text[0]==""&&kI(b.from,b.to)==0){return}var d=iK(c,b);iX(c,b,d,c.cm?c.cm.curOp.id:NaN);ik(c,b,d,h5(c,b));var a=[];iA(c,function(e,f){if(!f&&jb(a,e.history)==-1){kF(e.history,b);a.push(e.history)}ik(e,b,null,h5(e,b))})}function k5(c,d,a){if(c.cm&&c.cm.state.suppressEdits&&!a){return}var e=c.history,n,l=c.sel;var o=d=="undo"?e.done:e.undone,b=d=="undo"?e.undone:e.done;var h=0;for(;h=0;--k){var j=g(k);if(j){return j.v}}}function iC(d,b){if(b==0){return}d.first+=b;d.sel=new g0(mO(d.sel.ranges,function(e){return new lz(lA(e.anchor.line+b,e.anchor.ch),lA(e.head.line+b,e.head.ch))}),d.sel.primIndex);if(d.cm){np(d.cm,d.first,d.first-b,b);for(var c=d.cm.display,a=c.viewFrom;ab.lastLine()){return}if(a.from.linee){a={from:a.from,to:lA(e,hm(b,e).text.length),text:[a.text[0]],origin:a.origin}}a.removed=gY(b,a.from,a.to);if(!d){d=iK(b,a)}if(b.cm){n5(b.cm,a,f)}else{id(b,a,f)}jN(b,d,ln)}function n5(b,g,j){var c=b.doc,h=b.display,f=g.from,d=g.to;var e=false,k=f.line;if(!b.options.lineWrapping){k=m5(jV(hm(c,f.line)));c.iter(k,d.line+1,function(o){if(o==h.maxLine){e=true;return true}})}if(c.sel.contains(g.from,g.to)>-1){lD(b)}id(c,g,j,mp(b));if(!b.options.lineWrapping){c.iter(k,f.line+g.text.length,function(o){var p=hZ(o);if(p>h.maxLineLength){h.maxLine=o;h.maxLineLength=p;h.maxLineChanged=true;e=false}});if(e){b.curOp.updateMaxLine=true}}c.frontier=Math.min(c.frontier,f.line);ij(b,400);var a=g.text.length-(d.line-f.line)-1;if(g.full){np(b)}else{if(f.line==d.line&&g.text.length==1&&!lR(b.doc,g)){lS(b,f.line,"text")}else{np(b,f.line,d.line+1,a)}}var m=iI(b,"changes"),l=iI(b,"change");if(l||m){var n={from:f,to:d,text:g.text,removed:g.removed,origin:g.origin};if(l){nw(b,"change",b,n)}if(m){(b.curOp.changeObjs||(b.curOp.changeObjs=[])).push(n)}}b.display.selForContextMenu=null}function gT(d,e,a,b,c){if(!b){b=a}if(kI(b,a)<0){var f=b;b=a;a=f}if(typeof e=="string"){e=d.splitLines(e)}mi(d,{from:a,to:b,text:e,origin:c})}function ml(b,c,d,a){if(d1||!(this.children[0] instanceof hH))){var a=[];this.collapse(a);this.children=[new hH(a)];this.children[0].parent=this}},collapse:function(c){var a=this;for(var b=0;b50){var h=k.lines.length%25+25;for(var c=h;c10);c.parent.maybeSpill()},iterN:function(g,h,a){var b=this;for(var f=0;fc.display.maxLineLength){c.display.maxLine=m;c.display.maxLineLength=f;c.display.maxLineChanged=true}}}if(j!=null&&c&&this.collapsed){np(c,j,e+1)}this.lines.length=0;this.explicitlyCleared=true;if(this.atomic&&this.doc.cantEdit){this.doc.cantEdit=false;if(c){jt(c.doc)}}if(c){nw(c,"markerCleared",c,this)}if(k){ng(c)}if(this.parent){this.parent.clear()}};lV.prototype.find=function(d,f){var h=this;if(d==null&&this.type=="bookmark"){d=1}var a,b;for(var e=0;e0||b==0&&h.clearWhenEmpty!==false){return h}if(h.replacedWith){h.collapsed=true;h.widgetNode=gZ("span",[h.replacedWith],"CodeMirror-widget");if(!a.handleMouseEvents){h.widgetNode.setAttribute("cm-ignore-events","true")}if(a.insertLeft){h.widgetNode.insertLeft=true}}if(h.collapsed){if(jS(c,e.line,e,d,h)||e.line!=d.line&&jS(c,d.line,e,d,h)){throw new Error("Inserting collapsed marker partially overlapping an existing one")}lf()}if(h.addToHistory){iX(c,{from:e,to:d,origin:"markText"},c.sel,NaN)}var k=e.line,f=c.cm,l;c.iter(k,d.line+1,function(m){if(f&&h.collapsed&&!f.options.lineWrapping&&jV(m)==f.display.maxLine){l=true}if(h.collapsed&&k!=e.line){gV(m,0)}kP(m,new h7(h,k==e.line?e.ch:null,k==d.line?d.ch:null));++k});if(h.collapsed){c.iter(e.line,d.line+1,function(m){if(il(c,m)){gV(m,0)}})}if(h.clearOnEnter){nY(h,"beforeCursorEnter",function(){return h.clear()})}if(h.readOnly){ia();if(c.history.done.length||c.history.undone.length){c.clearHistory()}}if(h.collapsed){h.id=++gQ;h.atomic=true}if(f){if(l){f.curOp.updateMaxLine=true}if(h.collapsed){np(f,e.line,d.line+1)}else{if(h.className||h.title||h.startStyle||h.endStyle||h.css){for(var j=e.line;j<=d.line;j++){lS(f,j,"text")}}}if(h.atomic){jt(f.doc)}nw(f,"markerAdded",f,h)}return h}function jZ(b,c){var a=this;this.markers=b;this.primary=c;for(var d=0;d=0;h--){mi(e,c[h])}if(k){ht(this,k)}else{if(this.cm){jl(this.cm)}}}),undo:mg(function(){k5(this,"undo")}),redo:mg(function(){k5(this,"redo")}),undoSelection:mg(function(){k5(this,"undo",true)}),redoSelection:mg(function(){k5(this,"redo",true)}),setExtending:function(a){this.extend=a},getExtending:function(){return this.extend},historySize:function(){var b=this.history,e=0,c=0;for(var d=0;d=a.ch)){b.push(c.marker.parent||c.marker)}}}return b},findMarks:function(a,c,e){a=i3(this,a);c=i3(this,c);var d=[],b=a.line;this.iter(a.line,c.line+1,function(h){var f=h.markedSpans;if(f){for(var g=0;g=j.to||j.from==null&&b!=a.line||j.from!=null&&b==c.line&&j.from>=c.ch)&&(!e||e(j.marker))){d.push(j.marker.parent||j.marker)}}}++b});return d},getAllMarks:function(){var a=[];this.iter(function(c){var d=c.markedSpans;if(d){for(var b=0;bb){d=b;return true}b-=e;++a});return i3(this,lA(a,d))},indexFromPos:function(b){b=i3(this,b);var a=b.ch;if(b.linea){a=b.from}if(b.to!=null&&b.to-1){b.state.draggingText(d);setTimeout(function(){return b.display.input.focus()},20);return}try{var m=d.dataTransfer.getData("Text");if(m){var g;if(b.state.draggingText&&!b.state.draggingText.copy){g=b.listSelections()}jN(b.doc,j1(c,c));if(g){for(var j=0;j=0;h--){gT(g.doc,"",c[h].from,c[h].to,"+delete")}jl(g)})}var jg={selectAll:nh,singleSelection:function(a){return a.setSelection(a.getCursor("anchor"),a.getCursor("head"),ln)},killLine:function(a){return hL(a,function(b){if(b.empty()){var c=hm(a.doc,b.head.line).text.length;if(b.head.ch==c&&b.head.line0){b=new lA(b.line,b.ch+1);a.replaceRange(g.charAt(b.ch-1)+g.charAt(b.ch-2),lA(b.line,b.ch-2),b,"+transpose")}else{if(b.line>a.doc.first){var c=hm(a.doc,b.line-1).text;if(c){b=new lA(b.line,1);a.replaceRange(g.charAt(0)+a.doc.lineSeparator()+c.charAt(c.length-1),lA(b.line-1,c.length-1),b,"+transpose")}}}}f.push(new lz(b,b))}a.setSelections(f)})},newlineAndIndent:function(a){return lI(a,function(){var d=a.listSelections();for(var c=d.length-1;c>=0;c--){a.replaceRange(a.doc.lineSeparator(),d[c].anchor,d[c].head,"+input")}d=a.listSelections();for(var b=0;bg-400&&kI(jr.pos,a)==0){d="triple"}else{if(i1&&i1.time>g-400&&kI(i1.pos,a)==0){d="double";jr={time:g,pos:a}}else{d="single";i1={time:g,pos:a}}}var c=h.doc.sel,f=k9?b.metaKey:b.ctrlKey,e;if(h.options.dragDrop&&kr&&!h.isReadOnly()&&d=="single"&&(e=c.contains(a))>-1&&(kI((e=c.ranges[e]).from(),a)<0||a.xRel>0)&&(kI(e.to(),a)>0||a.xRel<0)){gR(h,b,a,f)}else{kB(h,b,a,d,f)}}function gR(f,b,a,g){var c=f.display,e=+new Date;var d=mQ(f,function(h){if(mU){c.scroller.draggable=false}f.state.draggingText=false;ih(document,"mouseup",d);ih(c.scroller,"drop",d);if(Math.abs(b.clientX-h.clientX)+Math.abs(b.clientY-h.clientY)<10){l7(h);if(!g&&+new Date-200-1){o=q[s]}else{o=new lz(n,n)}}else{o=r.sel.primary();s=r.sel.primIndex}if(i6?t.shiftKey&&t.metaKey:t.altKey){p="rect";if(!k){o=new lz(n,n)}n=kg(m,t,true,true);s=-1}else{if(p=="double"){var v=m.findWordAt(n);if(m.display.shift||r.extend){o=im(r,o,v.anchor,v.head)}else{o=v}}else{if(p=="triple"){var g=new lz(lA(n.line,0),i3(r,lA(n.line+1,0)));if(m.display.shift||r.extend){o=im(r,o,g.anchor,g.head)}else{o=g}}else{o=im(r,o,n)}}}if(!k){s=0;mL(r,new g0([o],0),l9);l=r.sel}else{if(s==-1){s=q.length;mL(r,mA(q.concat([o]),s),{scroll:false,origin:"*mouse"})}else{if(q.length>1&&q[s].empty()&&p=="single"&&!t.shiftKey){mL(r,mA(q.slice(0,s).concat(q.slice(s+1)),0),{scroll:false,origin:"*mouse"});l=r.sel}else{k2(r,s,o,l9)}}}var b=n;function c(B){if(kI(b,B)==0){return}b=B;if(p=="rect"){var J=[],E=m.options.tabSize;var K=mV(hm(r,n.line).text,n.ch,E);var x=mV(hm(r,B.line).text,B.ch,E);var I=Math.min(K,x),z=Math.max(K,x);for(var w=Math.min(n.line,B.line),G=Math.min(m.lastLine(),Math.max(n.line,B.line));w<=G;w++){var y=hm(r,w).text,H=jM(y,I,E);if(I==z){J.push(new lz(lA(w,H),lA(w,H)))}else{if(y.length>H){J.push(new lz(lA(w,H),lA(w,jM(y,z,E))))}}}if(!J.length){J.push(new lz(n,n))}mL(r,mA(l.ranges.slice(0,s).concat(J),s),{origin:"*mouse",scroll:false});m.scrollIntoView(B)}else{var D=o;var F=D.anchor,C=B;if(p!="single"){var A;if(p=="double"){A=m.findWordAt(B)}else{A=new lz(lA(B.line,0),i3(r,lA(B.line+1,0)))}if(kI(A.anchor,F)>0){C=A.head;F=m3(D.from(),A.anchor)}else{C=A.anchor;F=nD(D.to(),A.head)}}var L=l.ranges.slice(0);L[s]=new lz(i3(r,F),C);mL(r,mA(L,s),l9)}}var e=a.wrapper.getBoundingClientRect();var j=0;function u(x){var z=++j;var A=kg(m,x,true,p=="rect");if(!A){return}if(kI(A,b)!=0){m.curOp.focus=j3();c(A);var w=lc(a,r);if(A.line>=w.to||A.linee.bottom?20:0;if(y){setTimeout(mQ(m,function(){if(j!=z){return}a.scroller.scrollTop+=y;u(x)}),50)}}}function f(w){m.state.selectingText=false;j=Infinity;l7(w);a.input.focus();ih(document,"mousemove",d);ih(document,"mouseup",h);r.history.lastSelOrigin=null}var d=mQ(m,function(w){if(!hi(w)){f(w)}else{u(w)}});var h=mQ(m,f);m.state.selectingText=h;nY(document,"mousemove",d);nY(document,"mouseup",h)}function hC(b,f,d,c){var l,m;try{l=f.clientX;m=f.clientY}catch(f){return false}if(l>=Math.floor(b.display.gutters.getBoundingClientRect().right)){return false}if(c){l7(f)}var e=b.display;var g=e.lineDiv.getBoundingClientRect();if(m>g.bottom||!iI(b,d)){return na(f)}m-=g.top-e.viewOffset;for(var j=0;j=l){var a=nj(b.doc,m);var k=b.options.gutters[j];n9(b,d,b,a,k,f);return na(f)}}}function kE(a,b){return hC(a,b,"gutterClick",true)}function mI(a,b){if(my(a.display,b)||jh(a,b)){return}if(nT(a,b,"contextmenu")){return}a.display.input.onContextMenu(b)}function jh(a,b){if(!iI(a,"gutterContextMenu")){return false}return hC(a,b,"gutterContextMenu",false)}function lC(a){a.display.wrapper.className=a.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+a.options.theme.replace(/(^|\s)\s*/g," cm-s-");nm(a)}var kQ={toString:function(){return"CodeMirror.Init"}};var hB={};var mk={};function i0(a){var b=a.optionHandlers;function c(g,d,e,f){a.defaults[g]=d;if(e){b[g]=f?function(h,j,k){if(k!=kQ){e(h,j,k)}}:e}}a.defineOption=c;a.Init=kQ;c("value","",function(e,d){return e.setValue(d)},true);c("mode",null,function(e,d){e.doc.modeOption=d;ly(e)},true);c("indentUnit",2,ly,true);c("indentWithTabs",false);c("smartIndent",true);c("tabSize",4,function(d){h1(d);nm(d);np(d)},true);c("lineSeparator",null,function(h,e){h.doc.lineSep=e;if(!e){return}var f=[],d=h.doc.first;h.doc.iter(function(l){for(var j=0;;){var k=l.text.indexOf(e,j);if(k==-1){break}j=k+e.length;f.push(lA(d,k))}d++});for(var g=f.length-1;g>=0;g--){gT(h.doc,e,f[g],lA(f[g].line,f[g].ch+e.length))}});c("specialChars",/[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g,function(f,d,e){f.state.specialChars=new RegExp(d.source+(d.test("\t")?"":"|\t"),"g");if(e!=kQ){f.refresh()}});c("specialCharPlaceholder",hn,function(d){return d.refresh()},true);c("electricChars",true);c("inputStyle",ie?"contenteditable":"textarea",function(){throw new Error("inputStyle can not (yet) be changed in a running editor")},true);c("spellcheck",false,function(e,d){return e.getInputField().spellcheck=d},true);c("rtlMoveVisually",!nW);c("wholeLineUpdateBefore",true);c("theme","default",function(d){lC(d);k1(d)},true);c("keyMap","default",function(h,d,g){var f=g6(d);var e=g!=kQ&&g6(g);if(e&&e.detach){e.detach(h,f)}if(f.attach){f.attach(h,e||null)}});c("extraKeys",null);c("lineWrapping",false,i2,true);c("gutters",[],function(d){kK(d.options);k1(d)},true);c("fixedGutter",true,function(e,d){e.display.gutters.style.left=d?lw(e.display)+"px":"0";e.refresh()},true);c("coverGutterNextToScrollbar",false,function(d){return hK(d)},true);c("scrollbarStyle","native",function(d){oa(d);hK(d);d.display.scrollbars.setScrollTop(d.doc.scrollTop);d.display.scrollbars.setScrollLeft(d.doc.scrollLeft)},true);c("lineNumbers",false,function(d){kK(d.options);k1(d)},true);c("firstLineNumber",1,k1,true);c("lineNumberFormatter",function(d){return d},k1,true);c("showCursorWhenSelecting",false,nu,true);c("resetSelectionOnContextMenu",true);c("lineWiseCopyCut",true);c("readOnly",false,function(e,d){if(d=="nocursor"){nL(e);e.display.input.blur();e.display.disabled=true}else{e.display.disabled=false}e.display.input.readOnlyChanged(d)});c("disableInput",false,function(e,d){if(!d){e.display.input.reset()}},true);c("dragDrop",true,g2);c("allowDropFileTypes",null);c("cursorBlinkRate",530);c("cursorScrollMargin",0);c("cursorHeight",1,nu,true);c("singleCursorHeightPerLine",true,nu,true);c("workTime",100);c("workDelay",100);c("flattenSpans",true,h1,true);c("addModeClass",false,h1,true);c("pollInterval",100);c("undoDepth",200,function(e,d){return e.doc.history.undoDepth=d});c("historyEventDelay",1250);c("viewportMargin",10,function(d){return d.refresh()},true);c("maxHighlightLength",10000,h1,true);c("moveInputWithCursor",true,function(e,d){if(!d){e.display.input.resetPosition()}});c("tabindex",null,function(e,d){return e.display.input.getField().tabIndex=d||""});c("autofocus",null)}function k1(a){io(a);np(a);setTimeout(function(){return ja(a)},20)}function g2(f,b,e){var a=e&&e!=kQ;if(!b!=!a){var d=f.display.dragFunctions;var c=b?nY:ih;c(f.display.scroller,"dragstart",d.start);c(f.display.scroller,"dragenter",d.enter);c(f.display.scroller,"dragover",d.over);c(f.display.scroller,"dragleave",d.leave);c(f.display.scroller,"drop",d.drop)}}function i2(a){if(a.options.lineWrapping){h6(a.display.wrapper,"CodeMirror-wrap");a.display.sizer.style.minWidth="";a.display.sizerWidth=null}else{kS(a.display.wrapper,"CodeMirror-wrap");kY(a)}lr(a);np(a);nm(a);setTimeout(function(){return hK(a)},100)}function hx(g,e){var h=this;if(!(this instanceof hx)){return new hx(g,e)}this.options=e=e?n0(e):{};n0(hB,e,false);kK(e);var a=e.value;if(typeof a=="string"){a=new mT(a,e.mode,null,e.lineSeparator)}this.doc=a;var f=new hx.inputStyles[e.inputStyle](this);var b=this.display=new iU(g,a,f);b.wrapper.CodeMirror=this;io(this);lC(this);if(e.lineWrapping){this.display.wrapper.className+=" CodeMirror-wrap"}if(e.autofocus&&!ie){b.input.focus()}oa(this);this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:false,delayingBlurEvent:false,focused:false,suppressEdits:false,pasteIncoming:false,cutIncoming:false,selectingText:false,draggingText:false,highlight:new hA(),keySeq:null,specialChars:null};if(kj&&kH<11){setTimeout(function(){return h.display.input.reset(true)},20)}hf(this);l8();lY(this);this.curOp.forceUpdate=true;ip(this,a);if((e.autofocus&&!ie)||this.hasFocus()){setTimeout(mD(mm,this),20)}else{nL(this)}for(var c in mk){if(mk.hasOwnProperty(c)){mk[c](h,e[c],kQ)}}iE(this);if(e.finishInit){e.finishInit(this)}for(var d=0;d20*20}nY(c.scroller,"touchstart",function(j){if(!nT(h,j)&&!e(j)){c.input.ensurePolled();clearTimeout(a);var k=+new Date;c.activeTouch={start:k,moved:false,prev:k-f.end<=300?f:null};if(j.touches.length==1){c.activeTouch.left=j.touches[0].pageX;c.activeTouch.top=j.touches[0].pageY}}});nY(c.scroller,"touchmove",function(){if(c.activeTouch){c.activeTouch.moved=true}});nY(c.scroller,"touchend",function(l){var j=c.activeTouch;if(j&&!my(c,l)&&j.left!=null&&!j.moved&&new Date-j.start<300){var k=h.coordsChar(c.activeTouch,"page"),m;if(!j.prev||g(j,j.prev)){m=new lz(k,k)}else{if(!j.prev.prev||g(j,j.prev.prev)){m=h.findWordAt(k)}else{m=new lz(lA(k.line,0),i3(h.doc,lA(k.line+1,0)))}}h.setSelection(m.anchor,m.head);h.focus();l7(l)}b()});nY(c.scroller,"touchcancel",b);nY(c.scroller,"scroll",function(){if(c.scroller.clientHeight){l4(h,c.scroller.scrollTop);nq(h,c.scroller.scrollLeft,true);n9(h,"scroll",h)}});nY(c.scroller,"mousewheel",function(j){return k8(h,j)});nY(c.scroller,"DOMMouseScroll",function(j){return k8(h,j)});nY(c.wrapper,"scroll",function(){return c.wrapper.scrollTop=c.wrapper.scrollLeft=0});c.dragFunctions={enter:function(j){if(!nT(h,j)){jK(j)}},over:function(j){if(!nT(h,j)){hD(h,j);jK(j)}},start:function(j){return l0(h,j)},drop:mQ(h,l5),leave:function(j){if(!nT(h,j)){jx(h)}}};var d=c.input.getField();nY(d,"keyup",function(j){return me.call(h,j)});nY(d,"keydown",mQ(h,kp));nY(d,"keypress",mQ(h,mw));nY(d,"focus",function(j){return mm(h,j)});nY(d,"blur",function(j){return nL(h,j)})}var a9=[];hx.defineInitHook=function(a){return a9.push(a)};function nz(r,l,a,n){var b=r.doc,o;if(a==null){a="add"}if(a=="smart"){if(!b.mode.indent){a="prev"}else{o=kM(r,l)}}var g=r.options.tabSize;var q=hm(b,l),h=mV(q.text,null,g);if(q.stateAfter){q.stateAfter=null}var p=q.text.match(/^\s*/)[0],d;if(!n&&!/\S/.test(q.text)){d=0;a="not"}else{if(a=="smart"){d=b.mode.indent(o,q.text.slice(p.length),q.text);if(d==kU||d>150){if(!n){return}a="prev"}}}if(a=="prev"){if(l>b.first){d=mV(hm(b,l-1).text,null,g)}else{d=0}}else{if(a=="add"){d=h+r.options.indentUnit}else{if(a=="subtract"){d=h-r.options.indentUnit}else{if(typeof a=="number"){d=h+a}}}}d=Math.max(0,d);var c="",e=0;if(r.options.indentWithTabs){for(var k=Math.floor(d/g);k;--k){e+=g;c+="\t"}}if(e1){if(lZ&&lZ.text.join("\n")==k){if(n.ranges.length%lZ.text.length==0){p=[];for(var j=0;j=0;o--){var q=n.ranges[o];var d=q.from(),e=q.to();if(q.empty()){if(m&&m>0){d=lA(d.line,d.ch-m)}else{if(a.state.overwrite&&!l){e=lA(e.line,Math.min(hm(c,e.line).text.length,e.ch+ji(h).length))}else{if(lZ&&lZ.lineWise&&lZ.text.join("\n")==k){d=e=lA(d.line,0)}}}}g=a.curOp.updateInput;var f={from:d,to:e,text:p?p[o%p.length]:h,origin:b||(l?"paste":a.state.cutIncoming?"cut":"+input")};mi(a.doc,f);nw(a,"inputRead",a,f)}if(k&&!l){g7(a,k)}jl(a);a.curOp.updateInput=g;a.curOp.typing=true;a.state.pasteIncoming=a.state.cutIncoming=false}function mz(b,a){var c=b.clipboardData&&b.clipboardData.getData("Text");if(c){b.preventDefault();if(!a.isReadOnly()&&!a.options.disableInput){lI(a,function(){return g4(a,c,0,null,"paste")})}return true}}function g7(g,d){if(!g.options.electricChars||!g.options.smartIndent){return}var c=g.doc.sel;for(var e=c.ranges.length-1;e>=0;e--){var a=c.ranges[e];if(a.head.ch>100||(e&&c.ranges[e-1].head.line==a.head.line)){continue}var b=g.getModeAt(a.head);var h=false;if(b.electricChars){for(var f=0;f-1){h=nz(g,a.head.line,"smart");break}}}else{if(b.electricInput){if(b.electricInput.test(hm(g.doc,a.head.line).text.slice(0,a.head.ch))){h=nz(g,a.head.line,"smart")}}}if(h){nw(g,"electricInput",g,a.head.line)}}}function jf(f){var a=[],d=[];for(var c=0;c0){k2(h.doc,j,new lz(g,m[j].to()),ln)}}else{if(d.head.line>l){nz(h,d.head.line,e,true);l=d.head.line;if(j==h.doc.sel.primIndex){jl(h)}}}}}),getTokenAt:function(d,e){return j4(this,d,e)},getLineTokens:function(d,e){return j4(this,lA(d),e,true)},getTokenTypeAt:function(j){j=i3(this.doc,j);var e=jG(this,hm(this.doc,j.line));var l=0,k=(e.length-1)/2,f=j.ch;var g;if(f==0){g=e[2]}else{for(;;){var h=(l+k)>>1;if((h?e[h*2-1]:0)>=f){k=h}else{if(e[h*2+1]e){g=e;h=true}}f=hm(this.doc,g)}else{f=g}return j8(this,f,{top:0,left:0},j||"page",d).top+(h?this.doc.height-m7(f):0)},defaultTextHeight:function(){return nF(this.display)},defaultCharWidth:function(){return kJ(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(h,l,f,k,d){var j=this.display;h=lL(this,i3(this.doc,h));var g=h.bottom,m=h.left;l.style.position="absolute";l.setAttribute("cm-ignore-events","true");this.display.input.setUneditable(l);j.sizer.appendChild(l);if(k=="over"){g=h.top}else{if(k=="above"||k=="near"){var n=Math.max(j.wrapper.clientHeight,this.doc.height),e=Math.max(j.sizer.clientWidth,j.lineSpace.clientWidth);if((k=="above"||h.bottom+l.offsetHeight>n)&&h.top>l.offsetHeight){g=h.top-l.offsetHeight}else{if(h.bottom+l.offsetHeight<=n){g=h.bottom}}if(m+l.offsetWidth>e){m=e-l.offsetWidth}}}l.style.top=g+"px";l.style.left=l.style.right="";if(d=="right"){m=j.sizer.clientWidth-l.offsetWidth;l.style.right="0px"}else{if(d=="left"){m=0}else{if(d=="middle"){m=(j.sizer.clientWidth-l.offsetWidth)/2}}l.style.left=m+"px"}if(f){mt(this,m,g,m+l.offsetWidth,g+l.offsetHeight)}},triggerOnKeyDown:jC(kp),triggerOnKeyPress:jC(mw),triggerOnKeyUp:me,execCommand:function(d){if(jg.hasOwnProperty(d)){return jg[d].call(null,this)}},triggerElectric:jC(function(d){g7(this,d)}),findPosH:function(j,e,d,g){var k=this;var h=1;if(e<0){h=-1;e=-e}var l=i3(this.doc,j);for(var f=0;f0&&h(e.charAt(k-1))){--k}while(f0.5){lr(this)}n9(this,"refresh",this)}),swapDoc:jC(function(d){var e=this.doc;e.cm=null;ip(this,d);nm(this);this.display.input.reset();this.scrollTo(d.scrollLeft,d.scrollTop);this.curOp.forceScroll=true;nw(this,"swapDoc",this,e);return e}),getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}};nA(a);a.registerHelper=function(e,f,d){if(!b.hasOwnProperty(e)){b[e]=a[e]={_global:[]}}b[e][f]=d};a.registerGlobalHelper=function(e,f,g,d){a.registerHelper(e,f,d);b[e]._global.push({pred:g,val:d})}};function lg(r,m,d,e,k){var g=m.line,f=m.ch,s=d;var p=hm(r,g);function a(){var t=g+d;if(t=r.first+r.size){return false}g=t;return p=hm(r,t)}function b(t){var u=(k?ka:nn)(p,f,d,true);if(u==null){if(!t&&a()){if(k){f=(d<0?lj:md)(p)}else{f=d<0?p.text.length:0}}else{return false}}else{f=u}return true}if(e=="char"){b()}else{if(e=="column"){b(true)}else{if(e=="word"||e=="group"){var c=null,j=e=="group";var q=r.cm&&r.cm.getHelper(m,"wordChars");for(var l=true;;l=false){if(d<0&&!b(!l)){break}var o=p.text.charAt(f)||"\n";var n=mo(o,q)?"w":j&&o=="\n"?"n":!j||/\s/.test(o)?null:"p";if(j&&!l&&!n){n="s"}if(c&&c!=n){if(d<0){d=1;b()}break}if(n){c=n}if(d>0&&!b(!l)){break}}}}}var h=mJ(r,lA(g,f),m,s,true);if(!kI(m,h)){h.hitSide=true}return h}function lB(b,g,k,c){var d=b.doc,e=g.left,f;if(c=="page"){var h=Math.min(b.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight);var a=Math.max(h-0.5*nF(b.display),3);f=(k>0?g.bottom:g.top)+k*a}else{if(c=="line"){f=k>0?g.bottom+3:g.top-3}}var j;for(;;){j=hg(b,e,f);if(!j.outside){break}if(k<0?f<=0:f>=d.height){j.hitSide=true;break}f+=k*5}return j}function k7(a){this.cm=a;this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null;this.polling=new hA();this.composing=null;this.gracePeriod=false;this.readDOMTimeout=null}k7.prototype=n0({init:function(d){var a=this;var e=this,c=e.cm;var b=e.div=d.lineDiv;hh(b,c.options.spellcheck);nY(b,"paste",function(g){if(nT(c,g)||mz(g,c)){return}if(kH<=11){setTimeout(mQ(c,function(){if(!e.pollContent()){np(c)}}),20)}});nY(b,"compositionstart",function(g){a.composing={data:g.data}});nY(b,"compositionupdate",function(g){if(!a.composing){a.composing={data:g.data}}});nY(b,"compositionend",function(g){if(a.composing){if(g.data!=a.composing.data){a.readFromDOMSoon()}a.composing=null}});nY(b,"touchstart",function(){return e.forceCompositionEnd()});nY(b,"input",function(){if(!a.composing){a.readFromDOMSoon()}});function f(k){if(nT(c,k)){return}if(c.somethingSelected()){jJ({lineWise:false,text:c.getSelections()});if(k.type=="cut"){c.replaceSelection("",null,"cut")}}else{if(!c.options.lineWiseCopyCut){return}else{var g=jf(c);jJ({lineWise:true,text:g.text});if(k.type=="cut"){c.operation(function(){c.setSelections(g.ranges,0,ln);c.replaceSelection("",null,"cut")})}}}if(k.clipboardData){k.clipboardData.clearData();var l=lZ.text.join("\n");k.clipboardData.setData("Text",l);if(k.clipboardData.getData("Text")==l){k.preventDefault();return}}var m=nG(),j=m.firstChild;c.display.lineSpace.insertBefore(m,c.display.lineSpace.firstChild);j.value=lZ.text.join("\n");var h=document.activeElement;kf(j);setTimeout(function(){c.display.lineSpace.removeChild(m);h.focus();if(h==b){e.showPrimarySelection()}},50)}nY(b,"copy",f);nY(b,"cut",f)},prepareSelection:function(){var a=i9(this.cm,false);a.focus=this.cm.state.focused;return a},showSelection:function(b,a){if(!b||!this.cm.display.view.length){return}if(b.focus||a){this.showPrimarySelection()}this.showMultipleSelections(b)},showPrimarySelection:function(){var k=window.getSelection(),g=this.cm.doc.sel.primary();var j=mG(this.cm,k.anchorNode,k.anchorOffset);var e=mG(this.cm,k.focusNode,k.focusOffset);if(j&&!j.bad&&e&&!e.bad&&kI(m3(j,e),g.from())==0&&kI(nD(j,e),g.to())==0){return}var l=mq(this.cm,g.from());var f=mq(this.cm,g.to());if(!l&&!f){return}var b=this.cm.display.view;var h=k.rangeCount&&k.getRangeAt(0);if(!l){l={node:b[0].measure.map[2],offset:0}}else{if(!f){var m=b[b.length-1].measure;var a=m.maps?m.maps[m.maps.length-1]:m.map;f={node:a[a.length-1],offset:a[a.length-2]-a[a.length-3]}}}var d;try{d=ki(l.node,l.offset,f.offset,f.node)}catch(c){}if(d){if(!kc&&this.cm.state.focused){k.collapse(l.node,l.offset);if(!d.collapsed){k.removeAllRanges();k.addRange(d)}}else{k.removeAllRanges();k.addRange(d)}if(h&&k.anchorNode==null){k.addRange(h)}else{if(kc){this.startGracePeriod()}}}this.rememberSelection()},startGracePeriod:function(){var a=this;clearTimeout(this.gracePeriod);this.gracePeriod=setTimeout(function(){a.gracePeriod=false;if(a.selectionChanged()){a.cm.operation(function(){return a.cm.curOp.selectionChanged=true})}},20)},showMultipleSelections:function(a){mR(this.cm.display.cursorDiv,a.cursors);mR(this.cm.display.selectionDiv,a.selection)},rememberSelection:function(){var a=window.getSelection();this.lastAnchorNode=a.anchorNode;this.lastAnchorOffset=a.anchorOffset;this.lastFocusNode=a.focusNode;this.lastFocusOffset=a.focusOffset},selectionInEditor:function(){var b=window.getSelection();if(!b.rangeCount){return false}var a=b.getRangeAt(0).commonAncestorContainer;return hR(this.div,a)},focus:function(){if(this.cm.options.readOnly!="nocursor"){if(!this.selectionInEditor()){this.showSelection(this.prepareSelection(),true)}this.div.focus()}},blur:function(){this.div.blur()},getField:function(){return this.div},supportsTouch:function(){return true},receivedFocus:function(){var a=this;if(this.selectionInEditor()){this.pollSelection()}else{lI(this.cm,function(){return a.cm.curOp.selectionChanged=true})}function b(){if(a.cm.state.focused){a.pollSelection();a.polling.set(a.cm.options.pollInterval,b)}}this.polling.set(this.cm.options.pollInterval,b)},selectionChanged:function(){var a=window.getSelection();return a.anchorNode!=this.lastAnchorNode||a.anchorOffset!=this.lastAnchorOffset||a.focusNode!=this.lastFocusNode||a.focusOffset!=this.lastFocusOffset},pollSelection:function(){if(!this.composing&&this.readDOMTimeout==null&&!this.gracePeriod&&this.selectionChanged()){var b=window.getSelection(),a=this.cm;this.rememberSelection();var d=mG(a,b.anchorNode,b.anchorOffset);var c=mG(a,b.focusNode,b.focusOffset);if(d&&c){lI(a,function(){mL(a.doc,j1(d,c),ln);if(d.bad||c.bad){a.curOp.selectionChanged=true}})}}},pollContent:function(){if(this.readDOMTimeout!=null){clearTimeout(this.readDOMTimeout);this.readDOMTimeout=null}var f=this.cm,t=f.display,v=f.doc.sel.primary();var u=v.from(),m=v.to();if(u.ch==0&&u.line>f.firstLine()){u=lA(u.line-1,hm(f.doc,u.line-1).length)}if(m.ch==hm(f.doc,m.line).text.length&&m.linet.viewTo-1){return false}var j,l,g;if(u.line==t.viewFrom||(j=iR(f,u.line))==0){l=m5(t.view[0].line);g=t.view[0].node}else{l=m5(t.view[j].line);g=t.view[j-1].node.nextSibling}var w=iR(f,m.line);var d,a;if(w==t.view.length-1){d=t.viewTo-1;a=t.lineDiv.lastChild}else{d=m5(t.view[w+1].line)-1;a=t.view[w+1].node.previousSibling}if(!g){return false}var s=f.doc.splitLines(g3(f,g,a,l,d));var b=gY(f.doc,lA(l,0),lA(d,hm(f.doc,d).text.length));while(s.length>1&&b.length>1){if(ji(s)==ji(b)){s.pop();b.pop();d--}else{if(s[0]==b[0]){s.shift();b.shift();l++}else{break}}}var x=0,o=0;var e=s[0],p=b[0],q=Math.min(e.length,p.length);while(x1||s[0]||kI(k,c)){gT(f.doc,s,k,c,"+input");return true}},ensurePolled:function(){this.forceCompositionEnd()},reset:function(){this.forceCompositionEnd()},forceCompositionEnd:function(){if(!this.composing){return}this.composing=null;if(!this.pollContent()){np(this.cm)}this.div.blur();this.div.focus()},readFromDOMSoon:function(){var a=this;if(this.readDOMTimeout!=null){return}this.readDOMTimeout=setTimeout(function(){a.readDOMTimeout=null;if(a.composing){return}if(a.cm.isReadOnly()||!a.pollContent()){lI(a.cm,function(){return np(a.cm)})}},80)},setUneditable:function(a){a.contentEditable="false"},onKeyPress:function(a){a.preventDefault();if(!this.cm.isReadOnly()){mQ(this.cm,g4)(this.cm,String.fromCharCode(a.charCode==null?a.keyCode:a.charCode),0)}},readOnlyChanged:function(a){this.div.contentEditable=String(a!="nocursor")},onContextMenu:g8,resetPosition:g8,needsContentAttribute:true},k7.prototype);function mq(d,f){var e=hp(d,f.line);if(!e||e.hidden){return null}var b=hm(d.doc,f.line);var j=jT(e,b,f.line);var h=ld(b),g="left";if(h){var a=n6(h,f.ch);g=a%2?"right":"left"}var c=n3(j.map,f.ch,g);c.offset=c.collapse=="right"?c.end:c.start;return c}function jH(b,a){if(a){b.bad=true}return b}function g3(c,e,d,h,k){var a="",j=false,b=c.doc.lineSeparator();function g(l){return function(m){return m.id==l}}function f(o){if(o.nodeType==1){var r=o.getAttribute("cm-text");if(r!=null){if(r==""){a+=o.textContent.replace(/\u200b/g,"")}else{a+=r}return}var p=o.getAttribute("cm-marker"),l;if(p){var n=c.findMarks(lA(h,0),lA(k+1,0),g(+p));if(n.length&&(l=n[0].find())){a+=gY(c.doc,l.from,l.to).join(b)}return}if(o.getAttribute("contenteditable")=="false"){return}for(var q=0;q=9&&a.hasSelection){a.hasSelection=null}g.poll()});nY(d,"paste",function(h){if(nT(e,h)||mz(h,e)){return}e.state.pasteIncoming=true;g.fastPoll()});function c(j){if(nT(e,j)){return}if(e.somethingSelected()){jJ({lineWise:false,text:e.getSelections()});if(g.inaccurateSelection){g.prevInput="";g.inaccurateSelection=false;d.value=lZ.text.join("\n");kf(d)}}else{if(!e.options.lineWiseCopyCut){return}else{var h=jf(e);jJ({lineWise:true,text:h.text});if(j.type=="cut"){e.setSelections(h.ranges,null,ln)}else{g.prevInput="";d.value=h.text.join("\n");kf(d)}}}if(j.type=="cut"){e.state.cutIncoming=true}}nY(d,"cut",c);nY(d,"copy",c);nY(f.scroller,"paste",function(h){if(my(f,h)||nT(e,h)){return}e.state.pasteIncoming=true;g.focus()});nY(f.lineSpace,"selectstart",function(h){if(!my(f,h)){l7(h)}});nY(d,"compositionstart",function(){var h=e.getCursor("from");if(g.composing){g.composing.range.clear()}g.composing={start:h,range:e.markText(h,e.getCursor("to"),{className:"CodeMirror-composing"})}});nY(d,"compositionend",function(){if(g.composing){g.poll();g.composing.range.clear();g.composing=null}})},prepareSelection:function(){var g=this.cm,b=g.display,c=g.doc;var e=i9(g);if(g.options.moveInputWithCursor){var a=lL(g,c.sel.primary().head,"div");var f=b.wrapper.getBoundingClientRect(),d=b.lineDiv.getBoundingClientRect();e.teTop=Math.max(0,Math.min(b.wrapper.clientHeight-10,a.top+d.top-f.top));e.teLeft=Math.max(0,Math.min(b.wrapper.clientWidth-10,a.left+d.left-f.left))}return e},showSelection:function(b){var a=this.cm,c=a.display;mR(c.cursorDiv,b.cursors);mR(c.selectionDiv,b.selection);if(b.teTop!=null){this.wrapper.style.top=b.teTop+"px";this.wrapper.style.left=b.teLeft+"px"}},reset:function(e){if(this.contextMenuPending){return}var g,f,d=this.cm,b=d.doc;if(d.somethingSelected()){this.prevInput="";var a=b.sel.primary();g=jz&&(a.to().line-a.from().line>100||(f=d.getSelection()).length>1000);var c=g?"-":f||d.getSelection();this.textarea.value=c;if(d.state.focused){kf(this.textarea)}if(kj&&kH>=9){this.hasSelection=c}}else{if(!e){this.prevInput=this.textarea.value="";if(kj&&kH>=9){this.hasSelection=null}}}this.inaccurateSelection=g},getField:function(){return this.textarea},supportsTouch:function(){return false},focus:function(){if(this.cm.options.readOnly!="nocursor"&&(!ie||j3()!=this.textarea)){try{this.textarea.focus()}catch(a){}}},blur:function(){this.textarea.blur()},resetPosition:function(){this.wrapper.style.top=this.wrapper.style.left=0},receivedFocus:function(){this.slowPoll()},slowPoll:function(){var a=this;if(this.pollingFast){return}this.polling.set(this.cm.options.pollInterval,function(){a.poll();if(a.cm.state.focused){a.slowPoll()}})},fastPoll:function(){var c=false,a=this;a.pollingFast=true;function b(){var d=a.poll();if(!d&&!c){c=true;a.polling.set(60,b)}else{a.pollingFast=false;a.slowPoll()}}a.polling.set(20,b)},poll:function(){var a=this;var f=this.cm,g=this.textarea,e=this.prevInput;if(this.contextMenuPending||!f.state.focused||(lv(g)&&!e&&!this.composing)||f.isReadOnly()||f.options.disableInput||f.state.keySeq){return false}var c=g.value;if(c==e&&!f.somethingSelected()){return false}if(kj&&kH>=9&&this.hasSelection===c||k9&&/[\uf700-\uf7ff]/.test(c)){f.display.input.reset();return false}if(f.doc.sel==f.display.selForContextMenu){var d=c.charCodeAt(0);if(d==8203&&!e){e="\u200b"}if(d==8666){this.reset();return this.cm.execCommand("undo")}}var b=0,h=Math.min(e.length,c.length);while(b1000||c.indexOf("\n")>-1){g.value=a.prevInput=""}else{a.prevInput=c}if(a.composing){a.composing.range.clear();a.composing.range=f.markText(a.composing.start,f.getCursor("to"),{className:"CodeMirror-composing"})}});return true},ensurePolled:function(){if(this.pollingFast&&this.poll()){this.pollingFast=false}},onKeyPress:function(){if(kj&&kH>=9){this.hasSelection=null}this.fastPoll()},onContextMenu:function(l){var e=this,d=e.cm,j=d.display,p=e.textarea;var f=kg(d,l),g=j.scroller.scrollTop;if(!f||iL){return}var m=d.options.resetSelectionOnContextMenu;if(m&&d.doc.sel.contains(f)==-1){mQ(d,mL)(d.doc,j1(f),ln)}var k=p.style.cssText,a=e.wrapper.style.cssText;e.wrapper.style.cssText="position: absolute";var b=e.wrapper.getBoundingClientRect();p.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(l.clientY-b.top-5)+"px; left: "+(l.clientX-b.left-5)+"px;\n z-index: 1000; background: "+(kj?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";var c;if(mU){c=window.scrollY}j.input.focus();if(mU){window.scrollTo(null,c)}j.input.reset();if(!d.somethingSelected()){p.value=e.prevInput=" "}e.contextMenuPending=true;j.selForContextMenu=d.doc.sel;clearTimeout(j.detectingSelectAll);function n(){if(p.selectionStart!=null){var r=d.somethingSelected();var q="\u200b"+(r?p.value:"");p.value="\u21da";p.value=q;e.prevInput=r?"":"\u200b";p.selectionStart=1;p.selectionEnd=q.length;j.selForContextMenu=d.doc.sel}}function h(){e.contextMenuPending=false;e.wrapper.style.cssText=a;p.style.cssText=k;if(kj&&kH<9){j.scrollbars.setScrollTop(j.scroller.scrollTop=g)}if(p.selectionStart!=null){if(!kj||(kj&&kH<9)){n()}var r=0,q=function(){if(j.selForContextMenu==d.doc.sel&&p.selectionStart==0&&p.selectionEnd>0&&e.prevInput=="\u200b"){mQ(d,nh)(d)}else{if(r++<10){j.detectingSelectAll=setTimeout(q,500)}else{j.input.reset()}}};j.detectingSelectAll=setTimeout(q,200)}}if(kj&&kH>=9){n()}if(hT){jK(l);var o=function(){ih(window,"mouseup",o);setTimeout(h,20)};nY(window,"mouseup",o)}else{setTimeout(h,50)}},readOnlyChanged:function(a){if(!a){this.reset()}},setUneditable:g8,needsContentAttribute:false},lt.prototype);function je(c,b){b=b?n0(b):{};b.value=c.value;if(!b.tabindex&&c.tabIndex){b.tabindex=c.tabIndex}if(!b.placeholder&&c.placeholder){b.placeholder=c.placeholder}if(b.autofocus==null){var a=j3();b.autofocus=a==c||c.getAttribute("autofocus")!=null&&a==document.body}function f(){c.value=d.getValue()}var e;if(c.form){nY(c.form,"submit",f);if(!b.leaveSubmitMethodAlone){var j=c.form;e=j.submit;try{var g=j.submit=function(){f();j.submit=e;j.submit();j.submit=g}}catch(h){}}}b.finishInit=function(k){k.save=f;k.getTextArea=function(){return c};k.toTextArea=function(){k.toTextArea=isNaN;f();c.parentNode.removeChild(k.getWrapperElement());c.style.display="";if(c.form){ih(c.form,"submit",f);if(typeof c.form.submit=="function"){c.form.submit=e}}}};c.style.display="none";var d=hx(function(k){return c.parentNode.insertBefore(k,c.nextSibling)},b);return d}function iZ(a){a.off=ih;a.on=nY;a.wheelEventPixels=mF;a.Doc=mT;a.splitLines=hd;a.countColumn=mV;a.findColumn=jM;a.isWordChar=h0;a.Pass=kU;a.signal=n9;a.Line=gW;a.changeEnd=m1;a.scrollbarModel=lX;a.Pos=lA;a.cmpPos=kI;a.modes=le;a.mimeModes=nQ;a.resolveMode=hP;a.getMode=h8;a.modeExtensions=iV;a.extendMode=jc;a.copyState=nK;a.startState=nR;a.innerMode=hX;a.commands=jg;a.keyMap=hq;a.keyName=iw;a.isModifierKey=jm;a.lookupKey=kN;a.normalizeKeyMap=l2;a.StringStream=hU;a.SharedTextMarker=jZ;a.TextMarker=lV;a.LineWidget=kO;a.e_preventDefault=l7;a.e_stopPropagation=iT;a.e_stop=jK;a.addClass=h6;a.contains=hR;a.rmClass=kS;a.keyNames=iQ}i0(hx);jX(hx);var k4="iter insert remove copy getEditor constructor".split(" ");for(var nd in mT.prototype){if(mT.prototype.hasOwnProperty(nd)&&jb(k4,nd)<0){hx.prototype[nd]=(function(a){return function(){return a.apply(this.doc,arguments)}})(mT.prototype[nd])}}nA(mT);hx.inputStyles={textarea:lt,contenteditable:k7};hx.defineMode=function(a){if(!hx.defaults.mode&&a!="null"){hx.defaults.mode=a}jE.apply(this,arguments)};hx.defineMIME=lT;hx.defineMode("null",function(){return({token:function(a){return a.skipToEnd()}})});hx.defineMIME("text/plain","null");hx.defineExtension=function(a,b){hx.prototype[a]=b};hx.defineDocExtension=function(a,b){mT.prototype[a]=b};hx.fromTextArea=je;iZ(hx);hx.version="5.21.0";return hx}))); \ No newline at end of file diff --git a/route/static/codemirror/lib/index.html b/route/static/codemirror/lib/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/lib/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/codemirror/mode/index.html b/route/static/codemirror/mode/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/mode/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/codemirror/mode/modeAll.js b/route/static/codemirror/mode/modeAll.js deleted file mode 100755 index 33a90a45b..000000000 --- a/route/static/codemirror/mode/modeAll.js +++ /dev/null @@ -1 +0,0 @@ -(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(b){b.defineMode("sql",function(C,z){var B=z.client||{},w=z.atoms||{"false":true,"true":true,"null":true},s=z.builtin||{},y=z.keywords||{},x=z.operatorChars||/^[*+\-%<>!=&|~^]/,r=z.support||{},a=z.hooks||{},D=z.dateSQL||{date:true,time:true,timestamp:true};function A(c,e){var f=c.next();if(a[f]){var g=a[f](c,e);if(g!==false){return g}}if(r.hexNumber&&((f=="0"&&c.match(/^[xX][0-9a-fA-F]+/))||(f=="x"||f=="X")&&c.match(/^'[0-9a-fA-F]+'/))){return"number"}else{if(r.binaryNumber&&(((f=="b"||f=="B")&&c.match(/^'[01]+'/))||(f=="0"&&c.match(/^b[01]+/)))){return"number"}else{if(f.charCodeAt(0)>47&&f.charCodeAt(0)<58){c.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);r.decimallessFloat&&c.eat(".");return"number"}else{if(f=="?"&&(c.eatSpace()||c.eol()||c.eat(";"))){return"variable-3"}else{if(f=="'"||(f=='"'&&r.doubleQuote)){e.tokenize=q(f);return e.tokenize(c,e)}else{if((((r.nCharCast&&(f=="n"||f=="N"))||(r.charsetCast&&f=="_"&&c.match(/[a-z][a-z0-9]*/i)))&&(c.peek()=="'"||c.peek()=='"'))){return"keyword"}else{if(/^[\(\),\;\[\]]/.test(f)){return null}else{if(r.commentSlashSlash&&f=="/"&&c.eat("/")){c.skipToEnd();return"comment"}else{if((r.commentHash&&f=="#")||(f=="-"&&c.eat("-")&&(!r.commentSpaceRequired||c.eat(" ")))){c.skipToEnd();return"comment"}else{if(f=="/"&&c.eat("*")){e.tokenize=u;return e.tokenize(c,e)}else{if(f=="."){if(r.zerolessFloat&&c.match(/^(?:\d+(?:e[+-]?\d+)?)/i)){return"number"}if(r.ODBCdotTable&&c.match(/^[a-zA-Z_]+/)){return"variable-2"}}else{if(x.test(f)){c.eatWhile(x);return null}else{if(f=="{"&&(c.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/)||c.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))){return"number"}else{c.eatWhile(/^[_\w\d]/);var d=c.current().toLowerCase();if(D.hasOwnProperty(d)&&(c.match(/^( )+'[^']*'/)||c.match(/^( )+"[^"]*"/))){return"number"}if(w.hasOwnProperty(d)){return"atom"}if(s.hasOwnProperty(d)){return"builtin"}if(y.hasOwnProperty(d)){return"keyword"}if(B.hasOwnProperty(d)){return"string-2"}return null}}}}}}}}}}}}}}function q(c){return function(d,f){var e=false,g;while((g=d.next())!=null){if(g==c&&!e){f.tokenize=A;break}e=!e&&g=="\\"}return"string"}}function u(c,d){while(true){if(c.skipTo("*")){c.next();if(c.eat("/")){d.tokenize=A;break}}else{c.skipToEnd();break}}return"comment"}function v(c,d,e){d.context={prev:d.context,indent:c.indentation(),col:c.column(),type:e}}function t(c){c.indent=c.context.indent;c.context=c.context.prev}return{startState:function(){return{tokenize:A,context:null}},token:function(c,d){if(c.sol()){if(d.context&&d.context.align==null){d.context.align=false}}if(c.eatSpace()){return null}var e=d.tokenize(c,d);if(e=="comment"){return e}if(d.context&&d.context.align==null){d.context.align=true}var f=c.current();if(f=="("){v(c,d,")")}else{if(f=="["){v(c,d,"]")}else{if(d.context&&d.context.type==f){t(d)}}}return e},indent:function(c,e){var f=c.context;if(!f){return b.Pass}var d=e.charAt(0)==f.type;if(f.align){return f.col+(d?0:1)}else{return f.indent+(d?0:C.indentUnit)}},blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:r.commentSlashSlash?"//":r.commentHash?"#":null}});(function(){function j(c){var d;while((d=c.next())!=null){if(d=="`"&&!c.eat("`")){return"variable-2"}}c.backUp(c.current().length-1);return c.eatWhile(/\w/)?"variable-2":null}function a(c){if(c.eat("@")){c.match(/^session\./);c.match(/^local\./);c.match(/^global\./)}if(c.eat("'")){c.match(/^.*'/);return"variable-2"}else{if(c.eat('"')){c.match(/^.*"/);return"variable-2"}else{if(c.eat("`")){c.match(/^.*`/);return"variable-2"}else{if(c.match(/^[0-9a-zA-Z$\.\_]+/)){return"variable-2"}}}}return null}function i(c){if(c.eat("N")){return"atom"}return c.match(/^[a-zA-Z.#!?]/)?"variable-2":null}var h="alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit ";function g(c){var e={},d=c.split(" ");for(var f=0;f!=]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable doubleQuote binaryNumber hexNumber")});b.defineMIME("text/x-mssql",{name:"sql",client:g("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:g(h+"begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec"),builtin:g("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=]/,dateSQL:g("date datetimeoffset datetime2 smalldatetime datetime time"),hooks:{"@":a}});b.defineMIME("text/x-mysql",{name:"sql",client:g("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:g(h+"accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),builtin:g("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),hooks:{"@":a,"`":j,"\\":i}});b.defineMIME("text/x-mariadb",{name:"sql",client:g("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:g(h+"accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),builtin:g("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),hooks:{"@":a,"`":j,"\\":i}});b.defineMIME("text/x-cassandra",{name:"sql",client:{},keywords:g("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"),builtin:g("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"),atoms:g("false true infinity NaN"),operatorChars:/^[<>=]/,dateSQL:{},support:g("commentSlashSlash decimallessFloat"),hooks:{}});b.defineMIME("text/x-plsql",{name:"sql",client:g("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),keywords:g("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),builtin:g("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),operatorChars:/^[*+\-%<>!=~]/,dateSQL:g("date time timestamp"),support:g("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")});b.defineMIME("text/x-hive",{name:"sql",keywords:g("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"),builtin:g("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=]/,dateSQL:g("date timestamp"),support:g("ODBCdotTable doubleQuote binaryNumber hexNumber")});b.defineMIME("text/x-pgsql",{name:"sql",client:g("source"),keywords:g(h+"a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat"),builtin:g("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^\/#@?~]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")});b.defineMIME("text/x-gql",{name:"sql",keywords:g("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"),atoms:g("false true"),builtin:g("blob datetime first key __key__ string integer double boolean null"),operatorChars:/^[*+\-%<>!=]/})}())});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(e){var d={autoSelfClosers:{area:true,base:true,br:true,col:true,command:true,embed:true,frame:true,hr:true,img:true,input:true,keygen:true,link:true,meta:true,param:true,source:true,track:true,wbr:true,menuitem:true},implicitlyClosed:{dd:true,li:true,optgroup:true,option:true,p:true,rp:true,rt:true,tbody:true,td:true,tfoot:true,th:true,tr:true},contextGrabbers:{dd:{dd:true,dt:true},dt:{dd:true,dt:true},li:{li:true},option:{option:true,optgroup:true},optgroup:{optgroup:true},p:{address:true,article:true,aside:true,blockquote:true,dir:true,div:true,dl:true,fieldset:true,footer:true,form:true,h1:true,h2:true,h3:true,h4:true,h5:true,h6:true,header:true,hgroup:true,hr:true,menu:true,nav:true,ol:true,p:true,pre:true,section:true,table:true,ul:true},rp:{rp:true,rt:true},rt:{rp:true,rt:true},tbody:{tbody:true,tfoot:true},td:{td:true,th:true},tfoot:{tbody:true},th:{td:true,th:true},thead:{tbody:true,tfoot:true},tr:{tr:true}},doNotIndent:{pre:true},allowUnquoted:true,allowMissing:true,caseFold:true};var f={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:false,allowMissing:false,caseFold:false};e.defineMode("xml",function(O,b){var H=O.indentUnit;var a={};var P=b.htmlMode?d:f;for(var V in P){a[V]=P[V]}for(var V in b){a[V]=b[V]}var U,T;function L(g,h){function j(l){h.tokenize=l;return l(g,h)}var i=g.next();if(i=="<"){if(g.eat("!")){if(g.eat("[")){if(g.match("CDATA[")){return j(c("atom","]]>"))}else{return null}}else{if(g.match("--")){return j(c("comment","-->"))}else{if(g.match("DOCTYPE",true,true)){g.eatWhile(/[\w\._\-]/);return j(K(1))}else{return null}}}}else{if(g.eat("?")){g.eatWhile(/[\w\._\-]/);h.tokenize=c("meta","?>");return"meta"}else{U=g.eat("/")?"closeTag":"openTag";h.tokenize=M;return"tag bracket"}}}else{if(i=="&"){var k;if(g.eat("#")){if(g.eat("x")){k=g.eatWhile(/[a-fA-F\d]/)&&g.eat(";")}else{k=g.eatWhile(/[\d]/)&&g.eat(";")}}else{k=g.eatWhile(/[\w\.\-:]/)&&g.eat(";")}return k?"atom":"error"}else{g.eatWhile(/[^&<]/);return null}}}L.isInText=true;function M(g,h){var i=g.next();if(i==">"||(i=="/"&&g.eat(">"))){h.tokenize=L;U=i==">"?"endTag":"selfcloseTag";return"tag bracket"}else{if(i=="="){U="equals";return null}else{if(i=="<"){h.tokenize=L;h.state=N;h.tagName=h.tagStart=null;var j=h.tokenize(g,h);return j?j+" tag error":"tag error"}else{if(/[\'\"]/.test(i)){h.tokenize=Q(i);h.stringStartCol=g.column();return h.tokenize(g,h)}else{g.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);return"word"}}}}}function Q(h){var g=function(i,j){while(!i.eol()){if(i.next()==h){j.tokenize=M;break}}return"string"};g.isInAttribute=true;return g}function c(g,h){return function(i,j){while(!i.eol()){if(i.match(h)){j.tokenize=L;break}i.next()}return g}}function K(g){return function(h,i){var j;while((j=h.next())!=null){if(j=="<"){i.tokenize=K(g+1);return i.tokenize(h,i)}else{if(j==">"){if(g==1){i.tokenize=L;break}else{i.tokenize=K(g-1);return i.tokenize(h,i)}}}}return"meta"}}function F(h,i,g){this.prev=h.context;this.tagName=i;this.indent=h.indented;this.startOfLine=g;if(a.doNotIndent.hasOwnProperty(i)||(h.context&&h.context.noIndent)){this.noIndent=true}}function C(g){if(g.context){g.context=g.context.prev}}function G(g,h){var i;while(true){if(!g.context){return}i=g.context.tagName;if(!a.contextGrabbers.hasOwnProperty(i)||!a.contextGrabbers[i].hasOwnProperty(h)){return}C(g)}}function N(i,g,h){if(i=="openTag"){h.tagStart=g.column();return X}else{if(i=="closeTag"){return D}else{return N}}}function X(i,g,h){if(i=="word"){h.tagName=g.current();T="tag";return W}else{T="error";return X}}function D(i,g,h){if(i=="word"){var j=g.current();if(h.context&&h.context.tagName!=j&&a.implicitlyClosed.hasOwnProperty(h.context.tagName)){C(h)}if((h.context&&h.context.tagName==j)||a.matchClosing===false){T="tag";return E}else{T="tag error";return J}}else{T="error";return J}}function E(h,i,g){if(h!="endTag"){T="error";return E}C(g);return N}function J(i,g,h){T="error";return E(i,g,h)}function W(h,j,g){if(h=="word"){T="attribute";return R}else{if(h=="endTag"||h=="selfcloseTag"){var i=g.tagName,k=g.tagStart;g.tagName=g.tagStart=null;if(h=="selfcloseTag"||a.autoSelfClosers.hasOwnProperty(i)){G(g,i)}else{G(g,i);g.context=new F(g,i,k==g.indented)}return N}}T="error";return W}function R(i,g,h){if(i=="equals"){return I}if(!a.allowMissing){T="error"}return W(i,g,h)}function I(i,g,h){if(i=="string"){return S}if(i=="word"&&a.allowUnquoted){T="string";return W}T="error";return W(i,g,h)}function S(i,g,h){if(i=="string"){return S}return W(i,g,h)}return{startState:function(h){var g={tokenize:L,state:N,indented:h||0,tagName:null,tagStart:null,context:null};if(h!=null){g.baseIndent=h}return g},token:function(g,h){if(!h.tagName&&g.sol()){h.indented=g.indentation()}if(g.eatSpace()){return null}U=null;var i=h.tokenize(g,h);if((i||U)&&i!="comment"){T=null;h.state=h.state(U||i,g,h);if(T){i=T=="error"?i+" error":T}}return i},indent:function(g,k,h){var i=g.context;if(g.tokenize.isInAttribute){if(g.tagStart==g.indented){return g.stringStartCol+1}else{return g.indented+H}}if(i&&i.noIndent){return e.Pass}if(g.tokenize!=M&&g.tokenize!=L){return h?h.match(/^(\s*)/)[0].length:0}if(g.tagName){if(a.multilineTagIndentPastTag!==false){return g.tagStart+g.tagName.length+2}else{return g.tagStart+H*(a.multilineTagIndentFactor||1)}}if(a.alignCDATA&&/$/,blockCommentStart:"",configuration:a.htmlMode?"html":"xml",helperType:a.htmlMode?"html":"xml",skipAttribute:function(g){if(g.state==I){g.state=W}}}});e.defineMIME("text/xml","xml");e.defineMIME("application/xml","xml");if(!e.mimeModes.hasOwnProperty("text/html")){e.defineMIME("text/html",{name:"xml",htmlMode:true})}});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(A){function B(f,b,c,d,a,e){this.indented=f;this.column=b;this.type=c;this.info=d;this.align=a;this.prev=e}function L(d,b,e,a){var c=d.indented;if(d.context&&d.context.type=="statement"&&e!="statement"){c=d.context.indented}return d.context=new B(c,b,e,a,null,d.context)}function y(a){var b=a.context.type;if(b==")"||b=="]"||b=="}"){a.indented=a.context.indented}return a.context=a.context.prev}function R(a,b,c){if(b.prevToken=="variable"||b.prevToken=="variable-3"){return true}if(/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(a.string.slice(0,c))){return true}if(b.typeAtEndOfLine&&a.column()==a.indentation()){return true}}function C(a){for(;;){if(!a||a.type=="top"){return true}if(a.type=="}"&&a.prev.info!="namespace"){return false}a=a.prev}}A.defineMode("clike",function(d,W){var h=d.indentUnit,k=W.statementIndentUnit||h,g=W.dontAlignCalls,t=W.keywords||{},n=W.types||{},c=W.builtin||{},f=W.blockKeywords||{},i=W.defKeywords||{},o=W.atoms||{},q=W.hooks||{},m=W.multiLineStrings,p=W.indentStatements!==false,u=W.indentSwitch!==false,Z=W.namespaceSeparator,X=W.isPunctuationChar||/[\[\]{}\(\),;\:\.]/,l=W.numberStart||/[\d\.]/,s=W.number||/^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,r=W.isOperatorChar||/[+\-*&%=<>!?|\/]/;var e,Y;function a(U,V){var aa=U.next();if(q[aa]){var S=q[aa](U,V);if(S!==false){return S}}if(aa=='"'||aa=="'"){V.tokenize=v(aa);return V.tokenize(U,V)}if(X.test(aa)){e=aa;return null}if(l.test(aa)){U.backUp(1);if(U.match(s)){return"number"}U.next()}if(aa=="/"){if(U.eat("*")){V.tokenize=j;return j(U,V)}if(U.eat("/")){U.skipToEnd();return"comment"}}if(r.test(aa)){while(!U.match(/^\/[\/*]/,false)&&U.eat(r)){}return"operator"}U.eatWhile(/[\w\$_\xa1-\uffff]/);if(Z){while(U.match(Z)){U.eatWhile(/[\w\$_\xa1-\uffff]/)}}var T=U.current();if(K(t,T)){if(K(f,T)){e="newstatement"}if(K(i,T)){Y=true}return"keyword"}if(K(n,T)){return"variable-3"}if(K(c,T)){if(K(f,T)){e="newstatement"}return"builtin"}if(K(o,T)){return"atom"}return"variable"}function v(S){return function(T,V){var U=false,aa,ab=false;while((aa=T.next())!=null){if(aa==S&&!U){ab=true;break}U=!U&&aa=="\\"}if(ab||!(U||m)){V.tokenize=null}return"string"}}function j(T,U){var S=false,V;while(V=T.next()){if(V=="/"&&S){U.tokenize=null;break}S=(V=="*")}return"comment"}function b(T,S){if(W.typeFirstDefinitions&&T.eol()&&C(S.context)){S.typeAtEndOfLine=R(T,S,T.pos)}}return{startState:function(S){return{tokenize:null,context:new B((S||0)-h,0,"top",null,false),indented:0,startOfLine:true,prevToken:null}},token:function(U,T){var aa=T.context;if(U.sol()){if(aa.align==null){aa.align=false}T.indented=U.indentation();T.startOfLine=true}if(U.eatSpace()){b(U,T);return null}e=Y=null;var V=(T.tokenize||a)(U,T);if(V=="comment"||V=="meta"){return V}if(aa.align==null){aa.align=true}if(e==";"||e==":"||(e==","&&U.match(/^\s*(?:\/\/.*)?$/,false))){while(T.context.type=="statement"){y(T)}}else{if(e=="{"){L(T,U.column(),"}")}else{if(e=="["){L(T,U.column(),"]")}else{if(e=="("){L(T,U.column(),")")}else{if(e=="}"){while(aa.type=="statement"){aa=y(T)}if(aa.type=="}"){aa=y(T)}while(aa.type=="statement"){aa=y(T)}}else{if(e==aa.type){y(T)}else{if(p&&(((aa.type=="}"||aa.type=="top")&&e!=";")||(aa.type=="statement"&&e=="newstatement"))){L(T,U.column(),"statement",U.current())}}}}}}}if(V=="variable"&&((T.prevToken=="def"||(W.typeFirstDefinitions&&R(U,T,U.start)&&C(T.context)&&U.match(/^\s*\(/,false))))){V="def"}if(q.token){var S=q.token(U,T,V);if(S!==undefined){V=S}}if(V=="def"&&W.styleDefs===false){V="variable"}T.startOfLine=false;T.prevToken=Y?"def":V||e;b(U,T);return V},indent:function(T,ad){if(T.tokenize!=a&&T.tokenize!=null||T.typeAtEndOfLine){return A.Pass}var S=T.context,ab=ad&&ad.charAt(0);if(S.type=="statement"&&ab=="}"){S=S.prev}if(W.dontIndentStatements){while(S.type=="statement"&&W.dontIndentStatements.test(S.info)){S=S.prev}}if(q.indent){var U=q.indent(T,S,ad);if(typeof U=="number"){return U}}var ac=ab==S.type;var V=S.prev&&S.prev.info=="switch";if(W.allmanIndentation&&/[{(]/.test(ab)){while(S.type!="top"&&S.type!="}"){S=S.prev}return S.indented}if(S.type=="statement"){return S.indented+(ab=="{"?0:k)}if(S.align&&(!g||S.type!=")")){return S.column+(ac?0:1)}if(S.type==")"&&!ac){return S.indented+k}return S.indented+(ac?0:h)+(!ac&&V&&!/^(?:case|default)\b/.test(ad)?h:0)},electricInput:u?/^\s*(?:case .*?:|default:|\{\}?|\})$/:/^\s*[{}]$/,blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:"//",fold:"brace"}});function D(c){var a={},d=c.split(" ");for(var b=0;b <- <: <% >: # @ assert assume require print println printf readLine readBoolean readByte readShort readChar readInt readLong readFloat readDouble :: #:: "),types:D("AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"),multiLineStrings:true,blockKeywords:D("catch class do else finally for forSome if match switch try while"),defKeywords:D("class def object package trait type val var"),atoms:D("true false null"),indentStatements:false,indentSwitch:false,hooks:{"@":function(a){a.eatWhile(/[\w\$_]/);return"meta"},'"':function(a,b){if(!a.match('""')){return false}b.tokenize=P;return b.tokenize(a,b)},"'":function(a){a.eatWhile(/[\w\$_\xa1-\uffff]/);return"atom"},"=":function(c,a){var b=a.context;if(b.type=="}"&&b.align&&c.eat(">")){a.context=new B(b.indented,b.column,b.type,b.info,null,b.prev);return"operator"}else{return false}}},modeProps:{closeBrackets:{triples:'"'}}});function Q(a){return function(f,d){var c=false,e,b=false;while(!f.eol()){if(!a&&!c&&f.match('"')){b=true;break}if(a&&f.match('"""')){b=true;break}e=f.next();if(!c&&e=="$"&&f.match("{")){f.skipTo("}")}c=!c&&e=="\\"&&!a}if(b||!a){d.tokenize=null}return"string"}}H("text/x-kotlin",{name:"clike",keywords:D("package as typealias class interface this super val var fun for is in This throw return break continue object if else while do try when !in !is as? file import where by get set abstract enum open inner override private public internal protected catch finally out final vararg reified dynamic companion constructor init sealed field property receiver param sparam lateinit data inline noinline tailrec external annotation crossinline const operator infix"),types:D("Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"),intendSwitch:false,indentStatements:false,multiLineStrings:true,blockKeywords:D("catch class do else finally for if where try while enum"),defKeywords:D("class val var object package interface fun"),atoms:D("true false null this"),hooks:{'"':function(a,b){b.tokenize=Q(a.match('""'));return b.tokenize(a,b)}},modeProps:{closeBrackets:{triples:'"'}}});H(["x-shader/x-vertex","x-shader/x-fragment"],{name:"clike",keywords:D("sampler1D sampler2D sampler3D samplerCube sampler1DShadow sampler2DShadow const attribute uniform varying break continue discard return for while do if else struct in out inout"),types:D("float int bool void vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 mat2 mat3 mat4"),blockKeywords:D("for while do if else struct"),builtin:D("radians degrees sin cos tan asin acos atan pow exp log exp2 sqrt inversesqrt abs sign floor ceil fract mod min max clamp mix step smoothstep length distance dot cross normalize ftransform faceforward reflect refract matrixCompMult lessThan lessThanEqual greaterThan greaterThanEqual equal notEqual any all not texture1D texture1DProj texture1DLod texture1DProjLod texture2D texture2DProj texture2DLod texture2DProjLod texture3D texture3DProj texture3DLod texture3DProjLod textureCube textureCubeLod shadow1D shadow2D shadow1DProj shadow2DProj shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod dFdx dFdy fwidth noise1 noise2 noise3 noise4"),atoms:D("true false gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 gl_FogCoord gl_PointCoord gl_Position gl_PointSize gl_ClipVertex gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor gl_TexCoord gl_FogFragCoord gl_FragCoord gl_FrontFacing gl_FragData gl_FragDepth gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose gl_ProjectionMatrixInverseTranspose gl_ModelViewProjectionMatrixInverseTranspose gl_TextureMatrixInverseTranspose gl_NormalScale gl_DepthRange gl_ClipPlane gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel gl_FrontLightModelProduct gl_BackLightModelProduct gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ gl_FogParameters gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits gl_MaxDrawBuffers"),indentSwitch:false,hooks:{"#":F},modeProps:{fold:["brace","include"]}});H("text/x-nesc",{name:"clike",keywords:D(G+"as atomic async call command component components configuration event generic implementation includes interface module new norace nx_struct nx_union post provides signal task uses abstract extends"),types:D(z),blockKeywords:D("case do else for if switch while struct"),atoms:D("null true false"),hooks:{"#":F},modeProps:{fold:["brace","include"]}});H("text/x-objectivec",{name:"clike",keywords:D(G+"inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),types:D(z),atoms:D("YES NO NULL NILL ON OFF true false"),hooks:{"@":function(a){a.eatWhile(/[\w\$]/);return"keyword"},"#":F,indent:function(c,b,a){if(b.type=="statement"&&/^@\w/.test(a)){return b.indented}}},modeProps:{fold:"brace"}});H("text/x-squirrel",{name:"clike",keywords:D("base break clone continue const default delete enum extends function in class foreach local resume return this throw typeof yield constructor instanceof static"),types:D(z),blockKeywords:D("case catch class else for foreach if switch try while"),defKeywords:D("function local class"),typeFirstDefinitions:true,atoms:D("true false null"),hooks:{"#":F},modeProps:{fold:["brace","include"]}});var N=null;function I(a){return function(f,d){var c=false,e,b=false;while(!f.eol()){if(!c&&f.match('"')&&(a=="single"||f.match('""'))){b=true;break}if(!c&&f.match("``")){N=I(a);b=true;break}e=f.next();c=a=="single"&&!c&&e=="\\"}if(b){d.tokenize=null}return"string"}}H("text/x-ceylon",{name:"clike",keywords:D("abstracts alias assembly assert assign break case catch class continue dynamic else exists extends finally for function given if import in interface is let module new nonempty object of out outer package return satisfies super switch then this throw try value void while"),types:function(b){var a=b.charAt(0);return(a===a.toUpperCase()&&a!==a.toLowerCase())},blockKeywords:D("case catch class dynamic else finally for function if interface module new object switch try while"),defKeywords:D("class dynamic function interface module object package value"),builtin:D("abstract actual aliased annotation by default deprecated doc final formal late license native optional sealed see serializable shared suppressWarnings tagged throws variable"),isPunctuationChar:/[\[\]{}\(\),;\:\.`]/,isOperatorChar:/[+\-*&%=<>!?|^~:\/]/,numberStart:/[\d#$]/,number:/^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,multiLineStrings:true,typeFirstDefinitions:true,atoms:D("true false null larger smaller equal empty finished"),indentSwitch:false,styleDefs:false,hooks:{"@":function(a){a.eatWhile(/[\w\$_]/);return"meta"},'"':function(a,b){b.tokenize=I(a.match('""')?"triple":"single");return b.tokenize(a,b)},"`":function(a,b){if(!N||!a.match("`")){return false}b.tokenize=N;N=null;return b.tokenize(a,b)},"'":function(a){a.eatWhile(/[\w\$_\xa1-\uffff]/);return"atom"},token:function(b,c,a){if((a=="variable"||a=="variable-3")&&c.prevToken=="."){return"variable-2"}}},modeProps:{fold:["brace","import"],closeBrackets:{triples:'"'}}})});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(E){E.defineMode("css",function(b,r){var ac=r.inline;if(!r.propertyKeywords){r=E.resolveMode("text/css")}var j=b.indentUnit,af=r.tokenHooks,s=r.documentTypes||{},c=r.mediaTypes||{},o=r.mediaFeatures||{},k=r.mediaValueKeywords||{},t=r.propertyKeywords||{},ae=r.nonStandardPropertyKeywords||{},x=r.fontProperties||{},d=r.counterDescriptors||{},l=r.colorKeywords||{},h=r.valueKeywords||{},n=r.allowNested,f=r.supportsAtComponent===true;var ad,m;function a(X,W){ad=W;return X}function ag(W,X){var Y=W.next();if(af[Y]){var Z=af[Y](W,X);if(Z!==false){return Z}}if(Y=="@"){W.eatWhile(/[\w\\\-]/);return a("def",W.current())}else{if(Y=="="||(Y=="~"||Y=="|")&&W.eat("=")){return a(null,"compare")}else{if(Y=='"'||Y=="'"){X.tokenize=q(Y);return X.tokenize(W,X)}else{if(Y=="#"){W.eatWhile(/[\w\\\-]/);return a("atom","hash")}else{if(Y=="!"){W.match(/^\s*\w*/);return a("keyword","important")}else{if(/\d/.test(Y)||Y=="."&&W.eat(/\d/)){W.eatWhile(/[\w.%]/);return a("number","unit")}else{if(Y==="-"){if(/[\d.]/.test(W.peek())){W.eatWhile(/[\w.%]/);return a("number","unit")}else{if(W.match(/^-[\w\\\-]+/)){W.eatWhile(/[\w\\\-]/);if(W.match(/^\s*:/,false)){return a("variable-2","variable-definition")}return a("variable-2","variable")}else{if(W.match(/^\w+-/)){return a("meta","meta")}}}}else{if(/[,+>*\/]/.test(Y)){return a(null,"select-op")}else{if(Y=="."&&W.match(/^-?[_a-z][_a-z0-9-]*/i)){return a("qualifier","qualifier")}else{if(/[:;{}\[\]\(\)]/.test(Y)){return a(null,Y)}else{if((Y=="u"&&W.match(/rl(-prefix)?\(/))||(Y=="d"&&W.match("omain("))||(Y=="r"&&W.match("egexp("))){W.backUp(1);X.tokenize=ah;return a("property","word")}else{if(/[\w\\\-]/.test(Y)){W.eatWhile(/[\w\\\-]/);return a("property","word")}else{return a(null,null)}}}}}}}}}}}}}function q(W){return function(X,Z){var Y=false,aa;while((aa=X.next())!=null){if(aa==W&&!Y){if(W==")"){X.backUp(1)}break}Y=!Y&&aa=="\\"}if(aa==W||!Y&&W!=")"){Z.tokenize=null}return a("string","string")}}function ah(W,X){W.next();if(!W.match(/\s*[\"\')]/,false)){X.tokenize=q(")")}else{X.tokenize=null}return a(null,"(")}function i(X,Y,W){this.type=X;this.indent=Y;this.prev=W}function v(X,W,Y,Z){X.context=new i(Y,W.indentation()+(Z===false?0:j),X.context);return Y}function g(W){if(W.context.prev){W.context=W.context.prev}return W.context.type}function p(Y,W,X){return w[X.context.type](Y,W,X)}function e(Z,X,Y,W){for(var aa=W||1;aa>0;aa--){Y.context=Y.context.prev}return p(Z,X,Y)}function u(W){var X=W.current().toLowerCase();if(h.hasOwnProperty(X)){m="atom"}else{if(l.hasOwnProperty(X)){m="keyword"}else{m="variable"}}}var w={};w.top=function(Y,W,X){if(Y=="{"){return v(X,W,"block")}else{if(Y=="}"&&X.context.prev){return g(X)}else{if(f&&/@component/.test(Y)){return v(X,W,"atComponentBlock")}else{if(/^@(-moz-)?document$/.test(Y)){return v(X,W,"documentTypes")}else{if(/^@(media|supports|(-moz-)?document|import)$/.test(Y)){return v(X,W,"atBlock")}else{if(/^@(font-face|counter-style)/.test(Y)){X.stateArg=Y;return"restricted_atBlock_before"}else{if(/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(Y)){return"keyframes"}else{if(Y&&Y.charAt(0)=="@"){return v(X,W,"at")}else{if(Y=="hash"){m="builtin"}else{if(Y=="word"){m="tag"}else{if(Y=="variable-definition"){return"maybeprop"}else{if(Y=="interpolation"){return v(X,W,"interpolation")}else{if(Y==":"){return"pseudo"}else{if(n&&Y=="("){return v(X,W,"parens")}}}}}}}}}}}}}}return X.context.type};w.block=function(Z,W,Y){if(Z=="word"){var X=W.current().toLowerCase();if(t.hasOwnProperty(X)){m="property";return"maybeprop"}else{if(ae.hasOwnProperty(X)){m="string-2";return"maybeprop"}else{if(n){m=W.match(/^\s*:(?:\s|$)/,false)?"property":"tag";return"block"}else{m+=" error";return"maybeprop"}}}}else{if(Z=="meta"){return"block"}else{if(!n&&(Z=="hash"||Z=="qualifier")){m="error";return"block"}else{return w.top(Z,W,Y)}}}};w.maybeprop=function(Y,W,X){if(Y==":"){return v(X,W,"prop")}return p(Y,W,X)};w.prop=function(Y,W,X){if(Y==";"){return g(X)}if(Y=="{"&&n){return v(X,W,"propBlock")}if(Y=="}"||Y=="{"){return e(Y,W,X)}if(Y=="("){return v(X,W,"parens")}if(Y=="hash"&&!/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(W.current())){m+=" error"}else{if(Y=="word"){u(W)}else{if(Y=="interpolation"){return v(X,W,"interpolation")}}}return"prop"};w.propBlock=function(X,Y,W){if(X=="}"){return g(W)}if(X=="word"){m="property";return"maybeprop"}return W.context.type};w.parens=function(Y,W,X){if(Y=="{"||Y=="}"){return e(Y,W,X)}if(Y==")"){return g(X)}if(Y=="("){return v(X,W,"parens")}if(Y=="interpolation"){return v(X,W,"interpolation")}if(Y=="word"){u(W)}return"parens"};w.pseudo=function(Y,W,X){if(Y=="word"){m="variable-3";return X.context.type}return p(Y,W,X)};w.documentTypes=function(Y,W,X){if(Y=="word"&&s.hasOwnProperty(W.current())){m="tag";return X.context.type}else{return w.atBlock(Y,W,X)}};w.atBlock=function(Z,W,Y){if(Z=="("){return v(Y,W,"atBlock_parens")}if(Z=="}"||Z==";"){return e(Z,W,Y)}if(Z=="{"){return g(Y)&&v(Y,W,n?"block":"top")}if(Z=="interpolation"){return v(Y,W,"interpolation")}if(Z=="word"){var X=W.current().toLowerCase();if(X=="only"||X=="not"||X=="and"||X=="or"){m="keyword"}else{if(c.hasOwnProperty(X)){m="attribute"}else{if(o.hasOwnProperty(X)){m="property"}else{if(k.hasOwnProperty(X)){m="keyword"}else{if(t.hasOwnProperty(X)){m="property"}else{if(ae.hasOwnProperty(X)){m="string-2"}else{if(h.hasOwnProperty(X)){m="atom"}else{if(l.hasOwnProperty(X)){m="keyword"}else{m="error"}}}}}}}}}return Y.context.type};w.atComponentBlock=function(Y,W,X){if(Y=="}"){return e(Y,W,X)}if(Y=="{"){return g(X)&&v(X,W,n?"block":"top",false)}if(Y=="word"){m="error"}return X.context.type};w.atBlock_parens=function(Y,W,X){if(Y==")"){return g(X)}if(Y=="{"||Y=="}"){return e(Y,W,X,2)}return w.atBlock(Y,W,X)};w.restricted_atBlock_before=function(Y,W,X){if(Y=="{"){return v(X,W,"restricted_atBlock")}if(Y=="word"&&X.stateArg=="@counter-style"){m="variable";return"restricted_atBlock_before"}return p(Y,W,X)};w.restricted_atBlock=function(Y,W,X){if(Y=="}"){X.stateArg=null;return g(X)}if(Y=="word"){if((X.stateArg=="@font-face"&&!x.hasOwnProperty(W.current().toLowerCase()))||(X.stateArg=="@counter-style"&&!d.hasOwnProperty(W.current().toLowerCase()))){m="error"}else{m="property"}return"maybeprop"}return"restricted_atBlock"};w.keyframes=function(Y,W,X){if(Y=="word"){m="variable";return"keyframes"}if(Y=="{"){return v(X,W,"top")}return p(Y,W,X)};w.at=function(Y,W,X){if(Y==";"){return g(X)}if(Y=="{"||Y=="}"){return e(Y,W,X)}if(Y=="word"){m="tag"}else{if(Y=="hash"){m="builtin"}}return"at"};w.interpolation=function(Y,W,X){if(Y=="}"){return g(X)}if(Y=="{"||Y==";"){return e(Y,W,X)}if(Y=="word"){m="variable"}else{if(Y!="variable"&&Y!="("&&Y!=")"){m="error"}}return"interpolation"};return{startState:function(W){return{tokenize:null,state:ac?"block":"top",stateArg:null,context:new i(ac?"block":"top",W||0,null)}},token:function(W,X){if(!X.tokenize&&W.eatSpace()){return null}var Y=(X.tokenize||ag)(W,X);if(Y&&typeof Y=="object"){ad=Y[1];Y=Y[0]}m=Y;X.state=w[X.state](ad,W,X);return m},indent:function(W,Y){var Z=W.context,X=Y&&Y.charAt(0);var aa=Z.indent;if(Z.type=="prop"&&(X=="}"||X==")")){Z=Z.prev}if(Z.prev){if(X=="}"&&(Z.type=="block"||Z.type=="top"||Z.type=="interpolation"||Z.type=="restricted_atBlock")){Z=Z.prev;aa=Z.indent}else{if(X==")"&&(Z.type=="parens"||Z.type=="atBlock_parens")||X=="{"&&(Z.type=="at"||Z.type=="atBlock")){aa=Math.max(0,Z.indent-j);Z=Z.prev}}}return aa},electricChars:"}",blockCommentStart:"/*",blockCommentEnd:"*/",fold:"brace"}});function P(a){var b={};for(var c=0;c-1){e.backUp(a.length-b)}else{if(a.match(/<\/?$/)){e.backUp(a.length);if(!e.match(d,false)){e.match(a)}}}return c}var n={};function p(b){var a=n[b];if(a){return a}return n[b]=new RegExp("\\s+"+b+"\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*")}function r(a,c){var b=a.match(p(c));return b?/^\s*(.*?)\s*$/.exec(b[2])[1]:""}function j(a,b){return new RegExp((b?"^":"")+"","i")}function k(e,f){for(var d in e){var c=f[d]||(f[d]=[]);var a=e[d];for(var b=a.length-1;b>=0;b--){c.unshift(a[b])}}}function q(b,a){for(var c=0;c=0;a--){d.script.unshift(["type",h[a].matches,h[a].mode])}}function b(C,i){var J=e.token(C,i.htmlState),B=/\btag\b/.test(J),H;if(B&&!/[<>\s\/]/.test(C.current())&&(H=i.htmlState.tagName&&i.htmlState.tagName.toLowerCase())&&d.hasOwnProperty(H)){i.inTag=H+" "}else{if(i.inTag&&B&&/>$/.test(C.current())){var I=/^([\S]+) (.*)/.exec(i.inTag);i.inTag=null;var D=C.current()==">"&&q(d[I[1]],I[2]);var E=o.getMode(c,D);var F=j(I[1],true),G=j(I[1],false);i.token=function(s,t){if(s.match(F,false)){t.token=b;t.localState=t.localMode=null;return null}return m(s,G,t.localMode.token(s,t.localState))};i.localMode=E;i.localState=o.startState(E,e.indent(i.htmlState,""))}else{if(i.inTag){i.inTag+=C.current();if(C.eol()){i.inTag+=" "}}}}return J}return{startState:function(){var i=o.startState(e);return{token:b,inTag:null,localMode:null,localState:null,htmlState:i}},copyState:function(i){var t;if(i.localState){t=o.copyState(i.localMode,i.localState)}return{token:i.token,inTag:i.inTag,localMode:i.localMode,localState:t,htmlState:o.copyState(e,i.htmlState)}},token:function(i,t){return t.token(i,t)},indent:function(i,t){if(!i.localMode||/^\s*<\//.test(t)){return e.indent(i.htmlState,t)}else{if(i.localMode.indent){return i.localMode.indent(i.localState,t)}else{return o.Pass}}},innerMode:function(i){return{state:i.localState||i.htmlState,mode:i.localMode||e}}}},"xml","javascript","css");o.defineMIME("text/html","htmlmixed")});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(d){function c(a,b,f){return/^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(b.lastType)||(b.lastType=="quasi"&&/\{\s*$/.test(a.string.slice(0,a.pos-(f||0))))}d.defineMode("javascript",function(bU,bK){var a0=bU.indentUnit;var bE=bK.statementIndent;var b8=bK.jsonld;var bG=bK.json||b8;var a6=bK.typescript;var cd=bK.wordCharacters||/[\w$\xa1-\uffff]/;var bt=function(){function f(o){return{type:o,style:"keyword"}}var k=f("keyword a"),m=f("keyword b"),n=f("keyword c");var l=f("operator"),h={type:"atom",style:"atom"};var j={"if":f("if"),"while":k,"with":k,"else":m,"do":m,"try":m,"finally":m,"return":n,"break":n,"continue":n,"new":f("new"),"delete":n,"throw":n,"debugger":n,"var":f("var"),"const":f("var"),let:f("var"),"function":f("function"),"catch":f("catch"),"for":f("for"),"switch":f("switch"),"case":f("case"),"default":f("default"),"in":l,"typeof":l,"instanceof":l,"true":h,"false":h,"null":h,"undefined":h,"NaN":h,"Infinity":h,"this":f("this"),"class":f("class"),"super":f("atom"),yield:n,"export":f("export"),"import":f("import"),"extends":n,await:n,async:f("async")};if(a6){var e={type:"variable",style:"variable-3"};var i={"interface":f("class"),"implements":n,namespace:n,module:f("module"),"enum":f("module"),type:f("type"),"public":f("modifier"),"private":f("modifier"),"protected":f("modifier"),"abstract":f("modifier"),as:l,string:e,number:e,"boolean":e,any:e};for(var g in i){j[g]=i[g]}}return j}();var bh=/[+\-*&%=<>!?|~^]/;var bv=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function bu(e){var g=false,h,f=false;while((h=e.next())!=null){if(!g){if(h=="/"&&!f){return}if(h=="["){f=true}else{if(f&&h=="]"){f=false}}}g=!g&&h=="\\"}}var bd,bs;function bl(e,f,g){bd=e;bs=g;return f}function bb(i,f){var h=i.next();if(h=='"'||h=="'"){f.tokenize=be(h);return f.tokenize(i,f)}else{if(h=="."&&i.match(/^\d+(?:[eE][+\-]?\d+)?/)){return bl("number","number")}else{if(h=="."&&i.match("..")){return bl("spread","meta")}else{if(/[\[\]{}\(\),;\:\.]/.test(h)){return bl(h)}else{if(h=="="&&i.eat(">")){return bl("=>","operator")}else{if(h=="0"&&i.eat(/x/i)){i.eatWhile(/[\da-f]/i);return bl("number","number")}else{if(h=="0"&&i.eat(/o/i)){i.eatWhile(/[0-7]/i);return bl("number","number")}else{if(h=="0"&&i.eat(/b/i)){i.eatWhile(/[01]/i);return bl("number","number")}else{if(/\d/.test(h)){i.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);return bl("number","number")}else{if(h=="/"){if(i.eat("*")){f.tokenize=b9;return b9(i,f)}else{if(i.eat("/")){i.skipToEnd();return bl("comment","comment")}else{if(c(i,f,1)){bu(i);i.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);return bl("regexp","string-2")}else{i.eatWhile(bh);return bl("operator","operator",i.current())}}}}else{if(h=="`"){f.tokenize=b7;return b7(i,f)}else{if(h=="#"){i.skipToEnd();return bl("error","error")}else{if(bh.test(h)){i.eatWhile(bh);return bl("operator","operator",i.current())}else{if(cd.test(h)){i.eatWhile(cd);var e=i.current(),g=bt.propertyIsEnumerable(e)&&bt[e];return(g&&f.lastType!=".")?bl(g.type,g.style,e):bl("variable","variable",e)}}}}}}}}}}}}}}}function be(e){return function(i,g){var f=false,h;if(b8&&i.peek()=="@"&&i.match(bv)){g.tokenize=bb;return bl("jsonld-keyword","meta")}while((h=i.next())!=null){if(h==e&&!f){break}f=!f&&h=="\\"}if(!f){g.tokenize=bb}return bl("string","string")}}function b9(e,f){var h=false,g;while(g=e.next()){if(g=="/"&&h){f.tokenize=bb;break}h=(g=="*")}return bl("comment","comment")}function b7(e,g){var f=false,h;while((h=e.next())!=null){if(!f&&(h=="`"||h=="$"&&e.eat("{"))){g.tokenize=bb;break}f=!f&&h=="\\"}return bl("quasi","string-2",e.current())}var aZ="([{}])";function cb(e,k){if(k.fatArrowAt){k.fatArrowAt=null}var f=e.string.indexOf("=>",e.start);if(f<0){return}if(a6){var i=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,f));if(i){f=i.index}}var h=0,j=false;for(var g=f-1;g>=0;--g){var m=e.string.charAt(g);var l=aZ.indexOf(m);if(l>=0&&l<3){if(!h){++g;break}if(--h==0){if(m=="("){j=true}break}}else{if(l>=3&&l<6){++h}else{if(cd.test(m)){j=true}else{if(/["'\/]/.test(m)){return}else{if(j&&!h){++g;break}}}}}}if(j&&!h){k.fatArrowAt=g}}var ba={atom:true,number:true,variable:true,string:true,regexp:true,"this":true,"jsonld-keyword":true};function bn(i,g,h,j,f,e){this.indented=i;this.column=g;this.type=h;this.prev=f;this.info=e;if(j!=null){this.align=j}}function aS(e,f){for(var g=e.localVars;g;g=g.next){if(g.name==f){return true}}for(var h=e.context;h;h=h.prev){for(var g=h.vars;g;g=g.next){if(g.name==f){return true}}}}function a7(k,g,h,e,j){var i=k.cc;bx.state=k;bx.stream=j;bx.marked=null,bx.cc=i;bx.style=g;if(!k.lexical.hasOwnProperty("align")){k.lexical.align=true}while(true){var f=i.length?i.pop():bG?bD:b2;if(f(h,e)){while(i.length&&i[i.length-1].lex){i.pop()()}if(bx.marked){return bx.marked}if(h=="variable"&&aS(k,e)){return"variable-2"}return g}}}var bx={state:null,column:null,marked:null,cc:null};function bT(){for(var e=arguments.length-1;e>=0;e--){bx.cc.push(arguments[e])}}function bO(){bT.apply(null,arguments);return true}function cc(f){function g(i){for(var h=i;h;h=h.next){if(h.name==f){return true}}return false}var e=bx.state;bx.marked="def";if(e.context){if(g(e.localVars)){return}e.localVars={name:f,next:e.localVars}}else{if(g(e.globalVars)){return}if(bK.globalVars){e.globalVars={name:f,next:e.globalVars}}}}var aU={name:"this",next:{name:"arguments"}};function b(){bx.state.context={prev:bx.state.context,vars:bx.state.localVars};bx.state.localVars=aU}function a(){bx.state.localVars=bx.state.context.vars;bx.state.context=bx.state.context.prev}function b4(f,e){var g=function(){var i=bx.state,h=i.indented;if(i.lexical.type=="stat"){h=i.lexical.indented}else{for(var j=i.lexical;j&&j.type==")"&&j.align;j=j.prev){h=j.indented}}i.lexical=new bn(h,bx.stream.column(),f,null,i.lexical,e)};g.lex=true;return g}function a5(){var e=bx.state;if(e.lexical.prev){if(e.lexical.type==")"){e.indented=e.lexical.indented}e.lexical=e.lexical.prev}}a5.lex=true;function aT(f){function e(g){if(g==f){return bO()}else{if(f==";"){return bT()}else{return bO(e)}}}return e}function b2(f,e){if(f=="var"){return bO(b4("vardef",e.length),a9,aT(";"),a5)}if(f=="keyword a"){return bO(b4("form"),bB,b2,a5)}if(f=="keyword b"){return bO(b4("form"),b2,a5)}if(f=="{"){return bO(b4("}"),bH,a5)}if(f==";"){return bO()}if(f=="if"){if(bx.state.lexical.info=="else"&&bx.state.cc[bx.state.cc.length-1]==a5){bx.state.cc.pop()()}return bO(b4("form"),bB,b2,a5,a8)}if(f=="function"){return bO(bk)}if(f=="for"){return bO(b4("form"),aQ,b2,a5)}if(f=="variable"){return bO(b4("stat"),b1)}if(f=="switch"){return bO(b4("form"),bB,b4("}","switch"),aT("{"),bH,a5,a5)}if(f=="case"){return bO(bD,aT(":"))}if(f=="default"){return bO(aT(":"))}if(f=="catch"){return bO(b4("form"),b,aT("("),bP,aT(")"),b2,a5,a)}if(f=="class"){return bO(b4("form"),bY,a5)}if(f=="export"){return bO(b4("stat"),b3,a5)}if(f=="import"){return bO(b4("stat"),bN,a5)}if(f=="module"){return bO(b4("form"),a4,b4("}"),aT("{"),bH,a5,a5)}if(f=="type"){return bO(a3,aT("operator"),a3,aT(";"))}if(f=="async"){return bO(b2)}return bT(b4("stat"),bD,aT(";"),a5)}function bD(e){return bV(e,false)}function b5(e){return bV(e,true)}function bB(e){if(e!="("){return bT()}return bO(b4(")"),bD,aT(")"),a5)}function bV(g,e){if(bx.state.fatArrowAt==bx.stream.start){var h=e?bj:bX;if(g=="("){return bO(b,b4(")"),ce(a4,")"),a5,aT("=>"),h,a)}else{if(g=="variable"){return bT(b,a4,aT("=>"),h,a)}}}var f=e?a2:bS;if(ba.hasOwnProperty(g)){return bO(f)}if(g=="function"){return bO(bk,f)}if(g=="class"){return bO(b4("form"),bp,a5)}if(g=="keyword c"||g=="async"){return bO(e?bJ:bL)}if(g=="("){return bO(b4(")"),bL,aT(")"),a5,f)}if(g=="operator"||g=="spread"){return bO(e?b5:bD)}if(g=="["){return bO(b4("]"),aY,a5,f)}if(g=="{"){return ca(aR,"}",null,f)}if(g=="quasi"){return bT(bg,f)}if(g=="new"){return bO(bq(e))}return bO()}function bL(e){if(e.match(/[;\}\)\],]/)){return bT()}return bT(bD)}function bJ(e){if(e.match(/[;\}\)\],]/)){return bT()}return bT(b5)}function bS(f,e){if(f==","){return bO(bD)}return a2(f,e,false)}function a2(h,f,i){var g=i==false?bS:a2;var e=i==false?bD:b5;if(h=="=>"){return bO(b,i?bj:bX,a)}if(h=="operator"){if(/\+\+|--/.test(f)){return bO(g)}if(f=="?"){return bO(bD,aT(":"),e)}return bO(e)}if(h=="quasi"){return bT(bg,g)}if(h==";"){return}if(h=="("){return ca(b5,")","call",g)}if(h=="."){return bO(bI,g)}if(h=="["){return bO(b4("]"),bL,aT("]"),a5,g)}}function bg(f,e){if(f!="quasi"){return bT()}if(e.slice(e.length-2)!="${"){return bO(bg)}return bO(bD,aV)}function aV(e){if(e=="}"){bx.marked="string-2";bx.state.tokenize=b7;return bO(bg)}}function bX(e){cb(bx.stream,bx.state);return bT(e=="{"?b2:bD)}function bj(e){cb(bx.stream,bx.state);return bT(e=="{"?b2:b5)}function bq(e){return function(f){if(f=="."){return bO(e?aX:bZ)}else{return bT(e?b5:bD)}}}function bZ(f,e){if(e=="target"){bx.marked="keyword";return bO(bS)}}function aX(f,e){if(e=="target"){bx.marked="keyword";return bO(a2)}}function b1(e){if(e==":"){return bO(a5,b2)}return bT(bS,aT(";"),a5)}function bI(e){if(e=="variable"){bx.marked="property";return bO()}}function aR(f,e){if(f=="async"){bx.marked="property";return bO(aR)}else{if(f=="variable"||bx.style=="keyword"){bx.marked="property";if(e=="get"||e=="set"){return bO(bo)}return bO(bm)}else{if(f=="number"||f=="string"){bx.marked=b8?"property":(bx.style+" property");return bO(bm)}else{if(f=="jsonld-keyword"){return bO(bm)}else{if(f=="modifier"){return bO(aR)}else{if(f=="["){return bO(bD,aT("]"),bm)}else{if(f=="spread"){return bO(bD)}else{if(f==":"){return bT(bm)}}}}}}}}}function bo(e){if(e!="variable"){return bT(bm)}bx.marked="property";return bO(bk)}function bm(e){if(e==":"){return bO(b5)}if(e=="("){return bT(bk)}}function ce(e,g){function f(j,i){if(j==","){var h=bx.state.lexical;if(h.info=="call"){h.pos=(h.pos||0)+1}return bO(function(l,k){if(l==g||k==g){return bT()}return bT(e)},f)}if(j==g||i==g){return bO()}return bO(aT(g))}return function(h,i){if(h==g||i==g){return bO()}return bT(e,f)}}function ca(e,h,f){for(var g=3;g"){return bO(a3)}}function bF(e){if(e=="variable"||bx.style=="keyword"){bx.marked="property";return bO(bF)}else{if(e==":"){return bO(a3)}}}function bR(e){if(e=="variable"){return bO(bR)}else{if(e==":"){return bO(a3)}}}function br(f,e){if(e=="<"){return bO(ce(a3,">"),br)}if(f=="["){return bO(aT("]"),br)}}function a9(){return bT(a4,bc,bQ,bW)}function a4(f,e){if(f=="modifier"){return bO(a4)}if(f=="variable"){cc(e);return bO()}if(f=="spread"){return bO(a4)}if(f=="["){return ca(a4,"]")}if(f=="{"){return ca(b6,"}")}}function b6(f,e){if(f=="variable"&&!bx.stream.match(/^\s*:/,false)){cc(e);return bO(bQ)}if(f=="variable"){bx.marked="property"}if(f=="spread"){return bO(a4)}if(f=="}"){return bT()}return bO(aT(":"),a4,bQ)}function bQ(f,e){if(e=="="){return bO(b5)}}function bW(e){if(e==","){return bO(a9)}}function a8(f,e){if(f=="keyword b"&&e=="else"){return bO(b4("form","else"),b2,a5)}}function aQ(e){if(e=="("){return bO(b4(")"),bz,aT(")"),a5)}}function bz(e){if(e=="var"){return bO(a9,aT(";"),bA)}if(e==";"){return bO(bA)}if(e=="variable"){return bO(aP)}return bT(bD,aT(";"),bA)}function aP(f,e){if(e=="in"||e=="of"){bx.marked="keyword";return bO(bD)}return bO(bS,bA)}function bA(f,e){if(f==";"){return bO(bC)}if(e=="in"||e=="of"){bx.marked="keyword";return bO(bD)}return bT(bD,aT(";"),bC)}function bC(e){if(e!=")"){bO(bD)}}function bk(f,e){if(e=="*"){bx.marked="keyword";return bO(bk)}if(f=="variable"){cc(e);return bO(bk)}if(f=="("){return bO(b,b4(")"),ce(bP,")"),a5,bc,b2,a)}}function bP(e){if(e=="spread"){return bO(bP)}return bT(a4,bc,bQ)}function bp(f,e){if(f=="variable"){return bY(f,e)}return bi(f,e)}function bY(f,e){if(f=="variable"){cc(e);return bO(bi)}}function bi(f,e){if(e=="extends"||e=="implements"){return bO(a6?a3:bD,bi)}if(f=="{"){return bO(b4("}"),aW,a5)}}function aW(f,e){if(f=="variable"||bx.style=="keyword"){if((e=="static"||e=="get"||e=="set"||(a6&&(e=="public"||e=="private"||e=="protected"||e=="readonly"||e=="abstract")))&&bx.stream.match(/^\s+[\w$\xa1-\uffff]/,false)){bx.marked="keyword";return bO(aW)}bx.marked="property";return bO(a6?b0:bk,aW)}if(e=="*"){bx.marked="keyword";return bO(aW)}if(f==";"){return bO(aW)}if(f=="}"){return bO()}}function b0(f,e){if(e=="?"){return bO(b0)}if(f==":"){return bO(a3,bQ)}return bT(bk)}function b3(f,e){if(e=="*"){bx.marked="keyword";return bO(bM,aT(";"))}if(e=="default"){bx.marked="keyword";return bO(bD,aT(";"))}return bT(b2)}function bN(e){if(e=="string"){return bO()}return bT(bw,bM)}function bw(f,e){if(f=="{"){return ca(bw,"}")}if(f=="variable"){cc(e)}if(e=="*"){bx.marked="keyword"}return bO(a1)}function a1(f,e){if(e=="as"){bx.marked="keyword";return bO(bw)}}function bM(f,e){if(e=="from"){bx.marked="keyword";return bO(bD)}}function aY(e){if(e=="]"){return bO()}return bT(ce(b5,"]"))}function by(e,f){return e.lastType=="operator"||e.lastType==","||bh.test(f.charAt(0))||/[,.]/.test(f.charAt(0))}return{startState:function(e){var f={tokenize:bb,lastType:"sof",cc:[],lexical:new bn((e||0)-a0,0,"block",false),localVars:bK.localVars,context:bK.localVars&&{vars:bK.localVars},indented:e||0};if(bK.globalVars&&typeof bK.globalVars=="object"){f.globalVars=bK.globalVars}return f},token:function(e,f){if(e.sol()){if(!f.lexical.hasOwnProperty("align")){f.lexical.align=false}f.indented=e.indentation();cb(e,f)}if(f.tokenize!=b9&&e.eatSpace()){return null}var g=f.tokenize(e,f);if(bd=="comment"){return g}f.lastType=bd=="operator"&&(bs=="++"||bs=="--")?"incdec":bd;return a7(f,g,bd,bs,e)},indent:function(m,j){if(m.tokenize==b9){return d.Pass}if(m.tokenize!=bb){return 0}var f=j&&j.charAt(0),e=m.lexical,g;if(!/^\s*else\b/.test(j)){for(var k=m.cc.length-1;k>=0;--k){var i=m.cc[k];if(i==a5){e=e.prev}else{if(i!=a8){break}}}}while((e.type=="stat"||e.type=="form")&&(f=="}"||((g=m.cc[m.cc.length-1])&&(g==bS||g==a2)&&!/^[,\.=+\-*:?[\(]/.test(j)))){e=e.prev}if(bE&&e.type==")"&&e.prev.type=="stat"){e=e.prev}var h=e.type,l=f==h;if(h=="vardef"){return e.indented+(m.lastType=="operator"||m.lastType==","?e.info+1:0)}else{if(h=="form"&&f=="{"){return e.indented}else{if(h=="form"){return e.indented+a0}else{if(h=="stat"){return e.indented+(by(m,j)?bE||a0:0)}else{if(e.info=="switch"&&!l&&bK.doubleIndentSwitch!=false){return e.indented+(/^(?:case|default)\b/.test(j)?a0:2*a0)}else{if(e.align){return e.column+(l?0:1)}else{return e.indented+(l?0:a0)}}}}}}},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:bG?null:"/*",blockCommentEnd:bG?null:"*/",lineComment:bG?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:bG?"json":"javascript",jsonldMode:b8,jsonMode:bG,expressionAllowed:c,skipExpression:function(f){var e=f.cc[f.cc.length-1];if(e==bD||e==b5){f.cc.pop()}}}});d.registerHelper("wordChars","javascript",/[\w$]/);d.defineMIME("text/javascript","javascript");d.defineMIME("text/ecmascript","javascript");d.defineMIME("application/javascript","javascript");d.defineMIME("application/x-javascript","javascript");d.defineMIME("application/ecmascript","javascript");d.defineMIME("application/json",{name:"javascript",json:true});d.defineMIME("application/x-json",{name:"javascript",json:true});d.defineMIME("application/ld+json",{name:"javascript",jsonld:true});d.defineMIME("text/typescript",{name:"javascript",typescript:true});d.defineMIME("application/typescript",{name:"javascript",typescript:true})});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(b){b.defineMode("nginx",function(w){function p(c){var e={},d=c.split(" ");for(var f=0;f*\/]/.test(f)){return r(null,"select-op")}else{if(/[;{}:\[\]]/.test(f)){return r(null,f)}else{c.eatWhile(/[\w\\\-]/);return r("variable","variable")}}}}}}}}}}}}function a(c,d){var f=false,e;while((e=c.next())!=null){if(f&&e=="/"){d.tokenize=v;break}f=(e=="*")}return r("comment","comment")}function x(c,d){var e=0,f;while((f=c.next())!=null){if(e>=2&&f==">"){d.tokenize=v;break}e=(f=="-")?e+1:0}return r("comment","comment")}function n(c){return function(d,f){var e=false,g;while((g=d.next())!=null){if(g==c&&!e){break}e=!e&&g=="\\"}if(!e){f.tokenize=v}return r("string","string")}}return{startState:function(c){return{tokenize:v,baseIndent:c||0,stack:[]}},token:function(c,d){if(c.eatSpace()){return null}o=null;var e=d.tokenize(c,d);var f=d.stack[d.stack.length-1];if(o=="hash"&&f=="rule"){e="atom"}else{if(e=="variable"){if(f=="rule"){e="number"}else{if(!f||f=="@media{"){e="tag"}}}}if(f=="rule"&&/^[\{\};]$/.test(o)){d.stack.pop()}if(o=="{"){if(f=="@media"){d.stack[d.stack.length-1]="@media{"}else{d.stack.push("{")}}else{if(o=="}"){d.stack.pop()}else{if(o=="@media"){d.stack.push("@media")}else{if(f=="{"&&o!="comment"){d.stack.push("rule")}}}}return e},indent:function(d,e){var c=d.stack.length;if(/^\}/.test(e)){c-=d.stack[d.stack.length-1]=="rule"?2:1}return d.baseIndent+c*s},electricChars:"}"}});b.defineMIME("text/x-nginx-conf","nginx")});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"),require("../htmlmixed/htmlmixed"),require("../clike/clike"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../htmlmixed/htmlmixed","../clike/clike"],b)}else{b(CodeMirror)}}})(function(o){function m(a){var c={},b=a.split(" ");for(var d=0;d\w/,false)){b.tokenize=n([[["->",null]],[[/[\w]+/,"variable"]]],c,d)}return"variable-2"}var a=false;while(!e.eol()&&(a||d===false||(!e.match("{$",false)&&!e.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/,false)))){if(!a&&e.match(c)){b.tokenize=null;b.tokStack.pop();b.tokStack.pop();break}a=e.next()=="\\"&&!a}return"string"}var k="abstract and array as break case catch class clone const continue declare default do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global goto if implements interface instanceof namespace new or private protected public static switch throw trait try use var while xor die echo empty exit eval include include_once isset list require require_once return print unset __halt_compiler self static parent yield insteadof finally";var j="true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__";var r="func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count";o.registerHelper("hintWords","php",[k,j,r].join(" ").split(" "));o.registerHelper("wordChars","php",/[\w$]/);var l={name:"clike",helperType:"php",keywords:m(k),blockKeywords:m("catch do else elseif for foreach if switch try while finally"),defKeywords:m("class function interface namespace trait"),atoms:m(j),builtin:m(r),multiLineStrings:true,hooks:{"$":function(a){a.eatWhile(/[\w\$_]/);return"variable-2"},"<":function(e,b){var c;if(c=e.match(/<<\s*/)){var d=e.eat(/['"]/);e.eatWhile(/[\w\.]/);var a=e.current().slice(c[0].length+(d?2:1));if(d){e.eat(d)}if(a){(b.tokStack||(b.tokStack=[])).push(a,0);b.tokenize=q(a,d!="'");return"string"}}return false},"#":function(a){while(!a.eol()&&!a.match("?>",false)){a.next()}return"comment"},"/":function(a){if(a.eat("/")){while(!a.eol()&&!a.match("?>",false)){a.next()}return"comment"}return false},'"':function(b,a){(a.tokStack||(a.tokStack=[])).push('"',0);a.tokenize=q('"');return"string"},"{":function(b,a){if(a.tokStack&&a.tokStack.length){a.tokStack[a.tokStack.length-1]++}return false},"}":function(b,a){if(a.tokStack&&a.tokStack.length>0&&!--a.tokStack[a.tokStack.length-1]){a.tokenize=q(a.tokStack[a.tokStack.length-2])}return false}}};o.defineMode("php",function(b,a){var e=o.getMode(b,"text/html");var d=o.getMode(b,l);function c(f,h){var i=h.curMode==d;if(f.sol()&&h.pending&&h.pending!='"'&&h.pending!="'"){h.pending=null}if(!i){if(f.match(/^<\?\w*/)){h.curMode=d;if(!h.php){h.php=o.startState(d,e.indent(h.html,""))}h.curState=h.php;return"meta"}if(h.pending=='"'||h.pending=="'"){while(!f.eol()&&f.next()!=h.pending){}var v="string"}else{if(h.pending&&f.pos/.test(g)){h.pending=x[0]}else{h.pending={end:f.pos,style:v}}f.backUp(g.length-w)}return v}else{if(i&&h.php.tokenize==null&&f.match("?>")){h.curMode=e;h.curState=h.html;if(!h.php.context.prev){h.php=null}return"meta"}else{return d.token(f,h.curState)}}}return{startState:function(){var g=o.startState(e);var f=a.startOpen?o.startState(d):null;return{html:g,php:f,curMode:a.startOpen?d:e,curState:a.startOpen?f:g,pending:null}},copyState:function(h){var u=h.html,i=o.copyState(e,u),f=h.php,v=f&&o.copyState(d,f),g;if(h.curMode==e){g=i}else{g=v}return{html:i,php:v,curMode:h.curMode,curState:g,pending:h.pending}},token:c,indent:function(f,g){if((f.curMode!=d&&/^\s*<\//.test(g))||(f.curMode==d&&/^\?>/.test(g))){return e.indent(f.html,g)}return f.curMode.indent(f.curState,g)},blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:"//",innerMode:function(f){return{state:f.curState,mode:f.curMode}}}},"htmlmixed","clike");o.defineMIME("application/x-httpd-php","php");o.defineMIME("application/x-httpd-php-open",{name:"php",startOpen:true});o.defineMIME("text/x-php",l)}); \ No newline at end of file diff --git a/route/static/css/ensite.css b/route/static/css/ensite.css deleted file mode 100755 index 531a660fe..000000000 --- a/route/static/css/ensite.css +++ /dev/null @@ -1,3703 +0,0 @@ -body { - line-height: 1.4; - color: #333; - font-family: "微软雅黑", Arial, Helvetica, sans-serif; - font-size: 12px -} - -input, -textarea, -select { - font-size: 100%; - font-family: inherit -} - -body, -h1, -h2, -h3, -h4, -h5, -h6, -p, -ul, -ol, -form { - margin: 0 -} - -h4, -h5, -h6 { - font-size: 1em -} - -ul, -ol { - padding-left: 0; - list-style-type: none -} - -fieldset, -img { - border: 0 -} - -a { - color: #333; - border: 0; - text-decoration: none -} - -a:hover { - text-decoration: none -} - -a:link { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-touch-callout: none -} - -:-moz-placeholder { - color: #999 -} - -::-moz-placeholder { - color: #999 -} - -input:-ms-input-placeholder { - color: #999 -} - -input::-webkit-input-placeholder { - color: #999 -} - -body, -html { - height: 100% -} - -.f12 { - font-size: 12px -} - -.f14 { - font-size: 14px -} - -.f15 { - font-size: 15px -} - -.f16 { - font-size: 16px -} - -.f18 { - font-size: 18px -} - -.f20 { - font-size: 20px -} - -.cw { - color: white -} - -.c0 { - color: #000 -} - -.c3 { - color: #333 -} - -.c4 { - color: #444 -} - -.c5 { - color: #555 -} - -.c6 { - color: #666 -} - -.c7 { - color: #777 -} - -.c8 { - color: #888 -} - -.c9 { - color: #999 -} - -.cbt { - color: #20a53a -} - -.bgw { - background-color: white -} - -.bge6 { - background-color: #e6e9ee -} - -.plr10 { - padding: 0 10px -} - -.plr15 { - padding: 0 15px -} - -.plr20 { - padding: 0 20px -} - -.ptb10 { - padding: 10px 0 -} - -.ptb15 { - padding: 15px 0 -} - -.ptb20 { - padding: 20px 0 -} - -.pd0 { - padding: 0 -} - -.pd15 { - padding: 15px -} - -.pd20 { - padding: 20px -} - -.pr8 { - padding-right: 8px -} - -.pl7 { - padding-left: 7px -} - -.pb15 { - padding-bottom: 15px -} - -.pb55 { - padding-bottom: 55px -} - -.pb70 { - padding-bottom: 70px -} - -.mt10 { - margin-top: 10px -} - -.mtb10 { - margin: 10px 0 -} - -.mtb15 { - margin: 15px 0 -} - -.mtb20 { - margin: 20px 0 -} - -.mlr15 { - margin: 0 15px -} - -.mlr20 { - margin: 0 20px -} - -.mb15 { - margin-bottom: 15px -} - -.mr50 { - margin-right: 50px -} - -.ml5 { - margin-left: 5px -} - -.mr5 { - margin-right: 5px -} - -.mr20 { - margin-right: 20px -} - -.mg10 { - margin: 10px -} - -.va0 { - vertical-align: 0 -} - -.ico-font-ask { - border: 1px solid #999; - border-radius: 8px; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px; - cursor: help -} - -.btlink { - color: #20a53a -} - -.btlink:hover { - cursor: pointer -} - -.btn-btlink { - border-color: #20a53a; - color: #20a53a; - vertical-align: 1px -} - -.btn-btlink:hover { - border-color: orange; - color: orange -} - -.b-shadown { - transition: border-color .15s ease-in-out 0s, box-shadow .15s ease-in-out 0s -} - -.b-shadown:hover { - -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) -} - -.important-title { - background-color: #fbfbfb; - border: 1px solid #eee; - border-radius: 3px; - line-height: 28px; - margin-bottom: 15px; - padding: 5px 10px -} - -.bt-input-text { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 3px; - -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 -} - -.bt-input-text:focus, -.bt-input-text:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bt-submit { - background-color: #20a53a; - border-radius: 3px; - width: 140px; - height: 34px; - line-height: 34px; - text-align: center; - color: #fff; - cursor: pointer -} - -.cursor { - cursor: pointer -} - -.help-info-text { - margin-top: 15px -} - -.help-info-text>li { - list-style: inside disc; - line-height: 24px -} - -.relative { - position: relative -} - -.ico-copy { - background: url(../img/ico-copy.png) no-repeat; - height: 14px; - width: 12px; - display: inline-block; - vertical-align: -2px -} - -.zclip embed { - vertical-align: top -} - -.webDelete .options { - padding: 20px 0 -} - -.webDelete .options label { - width: 30%; - float: left; - font-weight: normal -} - -.webDelete .options label input { - float: left; - margin: 0 10px 0 0; - margin-top: 1px -} - -.webDelete .options label span { - float: left; - margin: 0; - line-height: 16px -} - -.webDelete .vcode { - background-color: #f0f0f0; - clear: both; - font-size: 14px; - height: 40px; - line-height: 40px; - margin: 10px 0; - padding-left: 12px; - text-align: left; - color: #444 -} - -.webDelete .vcode .text { - margin-right: 10px; - margin-left: 10px -} - -.webDelete .vcode #vcodeResult { - display: inline; - height: 26px; - line-height: 26px; - margin-left: 10px; - width: 50px; - color: #444 -} - -.btswitch { - display: none -} - -.btswitch+.btswitch-btn { - outline: 0; - display: block; - width: 3em; - height: 1.8em; - position: relative; - cursor: pointer -} - -.btswitch+.btswitch-btn:after, -.btswitch+.btswitch-btn:before { - position: relative; - display: block; - content: ""; - width: 50%; - height: 100% -} - -.btswitch+.btswitch-btn:after { - left: 0 -} - -.btswitch+.btswitch-btn:before { - display: none -} - -.btswitch:checked+.btswitch-btn:after { - left: 50% -} - -.btswitch-ios+.btswitch-btn { - background: #cdcdcd; - border-radius: .9em; - padding: 2px; - -webkit-transition: all .4s ease; - transition: all .4s ease; - border: 1px solid #e8eae9 -} - -.btswitch-ios+.btswitch-btn:after { - border-radius: .9em; - background: #fbfbfb; - -webkit-transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08); - box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08) -} - -.btswitch-ios+.btswitch-btn:active { - -webkit-box-shadow: inset 0 0 0 2em #e8eae9; - box-shadow: inset 0 0 0 2em #e8eae9 -} - -.btswitch-ios+.btswitch-btn:active:after { - padding-right: .8em -} - -.btswitch-ios:checked+.btswitch-btn { - background: #20a53a -} - -.btswitch-ios:checked+.btswitch-btn:active { - -webkit-box-shadow: none; - box-shadow: none -} - -.btswitch-ios:checked+.btswitch-btn:active:after { - margin-left: -.8em -} - -.bt-warp { - position: relative; - min-height: 100% -} - -.bt-warp>.container-fluid { - padding: 0 -} - -.main-content { - margin-left: 180px -} - -.sidebar-scroll { - background-color: #3c444d; - width: 180px; - z-index: 100; - height: 100%; - position: fixed; - overflow: hidden -} - -.sidebar-auto { - overflow: auto; - height: 100%; - margin-right: -18px -} - -.mypcip { - display: block; - padding: 0 10px; - position: relative; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease; - width: 100%; - cursor: pointer; - margin: 1px 0 -} - -.mypcip:hover { - background: #20a53a; - opacity: 1 -} - -.mypcip span { - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAYAAAAS7Y8mAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGPSURBVDiNldQ9ixRBEAbgZ3Zn1fVOuQU/wK9AMDDS1MvuRxj5CwTBP2CiYmYmGBsYGRsaa2qkkSiCgRd4y3ri3XqzbdDV7Lgs7uwLRc90V1dXv29VVymluxiiRoVkNaoYG7wN+9chpTTBBn7jKA5YhYR+JPQEDxYd6rAPeIwDnOgQuMEIz3F8mUMdC9/wqkPAxb1P0Yv/6ziLX/hTy9c6jXPYXSPwBQywF+Mz7GCCSRGMbqK1Ufwbmb4beI9TuNZFqFWoZCH7Mp0XcaX33y3dUDKfmRdD1bM+BYuoWtaEKeIdyqSvg5+RZSWX6Ri3ZUqmRbxjcqnsx3cyF7Utbpmf4YxcapXcWFNcjiSbOk7Yxhu5BocRaGBeoyXYNII32MTJsJ7cvR/D92aN1xFsFA6HYZ/iiiWjTVyVGyrhBz7jXSQxxq3wPahxJ7JuX30sd9IoshviC77KzdSmag+XYm4QNq0tF+0e7sc1SwPs4xFeLPHvt2ibsfwl24rFl/KLV/jdCho2ZC3a+I6HOB9rR38B10ZjDE49T6kAAAAASUVORK5CYII=") no-repeat 0 center; - display: inline-block; - line-height: 46px; - padding-left: 30px -} - -.btpc-plus { - line-height: 40px; - color: #aaa; - font-family: arial; - font-size: 26px; - cursor: pointer; - padding-left: 80px; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease -} - -.btpc-plus:hover { - background-color: #20a53a; - color: #fff -} - -.mypcip .btedit { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNndO/ahRRFMfxz+zOav6goKC1S54gBBUECy0lIKTeyrXwFcROyBuENCHdNhaCoCgiWoggGPAJxFgIsbEJJBmTrCfFnBFZNjHrDy4z5849935/Z84tIsJ/6gneFhFR4AHuYHjM4jN4iZWM13ALSyWWcR+rOERrzAYFNvP9Ka7jKrZExKeIWI8IpxivIuJrRFxo5lqJXfzDbwfvcQU3cAnTUOIsfif6zeYDfuEDzuM5LuIatrGBu/hcInCAy1mH2STawyLmknI+56bykFZDsJsUP7CAdhIMUeELXo9YGmbOn4o3Nah6vd5Ot9vd6ff7lfr3dXKMVZnIVVp4MRgMZtLW6LpneJhxpB1lJnfwE49yblRtfPsrLhoLpbqA7fT15jjUERXNQeO6biK11DXYnyCnymdIjI+4jXuJdlJXHqq7cArf620izkXEu5hMj5u7cAQCwbENi6a1WQAAAABJRU5ErkJggg==) no-repeat center center; - width: 16px; - height: 16px; - display: none; - position: absolute; - left: 156px; - top: 14px -} - -.mypcip:hover .btedit { - display: block -} - -.task { - position: absolute; - right: 6px; - top: 14px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold -} - -.softnum { - position: absolute; - left: 154px; - top: 12px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold; - display: none; - color: #fff -} - -.cmdlist li { - border-bottom: 1px solid #dbdbea; - line-height: 48px -} - -.cmdlist li .titlename { - padding-left: 12px; - position: relative -} - -.cmdlist li .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: 0; - position: absolute; - top: 6px; - width: 5px -} - -.cmdlist li .cmd { - height: 200px; - background-color: #424251; - overflow: auto; - line-height: 22px; - color: #fff; - padding-left: 10px; - font-family: arial -} - -#remind td { - vertical-align: middle -} - -#remind .titlename { - position: relative -} - -#remind .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: -10px; - position: absolute; - top: 6px; - width: 5px -} - -.btn-default[disabled], -.btn-default:active[disabled] { - background-color: #f7f7f7; - color: #bbb; - opacity: 1 -} - -.table-page { - height: 32px -} - -.table-page a { - border: 1px solid #ccc; - float: left; - height: 30px; - margin-left: -1px; - outline: 0 none; - position: relative; - width: 34px; - z-index: 1; - color: #666; - vertical-align: middle; - text-align: center; - line-height: 30px -} - -.table-page a:hover { - border: 1px solid #20a53a; - color: #20a53a; - z-index: 3 -} - -.table-page a.disable { - background-color: #f3f3f3; - cursor: not-allowed; - color: #bbb -} - -.table-page a.disable:hover { - border: 1px solid #ccc -} - -.table-page-select { - float: left; - position: relative -} - -.table-page .table-page-num { - width: 60px -} - -.page-select-ul { - background-color: #fff; - border: 1px solid #ccc; - bottom: 29px; - left: -1px; - line-height: 22px; - max-height: 150px; - min-width: 100%; - overflow: auto; - position: absolute; - top: auto; - width: 60px; - display: none -} - -.page-selected .page-select-ul { - display: block -} - -.page-select-ul li { - padding: 0 12px -} - -.page-select-ul li:hover { - background-color: #f0f0f0 -} - -.sidebar-auto .menu { - background-color: #353d44 -} - -.menu li { - margin-bottom: 1px; - position: relative -} - -.menu li a { - font-size: 15px; - color: #d6d7d9; - display: block; - line-height: 44px; - padding-left: 52px; - background-repeat: no-repeat; - background-size: 16px auto; - background-position: 25px 14px; - border-left: #404040 2px solid -} - -.menu li.current a, -.menu li a:hover { - background-color: #2c3138; - color: #fff; - border-left: #20a53a 2px solid -} - -.menu .menu_home { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII=") -} - -.menu .current .menu_home, -.menu .menu_home:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKTQAACk0BtZPkxgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNpdO9atVBFATw3/UjBEGidkJSRFFIiKJBsAmaBxDFQptUIoJPYCc2Wmpjo4haRUFMnsEmnXUIKQXFRlEvaiIxY/HfheXCNUUWht2dM+fsYXe2l8Ruxp4d4jN4gDNlvw/70dMQw8ZRXMdFbOAvpjGJ93iHrWEdHMdDfMMcPuMpjiC4hYPDOpjCPazgceGeYS8m8BYj2AZJWpxOspTk9gBfcTPJSpJrlRtMXk5yY0hyxUKSF0lOtQWuJHmV5NIOyRWXk7xOcrVe4iSW8BXj/3mZ+jpfiv5ErThS5udJzjcnHUgyXubKnU3yUrcerR38wSH0sVm4C3iDO+Xm5wu/hX44jI3WB4OePoaPeIIPOm9U3XYVtQV6JfC77PtYxyrW8KPwP9vDWiNFZ5BzOtvO4Fejm8ZJzGqM1BbYLHiE77pPc7fEPuE+FjCGxaLV2+13/gdXJgTGYi2BZQAAAABJRU5ErkJggg==") -} - -.menu .menu_web { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHuSURBVDiNpZO/S9ZRGMU/5xZl0NJQDf2ybAgrGgylGloiCR0jDAO3+4WQlqA/oiHkpUGvgYtFRENL5g+CCEIiqSjMoFKsaJFqScMXfU9DV3vVluiZDs997odzuM+Vbf6n1v+tmVJqA9okHba9CXgF9McY+1fPqtpBSmkH0Al8B45Iema7DDRlyHagK8b4ZQ2gp6dnn6QB2+2SdgFvJW2xPQeUbdcCM5L6gNYY4/QyIKW0AbgGPAb2AueALuAQMA9MApeAu7YnJZ0ALscYyyE7aZf0JsZ4R1I/MAKMAwFYl+0/AW4URXFb0gTQTh4AOGP7Xta7gbEY43NJw8BA1hPAHoA821wdYUxSCZi13QhslPTI9kmgLGkUOG37m6QXwGbbF2OMTUvPuGD7JzCbM2N7FigD87bnJC3r7HyhOsK0pNEY46CkoRxhRNJDYDjGOGz7fdYPsqNP1YBB2y0Ai4uLH4GjKaUG281AS0qpAThQqVSmsrsWYAj+bOIt4Gpvb+/ZEMJ+4BTwLp8ZaASOhRA6UkofgHrgyupFqpN0H7gA7ATGJW21/SNvY10IYcZ2n+3WoiimVgAAuru7a0MIncBXoF7SU9sLwHHgJbDN9vWiKD4v3VkBACiVSqqpqekAzgMHc/s1vz/TTVbVGsC/1i9dw/hm1FHr2QAAAABJRU5ErkJggg==") -} - -.menu .current .menu_web, -.menu .menu_web:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGCSURBVDiNpdLPaw9wGAfw13fExWVq1IzYJEkc5kezgws5zM2S+irOWi7KH+EgB0e1m9ZycMGWm0jLRYqUWNNyIDkw2lq9Hb7P5rsfDvKpT717Pp/n/byf53k3kvif0/GX+AXcx3t8wgQurvexsUrBDozgGw7jBRZwHK+wHbeKdA1BLx6iiZ14i078LJLd+IJRnMVMO8Em3MQT7MH5qnQQ8/iAq7hXeBDXsLCxqjfxBuPVRide4xA2lPxnuIPP2Fo5o5JIMp6ku/BAkuHCp5MMFr6S5Gjh7iRjSSwp6MUpzOEYNmMRJ6v/rmqnG7uwpVpdJljEryKYr9hcJc+3DXIJd1TOMsEMnmMW36vSYzQq4Sn21Vqn0INz/DHSBIYKf8QR9ONMxfuxH9P1ZwiT7Qru4gaGsbfm8a7eUnMZwCUtdx7AdVYaqQ8PtCzbo7XGLvyo/vusNNL0agJabhvB16oyVcM6gZfYhts1q9LX2mv7bSS5nGQyyWzdR0ma6/xdo+Cfz28JnsxkWP6vVAAAAABJRU5ErkJggg==") -} - -.menu .menu_ftp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH6SURBVDiNpdM/qJZlGAbw3/1+n31o/0BJrMFaGmoRRBsM14M4iINLixBxHs6pxSxBmhpCqEk5CL2Pp5ZAdBD/UIJNhdBQzTqJYKkoB8HEY+r53rvB99hnuXlNNzfXfT3X9dzPE5npWTB8WrPWuhvvYwvGOI+vSykn/8uNSQfz8/MvdV33DRaxBlMInMZ9LI7H449mZ2cfLM80y0Xbtiu7rjsZEecz8ws8xBEc7l3swfXBYHB8bm6u+Z9A0zSfYmtmXoyIg/gKV3AJLQ7hV+wcjUYzT0Sota7C2Yg4kJm7sQM/YQOWcAFb+ygn8HHXdVMzMzMPlx1swML09PSPEXEIpzCNc/gBH+D7zPyylHIGt5umeWsywou4B5n5BhZKKTdwFddKKTdxJyLe7PmLWD0pcB2v9PVlvN627WZsxKZa6ya8hj96zsuZuUD/DobD4YWlpaUVtdbP8A62RcRNrO+38SG2I2qtv2AUERcfXyLUWnfhu8zcGBEFv+PVPtp9vI2j+A3vlVKOPSHQi+zHrcz8NiLebZrmr3yE1ePx+OfBYLAfK0opnz9eY9u2WzJzHRJ/Yy3uRsTePnfgSmYexAu4gZX9/NUh9kXElH8xxgjPTfTWR8RmPMBg+XCcGOKTiHh+gtxFxKqu685gXd/7MyJ29g6bCe7teNbv/A8ZE8Q3GMBOFwAAAABJRU5ErkJggg==") -} - -.menu .current .menu_ftp, -.menu .menu_ftp:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGVSURBVDiNpdLdaw9wFAbwzw+jzVspIxdIuXHhQqbGHVmSRLlx4w9YKbkhpVz4A6SkKOWlldA0uXA1Um645motL7NImsnYNI+LnZ9+W63IqW+dzvc8zznPOaeRxP/YgnnixzCISUzgEQ7/DcEK3MEefEMDbfiKI7iKxfMRtKMfT3EePwtwCdM4gVHcnoVL0nxnk0wl2ZvkYZLuJKeSHE+yO0lfkgOZsd4mrgnuSPI4SU+SW0nGkwwkeZ1kKMmDJGNJric5mGQwSVsrQXeSu+V3JbmRZE2SK0kuJuks4i2Vcz/J1iR/tCzH9/I34hM+YATv8bEGublyJrCqdYijWF3+MDagC9uwvd46vK2clVXEogq8rHWdwQ7sq6rraxu92F9rfYYleNVKMI3LuFlVh/ECQyVtEmPow3McLYzGnFM+jc+4hl0YR0rvk/pvw7kmoJFkJ9ZW4g90mrnCk6W7gTe4gGU13PbCjzSS9KOnpYvp0jjrZEvGFBY2i+NeI8kmLG1J/IUODFRn8A6HqsPW8/8ydwb/bL8B1eb4OuOuSusAAAAASUVORK5CYII=") -} - -.menu .menu_data { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHISURBVDiNpZPPi81RGMY/z+3mpmasGBOZEgthQ5SFTDM2svMjOyt9X1eUEhshRbGx1e1cO5M/YVIzSWbjx4RidTeKbl3XgoyYW5rHwvlO35TZzFtn8fac5znPc855ZZvVVL3apJR2AGeAw5JGbI9m6Iukvu0ZIEVEp+TINimlOnANGAc6wCvbHUm/AAHrgZ3AHmA7MC3pVlEUS6WDWaALHIuIb/9xOw3QarVGa7XafduzwGQtg+PA+xXIy9VsNnvAHDBRvYMBcDyldEDSvO1nkj7lCNhea3uLpEO29wOjmbMsUAfOAYu2C+CC7XW2hzL+A1iw3QWu5/0vqgIAD4A7EXFxpQgppRPA1bIvBX4Dt4GJlNIV4DPQlTTIERrApryeA3eBh1WBBkBEnM+n7AO22R7O+ALwISJeZvxkySn/wQD4CDyV9Nj2XET0/7G+ATgIHAEmgbGIaJQO1gCngF22A7iRUloCfgLOpwnoA4+AFvC6GmEGOA1ciogpgHa7vRkYygLfi6LoVdzcI79CGUHAZf5+jh4wb/udpK/5EoclbQX2AruBt8DNiFhUdRpTSmPAWeCopI22R7KDfh6mJ8BURLwpOVrtOP8BlJPKP95zNKgAAAAASUVORK5CYII=") -} - -.menu .current .menu_data, -.menu .menu_data:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF3SURBVDiNpdI5a5VhEAXg58qFRDBi4RIiBkSJoiIoihZi0EpsFWy0FAtbSwvB0uUn2LiUliIWMWgT90i0sRFcYxqFiCYQcizufHK9hUQcGIZh3jnvOYdpJfE/sayn34pLeI4PWKj8jJe4jJHuhVYxaOM8RvEGT6r+RAursQ27sBl3cBGLkkgynuRmklXV/y0Hk9xOMpbkt4RRvMK3JciexkMc6pYwhyl8wlM8wPuSAMuxAQexF4PYif4GYAH7C+g0hrASKwrgO2bxEdfKs0doN7oWkkwmObEED44leVY7f0g4Wbr24Uv9Nl8M+orVECZwH9fR3+56AGer7sEmDFQ/i7d4XP3xZqdhMI93GMfdcnmmx/01OIAjOIxh9DUAwW5sx6lyeRE/atanc1AzuIXXOtfaaoy5l+Rqj1nrk2xJMlLH0z27kmSi28QWzpWJ0zq3MIWvRX8AG4vlDkziAuYagCaGcQZHsQ5rS8JM5Rhu4EWz0Avwz/ELJiL9PSckq44AAAAASUVORK5CYII=") -} - -.menu .menu_set { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH3SURBVDiNpZI/SJZhFMV/90kicsv+EdEQRUNQYC1S0R+ag2hxMKPgvc/r0Ba2FFEpJARhiF/P9QsaGippimgoG4QgI6egICcJEkoIEyI/9L0NvV98llLQmS6c5x7OPc8Rd+d/0LQcUa1W1xZFcR9ocvfOGOPkXwXMbDvQoqov3b0NuA00i8gBYNLM9gBfVXWiviP1E8xsB3AZmANmgHVAt4jMu/sl4DuwFfgI9KvqO4DQYKAFmFXVU8Bj4JqqfsiybAroBZ6r6jFglbtv+MNB6aIHeKKqL4aGhra4eycQRORelmXvU0pHgPYYoy4SSCltFJGDwEngCjANXHT3WyGEeXfvAq4Dze7eKyJ3i6IYyfN8KgCIyAjQDFxQ1VfAaaA/xjiWZdm4iPQBHao6LiLdwJoQwsiiX3D3z0VR1NNd4e5zDfnUACnnWeATsNB4wjYR2Q8cdfcbIYSau58D+kRkwd273X1QRGaAAWC4KIrRPM8nfg/xqrs/jTGOmtlOoKN08zDGOGZm+0SkPcuys7+W3J3Sxd6U0s1ybk0ptda5SqWyOqW0u+QepJTa6lxjD2rASjO7A5wBBszsEEAI4TzQZWaDwBfg23I92AWsV9VnZnYC2OTur0Vks6oOm9lhYFpV3ywp0IiySI/42bzjMca3S71bVuBf8QODpRL9eTmkdgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_set, -.menu .menu_set:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF0SURBVDiNpdJNiM5RGAXw38ukkJVvMYqZRsqYZmyYDTZWbCxsKLG3pCSaWNmNJU2NmhU7pQjFRhYsJCTWykfJRz5eH8fmefn39lLy1FPn3nPv7Zzz3FYS/1Oz/sItwg3cwuo/HWp1KRjEQtzBTizAfHzCDMbwDk97PTCECXzBWyzGYXzDCXzGGjzHJB6DJJ3ekuRc4R1JNja4VUl2FZ5OsrXDNTO4jRcYx1W8wTEcx1xcwna0cbM7xGXYgxF8LakTuIbLZWVdWevHXixvWniY5ECSkVqfbGBJBpOcKjyc5FCSR90WXjXSnV1hdqqNVuH3eInvTQUDSfYnmUkylmRDkvNJ1icZSjJV+wNJriQ5WKr01avPqtfW3O/hNPaVmrO1N14qp35pa/jclORM4dHqDjevMdYLSTZ3uD6/q405mMYHjOJojewIluJHjfdj51L3Vx7GElzHbqzAXazERWzDazzoZaG7+5PcT/Kkwux5rlvBP9dPgIpDWf6ENxgAAAAASUVORK5CYII=") -} - -.menu .menu_folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE3SURBVDiNpZO9SgNBFIW/OySiaBCtbCwE8ReUgGDpE9gExNJmd6O+hM8gmGJn3yKxt7WyEARBUUSbIPhTGFAwxyarSwoZ2AO3mjkf98y9Y5IoI1fKDVQA0jRtOOd2B8BRST5Jkk4IwCSRZVkbOAYeACdpD3iX9DrUpQO+gHaz2XwuRnCSLuM4vpU0AnSAvplNmdlkocbNbN3MTrz3de/9WKVA/07TdN85twbcA5/A8AsLuAO6wI6k2UrhoGZmy3EcH4Zk995vAAd5hA9gBXgJMQ+0ZWanOaAH1IGrEGer1TJgxszOckAFWATOQwDVanUO6EVR9JYD5oGJJEmeAtvfBG7gb4xLwEWgGWA1v59PoQs8Zlm2ANg/RgETwLRz7voXIOnIzBrAdgCgBvgoivowWOUyKv0bfwCvBmEVd9ynHgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_folder, -.menu .menu_folder:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADuSURBVDiNpdO9SkQxEIbh56ynUHQbQbCxEMR/BEGw9B4s1m6t9W5svQgL3d7Wyk4QLES0EcGfQkGbsTgJuywIwTMQEsh8b+ZLJlVEaBOdVmrUad7HQQJO4hQXJYAqWTjHCR4S5BAfeBursoOflP8CIkJEDCJiLq03ozymcgUD9NHDFu7xjfEbjmR7Oo2FemSjizUcl3jHDo6yv0+s47VQDHsYZMAXtnFTKK4wj8sMqLGCq0LAYjr0PQOWMIOnQsAu7hi+8SquC8WwkfPzKzzjEcsaf39FpEpnccuwE3uadp4oAHQ1rX42Cvh3tP6Nv5Cebn/RRiyLAAAAAElFTkSuQmCC") -} - -.menu .menu_day { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNpZMxSFxBEIa/ed49tQsBQdEqgrVgithFW1sr62PhKttXpjy0vWb2WjkQrk6rjWUgjY3ICorYGCFV9C7vxuJ24fE4Y8SBZf/9d3b2n/1ZMTPeE1l10ev1Frz3Wy8lq+pXVf1Y5cTM6Ha7jTzPd4BtYBUYAHO184/ALnAJnJRl+b3dbo8aAM1m8xB4AO6BT2b2C5ivFxCRv8AtsJ5l2SZQYGao6kBVFyNeNjOmDVVdifOSqh6Z2aQF7/0xcGNmF4BMkV9tw0RkDfjgnGs14kYJXInIOTALjGOhukUZ8AQ0gc+JIJI/nHNnIYRWCME5505CCC7i04hbzrkz4Gc8Q1IwEwedTmcvXfcSruZnNZKiKPpFUfT/hZnYX1YVyGu31hRk6X2SgnEi/lPBb6JTSUFuZsM3KPiTWk4FbkXki/d+CORMbK3aKEzsTXhDRK6rBb4B+2bWAkbVN5kSDRG5E5EDiJ/pPfEM08DH4VH64rEAAAAASUVORK5CYII=") -} - -.menu .current .menu_day, -.menu .menu_day:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEPSURBVDiNpdO9SkNBEIbh58RoFKwEwT8QFOwEQQvt1NbW+7Cx8B7svAZLa1tbG0sbEQQh2KigjcZoxsI9YT2EGMnCMt8us+98s8sWEWGYUausp7HTJ38bU/lGPYt72MUyFjFeOfyOfdziAudol4BjPOMRS3jCRA/AJ5pYwxaORISIOIuImaTnU+w1F1KcjYjTiOi28IVD3KDoYT93EVhJ+hfgDtdooJNA1SeqoYVRbOSAFq5wmR0q+mhYzQEjaeYJ/XQ3v1bZlCrFH7rw03bXwSBVc10rYaWDzgBVc/0ivVTpYAwf/3DwJrVcAprYTJCx1F/15huZXsc9FOk3TuIAc2hXqlVHHQ84wWsx7Hf+BgvadUGnT3fcAAAAAElFTkSuQmCC") -} - -.menu .menu_control { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF5SURBVDiNpdM9ixNRFMbx3yRZ3/ANbCxEcBubpNJSLIQFaxs7D4JfQSz9CpYWItyPYCOIFtqvhQxopSyCb1gpEuNukrHImewQY7UHLsOce85z/vPMvVXTNA4SAyilfMI2Zpn/n2rV6RtGxOYgE9sITLK5Qa8jVGGezwpH8VgWwRTjiJigj6vYwrVcW5nrZ824pW0Jeh0xSVJhr4M86ez32/qBf6OH04nZCmzk1HbI0qN1AhO8zuYfOJ5i8xWKZq1AREzxpZRyAlfwJiI+r5RVrUCLNOvgtnER93FpDeV0laBnYcwMSimHcQpP7Z+V2xZelA7FkuA8opRyK9/P4gIeYq+Ucj2/fwM3Lc7Mua7AVzzH91LKHZxBExE/s/EudiwOz0u8wLeuwDgidvAMf3APr3LvHR7gfUT8joiPw+HwQwqrmqZRSnmSWL9wEpt4a/Hr5qPRaLeu60OdgcfwKCJutCZeTnNmuXZxpDWqruvG/j1o0vDhkuAg8RdE7nuSY6nc+gAAAABJRU5ErkJggg==") -} - -.menu .current .menu_control, -.menu .menu_control:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFaSURBVDiNpdM7a5RREAbg59uLJOgSkRQhfYSIhWWsQgj+gfgLTJEmBJLa0tZW0MbawtbaUkQEQ8Q/YCWIIAqb7G1S7JzNYdlUOfAxlzPznndevmkiwk1OJ+03nGKMBpNr6lsIdLGBrQJwimP05wCatFEBTHAHL2sGY/zHEMt4jBWMKqZ/8RkXVc8MoJX+MF8bJJth3nczV5i0s2cGUJ82eslgkLlbOU57vngRwAA/sqmftp35AhiFzSKAIX6mv4+3C2pa887YlTjlPMAJtjNeMtVC1k5qgA5Wa2Q8xQvsZnyI51jD3cK+jLCOA/xLyn1TET/m3U4+0M+xRpmfAfzGBzzBM/zB17Rf8B57+ISHpsI+AhEhIt6kbSLiKCLOImIzc72IOIyIbsblex0RMwZLuGf6t73Dd/zK/AVepYDLKV4v7zS5jfUyjXCO25Wok/TLnnRwH1vNTdf5EpkVg1v2kuagAAAAAElFTkSuQmCC") -} - -.menu .menu_soft { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF7SURBVDiNrdPPi41hFAfwz72uoVsUWZOFJbKThYkVe1mpx13YjcmU/NwrZCKRLNRZsrSZErvJ1h+glFgiYxC6Pyze8zSvOwsLzuZ5T9/n/T7n+z3ndCaTiX+JHkTELlzHHozQxQrOl1JeRcReLGIbxtiAN7jcS6JrCT5OcIx9mMcAZ/EBz5N8hCO4UQl240kp5XYtLSJmsZDpdjwspTxr4V9xppv5EP0peVuzEnlumcL7GFaCDn5OXfj+l/wHulXCBIci4rXG2BFmMZP4RhyLiBmNR0McxqgSLGBOY1iNIW7l96LGyNMtfBXznf81B/vzhR0pB37hbillOSIO4hw2JdbBJ9yrEu7gIx7504OLWMYFvMcLax6cwv1K0MPLUsrTWlr2eS7TEZZKKUstfCcGtY3jVnk1+i05E+vnZDPGlaBnfZ+/aMZWnqtT+Df0qoR3ON7qc92FlcQ/YxARB6ztwlG8rQRXNdt4Mn/u5AuXEn+AmziRcroaU6/8BgTXdRpxDzi5AAAAAElFTkSuQmCC") -} - -.menu .current .menu_soft, -.menu .menu_soft:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFsSURBVDiNrdO/jw1RFAfwz4xnVzYIydZeY0tEKETChkIoNGrNtkKyhQL/gUg0YgudQiciGpVEIRsdPdlkQ0usH4vNvHcUc27e9bbkm9zMmfnOPfP9nvneJiL8CwZ5PYS72I8xdmANN/EeQ9zGAkZosYHrTSp4gD14m+QIZ/ATl/AI83iRzcc4jC0RISIeR8S5rMu6EhFvsl6NiOUpfjEinrZpYZwKasyhy7rL+xp7MW6rB5tTL/xKO9Dg9xS/WQ9xJ85jJj12OJ2zgMBJvMs9IyxipgzxFK5lo4JvWMFrHMNV7Kv4DivN/8rBkVQwn3JhC/fwCiewjNnkGnzG/aLgJT7h4ZTHBVzEE3w0yUGHyxgWBQOs4lml7rvet2z4PFfBASzVOZj1N+YqO2F7DnapcjCwPQdfTXLQ6v9KjR8YFAsfcMEkByXrG8l/wRKOmpyVs1gvQzyoP23D3NzkF27oc3Acd7A77bT6od76A2AskgeNVoIQAAAAAElFTkSuQmCC") -} - -.menu .menu_firewall { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAG0SURBVDiNhdNPiI5RFAbw352ZsGBDMhkpYyE12cxOyYIFayW7SY2zkCxIlkrZKIrJjPe+X1FksrQQo/xp2IhZiPXUxIoaGxa+mGsx78dn+vDUvXVu53l6znO6qZSig6mpqbX9/f37cRB7sDOl1C6lzOIZHkXEK11IpRR1Xe8upRzFKNqYxzt8xGpsxy6swwCeYjIi5gcaoTt4i8BcRBQ90Gq11i8tLY3gCjZibABKKUu4FxGvexE7GB8fX8RsVVVPUkqDGjtQGqv/RM75CJ6nlH6gT+dCwvf/kE9hGlvwrZfAqrqu+3LOQz3Ix3EJpyPipeUwdQt8RbuUshkLjdUOeQzXcD4iLnfptvmdwXtsjYgPOefrmM45f0Y/buJiRJzrIm+wvOJfDl5gBCLiBFp4iPuYiIizK6YaSim96Ra4jeGc86ZG5BhyQz65Io9hDJZSZkApRSlFVVUTVVXd7dR/O1VVPaiq6mqn7jiQUjqD0Zxzd1B/IOc8iW1N7zKv+zM1I8zgE8YjYqF534EbTegHImKxp0DTnHABh3ALa3AY0ys20Vugg7qu95ZSHuML9kXEXK++ny1tzgEddf2OAAAAAElFTkSuQmCC") -} - -.menu .current .menu_firewall, -.menu .menu_firewall:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNhdMvaJZRGAXw36viFrRoENxw4IJYLDMJalkxC8OkrC5qNAwEi+CC4lAsikGMY0FQmGIQ/AMKyjDIUGYyqMXgmB7Dd9/P63j37cAN73OfczjPc+7bJFFhFyZxCsdxGGt4hqd4hFc1oSkCxzCNiUJYwXt8xRDGcQS7sQNPMI8VSST5lGQxydEkTal1nT1JTiR5k+RuEtuKkz9YwGv8N9MGfCvjLBUnfYEUq1vhDEbwu+W2Ag3WtyCfx32M4leXwM7yPdJBnsFVXMALvWWqBX7qbX8/PherLc7hBi5hrqqvURaBVRzAF9wsVr9jO+7gCmYr8l69iLXxXEyyUMV1O/9wrSPOpSRnk/QFxpK8S7Kvarq1CflgkuW2t764nuTBgEfUnoe1cH0xnORjkrkB5PkkH5IMdQkott4meVzGauuHkjxP8rI85z6n/ZlqNLiM07iHYUyVZGY3Ng+a9WSS9SQ/kkxs1vcXeVqZSyUF+yoAAAAASUVORK5CYII=") -} - -.menu .menu_exit { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEYSURBVDiNpZOtTkNBEEbPFCQIUHgEBoJAYFDgERgSCBjSvU9Q0/AkX5+ApB6PI6lAYPhJUCgEgkApkHyI/nBpb28DPWozu3MyuzsTtpmG2f5CUs32BlABouBsAK8RUU8pPY4IImIfOLd9k4/n+IyII2AZGBUAM8BZlmXX48qVtAksFl6hx/y45B5N4C4fqExI+EVK6QJYkLT9L0GPJ+BU0hYUP9YAScfACtDh52c6wAPQlHRQKoiIN+B5WGC7DXzYfi8VVKvV5nCs0WgsRcSh7b0sy1qlgiJszwG1lFIL/viIknaAdkrpsh8bruBlgmOXbq8UduIXcCLpvkDc31+j20wDBgdty/Y6sMroMJnuda+A2/xGTDvO32OQXrvPg7l3AAAAAElFTkSuQmCC") -} - -.menu .current .menu_exit, -.menu .menu_exit:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADvSURBVDiNpdO7LkRRFAbg7wwlBZVeoSEKEtHSayUSGs+g8RwqbyDR6ycaiYRC45KoVAqFuAySpdhzODn2mWMyf7KSvdflz7oWEWEUjFfee1hCB0XGt8AL9vGQI9jECa5r+hKf2MZsE8EYjnA1IOMVTDeVAJMDguEYt1VFpyWgji6msNaUwX/wiAN84LSNYAdz6PmdTA/3UjlbbQSveMoQvPUzeBcRpVxExGrl3yQzEdGNiOWIGLqJMCEt3TnDT2G9n/5Zqaj34LmFYEPalewmfmEXdxni0r4gdf8HVcdDLGLe32MKqdxL3FQNxajn/A0ZS19hUhhlTwAAAABJRU5ErkJggg==") -} - -html .menu .menu_home:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPPz8+np6d3d3c/Pz8vLy8XFxb+/v729vbu7u7e3t7W1tbGxsa+vr62traurq6mpqaenp6WlpaOjo6GhoZ2dnZubmwrPOpmZmQzLPJeXl5WVlRLFPhLDPhy5RI+Pjx63RImJiYeHhyypSiirSIODgzKfToGBgTabUDibUDiZUH5+fj6TUnx8fECRVEKPVHp6ekSNVkCPVESLVnZ2dnR0dEyDWnBwcFR4XlZ2Xlh2XlpyYGZmZgAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJHgA7ACwAAAEAEAAOAAAGhsCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFDOkMkBUQ2B0g1HyIdNUUGX0OTOy0jNyMrRQecOzYIKjsyKDspMDsvpkQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAkeADsALAAAAQAQAA4AAAZxwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hKJgZfQ5FUJgeWOzYIKlQvm0QGFEM6QxUGSQgAARYcIhsWAQAIQkEAIfkECR4AOwAsAAABABAADgAABnfAnXC4MyVIxORwphAsTBWGR7ljERw7iSDSINiSKgJkGDkukqRBJDkRXIjpCZVSMAk1BA31TsjsHhcgM1QzIBcNQwUhVCQFRDYHSEomBl9DkVQmB5Y7NggqQzpDL5tEBhQ5NR8iHTU4FQZJCAABGC47LhgBAAhCQQAh+QQJHgA7ACwAAAEAEAAOAAAGfcCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFUJAVENgdISiYGX0ORQzpDJgeWOzYIKjs1HyIdNTsvnUQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAUeADsALAAAAQAQAA4AAAaCwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hCOkMmBl9DkTs1HyIdNUUHmDs2CCo7Mig7KTA7L6JEBhQ5JSc7KCU4FQZJCAABGC47LhgBAAhCQQAh+QQFHgA7ACwGAAkABQADAAAGDsDdTiesfUSdWmt0G62CACH5BAkyADsALAAAAQAQAA4AAAYVwJ1wSCwaj8ikcslsOp/QqHRKdQYBADs=") -} - -html .menu .menu_web:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAPHx8e/v7+vr6+np6efn5+Xl5eHh4d/f393d3dXV1dHR0c3NzcvLy8nJycfHx7+/v729vbu7u7m5ube3t6+vr62traurq6mpqaenp6WlpaGhoZ+fn52dnZubm5mZmZeXlxTDPpOTkyqrSiqpSoeHhy6nSiynSoWFhS6lTDCjTDKhTjSfTjadTjadUDSdTjabUDyXUjqXUjyVUj6TUkCRVHp6ekSNVkaLVkSLVnh4eEqHWHZ2dkiHWEqFWE6BWlB+XFJ8XFJ6XFR6XFZ4XlZ2XlpyYFxwYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgBHACwAAAEADwAOAAAHbYBHgoIdCgIACRyDizsTFQ0XGA4WEjuLNQMnH4shJwU1gw8di4sdD4IaGKSkGBpHC5argzsMRwYbsoMbB0cHHrmCHghHDDnARzm1GhnHGa5HEL+yHxCDmJqcJASg1hEUDRgZkxLGqxoJAQGJi4EAIfkEBQ4ARwAsBAADAAgAAgAABxGAR0dDPkBDgi0gNCgqNCAsgQAh+QQFDgBHACwEAAUACAACAAAHEYBFKUAogyNGPjZHPT5HMD+BACH5BAUOAEcALAMABwAKAAIAAAcWgDcoJjclKDciKTc6Kig9KCo6Jis6gQAh+QQFDgBHACwEAAkACAABAAAHCoA+Nkc6PkcwP4EAIfkEBQ4ARwAsBAAKAAgAAQAABwqARShAJSlAI0aBACH5BAUOAEcALAQACwAIAAIAAAcRgCwgMyUpMiAvR0dDPkJDioEAIfkECTIARwAsAAABAA8ADgAABxaAR4KDhIWGh4iJiouMjY6PkJGSk5SBADs=") -} - -html .menu .menu_ftp:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/f39+3t7evr6+Pj49/f39nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7W1tbOzs7Gxsa+vr62trampqaenp6OjowDZNp+fnwLXNp2dnQTVOATTOJubmwrPOpmZmQrNOgzLPJeXl5WVlRi/QBa/QJGRkY+PjyC1RI2NjSSxRomJiYeHh4WFhYODg4GBgX5+fnx8fECRVESNVkSLVkaLVkiHWHR0dE6BWnJyclB+XFJ8XFR6XlR4Xlp0YFh0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJMgBJACwAAAEAEAAOAAAHvYBJgoIhCAICCiuDi4IOEQsBAQ4QE4yCCR06DRUWD0AYDYwaATALNBweMA8sAB6LCC8QAwwEBQwDESwIgzQOSTURQBMVQBA5SQw3grFJKBdJGRtJFCxJDzKCOApJNg01DA42DDZJCziDCRsMAhQGBxIBDBsJiygCORYjqB8XNgAmjDZ8SCLjxg0YSTRkGBSkh48fO3i0UKGiRY8dP3z4GBKDhMcSIkCIBCGihEcSM5AUWVlkyAmRKYiwLHIkEAAh+QQFDgBJACwAAAEADwALAAAHj4BJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBQIACH5BAUOAEkALAAADAACAAMAAAcIgEE9MSRIRYEAIfkEBQ4ASQAsAgAMAAEAAwAABwWAPiRFgQAh+QQJDgBJACwAAAEADwAOAAAHIIBJgoOEhYaHiImKi4yNjo+QkY8/OzyMJCUijEVFQ4mBACH5BAkOAEkALAAAAQAPAA4AAAepgEmCgiEIAgIKK4OLSQ4RCwEBDhATjEkJHToNFRYPQBgNixoBMAs0HB4wDywAHoMILxADDAQFDAMRLAiCNA5JNRFAExVAEDlJDDdJsUkoF0kZG0kULEkPMkk4Ckk2DTUMDjYMNkkLOIIJGwwCFAYHEgEMGwmDKAI5FiOoHxc2ACaLNnxIIuPGDRhJNGQQFKSHjx87eFgaFIOExRIiQGgEwQhJkY9FJg4KBAAh+QQFDgBJACwAAAEADwAOAAAHsYBJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBSkh48fO3gs6rHjh6AYJDKWEAGiIwgRJUgIQlKkpKUkJpMEAgAh+QQFDgBJACwNAAwAAwADAAAHC4A+PkMkJDNFRUeBACH5BAUOAEkALAQADAAGAAMAAAcOgElJLSoqLYKISUVDiYEAIfkECTIASQAsAAABABAADgAABx2ASYKDhIWGh4iJiouMjY6PkJGSk5SEJyAgKUSFgQA7") -} - -html .menu .menu_data:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/Hx8e/v7+np6ePj4+Hh4d/f393d3dfX19XV1dPT08XFxcPDw8HBwb+/v7u7u7m5ube3t62traenp6OjowDZNgLXNp2dnZubmwrPOpmZmQrNOpeXlxLFPhTDQBTDPhbBQBTBQBa/QBq9Qhi9Qh63RBy5QiC1RCC1RiKzRiSxRouLi4mJiYeHhyirSCynSoWFhS6lTIODgzKhTjSfTjadTjKfToGBgTibUDiZUHx8fD6TUnh4eHZ2dkiHWHR0dEyDWnJyclB+XFZ4XlZ2Xlh0YFxwYGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFMgBJACwAAAEADwAOAAAHr4BJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYSQEyOTErKCQkKCoxNDIBvAYaqYIcB8oCHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi/PAEkCBQ4cQaBBHg4F4AA0GQsGBgkJB6xloMFOQQRHP3jwkCcoAgJBFBg0qNAiRw4XGSIskADKxwQDAwIEIGAgAg1BgQAAIfkECQ8ASQAsAAABAA8ADgAABxaASYKDhIWGh4iJiouMjY6PkJGSk5SBACH5BAkPAEkALAAAAAAPAA8AAAe0gEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhigMSsoJCQoKjGgATI5qYI0MgFJAQYayUkcB80CHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi+CHABJAgUOHEGgQR4OBeQANBkLDBhIkOCAtgw04imI4OgHDx73BEVAIIgCgwYVWuTI4SJDhAUSQPmYYGBAgAAEDESgISgQACH5BAkPAEkALAAAAQAPAA4AAAevgEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhhJATI5MSsoJCQoKjE0MgG8BhqpghwHygIcEAcMDhAQDgwGDxwCSQAcgjIaFhYaL88ASQIFDhxBoEEeDgXgADQZCwYGCQkHrGWgwU5BBEc/ePCQJygCAkEUGDSo0CJHDhcZIiyQAMrHBAMDAgQgYCACDUGBAAAh+QQJDwBJACwAAAAADwAPAAAHtIBJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYoDErKCQkKCoxoAEyOamCNDIBSQEGGslJHAfNAhwQBwwOEBAODAYPHAJJAByCMhoWFhovghwASQIFDhxBoEEeDgXkADQZCwwYSJDggLYMNOIpiODoBw8e9wRFQCCIAoMGFVrkyOEiQ4QFEkD5mGBgQIAABAxEoCEoEAA7") -} - -html .menu .menu_control:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAPcAAOTk5N/f38bUxsnQycTTxMfOx8XLxcPKw8bKxsfHx8TIxMbGxsXGxcXFxcfFx8PGw8TExMXExcbCxsfCx8PDw8PEw8PCw8PBw8HBwcW/xcHAwb7AvsK/whzVHBzTHCDOICLLIjC2MDO0MzOzMzKzMjWxNTOyMzWvNTavNjirOE2hTTqpOjunOzylPEmhSUqgSjqlOoaGhj+iPz6iPoSEhD6hPj6fPj+fP4ODg0CfQDqhOoKCgkGcQUObQ4CAgEWXRUaWRkWWRUqQSkiSSEiRSEyNTFuHW3p6eluGW3l5eU6KTliFWE6JTk2JTVaFVlWFVWB/YFGGUXV1dVGEUVWAVVSAVHRzdHNzc3FxcVV9VXZvdlp4Wm9vb2hyaGF0YVt2W1x1XFx0XGFyYWNwY15yXl1zXWtra15xXmNtY2FuYWpqamBvYG1obWJtYmlpaWJsYmhpaGNqY2hoaGJrYmRqZGRpZGdnZ2VpZWVoZWRoZGZmZmVnZWdlZ2VlZWdkZ2djZ2hjaGxdbG9ab2ZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCACDACwAAAEADwAOAAAIhgAHAUgAYcGCBggbGISQIMCgBlwG9dFjp6IdPX0GYaEwCMKgQWqO4NixA8cRNR89LvjoJglJkkncpBy0EqSPGDRoxPCBsiPNj3KwSLlyRQoWOTNrflzK1GeDplAHcWQQtWmFQQOqMi0wCIHWpQoGWfj6cSzEQXC63IG6UWCCBhA2KDS4sGFAACH5BAUIAIMALAIACQACAAMAAAgJAMUMcsHjSZSAACH5BAUIAIMALAMACgADAAQAAAgQAGGUGbSCxaBBJ4IM8gIlIAAh+QQFCACDACwEAAoAAwAEAAAIEAAHhTkzKAeVFiCoDDIyKCAAIfkEBQgAgwAsBQAIAAMABAAACBAABzFJM6jElj0pwAwaMiggACH5BAUIAIMALAYABwADAAUAAAgSAAfpGFSlQ5FBQEIMGpRiYZiAACH5BAUIAIMALAgACQADAAQAAAgQAAcN2jJIiIwpHogMujEoIAAh+QQFCACDACwJAAcABQAFAAAIGQAHCRw4kI2gHiK+iBlEwoQNFYOaBMqyJCAAIfkECTIAgwAsAAABAA8ADgAACBkABwkcSLCgwYMIEypcyLChw4cQI0qcSDEgADs=") -} - -html .menu .menu_firewall:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP////39/fv7+/f39+3t7evr6+np6efn5+Xl5ePj4+Hh4d/f393d3dnZ2dfX19XV1dHR0cvLy8nJycfHx8PDw8HBwb+/v729vbu7u7m5ube3t7W1tbOzs7Gxsa+vr6enp6WlpaOjo5+fnwDZNpeXl42NjYuLi4mJiSitSCqpSiypSoeHhyirSC6lTIWFhTKhTjiZUDyXUnx8fECRVEKPVHp6enh4eEqHWEiHWHR0dFB+XHBwcFpyYGZmZgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAwA9ACwAAAAADwAQAAAGdMCesEeiHAACR2c17J0oiUXEE/pgHAsFptYjQFzN4c6UmPQKoXAYIzl/1M1M5AyCDzdzwtveu8wRdXwXbQ8afD0SGD0dEIcNIj01CDl2NQeUPRZzcA8WTQWKYRUGYTkJDlw9MgwLO3AaBh0bBhyHJQEDYE1BACH5BAUEAD0ALAMABgACAAMAAAYHwB6PN+qlggAh+QQFBAA9ACwFAAcAAQADAAAGBUDaCBUEACH5BAUIAD0ALAYACAACAAMAAAYHwBtuNGKpggAh+QQFCAA9ACwIAAcAAQADAAAGBcDZSBUEACH5BAUIAD0ALAkABgABAAMAAAYFwNioFQQAIfkEBQgAPQAsCgAFAAEAAwAABgVA2OgVBAAh+QQFBgA9ACwLAAQAAgADAAAGB0Aab6SD9YIAIfkECTIAPQAsAAAAAA8AEAAABhXAnnBILBqPyKRyyWw6n9CodEqtJoMAOw==") -} - -html .menu .menu_day:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAANPT09HR0c3NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7m5ubW1tbOzs7Gxsa2trampqaenp6WlpaOjo6GhoQDZNpubm5WVlY+Pj42NjYuLi4mJiYeHh4WFhYODg35+fnZ2dnR0dHJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAlACwAAAEADwAOAAAGesCSUKQRGkua0RGjAFQm0EkFoMAIFY9HAHPpXjABiINREohKoaMwHSKUBgvKJEKvTygLw1vCyWj+gBkcEnoGHCUWFmpCHAglBYeLRhsHjxuSRhqOBJeYSI4FnZgeegMdniUgBSULER8dHrCxG7QRDUIPCQq7vLsJDyVBACH5BAUKACUALAcABwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAcAAgABAAAGBECLJQgAIfkEBQoAJQAsBAAJAAIAAQAABgRAiyUIACH5BAUKACUALAcACQACAAEAAAYEQIslCAAh+QQFCgAlACwKAAkAAgABAAAGBECLJQgAIfkEBQoAJQAsBAALAAIAAQAABgRAiyUIACH5BAUKACUALAcACwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAsAAgABAAAGBECLJQgAIfkECTIAJQAsAAABAA8ADgAABhTAknBILBqPyKRyyWw6n9CodEoNAgA7") -} - -html .menu .menu_soft:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAOPj4+Hh4d/f393d3dvb29nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw7+/v729vbu7u6+vr62traurq6mpqaenp6WlpZmZmZeXl5WVlRTDQBTDPpOTkxLDPhbBQBi/QBTBQBq9Qha/QJGRkRi9Qhi9QI+Pjxq7Qhy5RB63RBy5QiC1RiC1RI2NjSKzRiSxRouLiyKxRiavSCitSCSvSImJiSatSCypSiirSCynSoWFhYODg4GBgTadUDadTjabUDSdTjibUDiZUDyXUn5+fjqXUjyVUnx8fHh4eESLVkaLVkaJWEaJVkqHWEiHWEqFWE6DWkyDWk6BWlB+WlB8XFJ8XFJ6XFR6XlR4Xlh2Xlp0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFHgBeACwAAAEAEAAPAAAHtoBeXTEdIR0qWF4/BgECAQtKXjIrQkBAMjVeEAwVFBUHCl4iRV6lUy5eCyalXhgAXidArE6oCxqsFQGiRKxStR+sGLonLk5MTTwsXgoSHBscCwJeWDUu1jFUXjMLCt0OOKzh4uI+DwndDDNeOAzdCQ89XgUKzhwTCcsR9dFeAxasKUItAFZKmBcCFFhxEHirVK5+F1jBwKeKVYZXDA50qtDAgZcHnDwdwKdEQaMABcD1KHAywZFAACH5BAUeAF4ALAAAAQAQAAcAAAdhgF5LDAECAQY+XloqHSEdMVxeDQcVFBUND147MkBAQisuXgMXXqUwCV4uUKVeRh1eBBSsHApeMUysRK8EFqwptTGrpa6wChwbHBMIqTxNTE4xIV49DwrWCzBeUzEu3TVWgQAh+QQFHgBeACwJAAEABwAPAAAHWIA/BgECAQtKEAwVFBUHCgsmXpIYAAsakl4Vhh+YGAEKEhwbHAsCMwsKqQ44mK1WMS6xNlguOk1MTjEhMVCYRh0xTJhEHS69kkkdNjJAQEIrLlckHSHFWoEAIfkECR4AXgAsAAABABAADwAAB3CAXoKDhIWGh4iJiouMjY6PiVg2LpQxVV44DAoKCQ89XiQuTkxNPCxeChEcGxwLAl4iRINSLl4LH4MYAV4nQINOtQsagxW7IkWDU8EmgxkAXjErQkBAMjVeDwwVFBUHCV5cLh0hHSRWXj0FAQIBCUeBADs=") -} - -html .menu .menu_set:hover { - background-image: url("data:image/gif;base64,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") -} - -html .menu .menu_folder:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPX19efn593d3dXV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcHBwb29vbm5ube3t7W1tbOzs7Gxsa+vr62traurq6mpqaenp6WlpaOjo6GhoQDZNp+fn52dnZubm5mZmZeXl5WVlZOTk4+Pj4WFhX5+fnx8fHp6enh4eHZ2dnJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgAsACwAAAIAEAAMAAAGYEAWCHEohFjIpJJVGJlKD0oGQ8VoVEkCdslFajEMiuVCJlskjwTz1OgmSQsWQhRxIysg1mLysbMcKywKCih2J3UsAgZ+Hh1IABJ+ECVIARwlmJmZJAxJHkQHoaKhA3ksQQAh+QQJDgAsACwAAAAAEAAOAAAGGUCWULgpGo/DpHLJbDqf0Kh0Sq1ar9isMAgAIfkECQ4ALAAsAAABABAADQAABmZAllC4KRqPQhDiUAgNn8/CyFR6UDKYLEajGhK60LDwi2FQLJd02iJ5JFiFU0M8JC1YCFGELqyAWAsTH3wsDissCgoofCd7LAIGhB4dQgAShBAlQgEcJZ6fnyQMQx5LB6eopwN/LEEAIfkECQ4ALAAsAAACABAADAAABmNAFghxKGyOyCSLVRiZSg9KBkPFaFRLFgGb7Xq1KgyDYrmYzRbJI8E8Nb5d0oKFEEXg2QqItZh88EsOKywKCiiAJ3csAgaALB4dSwASjhAlSwEcJZucnCQMWR5EB6SlpAN7LEEAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQFDgAsACwAAAIAEAAMAAAGZUAWCHEohFjIpJJVGJlKD0oGQ8VoVEkClrXper9ILYZBsVzOZ4vkkWCeGsslacFCiCJxZQXEWkw+eUkOKywKCiiBLCd4LAIGiSweHUgAEpAQJUgBHCWdnp4kDEkeRAemp6YDfCxBACH5BAkOACwALAAAAgAQAAwAAAYUQJZwSCwaj8ikcslsOp/QqHQqDAIAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQJDgAsACwAAAIAEAAMAAAGY0AWCHEobI7IJItVGJlKD0oGQ8VoVEsWAZvterUqDINiuZjNFskjwTw1vl3SgoUQReDZCoi1mHzwSw4rLAoKKIAndywCBoAsHh1LABKOECVLARwlm5ycJAxZHkQHpKWkA3ssQQAh+QQJDgAsACwAAAEAEAANAAAGZkCWULgpGo9CEOJQCA2fz8LIVHpQMpgsRqMaErrQsPCLYVAsl3TaInkkWIVTQzwkLVgIUYQurIBYCxMffCwOKywKCih8J3ssAgaEHh1CABKEECVCARwlnp+fJAxDHksHp6inA38sQQAh+QQJDgAsACwAAAAAEAAOAAAGZkCWULgpGo/DpHIJQhwKoaWyMDKVHpQMZovRqIaEr3QZxjAolotabZE8EqzCqTEWkhYshChSZ1VALAsTH30OKywKCih1J3wsAgZ9Hh1CABJ9ECVCARwlnp+fJAxDHk4Hp6inA4AsQQA7") -} - -html .menu .menu_exit:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAOPj4+Hh4dnZ2dXV1c3NzcvLy8fHx8PDw8HBwb+/v729vbu7u7W1ta2trampqaenp6Ojo6GhoZ2dnQzNOgjPOpeXlxq9Qhi9QI+Pj4uLi4mJiSatSCqpSjCjTDSfToGBgTadTjKfTjibUDqZUDqXUDyVUkCRVESNVnZ2dkyFWnR0dE6BWlB+XFB8XFR4Xlh2XlxwYF5uYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCAAyACwCAAEACwAOAAAGQMBGgUAsEgyoAEMSaTYlgwzgI6taZYkKQHO1ZlDb7jUsrpLLZ3G6ux5zxRgwVayoBBQPh17/EGQgCQiCgwcLKkEAIfkEBQgAMgAsBwAHAAEAAgAABgRA0ykIACH5BAUIADIALAgABwABAAIAAAYEwI4oCAAh+QQFCAAyACwJAAcAAgACAAAGBkCPhzQKAgAh+QQFCAAyACwLAAcAAQACAAAGBEDPKAgAIfkEBQgAMgAsDAAHAAIAAgAABgZA0KbECQIAIfkEBQgAMgAsDAAFAAIAAgAABgZAVypmCQIAIfkEBQgAMgAsDgAGAAIAAwAABgfAlYySmqyCACH5BAUIADIALAwACQADAAEAAAYFQNiFFQQAIfkEBQgAMgAsDAAKAAIAAQAABgTAFysIACH5BAkyADIALAIAAQAOAA4AAAYUQJlwSCwaj8ikcslsOp/QqHRqDAIAOw==") -} - -.pos-box { - height: 42px; - width: 100% -} - -.position { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII="); - background-position: 10px 10px; - background-repeat: no-repeat; - background-size: 22px auto; - line-height: 42px; - padding-left: 30px -} - -.search { - width: 306px -} - -.ser-text { - border: #20a53a 1px solid; - height: 30px; - width: 262px; - padding: 0 8px; - margin-top: 6px -} - -.ser-sub { - width: 38px; - height: 30px; - border: 0; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxAAAAMQBz4pYTAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE2SURBVDiNndJBS1VRFMXx9UzNQUQD0caFk6YhEUFCOPETiJQfIygQbNQgCCeOGjYLGjRJkPoKoSGIRKADs1nQoEIEfw3cry6v+3oPN2z2ufuu8z/7nruC9ORNvMRXHOIFbrTogjQfOniKX87iR2P9HSv/A1zA+xK/wRwmMYV5vKt3r/sBnpTgcb9R8aw0D3sB1/GzTum3uZsfSnupCVgr8vwQgKXSLnR7I0nuJJFkN4PjY9WZbmMkyXGtx4YAjFY9aQI2knSS3B4CMFd1508HV3CE3QHfP17G2sdo7298UJfzChMtmy9jszTLbT4InpfgCx6Vme5hFd/8jW1cbQME93Hg39jDYmOKT5hGOmi7rNkk15KcJvmcZKv6E0k2k9xNsp1keZBx2vIi3tYkG+cBxJmV13HrN6Szg7ZCfX3NAAAAAElFTkSuQmCC") no-repeat center #20a53a; - cursor: pointer; - margin-right: 2px; - margin-top: 6px -} - -.title { - height: 50px; - line-height: 50px; - border-bottom: 1px solid #ddd -} - -.title h3 { - display: inline-block; - line-height: 50px; - margin-right: 12px -} - -.divtable .table { - border: 1px solid #ddd; - color: #666; - font-size: 12px; - margin-bottom: 0 -} - -.divtable .table thead th { - background-color: #f5f6fa; - border-bottom: 1px solid #e1e6eb; - color: #999; - font-weight: normal; - padding: 8px -} - -.table .btlinkbed { - color: #666; - padding: 1px 5px; - border: 1px solid transparent; - white-space: nowrap; - overflow: hidden; - float: left; - display: block -} - -.table .btlinkbed:hover { - border: #ddd 1px solid -} - -.dataTables_paginate { - display: block; - height: 30px; - margin-bottom: 10px; - margin-top: 15px -} - -.page { - line-height: 16px; - text-align: right; - margin-top: 5px; - margin-right: 1px; - height: 30px -} - -.page div { - float: right -} - -.page span, -.page a { - display: inline-block; - height: 28px; - padding: 0 11px; - border-right: #ddd solid 1px; - border-top: #ddd solid 1px; - border-bottom: #ddd solid 1px; - border-left: #ddd solid 1px; - line-height: 28px; - font-family: "Arial"; - font-size: 13px; - color: #20a53a; - float: left; - margin-left: -1px -} - -.page a.prev { - border-left: #ddd solid 1px -} - -.page a:hover { - background: #e8e8e8; - color: #222; - text-shadow: 0 1px 0 #fff; - cursor: pointer -} - -.page spanold { - font-size: 12px; - padding: 4px 10px !important; - border-style: solid; - border-width: 1px; - border-color: #ddd #ddd #ccc; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - display: inline-block; - line-height: 16px; - background: #f5f5f5; - color: #333; - text-shadow: 0 1px 0 #fff -} - -.page .Pcurrent { - color: #999; - background: #f5f5f5; - cursor: default -} - -.page .Pcount { - color: #777 -} - -.system-info-con { - width: 100% -} - -.sys-i-c-box { - box-sizing: border-box; - max-width: 350px -} - -.sys-i-c-box .siteinfo-box { - height: 110px; - position: relative; - width: 100% -} - -.sys-i-c-box .siteinfo-box h3 { - background: #20a53a; - color: #fff; - font-style: normal; - height: 110px; - left: 0; - line-height: 110px; - position: absolute; - text-align: center; - top: 0; - width: 56px -} - -.siteinfo-box-right { - border-bottom: 1px solid #ddd; - border-right: 1px solid #ddd; - border-top: 1px solid #ddd; - height: 110px; - margin-left: 56px; - overflow: hidden -} - -.siteinfo-box-right h4 { - font-size: 32px; - height: 68px; - line-height: 60px; - overflow: hidden; - padding-top: 8px; - text-align: center -} - -.siteinfo-box-right .sbr-btn { - color: #ccc; - display: block; - font-size: 14px; - height: 27px; - line-height: 22px; - padding-top: 5px; - text-align: center -} - -.siteinfo-box-right .sbr-btn a { - color: #20a53a; - font-size: 16px; - padding: 0 10px -} - -.circle-box h3, -.circle-box h4 { - line-height: 30px -} - -.circle { - width: 100px; - height: 100px; - position: relative; - border-radius: 50%; - background: #20a53a; - margin: 0 auto -} - -.pie_left, -.pie_right { - width: 100px; - height: 100px; - position: absolute; - top: 0; - left: 0 -} - -.left, -.right { - width: 100px; - height: 100px; - background: #ccc; - border-radius: 50%; - position: absolute; - top: 0; - left: 0 -} - -.pie_right, -.right { - clip: rect(0, auto, auto, 50px); - transition: transform 1s ease-in 0s; - -webkit-transition: -webkit-transform 1s ease-in 0s; - -moz-transition: -moz-transform 1s ease-in 0s -} - -.pie_left, -.left { - clip: rect(0, 50px, auto, 0); - transition: transform .4s ease-in 1s; - -webkit-transition: -webkit-transform .4s ease-in 1s; - -moz-transition: -moz-transform .4s ease-in 1s -} - -.mask { - width: 88px; - height: 88px; - border-radius: 50%; - left: 6px; - top: 6px; - background: #FFF; - position: absolute; - line-height: 88px; - font-size: 18px; - color: #20a53a -} - -@-webkit-keyframes shineGreen { - from { - -webkit-box-shadow: 0 0 10px #999 - } - - 50% { - -webkit-box-shadow: 0 0 15px #20a53a - } - - to { - -webkit-box-shadow: 0 0 10px #999 - } -} - -.shine_green { - -webkit-animation-name: shineGreen; - -webkit-animation-duration: 3s; - -webkit-animation-iteration-count: infinite -} - -.mem-release { - cursor: pointer -} - -.mem-re-min { - background: url(../img/ico/rocket_min.png) no-repeat center center; - height: 35px; - left: 40px; - position: absolute; - top: 8px; - width: 20px; - z-index: 9; - opacity: .9 -} - -.mem-re-con { - background: url(../img/ico/ico-rocket.gif) no-repeat center center; - width: 100%; - height: 100%; - position: absolute; - z-index: 9; - display: none; - top: 15px -} - -.mem-release:hover .mem-re-con { - display: block -} - -.soft-man .col-lg-3 { - border-bottom: 1px solid #ececfb; - border-left: 1px solid #ececfb; - border-right: 1px solid #ececfb; - margin-right: -1px; - margin-bottom: -1px; - height: 148px; - cursor: pointer; - padding: 0 -} - -.soft-man .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .08) inset; - -webkit-transition: all .25s ease; - transition: all .25s ease -} - -.soft-man .dashed-border { - border: 1px dashed #20a53a; - z-index: 1 -} - -.soft-man .no-bg:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} - -.soft-man .image { - height: 40px; - margin: 30px 0 20px; - text-align: center -} - -.soft-man .sname { - text-align: center; - color: #555 -} - -.soft-man .col-sm-3>div { - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.soft-man .spanmove { - background: url(../images/move.png) no-repeat; - width: 30px; - height: 30px; - display: none; - position: absolute; - top: 5px; - right: 5px; - z-index: 3 -} - -.soft-man .col-lg-3:hover .spanmove { - display: block -} - -.soft-man .text { - background-color: rgba(0, 0, 0, 0.3); - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - display: none -} - -.soft-man .text a { - background-color: #fff; - border-radius: 4px; - display: block; - height: 30px; - line-height: 30px; - margin: 59px auto; - text-align: center; - width: 100px -} - -.soft-man-con p.status { - line-height: 40px; - margin-bottom: 20px; - font-size: 14px -} - -.soft-man-con .sfm-opt .btn { - margin-right: 15px -} - -.soft-man .sname .glyphicon-True { - color: #20a53a; - margin-left: 10px -} - -.soft-man .sname .glyphicon-False { - color: red; - margin-left: 10px -} - -.soft-man { - position: relative -} - -.soft-man .rowbg { - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.soft-man .rowbg .col-lg-3 { - cursor: default -} - -.soft-man .rowbg .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} - -.bw-info ul li { - height: 56px; - line-height: 56px -} - -.bw-info ul li.bi-line { - border-bottom: #ddd solid 1px -} - -.bw-info ul li span { - display: block; - margin-right: 15px -} - -.btn-zhm { - border-radius: 0 3px 0 0; - display: none; - position: absolute; - right: 42px; - top: 0 -} - -.line { - padding: 5px 0 -} - -.line .span_tit { - display: inline-block; - text-align: right; - width: 140px -} - -.line .tname { - display: block; - float: left; - height: 30px; - line-height: 30px; - overflow: hidden; - padding-right: 20px; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - width: 110px -} - -.line .info-r { - margin-bottom: 5px; - margin-left: 110px; - position: relative -} - -.placeholder { - cursor: text; - left: 20px; - line-height: 24px; - position: absolute; - top: 27px -} - -.bt-form-submit-btn { - background: #f6f8f8; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bt-form-submit-btn .btn:first-child { - margin-right: 4px -} - -.btn-danger { - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -.bt-w-main { - height: 525px -} - -.bt-form { - height: 100% -} - -.bt-w-menu { - float: left; - background-color: #f0f0f1; - height: 100%; - width: 110px -} - -.bt-w-menu p { - cursor: pointer; - height: 40px; - line-height: 40px; - padding-left: 20px; - position: relative; - text-overflow: ellipsis; - overflow: hidden -} - -.bt-w-menu p a { - display: block -} - -.bt-w-menu p .spanmove { - display: none -} - -.bt-w-con { - margin-left: 110px; - position: relative -} - -.label-input-group input { - margin-top: 0; - margin-right: 5px; - vertical-align: -2px -} - -.label-input-group label { - margin-bottom: 0; - font-weight: normal -} - -.bingfa .line { - margin-bottom: 10px -} - -.bingfa .line .span_tit { - text-align: right; - width: 120px; - display: inline-block -} - -.bingfa .bt-input-text { - width: 100px -} - -.ssl-con-key { - width: 47% -} - -.ssl-con-key textarea { - height: 240px; - line-height: 18px; - width: 100%; - margin-top: 5px -} - -.ssh-item { - float: left; - margin-left: 10px; - padding-top: 5px -} - -.ss-text em { - color: #555; - float: left; - font-style: normal; - line-height: 32px; - padding-right: 2px -} - -.view1, -.view2 { - margin-bottom: 15px -} - -.searcTime { - position: relative:z-index:999; - margin-top: 11px -} - -.searcTime .tit { - float: left; - padding: 5px 10px; - display: none -} - -.searcTime .gt { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left; - line-height: 16px -} - -.searcTime .gt.on, -.searcTime .gt:hover, -.searcTime .ss .st:hover, -.searcTime .ss .st.on { - background-color: #20a53a; - color: #fff -} - -.searcTime .ss { - display: inline-block; - position: relative; - float: left; - line-height: 16px -} - -.searcTime .ss .st { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left -} - -.searcTime .ss .time { - background-color: #fff; - border: 1px solid #ddd; - display: none; - padding: 10px; - position: absolute; - right: -1px; - top: 27px; - width: 217px; - z-index: 99 -} - -.searcTime .ss .time span { - margin-bottom: 10px; - display: block -} - -.searcTime .ss .time input { - border: 1px solid #ddd; - height: 22px; - padding: 0 5px; - width: 132px -} - -.searcTime .ss .time input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.searcTime .ss .sbtn { - background-color: #20a53a; - border: 1px solid #20a53a; - color: #fff; - line-height: 24px; - text-align: center; - cursor: pointer -} - -.plan { - padding: 10px 20px; - margin-bottom: 10px -} - -.plan .typename { - line-height: 34px; - width: 120px -} - -.plan .textname { - height: 34px; - line-height: 34px; - float: left -} - -.plan .planname input { - height: 34px; - width: 260px; - padding: 0 12px -} - -.plan .dropdown button b { - font-weight: normal -} - -.plan .dropdown-menu { - min-width: 100px -} - -.dropdown-menu>li>a { - padding: 3px 12px -} - -.plan-submit { - margin-left: 141px -} - -.plan_hms { - position: relative; - height: 34px -} - -.plan_hms span { - float: left; - height: 32px; - line-height: 32px; - position: relative -} - -.plan_hms .name { - border-left: 1px solid #ccc; - width: 44px; - text-align: center; - background-color: #f6f6f6 -} - -.planSign { - margin-left: 10px; - height: 35px; - line-height: 35px -} - -.planSign i { - margin-right: 8px; - font-size: 12px; - font-style: initial; - color: red -} - -.plan_hms span input { - float: left; - width: 48px; - height: 32px; - line-height: 32px; - border: 0; - text-align: center; - font-size: 12px -} - -.plan_hms span:first-child input { - border-radius: 4px 0 0 4px -} - -.plan_hms span:last-child { - border-radius: 0 4px 4px 0 -} - -.plan .dropdown button { - width: 94px -} - -.txtsjs { - width: 260px; - height: 80px -} - -.sl-s-info { - width: 124px; - border: #ccc 1px solid -} - -.rec-install { - padding: 16px; - width: 100%; - float: left -} - -.rec-install .rec-box { - width: 296px; - float: left -} - -.rec-install h3 { - font-size: 20px; - margin-bottom: 5px -} - -.rec-box-con { - border: #ddd 1px solid; - border-radius: 3px; - padding: 10px -} - -.rec-install .rec-box:hover .rec-box-con { - background-color: #f9f9f9; - border: #20a53a 1px solid -} - -.rec-box-con .ico img { - width: 22px; - margin-right: 10px -} - -.rec-box-con ul li { - line-height: 26px; - margin-bottom: 5px -} - -.fangshi { - line-height: 19px; - margin-top: 10px; - display: block; - margin-bottom: 15px -} - -.fangshi label { - font-weight: normal; - margin-right: 44px; - float: right -} - -.fangshi label input { - vertical-align: -2px; - margin-left: 5px -} - -.zun-form-new .fangshi { - padding: 0 30px 0; - line-height: 30px -} - -.zun-form-new .fangshi label { - height: 20px; - line-height: 20px; - margin: 4px 20px 0 0 -} - -.zun-form-new .version { - padding: 0 30px -} - -.zun-form-new .version select { - height: 28px; - margin-left: 30px; - width: 120px -} - -.sec-install-btn { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - cursor: pointer; - margin-top: 10px -} - -.rec-box-con .onekey { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - margin: 5px auto; - cursor: pointer -} - -.rec-box-con .onekey:hover, -.sec-install-btn:hover { - background: #10952a; - background: linear-gradient(#10952a, #088d22) -} - -.phppz { - margin: 14px 0 -} - -.phppz span { - padding: 10px -} - -.phppz textarea { - width: 500px; - margin: 0; - vertical-align: bottom; - padding: 0 5px; - height: 38px; - margin-right: 20px -} - -.soft-man-con .user_pw { - display: none; - margin-top: 30px -} - -.soft-man-con .user_pw_tit { - margin-top: 30px; - padding-top: 30px; - border-top: #ccc 1px dashed -} - -.soft-man-con .user_pw p { - margin-bottom: 15px -} - -.soft-man-con .user_pw_tit .tit { - float: left; - line-height: 22px -} - -.soft-man-con .user_pw span, -.soft-man-con .user_pw_tit span { - width: 50px; - display: inline-block -} - -.soft-man-con .user_pw span { - margin-right: 10px -} - -.btswitch-p { - margin-left: 10px -} - -.soft-man-con .user_pw input { - width: 200px -} - -.soft-man-con .conf_p { - margin-bottom: 10px -} - -.soft-man-con .conf_p input { - width: 100px -} - -.soft-man-con .funarea { - width: 100%; - height: 80px; - line-height: 22px; - margin-bottom: 10px -} - -.soft-man-con .ver .btn { - vertical-align: 0; - margin-left: 10px -} - -.webEdit-box .soft-man-con .phpmyadmindk { - width: 100px; - display: inline-block; - margin: 0; - padding-right: 0 -} - -.softlist td img { - width: 24px; - height: 20px; - margin-right: 5px -} - -.softlist .glyphicon-folder-open { - cursor: pointer; - color: #efd566 -} - -.softlist .btswitch+.btswitch-btn { - width: 2.4em; - height: 1.4em; - margin-bottom: 0 -} - -.wafConf_checkbox label { - font-weight: normal; - margin-right: 22px -} - -.wafConf button { - vertical-align: 0 -} - -.wafConf_cc { - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; - margin: 10px 0; - padding: 15px 0 -} - -.wafConf_cc span { - margin-right: 5px -} - -.wafConf fieldset { - border: 1px solid #ccc; - border-radius: 3px; - float: left; - padding-bottom: 0; - width: 240px -} - -.wafConf fieldset:nth-of-type(2) { - margin: 0 10px -} - -.wafConf legend { - border: 0 none; - font-size: 14px; - margin: 0 6px; - padding: 3px; - width: auto -} - -.wafConf fieldset input { - margin-left: 4px -} - -.wafConf fieldset .table { - margin-top: -1px; - margin-bottom: 0 -} - -.wafConf fieldset .table tr td:nth-of-type(2) { - width: 42px -} - -.wafConf fieldset .table-overflow { - height: 210px; - overflow: auto; - margin-top: 10px; - border-top: #ddd 1px solid -} - -.wafConf-btn { - border-bottom: #ddd 1px solid; - margin-bottom: 12px; - padding-bottom: 15px; - height: 45px -} - -.wafConf-btn span { - float: left; - margin-right: 8px; - line-height: 33px -} - -.wafConf-btn .btn { - margin-right: 10px -} - -.wafConf-btn .ssh-item { - margin-right: 50px -} - -.gzEdit { - padding: 13px 20px -} - -.gzEdit button { - margin: 8px; - width: 140px -} - -.setting-con p { - line-height: 30px -} - -.setting-con p .set-tit { - display: inline-block; - height: 22px; - margin-right: 10px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: -2px; - width: 90px -} - -.setting-con p .disable { - background-color: #ededed -} - -.setting-con p .inputtxt { - width: 260px -} - -.setting-con p .modify { - margin-left: -40px; - vertical-align: 0; - position: relative; - z-index: 10 -} - -.setting-con p .set-info { - margin-left: 20px -} - -.set-submit { - margin: 20px 0 10px 100px -} - -.changepath { - height: 500px -} - -.changepath .path-top { - height: 50px; - line-height: 50px; - padding-left: 10px; - border-bottom: #aaa 1px solid -} - -.changepath .path-top .btn { - margin-right: 10px -} - -.changepath .path-top .btn span { - -moz-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - -o-transform: scaleX(-1); - transform: scaleX(-1); -} - -.changepath .path-top .place { - display: inline-block -} - -.changepath .path-top .place span { - color: #444; - font-size: 12px; - font-weight: bold -} - -.changepath .path-con-left { - width: 130px; - height: 450px; - float: left; - border-right: #aaa 1px solid; - padding-top: 5px -} - -.changepath .path-con-left dl dt { - background: url("../img/ico-computer.png") no-repeat left center; - height: 30px; - line-height: 30px; - padding-left: 23px; - font-size: 14px; - font-weight: normal; - margin-left: 10px -} - -.changepath .path-con-left dl dd { - line-height: 30px; - padding-left: 12px; - cursor: pointer -} - -.changepath .path-con-left dl dd span { - color: #666 -} - -.changepath .path-con-right { - float: left; - height: 450px; - overflow: auto; - width: 520px -} - -.changepath .path-con-right .default li { - width: 25%; - float: left; - text-align: center; - margin-top: 20px; - cursor: pointer -} - -.changepath .path-con-right .default li span { - font-size: 40px; - color: #666 -} - -.path-con-right .file-list { - display: none -} - -.path-con-right .list-list td { - padding-left: 10px; - cursor: pointer -} - -.list-list span.glyphicon { - color: #666; - margin-right: 10px; - font-size: 18px -} - -.list-list span.glyphicon-folder-open { - color: #edca5c -} - -.list-list span.glyphicon-cloud-download { - font-size: 12px -} - -.newFolderName { - border: #ccc 1px solid; - height: 30px; - padding: 0 5px; - width: 90% -} - -.path-con-right .list-list td .delfile-btn { - display: none; - float: right; - font-size: 14px; - text-align: center; - width: 18px -} - -.path-con-right .list-list td .delfile-btn:hover { - color: red -} - -.path-con-right .list-list tr:hover .delfile-btn { - display: block -} - -.path-con-right .list-list td .btn-xs { - margin-top: 4px -} - -.getfile-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - padding: 8px 20px 10px; - text-align: right; - width: 100% -} - -.success-msg { - width: 100%; - padding: 30px 50px; - font-family: "Microsoft Yahei"; - float: left; - position: relative -} - -.success-msg .pic { - position: absolute; - top: 50%; - margin-top: -100px -} - -.success-msg .pic img { - width: 158px -} - -.success-msg .suc-con { - float: right; - width: 260px -} - -.success-msg .suc-con h3 { - font-size: 16px; - margin-bottom: 15px -} - -.success-msg .suc-con p { - line-height: 23px -} - -.success-msg .suc-con .p1 { - border-bottom: 1px dashed #aaa; - color: #000; - height: 30px; - margin-bottom: 8px -} - -.success-msg .bottom-btn { - margin-top: 60px; - width: 100%; - float: left -} - -.success-msg .bottom-btn a { - width: 200px; - height: 40px; - line-height: 40px; - text-align: center; - display: inline-block; - border-radius: 5px; - margin-left: 30px; - color: #fff; - font-size: 16px; - cursor: pointer -} - -.success-msg .bottom-btn .close-btn { - background-color: #cbcbcb -} - -.success-msg .bottom-btn .blue-btn { - background-color: #5cb85c -} - -.tasklist { - padding: 15px -} - -.tab-nav { - border-bottom: #cacad9 1px solid -} - -.tab-nav span { - background-color: #ddd; - background: -moz-linear-gradient(top, #f6f6f6, #ddd); - background: -webkit-gradient(linear, 0% 0, 0% 100%, from(#f6f6f6), to(#ddd)); - background: -ms-linear-gradient(top, #f6f6f6, #ddd); - height: 32px; - line-height: 32px; - padding: 0 12px; - border: #cacad9 1px solid; - color: #444; - display: inline-block; - margin: 0 -1px -1px 0; - cursor: pointer -} - -.tab-nav .on { - background: #fff; - border-bottom: #fff 1px solid; - color: #444 -} - -.tab-con { - height: 380px; - margin-right: -15px; - overflow: auto; - padding: 10px -} - -.tab-con ul.cmdlist { - list-style-type: decimal -} - -.tab-con ul.cmdlist li { - position: relative; - list-style-type: decimal; - list-style-position: inside; - line-height: 40px; - border-bottom: #dbdbea 1px solid; - margin-top: 6px -} - -.tab-con ul.cmdlist li .com-progress, -.tab-con ul.cmdlist li .state, -.opencmd { - float: right; - margin-left: 20px; - color: #535362 -} - -.tab-con ul.cmdlist li .line-progress { - position: absolute; - bottom: -1px; - left: 0; - height: 1px; - background-color: #20a53a -} - -.tab-con ul.cmdlist li .cmd { - border: 0 none; - border-radius: 0; - display: block; - width: 570px; - height: 200px; - line-height: 22px; - padding: 0 10px; - background-color: #333; - color: #eee; - overflow: auto -} - -.yuandian { - width: 10px; - height: 10px; - border-radius: 5px; - background-color: #20a53a; - z-index: 999999 -} - -.DrawRecordL i, -.DrawRecordR-T i { - background: url(../img/Detailsbg.png) no-repeat -} - -.DrawRecordCon { - font-size: 14px; - height: 422px; - overflow: auto -} - -.DrawRecord { - background: url(../img/DrawRecordord.png) repeat-y -204px center; - margin: 0 auto; - overflow: hidden; - line-height: 22px -} - -.DrawRecordlist { - padding-top: 6px -} - -.DrawRecord .DrawRecordL { - float: left; - color: #9a9a9a; - margin-top: 9px; - padding-right: 22px; - position: relative; - text-align: right; - width: 110px -} - -.DrawRecord .DrawRecordL i { - background-position: 0 0; - display: inline-block; - height: 5px; - overflow: hidden; - position: absolute; - right: 0; - top: 9px; - width: 16px -} - -.DrawRecord .DrawRecordR { - margin: 1px 1px 30px 112px; - color: #666; - padding-right: 5px -} - -.DrawRecord .DrawRecordR h3 { - font-size: 14px; - font-weight: normal; - margin-bottom: 5px -} - -.footer { - bottom: 0; - position: absolute; - text-align: center; - width: 100%; - line-height: 50px -} - -.bingfa { - padding: 10px 0 60px 0 -} - -.bingfa p { - margin-bottom: 10px -} - -.bingfa p .span_tit { - display: inline-block; - text-align: right; - width: 120px -} - -.bingfa p input { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 3px; - -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 -} - -.bingfa p input:focus, -.bingfa p input:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bingfa .submit-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bingfa .submit-btn .btn-danger { - margin-right: 5px -} - -.transfer .backupdata { - height: 150px -} - -.selectdata .slabel { - font-weight: normal; - margin: 10px 41px 10px 0 -} - -.selectdata .slabel input { - margin-right: 5px; - margin-top: 0; - vertical-align: -2px -} - -.backupbtn { - margin: 20px 0; - width: 100% -} - -.neice_con { - padding: 7px 20px 20px -} - -.neice_con .tit { - font-weight: bold; - line-height: 30px; - color: #777 -} - -.neice_con .nc_con { - background-color: #fdfdfd; - border: 1px solid #ddd; - line-height: 28px; - padding: 10px; - border-radius: 3px -} - -.nc_opt { - padding-top: 6px; - text-align: right; - height: 24px -} - -.neice_con .nc_opt input { - margin-right: 6px; - vertical-align: -2px -} - -.neice_con .nc_opt label { - float: right; - width: 150px -} - -.nc_con_user p { - margin: 15px 0 -} - -.nc_con p span { - width: 80px; - display: inline-block -} - -.nc_con p input { - background-color: #fff; - border: #ccc 1px solid; - height: 30px; - line-height: 30px; - padding: 0 5px; - width: 280px -} - -.nc_con p input.disabled { - background-color: #eee -} - -.nc-tips { - padding: 20px -} - -.nc-tips p { - line-height: 30px -} - -.nc-tips p span { - font-size: 14px; - font-weight: bold -} - -.nc-tips p a { - color: #20a53a -} - -#BarTools .glyphicon { - color: #666 -} - -#BarTools .glyphicon:hover { - color: #fff -} - -.list-list .ico { - background-position: center center; - background-repeat: no-repeat; - display: inline-block; - height: 30px; - margin-right: 10px; - width: 33px; - z-index: 1; - float: left -} - -.fileList .ico { - background-position: center center; - background-repeat: no-repeat; - display: block; - height: 80px; - margin: 12px auto 4px; - width: 84px; - z-index: 1 -} - -.list-list .column-name .text { - color: #595c5f; - display: inline-block; - max-width: 85%; - overflow: hidden; - text-overflow: ellipsis; - line-height: 30px -} - -.list-list .column-name .cursor { - display: inline-block; - cursor: pointer; - width: 200px\9 -} - -.table>tbody>tr>td { - vertical-align: middle -} - -.showpicdiv { - max-height: 500px; - overflow: hidden; - width: 500px -} - -#BarTools { - font-size: 16px; - float: left; - margin-right: 20px -} - -#BarTools>button { - top: 0 -} - -.comlist { - float: left; - line-height: 28px; - margin-right: 10px -} - -.comlist>span { - margin-right: 20px; - font-size: 14px; - float: left; - padding: 0 10px; - border: #fff 1px solid; - border-radius: 3px; - cursor: pointer -} - -.comlist>span:hover { - border: #ccc 1px solid; - border-radius: 3px; - background-color: #f3f3f3 -} - -.PathPlaceText { - position: absolute; - right: 0; - top: 0; - background-color: #fff -} - -.PathPlace { - color: #666; - float: left; - font-size: 16px; - line-height: 30px; - margin-right: 5px; - width: 170px\9; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.PathPlaceInfo { - font-size: 14px; - line-height: 30px; - float: left; - margin-right: 20px; - color: #999 -} - -.onPath { - float: left; - line-height: 30px; - font-size: 14px; - color: #666 -} - -.editmenu span { - display: none; - text-align: right; - width: 300px -} - -.table tr.hover, -.table tr.on, -.ui-selecting, -.ui-selected { - background-color: #f5f5f5 -} - -.table tr.on .editmenu span, -.table tr.ui-selected .editmenu span, -.table tr.ui-selecting .editmenu span, -.table tr:hover .editmenu span { - display: inline -} - -.ui-selectable-helper { - background: #6bb0c9; - border: 1px dotted #072246; - opacity: .25; - overflow: hidden; - position: absolute; - z-index: 99999 -} - -.list-list .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMpJREFUeNpi/P//P8NAAiaGAQYD7gAWdIFPpxIoMS8ZiOcQqfY5SD0LFT1jAsRTuaU0GZjZuAgq/vPto+S3V3fmUssBokC8llNEjp2ZFWjk/1+Eg56TE0RJsgCD3BPImAvikGs7IzMLAxuvKAMrNz9ZaWAul5iKJAsXPwUBAMzK//9AaDIcIAkODiKCbbQcGHXAqANGHTDqgFEHjDpg1AGjDqCVA57/+f6Z7hZD7XwBahOmfHv1ANQqlqCzG54CcRrjaOd0oB0AEGAAscwsxMSUtNsAAAAASUVORK5CYII=") -} - -.list-list .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//P8NAAiaGAQYD7gAWcjR9OpWASyoZiOcQacxzkHoWKnrGBIincktpMjCzcRFU/OfbR8lvr+7MZSQnEbIzosbc65NxokDqDKeInBwrNz/xIfnwMgPjx5PxnkD2XCCWJNfrjMwsDGy8ogzs/CKkRSXQAaAomMslpiLJwsVPQegDQ/H/HwhNRiKUZOHkZPjx4xvD799/6ZLyWVmZGTjYWVBzAchybsUKujjg6/0OuAMGvBwAJcL/A14Q8SkYD4jlnx6chaaB/79GK6NRB4w6YNQBow4YdcDIdsDfP98/0d1iqJ1/QdVx47dXDxsGIDRADdBGxtHO6UA7ACDAAIASR69Q0kB9AAAAAElFTkSuQmCC") -} - -.list-list .ico-access, -.list-list .ico-mdb, -.list-list .ico-accdb, -.list-list .ico-db, -.list-list .ico-sql { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwZJREFUeNrMV0trE1EU/jKJkzTTR4oJCX1lqq6q+GwriiLarS4VfCwqFEFxKShYUKE+Kv4GcVkquBE3glq0Vqt9iGg3qfZhSVttU0tNJk2aGc+9dGSSJmlaJqkfHG7OvZOc757v3HMnFk3TsJGwGZ2hoaEjNNwnazQxxh6yT8aJurq69AQIHT5CSUmJKZEDgQAbHpOdTCWhQ0jxfZIkQVVVU4zB7/c/XyaxOxcCpkMUxUtEojsTCaEQhUYkmmVZHkhHwpbPwKWlpXodMJxaHgfJLOsiEB4eRqi7B+HvI1CjUUQnp/i8o7ICgihC2rYVmw8fgrNW5vMejwderzddYa49A7NvujHzsgvlBw+gnIJYHXbYfT6+Fg0GocbiiAx/w/jDR/CdOA5XYz1f04sxpz6QDTMvXqHy7Gk4t9SuWHNUVPDRKft5NqafPvtHYE2NKBvUpSVYbDaEet5B2CRCS5BvoRoWBKjxGKx2OxKLi7C73dDo2XV1wmxQxsZRVFPNLRsSigJlYsJ8Aq76fZjvH4CtrIzLYBGSTzDbdYSKMzo1BVdDg/kEfvf1Q758EbFfM5j/2M/TzqRgEiSUCEnggJNOgaO6CsGOTvhbzptLgOnPdil63NwyShAOr8iOSRLsJQkGYS2WYKX7wkFHUKCjqOu+OP0TSwsLiM+G1iRBzlTn3n+Aa38DP2oMf6ihzHa95sa0Z5CoNtgzc7295mdAl4Dt3klWcAk8TUcRaLvLOyHLgoVaLxs16nTK+A/empXRMd4n3E3HzCdQde4MIiOjCL3twWQfOwVx7rPdFvlrIFAjYneAfKGF+6YT4K2WAugXjVkoyPvAf00gSYJQ602EChH1SWfmGrDa8vqShETKTZkx2s57t/H52vUkXwebN/pG6N/R143PGn8vp1Ogk0hHJlc/dS1vRcgCZdp1pmyZSoAFMu40VxJJEgjFxQlNUay72u+sSOFqNbDa7tln9keYxUi6Y4z/jvvaH7TGvny9pYbDeekPgiSp4o7tN+qvXmlLS2Aj8FeAAQC23WzgeNtzYAAAAABJRU5ErkJggg==") -} - -.list-list .ico-c { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuKz0K4QDTjbbc0N4qrvP7+p0yABWY5sKTqf7mmAHMuHVy5ATg9k5wJ+VoZ/n/79Z2JkZCQqyElNhKCOMB8Lwz+cDjAVYGw69f5P3ac/xIWMy4rLJPkW6MG/QDuaUeqXge6eAwQYABvvATpIEEu4AAAAAElFTkSuQmCC") -} - -.list-list .ico-cpp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuxZaFeIBpzttuYGSWIo1fef36RnQ5ChyIYh85EtxidG1TRAyFCy0wA5ABbk6CFErENZyLUUPbjJDSEUB/CzMvz79O8/EyMjI4aBhBIXMQDUEeZjYUBppTIi947LdlypP/X+f92nP7QpH4Ae/GsqwNjc5aHTiNUBAwEAAgwALWAgpic4qfUAAAAASUVORK5CYII=") -} - -.list-list .ico-cs { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFvxZaFeIBpzttuYGhjiyGNbq+89v8rMhyBJ0S0F8ZHF0NTRNAzCLYI4gJgRolgiRHUE3B8AsRPY9qY5gRO6cOq64/PfTPxYmRkZGmiRCkF28jH/+HYjUZcaaCE0FGJtOvf9T9+kP7pBxWXGZJHFkwM/K8BdoRzPOEBgIABBgAMrJHxYdaBS2AAAAAElFTkSuQmCC") -} - -.list-list .ico-fla, -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMMkLgZf1fQx/37yD85lFhBjEG4sYPizfCOYLRPpj6IElRqLKAWIAyBIuG1OqhRITwwCDAXcAyVEAim9YnLNrKDMI5ybS1wGjaWDA0wAu8O3IaTCGAVDaAKURQoARuUECbA/8V1ZWpqmP7969C2oPMI6mgVEHDM5syNI+k+EhkXn0DyUWLp6I2w5cliq1laDw71X1gMVANDa12MSxOZykggiXoXRLAyCfoYcELt8TUkfTECDWcqo3StHjHl9awB4F3Fx//5NhKbE+/g+1A2dldGPCrBqm6/caGb59Z6JJNuTi/PdPU6leoyCtBasDBgIABBgAWP2ttPWI30cAAAAASUVORK5CYII=") -} - -.list-list .ico-css { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALdSURBVFiF7ZfLTxNRFMZ/M/RBnwik0FYpoJIYhJioLFSIupCoWzVqotGNCeEPcGPiI+50Q9xAYli5MaEbFyTiI0EWhhghGkohlgVREVzQhpfals64qB067ZRpoaALv6Rp73dPzvnuud/cOxVkWeZvwpA+CAaDZ4BewFPEGoeBkXSisbFRWwDQ6/V6PTabrSiVQ6EQQB9woaGhYUQrRswYeywWC5IkFeUD4PP5BoC+UCh0KB8BRYfZbO6ora19A/i1RGy5AACTyXS9rq5uWEtEpgeKCqfTmfIBwKU/3+8BYVsEuFwuqqurVVyaoK0XAChmzIVNe0CS4drrGeLSxg60ggRMRKJMLcRU3Mf5X+yvMGMUBaYWYkxEosUXEEvIdAfC9IxHKDeXqOYGPi9zqsYOQLm5hJ7xCN2BMLFEfh3RFRAIR+kcmqXKaqCr1U1l6ZqAuCQTDEc5UFkKQGVpCV2tbqqsBjqHZgmE9buha8Jbw9/pOeHFY80OfTv3kyNuC6KwxgnAud1OjrqtdAx+49lZ37r5dTtQYzfSP72kabKXX5Zp99mz+Lgk0z+9RI3dqJdeX8CjNg8Ok0jn0KzKYMtxibkfq+xxmlTxE5HkljlMIo/a9C9V3S0QBbi4t4xjbisPP8xzo7GcpgozgzMrHPdaVbGBcJTHwQh3W1zstOmvHgp4DHfZjXS1uql3JBO/+rqiuD+FekcyJt/iUOBJKAA2Y1JzV6s7az41Vwi25Tb8L+CfFqAy4ZWRBIx8UsaiIX835wNpNQ7A6NpLcfZToFX0xfl9yu92/+SGedFgVETkFKBVPDN5u3+yYD4XhPR/RgefjMmZHVgvQWqlmfO5eEhuw+jVZuX62pQJ2/2Tyqrz4bVQsIBU0sx91uNzQbUFJ5+OJRYlgygIgiqoWCaUZRmHsCoNXm5W3mpUAm4+D9x5F5FvL65uzflQZiTRskO4/+B00z1NAQqZ0YFiI73mb2NmSwowsJSgAAAAAElFTkSuQmCC") -} - -.list-list .ico-js { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALFSURBVFiF7ZfLTxNRFIe/GTotffJKoa1SQCUx1cSFslBLdKFNdKtGFya6MSH8CybG6NJNwwYS496EblywwEeCLAwx1mgshVgWREVwQRteamk746K2dNopM5WCLvwlTXvPPTn3u+f+5lFBURT+pkylg3g8fgF4BHjruMYJIFoaCAQC2gDAI5/P57Xb7XVZOZFIAIwCV3p7e6NaOWLZ2Gu1WpFluS4fAL/fPw6MJhKJ40YA6i6LxTLQ1dX1EohoQew6AIDZbL7Z3d09pQVR7oG6yuVyFXwAcO339xtA2BMAt9tNR0eHKlYCtPsAQNGM1bRjD8gK3HixQEb+sxtaTQAzqTRzK5uq2PvlnxxptSCJAnMrm8yk0vUH2MwpDMeSjEynaLE0qObGP61zvtMBQIulgZHpFMOxJJs5Yx3RBYgl0wxOLtJuMxEOemhr3ALIyArxZJpjbY0AtDU2EA56aLeZGJxcJJbU74auCW9PfWPkrA+vrTL11dIPTnqsiMJWTAAuHXBxymNjYOIrTy76t62v24FOh8TY/JqmyZ59Xifkd1TEM7LC2PwanQ5Jr7w+wFC/F6dZZHByUWWw9YzM0vcsB11mVf5MKn9kTrPIUL/+Q1X3CEQBrh5q4rTHxoN3y9wKtHC01cLEwgZnfDZVbiyZ5mE8xd0+N/vs+ruHGi7D/Q6JcNBDjzNf+PmXjaL7C+px5nOMLg413gkFwC7lmcNBT8V8Ya4W7cnT8D/APw2gMuH1aA6iH4tj0WTczUYkZzMAvN16Ka68Cowu+vTyYUKR2eLvggoxLYkmqQhRFaBWlYJojfVUFw8Y7YCWdtyBWo+hXDV1oHSR8lgoMlvz7qGsA00S8qqsiIIgVMuvgAhFZg3vXlEUXCZUb6kqgL5m4d7rVPbOala7M+cef9AsXC1eriaJXF+zcL80Jhj5e75dR7aTkdq/AFCYGAYKWHWoAAAAAElFTkSuQmCC") -} - -.list-list .ico-htm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEg8FtzQ04GxsAycPkYGqR1SOL/fvzm7IGCbJjkA2maRoAWYbL97hCglgHMhHra2r4li65ABYKxDoYxQH8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAPufFoHFXwuVAAAAAElFTkSuQmCC") -} - -.list-list .ico-html { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFkgW2hWiweC25gacjQ2A5GFyMLXI6mHyyHL//vwmLxsiOwbZQJqmAZBluHyPKyRIdSATIYOp6Vua5gJs8U2yA/hZgU08GnRWYVEJMpuPhQGllcqIbGHZjiv1p97/r/v0hzblA9CDf00FGJu7PHQasTpgIABAgAEAooAb7fMLYAoAAAAASUVORK5CYII=") -} - -.list-list .ico-java { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvRJREFUeNrUV0toE1EUPfPJTJNp82lNY1PaBGwtpn6q9bNQQRFFpSAKgrgQu3PpXlBEBMGNWwU3LtwoxYULhSoKgkJbawVDta3WT5XWEPpL2iSTifeNjqY6TeNkavDA5c375N1z7z3v8cLlcjmUE2J+JxqNHqDmOlmdjT42k/XlD0QiEXMCzHkwGKxTFMUWz0NDQ6y5RXa0ubm5z2wN/1u/zul0QtM0W4yhsbHxPiNBZNqLIWAZI+PzpuOyLJ8KhUKP6fO2GQlbCLyPpfAhll50XpKkk+Fw+JkZCbFU56qWw72BKRxq9/0x53a7DR0wHPvR9pJxthCYSmbR1RPHnlY32GEei6cRrJZ+7u73+xEIBMyEWXoJBt4ncfXBOLavrkLv2wSVIIX6POcGzIRZMoGZ+Szu9MbRuasW/aMJ+BQB25oqLQViiYDIcwj7ZfirRF0D6az129QSAafEY+uqSnyZzKBjow89IwlEx+b+HQGGNfVOVDg4uJ0C2kIuVMr88hNgwnv+LoHYjKr3fYoIjlS3b71Hzwq7jNJqDk9ez+Bu/yS0IipT9DFkG8dnVYxPZ/DyYxJM0FnyUOHg4RA5NNRIlAkFEn3vaKnCtYcTiH5KYm2Dyx4CbOPddN5Hv6b0aAMex4J5Rkbgvx9ClURpCNW2DBiYoAw8fTMLmerPiLhkQXfICO7f4NXHWIlO7FwBUeDsIZBMaxj8PId6n6Srn1kheFyCnhHbCDyKTuMFXThNKyvgdYkUNdWdNmcOHMKvtCdSml4iNn94S/Xfv4gWw8E2L/au8+jpj02rmJrLIp5QkSFhZsixTOmXSYw1dDF1bPLqp8PSk6wQWKSsBMzsBI8yo+wEFpTgUjddXd3Dy+70RqRABiRZXmCXO1tt7f/fGrh4vAlnbg7rrYH8vjFfaH3JGSi0mZX14lLRFtM3oix2/aIZUCT2btSW/JFVsL1d5CN/jMv/d3yl69W5wQmcTaaXR5wUYLalFhdOH2k9b0qgHPgmwABqjFw0GGyiFAAAAABJRU5ErkJggg==") -} - -.list-list .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmQz1rdMxxGAAWQ6XOAz8+vWL9GxIyEEwPi7xAUsDhCynqQNAvkeODro7gBjfY6QBLk62f79+/2MCAqw+ghkMi290y9DFsTVQQXYgizEi946nzllbf+f+y7pv33/RJGS4ONn/qiiKNWenBDdidcBAAIAAAwBHPa+btF94QQAAAABJRU5ErkJggg==") -} - -.list-list .ico-mht { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEA0y7rbmBVQzEhsnBxNEBst5/f37TtkGC7BhkiylKAzCf4jIYJIbL93RJhCAHEeNbihyAHt80yYb8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAIH+FoEeflonAAAAAElFTkSuQmCC") -} - -.list-list .ico-php { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA1ZJREFUeNrsVktPU0EUPn3dPq73QmmbFlRaAw1Q5KUiYCIkmmjUGEMUdGWMSze6cmViTFwYVv4Aw8KNRhe6kA0SAonRGGNBHpWkBAXFIFAeLS307ZyB1ku90EJvdeOXTO7c6XS+b8755tyRxeNx+JdQCl9cLlcLeXSQdlRCjjrSBoUDDodDXADBUwsBx3GSMLvdbnw8J63NbrcPis2Rp7xbWJaFWCwmSUNYrdZuFEHE1GYiQHIwDHODiHizlQj53zAaEXHNZrM5xUQoc0nM83zCB4j2jecAabJdCVgLx8D5xQ9j06swOR+CGDnCkWgcWLUCrCY1VO3XQlWxDhTy9fVNJhOYzWYxY+48Akj8eniZkOjgdE0+mHgVMMp1In8wBnPeMJ3TM+KFC0f0YLdo6G8JM2ZUB7ZCn8tLCW6esYBG9ds27pk1sns5FOkZYEkEbKShmGfvPOBbjcKhA2zatdOacGI2SMIdhLZGwyZyBI5j+IVAQVebjfB+fIWK3lElFEM/2f3Zunzax0WnF0IgI5E/V6eHmaUwdA8tQa2N1A5S0Ycm/RAhET9RycOp6jzo/+yDSw0F2UVgZjkM5jwV7X+dC0JLBQ+lZg18J0ICJNwXGwzwaTJAhVXs1dL8D5H3YqMapkiEsk6B8GPl8UVAv0cJPxZDNNScVgFoeJVCRsONXvjmCdHxTJFWgJFTUUKaL0L08sMC5OmUEIrEwWpkqOlYzfoyvaNeGJ4KQH0JS71TSARl7YGTB3l45VyC9sYCKCvS0BQkaoKJV9LdN9k56B1ZhvOH9clj2eVchCvHDNlHoITku5Sc6SdvPVBWqE2O44nAhkUH09G8IQzT9Kh3Fo6XczQlWUcAga42kNx39s3SAlNtZYHTyJMEaEDfWhS6Bvy0QrbW66G8SLvzC8l2qLHqwLFPS3P8cWIF5slOkRhNiLnGyohV8nKTIVmKJRWAwHxjdcukwkl2CnKN/wI2eeBBD6l6PeM5J33s2MaEjFr9xx86rpcm+7c7x5NjqX2xeakIBYO7SwEumEqSzTxJ7oSZkkgiIEEmDK0wBdvNk0RApgtmOk/UAywD0XSXyGyAayOHcEwmvHA8fDF6Z+wn3AuEclMfdAzEys1w91Zr5X1RAf8CvwQYAB0rcvQMbg3OAAAAAElFTkSuQmCC") -} - -.list-list .ico-url { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAr5JREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakmgP+/P/LsP/FJYZzb+8wMDEyMchyizD4yVgwcLGwM4iKijKIi4tjS5jkh8DLHx8Ytj45xXDj4xOG59/fMfCwcDBYi2kxBMpZMajxSYPVgMRBDgABWGIkqhwgBO5+fs7QcWUVw4+/vxn42bgZtATkGAwElRjWPDzCsPf5RYZcTT8GIyFlBklOIfIKInwAZOnE6xsZ/gB9FKfszOAqaYgoWIAOmXlrG8P0m1sYuoyTGQTZeIh2ANG54Oirqwzvf31hiFC0R7EcnNe5xRhKtUMY/gGL9d3PzpNfFOMN/i8vGJR5JYHBfZhhyb19eNQ9J8kBRIeAOIcAgzA7Lzgq8IGfBOTJDgE9QUVwYmszTABnNWoBokPg+OvrYHrD42NY5S+9v88Qe6SH4dy7u7RxAMgCHQF5hlNvboFzw+sfH8Hiv/79AWbBC2AxDX5ZcDakSRR8+v2dockglmHu7Z0Mx4ChcebtbXBh8+3PT7A8yHHZGr6UNUjwATEOfgYWRmaGTHVvBlMRNYbTwJB4+u0tg7iAANByBQZHCT3KW0T4QISCHcPBl5eBiVGBwUBIicFEWJX6TTJ8ABS/IExtQJf2wKB2AEoUTHixg4HhBe0tnYXUKiYqBNqdMrDyQTQyxqWeau0BbKBy3wwUi5H5Qy8NkAOQg5tU3w+KKEBxADcT+9+v/34yY7OEVJ/C1KOrBdmBzGdE7h1PPbGm5vaPF43f/v2iSdrgYmL7p8ohUZ9tEdKC1QEDAQACDADzFSd59WqSKgAAAABJRU5ErkJggg==") -} - -.list-list .ico-xml { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57TwhAATCysK3hOhi5UPorHJIcujmwXCJEeB25obDLtCNMBsEA3i0z0NwByBbjk1HDc0cgHMdzDfEhM6ZDmAnxXYxEPrrCIbjssR+ByOrB5kNh8LA0orlRHZwrIdV+pPvf9f9+kPbaIG6MG/pgKMzV0eOo1YHTAQACDAAOGRHaFJlGXVAAAAAElFTkSuQmCC") -} - -.list-list .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAg9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh7SLgvdXNjK82N8K57OysjIwMzMTXw4QC96dX87w7elRBin3HgYmVg64uKCOP5D0R7WAhYXh79+/OM0iOQT+/f4BtpyJjZfh65MzROkBhQITExM4Cih2AMhSNkFVBl5Vb4bvz1DLljen5oCjARuApQGKHQCylFPKmIFTQofh1/vbDL8/vSBaL8UOAFkGspRbxoSBhVOAgUPMgOHbi8sUJVySHACyDGQpLOGxi2owfH2wj3q1IcH4B1r279dnhidbclCj5cU1YJRo0dYBIEtAAD3rgbLk9+cXyXYA0VEAsoRDVA/FchDglNQHZ0tQ9iQHMCK3iIANkv/Kyso0rZju3r0LapAw0rU2pFouGHUAzVvFLEfSGR4eoYOFWodxhwA7C3FYLWweXj4uPDzSgHzQPIaH65LA9IAlQmpYTlaTDOZ7XHzKQoCV798/GvZVwWaz8v7DWRfc2Fxbz/TmbB3D78+0SZxsfH//CRs1a/g2N2J1wEAAgAADAIk82p3e8s5qAAAAAElFTkSuQmCC") -} - -.list-list .ico-bmp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhs1Sx6biiIO46OLIetDVvPt708yygE0S5D5xOjDp4eoNAAyAIaRDUL2KSkOo1sIYIsmqldGsFDAZgkxDkVxABcT2z9gxcPEzMiEMyGSE8zIlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAA9C4L2PHEWagAAAAASUVORK5CYII=") -} - -.list-list .ico-cdr { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Cr/WPhHObj44H0OMGHFk8O3vT+ITIchAZEOQ+aSKUyUNEDKM5pURMQBblFEtBNDjGFcowTAx6lFCgIuJ7R+w4mFiZmSCG4bLR6T6FFapgexAadwg946nnlhTf/vHi7pv/37RpHzgZmL/q8Ih3pxtEdKI1QEDAQACDAD7sQauaYXLQgAAAABJRU5ErkJggg==") -} - -.list-list .ico-gif { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM77raBVTYiax6bChcDsZHlkOXRwbe/P8krCWEW4eLjs5QmaQCbZSBHoYcETeoCmCUDFgLkWERUCHAxsf0DVjxMzIxMWC0lNXixVWogO1AaN8i946kn1tTf/vGi7tu/XzQpH7iZ2P+qcIg3Z1uENGJ1wEAAgAADACWJ9+r79LDyAAAAAElFTkSuQmCC") -} - -.list-list .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAaVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLBSZ/Pc3w+/HVxhYZHUYGJlZMaT5+Phg6QAEIqD0GSBmpIoDfl7Zx/Dt4EIGLvt4BnZ9dwx5UVFRBnFxcWwJkzohwKpoxMD+9gmYxgVgiZGocoBUwMQnysDllEy9gogU8H5SFJzN5ZjEwK7rQp4nyHUAl1MKGFMKyHYAu44TGA+YA6gFRh0w4A5goTQLgsC3/fPAGAQE85bR3gGc1pEDGwIcxr6jiXD4OIARuVn+sECTLm10+QnX8TRIWNixa6rewvCw1QfOhjsaKoZPHAX8+UlZLkB2CDIflzjN0wAxlgzvXIAc7xSXhP85Bf4x/vrKxMDIhDfIsSU2XOIo4P8/oB38/3BmwzuzCutZ7h+vY/z+kSZR859L4O8fBYtmlbT+RqwOGAgAEGAAjiC9hXL1ZWMAAAAASUVORK5CYII=") -} - -.list-list .ico-jpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7hoZa+0SG5oPzwTQMgPgwOWSATRwmBgLf/v6krCREtgDGRrYAXQ4bn+ppAGQBekhgczRV6gKYRciG4rMAm3qiQwCbr0AG4bOQFN9jhAAXE9s/YMXDxMzIRFT8YXMozIHYHA+q1EB2oDRukHvHU0+sqb/940Xdt3+/aFI+cDOx/1XhEG/OtghpxOqAgQAAAQYA3ogNywRusk8AAAAASUVORK5CYII=") -} - -.list-list .ico-jpg, -.list-list .ico-JPG { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7/vaBVTZD07GpYBqeTYF8ZHls4jDw7e9P6pWEMAuQHYXuGGyOoFsaIGQ5xSEAC25sFuGTIysEkOMW2Ye4LCDG9xghwMXE9g9Y8TAxMzLhTYD4ghybQ5ErNZAdKI0b5N7x1BNr6m//eFH37d8vmqQNbib2vyoc4s3ZFiGNWB0wEAAgwADnlgGzkKaAXwAAAABJRU5ErkJggg==") -} - -.list-list .ico-png { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhuRHY9NRRFD5qPLIcsjg29/f5JRDmCxCGvjBU2ekHqK0gDIYGTf4nM4xQURepBTUw8Tsb7FZhC+UCDWsSghwMXE9g9Y8TAxMzLRJFpAlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAADIwGkBp21VIAAAAASUVORK5CYII=") -} - -.list-list .ico-psd { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFCYMKxewyvvv1CZHAJPoYgHSn8FrCwMPz9+5c6DgCBcC1JBn0pfoaXn38wLL/8jOHA3dcMDsqiePWAQgFU4mIrdclOA+K8HAyWMgIMl15+Ajvg5KN3DBtvvYLLt7looKgHxT9VQgAbAIUGyPIMYzkGOUEunOqwOYLsRPjo/TeGPfffMuiJ8zEIcLKCxd5//01ZZUQMWHntORiDgL+aGIO5nBA8bex/8JZhy+1XDC6KwnBxqjsAlgjRAUgMhC8++wh2oCQwjeCLDpqVAwpCXAzcrMwM3//8pU0I4EoPM84+gvPdgFGgLspLlF5G5LwJbJD8V1ZWpmnFdPfuXVCDhJGutSFd08CoAygqiGLOArPO2Vs0t/ScFp5syMTCiqFhmZ8anB216RaGOEwMWR26Whj49+c3eeUAsiUgNoxGFsPmQGyOoEoagDkCl0+pnghBlqH7CNkRNHcAyDJc8U+JI1DSAD8rw79P//4z4Wq9EBMFuBIsCICKfT4Whn8464KyHVfqT73/X/fpD23KB6AH/5oKMDZ3eeg0YnXAQACAAAMAI0n+E64by+4AAAAASUVORK5CYII=") -} - -.list-list .ico-webp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Q5V9PEPbwYUYGmHitfaJKOLNB+eD5ZABTD+yWpC6b39/UqckBBmGDpAdjewJmFqQYyr3zSCcBkAaYT5CppEtABmGHhIgNTCMLQSpWheQEgIwh2LTg9MBsFBApqnpWKwO4GJi+weseJiYGQnnTvTEhRxd6KGBXKmB7EBp3CD3jqeeWFN/+8eLum//ftGkfOBmYv+rwiHenG0R0ojVAQMBAAIMAErgG/MeljhhAAAAAElFTkSuQmCC") -} - -.list-list .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjFg1Cdf0MbxtKULhwwBMHFkMWRwZ/ELrh1CUC5AthrGxWUpxGoBZgO5DXGqJUUdyCGAzFCaG7GtSQ4CFWN9j45NqGUEH/OPk/vfvxzcmSvImtsQJNx9UqQHtwJkNH8zur2e7f6OO8ftXmpQP/7i4//5W0GhWSC1sxOqAgQAAAQYADkDCgkQGXJUAAAAASUVORK5CYII=") -} - -.list-list .ico-avi { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGz48SdqZpE8dHFkfHQTQPNcUoMtYvugWkYQObD5GmaCJEtp0kiJOR7bHxYKJDje4wQ4GYDtSH/0Sy+QWZzAe1AFmNE7h1PWHe1/sYrhrpvv2iTOIEe/KsuxtBcEKTdiNUBAwEAAgwAcCQxq0cx9U0AAAAASUVORK5CYII=") -} - -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA8sBqKR1eDjw8Cvnz/JS4TYDKNrGgD5CD0ksIUKPt9TlA0HPASIdSQpvsdwADcbqA35j4Fa0YIOQGZzAe1AFmNE7h1PWHe1/sYrhrpvv2hTPgA9+FddjKG5IEi7EasDBgIABBgAhSU3wy5Zmw0AAAAASUVORK5CYII=") -} - -.list-list .ico-mkv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAq1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYkayHfD333+GE4/fMVx88ZHhxecfYDEJXg4GfQl+BgtZIQZmJrjZDKKiogzi4uLYEiZ5IfDpx2+GBeceMvBzsDK4q4ozyPJzgsUff/zOcOTBG4azT98zJBjJM/AB5WEAlhiJKgcI+RxkuQ7Qp05KomCxGafugekMMyUGZSFuhn33XjPMB6rJtlBmYEEKCXyA6ER48sk7sM9hlsMAckEKkhMAqjkFVEtWSYgPXHj+ERzsyADkc3RgoyDCsPP2SwYrOWHqOuDpp+8MMnycKMGOjZ1opMDwDKiWWEBWOYAc7OhsRmDUMzEyUj8KpIG+fwL0GXKwY2PfffcVnC2pHgKgfA7KasgAFOywoIcBkBp1ER7qO8AcWMh8AJYDoKyGKzpAcg8/fAOnF2IBI3KDBNge+K+srEywIBLgZGWwlhfBKIhADowzlGNYevExgwgXO4OzsiiDKDc7ihl3794FtQcYyXIAclF8/tkHhldff4LFxICWGEoJwIviP0A1oLLg5OP3DIXWKngdQHJdALLAWl4YjHGmbKAaUDlATFlAl+p41AFEF0QxZ/8C26+3aG7pOS08IcDEwoqCt4ZogzEuMWQ5bGLofJpFweYANQbfDbdokwZABoMswGURuhgh9UMzF8B8hc03yD4mRj1OB/CzMvwjt7OKzRGY7Yj/DHwsDP9wVkZlO67Un3r/v+7TH9pEDdCDf00FGJu7PHQasTpgIABAgAEAZzJTrgcAEqEAAAAASUVORK5CYII=") -} - -.list-list .ico-mov { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAupJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnh7Ob45TAdO2iezjFYHx0MXQ9MP6vnz8pS4TIAJ9FVEsDIANhviTWAlL0DI1cAPMRKcFLrB6UNMDNxvDv579/TEBAkiXoiRAXADVQuYB2IIsxIveOJ6y7Wn/jFUPdt1+0iRqgB/+qizE0FwRpN2J1wEAAgAADAIjpSLqXWcBIAAAAAElFTkSuQmCC") -} - -.list-list .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjCh84Zo+MP22pQirGIyNTw0y+IXWD6FKFIAsQXYMiEYXoygNYDOckHpi1VIlBEAWYbOMaiGAbBg2H8GCGz3+SS4J/3Fy//v34xsTExXSBLZECGqe/gfagTMbPpjdX892/0Yd4/evNCkf/nFx//2toNGskFrYiNUBAwEAAgwArNbTCDlVe6IAAAAASUVORK5CYII=") -} - -.list-list .ico-mp4 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKcEpmsX3cMqBmOjq4GpQxb79fMnbaIAZAm6Y5DZFKcBZMPRfTUoEiHIUcgOI9aRTKQGMy6DYVGALe3giwoUB3CzgdqQ/6iaJpATJshsLqAdyOoYkXvHE9Zdrb/xiqHu2y/alA9AD/5VF2NoLgjSbsTqgIEAAAEGAKRaSLN4Zv7KAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwJJREFUeNrMV01ME0EUftS2221LscBSt0pbjBWsBjGAQRMT48FEL/5F44kYD8ajnjypMVw8cvFkSJSrxMSDMRoSUeNPDARJtKBVo0Sx0CZFC6Wldeu8laWz2y0sMoAvedmZN1/nfe/NezNQls/nYS3FTE/C4fAh8ukiKjL00UJ0gDaEQiF9Aujc6/WKDoeDiedIJIKfO0RPBoPBAT2MSTMXeZ4HSZKYKIrP53uIJAiZZiMEmAvHcef9fv8TMuzRI2FajUKzWq1nAoHAKz0S5qVuNp2R4Pn7JIyMpSGezMk2wWWGbRt52BN0goMrxORyuZQ6QDk99+0nWvZPBN5+m4F7/QnY6bfDsVY3eCossn38ZxaGRlPQ+SAKR1rcsGMT/5eYIIDH49ErzKVnAJ3fH5yEs/sFENdbVGu1VVZZm+sc0P0sLtsUEkoxlhKT0bRj5O37qoucq1qIrCEGsVNpyVBghgi8jExBo89e5LyrLwaJ6VwRCcS++JBkR2D4+ww0kXPXyqfxDNx4NAGRaFplRywWKTMCsV858Lr1U5+aleDW0zj0hQsRiwQbS+bYEVhUyHsmUY8a1t06gzsb6gLs87FEVq50rdgsJjjVVgkNXtu8Dduy0mlmRwAvGexzLYGAwMGJ3W6o0jhDbINoY3cEbVucMPQ1BT8msyr7uQNCkfMowSB279ZydgScNpN8w+EloyVBC67dJhjE4m+YHcH8zUbqrOtxDJoCdthFWk1wWea6JAuDJOo3X1JwtLVwFTMlIJOo5aGuhpMfo57XCfkxwmqvLjdDvcjDxcMbVI8RcwIo6OBgY4WsLGRV/h74rwmojuB6L6my3o8r7rQ7tEANWDkOOto3y+PL3Z/n7bRNGdMY2lbKjrbZTGZ5RajnQBnThLVryvzSzWFjNUBHqt2olCBOmwk90kwyoHdMC5FU8IYzQGdBb2O0GcnKYuRUGXBYQcpIkmm5WaEJao/ETnzQ8zL6v+POu++ujkzAldTsytwPJMDf9TXQceH49mu6BNZC/ggwAJR8VIseM6jsAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvlJREFUeNrMV81PE0EUf61tt9uWYoGlbpW2GCtYDWIAgyYmxoOJXvyKxhMxHoxH/QPUGC4euXhsor1KTDwYoyERNX7EQJBEK1o1ahQLbVK0UPrl1nkr2+5ut7CVAfwlLzP75nXfb2Z+b2ZrKBaLsJYwyR8ikcgh0oSI8RRzdBMblTuCwaA2AUzu8Xh4u91OJXM0GsXmFrGTgUBgVCvGqHrmWZYFQRCoGMLr9d5HEoRMlx4C1MEwzHmfz/eIdAe1SBhXQ2gWi+WM3+9/oUXCVOvL5rICPH2XgonJDCRSBdHHOU2wbSMLewIOsDPlOTmdTkkHiNML7Qgxwz8ReP1tHu6MJGGnzwbHelzgrjeL/qmfeRj/moaBezE40u2CHZvYv8Q4Dtxut5Ywa18BTH53bAbO7ueAX29WjLU0WkTrarVD+ElC9EkkJDFWg1HvsuPM+/Y1VSRXlBAZwxiMnc0Iuiami8Dz6Cx0eG0VyUPDcUjOFSpIYOyz9yl6BN5+n4dOsu9qfJzKwvUH0xCNZRR+jEWRUiMQ/1UAj0t76dM5AW48TsBwpDxjnsTGUwV6BJYEuc8E2aWGulun8826qgDrfDKZF5WuhtVshFO9DdDusZZ8WJYNDhM9AnjIYJ2rCfg5Bk7sdkGjKhnGtvNWelvQu8UB41/S8GMmr/CfO8BVJI+RGIzdu7WOHgGH1SiecHjIqEnIgWM3SQzG4m+obUHpZCM6Cz2MQ6ffBrtIqXFO80KV5GGMzPrV5zQc7SkfxVQJiCRaWGhtZsTLaPBlUryMUO1NdSZo41m4eHiD4jKiTgCBCQ521ItGA6vyPfBfE1BswbUhorKhDyueNBxcRAMWhin1+/s2i+2l8CdNn9SvFqP2I3LZ7PJEqIaUAJNKpNRk1CRq0oB8pku9bDGC1MpQDa1t0jNWUxVoLa18rFoCPaulWAG7BYSsIBgJlqUJuQjlwA9UG8kh9xnk/44Hbr+5MjENl9O5lTkfyAR/tzVD/4Xj269qElgL/BFgADHlS7OoZ6a9AAAAAElFTkSuQmCC") -} - -.list-list .ico-rm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA9FHMbHJYYMfv38SV4iRLcElwV0TwMgh8B8TY7DiA4B5Giglu+pFgWwUCDHYShRwM0GakP+o1nqB5nNBbQDWYwRuXc8Yd3V+huvGOq+/aJN+QD04F91MYbmgiDtRqwOGAgAEGAAnMw5uSNz54cAAAAASUVORK5CYII=") -} - -.list-list .ico-rmvb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08a6EzVROHXLroHppvjlFD46GIgNrocTBwZlM++TjgRYjOIEoCsH92DTKQaBPMRusPwyYH4MExUCCAHG6W+JxQCJEcBtvgmRo6oXMDNxvCPFlkTOUQbF17/NyNDG85nRO4dT1h3tf7GK4a6b79oUz4APfhXXYyhuSBIuxGrAwYCAAQYAPSMU/fnJ99KAAAAAElFTkSuQmCC") -} - -.list-list .ico-swf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUKp4d/3H2D4tm8fw/9fPxlEOjtR5Pj4+GDpAATCoPR5IGakigM+LVrM8PfdWwbBokIGJkFBDHlRUVEGcXFxbAmT8hD4+/Ytw69LlxhEuoC+ZsIdk7DEiAuQnQZ+Xb3KwGFpgddykgsiUsCfR48ZPq9eDY4GEOAJDmbgi4ulnwP+fvrIINLezsCmpUlRCJAdfv+//2Bg5OSgOIuS7QBQ6mfi4hoYB/z/9Yvh78tXDMzAbEZ/BwCz1ad58xg4rawozgFkOeB1UTFQFzMDX0I89dsDxADRCf1UrSfoUhmNOmDUAfgAI3Kz/KaTM13a6Or79uJukACbTxgaFGfPAtP3U9PgfBgbXR1IHKYeBpDV/gKWoiSVA8iW4bIYGyBWHRMxBqGHALIYMo1sKYiPHhJkJ0JswUqMHmJCgYmYKMDmCBgfmaa8LuDj+/vv2zdmJqRaDlsUkAvADVSgHTiz4Y2GxhrG8+cbGT5/pk35wMv777+hYb1GQ30LVgcMBAAIMACmqfNs4ifjnwAAAABJRU5ErkJggg==") -} - -.list-list .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JYw4Q4CNkRGOJWv7Ufi4xEnhD6008LaliEG4pg/MRqZB4jAA48PkselDVk+TEEC2nOJEiC2ekH2JzffE8kHgF9A+nInwHyc3gSYkhQ1UUKUGtANnCDyY3V/Pdv9GHeP3rzRJnP+4uP/+VtBoVkgtbMTqgIEAAAEGAH8AyCqN8wbhAAAAAElFTkSuQmCC") -} - -.list-list .ico-webm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwxJREFUeNrMV81PE0EUf137tbQUCyx1q7TFWEE0iAEMmpgYDyZ68SsaLxKjifGof4Aaw8UjHky8NFGuEhMPxmhIRI0fMRAkwYpWDRothTYpWmgLLVvnrSzdr2JLBuoveZndt2/m/ebNezOzhlwuB+WEUf4SDAYPkSZAhKfoo53IkFzR3NysTwCdu91u3mazUfEcCoWwuUfkpN/vH9KzYVTvPMuyIAgCFUF4PJ7HSIKQaSuGAHVYLJaLXq/3GXns0yPBrEWimc3msz6f740eCWOpg83OCfDyYwLGwmmIJbKijnMYYdtGFvb47WCz5OfkcDikPECcXmwHiRhWRGD0RwoeDMZhp7cCjnU4wVVlEvWTvzIw8j0JPY8icKTdCTs2sX+JcRy4XC69xCw9Auj84fA0nNvPAb/epPhWX2MWpa3BBr0vYqJOIiElYyEwxYYdZ961r1bjXFFC5BvaoO1MWihqYkUReB2agRZPhcZ5YCAK8dmshgTavvqUoEfgw88UtJJ1V+PL5BzcejIFoUhaoUdbTFJqBKK/s+B26oc+OS/AnecxGAjmZ8wT22giS4/AP0HOM0F2qGHerSty5KKqAOs8HM+Ima6G1cTAqc5qaHJbl3RYltV2Iz0CuMlgnasJ+DgLnNjthBqVM7Rt4q30lqBzix1GviVhYjqj0F84wGmcR4gN2u7dWkmPgN3KiDscbjJqEnLgt7vEBm2xD7UlWNrZSJ4Fnkah1VcBu0ipcQ7TYpVkYJjM+t14Eo525LdiqgREEvUsNNRZxMOo721cPIww22srjdDIs3D58AbFYUSdAAIdHGypEoUG1uQ+8F8TUCzBjX6SZf2fV91pb/5SDAb5f8FESshd6f2q6dDdtRlQj60chXRSH/m7XMezjGHFSahHUO1Ez6bwOUIiIMmZm6O5cHIhd/52SNNKz5Lo6dT6Qq3cZ9kjwOgNJl/zksJZgPBy4ygiYDODMCcIy5amPOnUCVcoQvl7gvaeqKiCnvvvr41NwdXk/OrsD2SCC4110H3p+PbrugTKgT8CDACPHLU9lTMolQAAAABJRU5ErkJggg==") -} - -.list-list .ico-wma { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgSmX93C8ODzC6IsVOCVYMjU9oHzf//7w3DgySUGVxkj4soBbABkeadFClEOKD8xB85+9f0Dw9LbexlefHuP1wE0SYRnXt9imAEMOWsJbYJqaeKA1XcPMaRpeTOYiWkMjANAQIJLkCh1dCkHBsQBz76+HTgHhCrbMcy+vo3h3Js7pFXHVKt/RdUYZLhFGZbd3ke5A0CFC3L+xq9WHCUR5ur6Mxx4dokyByCXbKQCViYWvIXQ8M4Fow4gq1U84cUOBoYXtLd0FlKrGCMEuJjZwbjFKQ3ORsYwcRCNrgaXGDJ/6KSBtoMLGars48FsZBokjksNshyyGEzNgCRCfJYTLAlhrkem8anB5ntcfKwhwMXE9u/v/380Cw2Q2SA7kMUYkXvHU0+sqb/940Xdt3+/aBI13Ezsf1U4xJuzLUIasTpgIABAgAEAkufcCgWFyTcAAAAASUVORK5CYII=") -} - -.list-list .ico-wmv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGzg3FnqiacjYxh4iAaXQ2yGDY5ED100kDtonsMzXFKYDYyDRLHpYZYuaGTC2C+QKbxqSFFDqsDuNlAbch/NPMtyGwuoB3IYozIveMJ667W33jFUPftF22iBujBv+piDM0FQdqNWB0wEAAgwAD570Ssrd8AngAAAABJRU5ErkJggg==") -} - -.list-list .ico-doc, -.list-list .ico-docm, -.list-list .ico-dotx, -.list-list .ico-dotm, -.list-list .ico-dot, -.list-list .ico-rtf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAArdJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFcYPnW+ww/f/1lSAhUQRFfsP4OmIaJP3j6hWHGiptgtoQIJ0NBvBYDCwsLw9+/f3GaTVQI6KkLMtx99Jnh959/cDEQ+97jz2AMExcWYGcIcpVn4OFiYdDXEIKrBYUCExMTOArIcoC6Ah/QAEawI2AAxJYU5QJjmDgvNyuDGlDt1+9/gA4QRDEDlgbIcgALCxODhhI/w417H+FiILamMj8YI4tfuf2eQVaSm0GInx3DHLIdAAK6aoIM1+5+YIBVXTfuf2TQVhFg0FIWQBG/fOsDgwFS8JNUGeGNBkU+hh8//zI8e/mNAeQRFmZGBlEhDkhqB4YQSBwUBU9efGWI9VeivgNYYdFwHxLcmkCfwwA4GoDinOzMDEqyvMBEyEq0A0gqB0C5ARTfIKyF4gABsNgVUPBrCpFUVpDkAHUFfoZXb38wvPvwk0FeGlFlK0jzMLx9/5Ph8cuvDDqqAuQ3SAiXbEzgbMYGpJmQUjQzMIuqAdPInz//GdjZmGnnABCI9sWewCK8FMkqrulSHY86gOg0MGHJIyD5iOaWzmrD3SoGNZ9Q+JU5Dij89ikHsMoRIw4Cv379Ij0XoBsO4sNoYsUHLA3gs3xk5AL0NIQ3F3BxMP/79ecfE6j5hMsQWJDC4ptYcRAA9ZZAdqA0UpB7x1MXHKm//eh73bcff2kSMtyczH9VZDmbsxNsGrE6YCAAQIABAHOPGcK/91HrAAAAAElFTkSuQmCC") -} - -.list-list .ico-docx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAphJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiA5ClZsu8/w4+dfhoRAFRTxBevvgGmY+IOnXxhmrLgJyU4inAwF8VpgB4AsBuUKbA4gKgR0VQUZ7j76zPD7zz+4GIh97/FnMIaJCwuwMwS5yjPwcrMy6GkIwtUyMzODMdkOUFPkA9MgR8AAiC0lxgXGMHGQxWoKfAyfv/5mMNAQQrWIiQmMyXIAKwsTg7aqAMONex/hYiC2hhI/GCOLX779nkFOiptBiJ8dwxyyQwAWDTfuIzngPpIDkMSv3v6A4Xt8gGgHgKLh67c/DC/efAdjZiZGcEIDYRAAiX349Ivh8YuvDLpqguTVhqREA7IlWsoQcQ52ZgYlGV5wWqC6A0BAW0WA4ei5V2C2h600XFxPXZBhx+GnYLaprghJZQVJBREovp+9+sbw+t0PcEKDARAbJAaSAzmS7AYJMdEAcgQTMP6ZkFI0iK0KzH6g8gAUDTRzAAhE+ShhFY/wUiSruKZLdTzqAKLTwIQlj4DkI5pbOqsNd6sY1HxiqMxxQBFrn3IAzkaWIyQOEkNX09i3i7hcgK4RxMdmID5xZDl0NTRNA8gW4bN8+OYC5PSAHAXYACNy57Swefefn78ZmavznGiWCHum7/3bX+vKgtUBUxcerbn98Fvjtx9/aRIyXBzM/1Tlueqz461bsDpgIABAgAEAbXE38hMP+G4AAAAASUVORK5CYII=") -} - -.list-list .ico-pdf, -.list-list .ico-fdf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7CQa+jf9x8ZGFlZGJh4cKcXPj4+WDoAgQgofQaIGSl2wJupCxk4tNUYuG1MGVhEhbGqERAQADuCkZERjh8+fMhAeQgAExgoBPgD3IF+YcSpjJWVleHPnz/wBEm9RMjExMAiLsLw/fwVhj8v3+AvaFhYGJiZmYFamMAhQLVEyOfryvBu/mpgGuAiqJYmDuDQVGFgFhJg+H72MlHqYWmAag748+oNOBF+OXya4ffjZ0Q7gmoO+LL/OAOPszWDcGokw7t5qxh+P3lOeWVEdCb4+g2cC76fvsjw6/5jhv/AlP5m2iKGvx8+M3CZGwALib8MrDKSDLxudtR3wO/nrxjeL14HLoQYOdgZ+IBZkVVCFJwdf964y/Dz3kMwn0VCjPoh8PXIaYaPG3cx8Hk7MfA4WGKW+xrKYExSjiZW4ffzVxl+3rzLwGWix8Bjb0G1eoJoB/x5847h/6/fDHxeTnhLP5olQl5XWwYGEKYyoEt1POoAotMAS/tMhof0sHDxRNyJEFuqVGorgbPvVfVgiBEjDs9N5OYCZAtgbGTDcYkPSBoAOQY9JCguB2AGEuM7UkKA5CigaS5g4Ob89//rdyZGEkOFWAeCu8FcnCgtVEbk3vGN/ln1TDfu1TF8+06b8oGb6+8/dcVmjcK0RqwOGAgAEGAASrzyfwJnztEAAAAASUVORK5CYII=") -} - -.list-list .ico-ppt, -.list-list .ico-pptm, -.list-list .ico-pot, -.list-list .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdtJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGYkywHvJ0VhCjKzMLCpmDNwmAYwMAtJo0iJiooyiIuLY0uY5IUAb3Atw+e1zQxczmkMLKLyYLH/v74x/Lp1nOHzimoG3pB6BmYxRRQ9sMRIVDlACDBDLWUWlESxiEVGm4GJT4zh+/FVDDz+5SRFE9USIbuOE8OfZzdJ1kfdXMDINHAO+HXzGDyKyK4LiAV/3z9nYGRhgyTCP78Yfj84z/Dj/DYGHt9S+jjg295ZiFBn42JgkdVm4PEpZmCV06WPA0DZjUVKnSpRR5eieNQBVEsDoAQnmLdsNAqGlwMYkZvlDws06dJGl59wHU+DhIUdU0PVZoQj23yxiiHz0dWigD8/ycsFyBbD2NjEkNnDqxyABTGy77CJ0cwB2CyhxGKsUfCfS+Avw/9/tAtvoNlgO3Blwzuzi2pY7h1rZPz+kSZp4z8n/78/Slb1Kql9LVgdMBAAIMAARLzKcEnwqqcAAAAASUVORK5CYII=") -} - -.list-list .ico-pptx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZyXLA+0lRGGKMrOwMrIrGDBymAQzMwjIocqKiogzi4uLYEiZ5IcATWMXwZX0bA5dzGgOLqDxY7P+fXwy/bh1j+Ly6noE3qJqBWUwJRQ8sMRJVDhACrLI6YJpZUBJokSLCECl1BiZeEYbvx1cz8PiXkxRNVEuE7LouDH+e3iBZHxVzATBd/f83cA74deMQA7O4MmV1AbHg39f3DP8+voIkwp9fGX7dOcXw88J2Bm6fIvo44Ov2SQgOMwsDCzDlc/sUM7DK6dLHAbwh9eCUTw1Al6J41AFUTQOCectGo2B4OYARuVn+sECTLm10+QnX8TRIWNghiqo2IxzW5otVDJmPLg7TA9MH5//5SXwuQLYYxsYmhm4hsiPQ5eiWBoixnGAIwIIYPTjRxWhWEGGzhFiLyYqC/5wC/8hp1eCyHCOxAs3+z8n/D2c2vDOrsJ7l/vE6xu8faZI2/nMJ/P2jYNGsktbfiNUBAwEAAgwA8LXlQoWLfqIAAAAASUVORK5CYII=") -} - -.list-list .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVJJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmRhi9a3TweIgGp86mFoY+PXrF4ocI3LHJL1own9sDkC2ANkwZDFsjkFXC3PAzL4CRqqlAWyWj6xcgBwFFDuAi5PtH6FGJK40QYwjQGaD7MCZCKfOWVt/5/7Lum/ff9Ekarg42f+qKIo1Z6cEN2J1wEAAgAADALEDt/dBJDrPAAAAAElFTkSuQmCC") -} - -.list-list .ico-xls, -.list-list .ico-csv, -.list-list .ico-xlsm, -.list-list .ico-xlsb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBAQYAAlaEZGRjAGgcePH9MvClhZWRlYWBB+hDmCLAeceHmdIWZPO5gmpObYi6uIOAY6AISRQ4GsNGAhrgmmp1zegMJHtnzSpfUMGdo+DFYS2ihyzMzMYMuxlbokpQFcjoBZnqLpyWAnpYdVLxMTE+UOwOYIEABZHqfuyuAkY4hXL0VRgMsR/4C+ilJ1YvCQMyUroVKUC/5Bg1SEk586tSEpOQIU7AGK1gxyvGI4EyZNHACz3EfegiFMxR4uTq4jWMixXISDn0FPRInoLEoVB8As//PvL8OLb+8Y9j05z6AjpECxIxiR8yawRfRfWVmZpjXj3bt3QS0iRrpWxzTLhsPCASiJcPKr3QwMIExjMB2pVYwRAuyMLCi42DYKKx9dHCYGw+hyMExyFEw5soohxwbSmgLRID42AJODYZgeqqQBmIG4LEd3KIxPs7qAmo4gKgRgvscXrDA5WBSQlQu4mNj+/v7/l5kJ0WpGCXr0qEC3lFC8/2P4D7YDZ1E8/eS6mts/XzZ+//eLJuUDJxPbP1V28fpM86AWrA4YCAAQYADJhRMxzVK59wAAAABJRU5ErkJggg==") -} - -.list-list .ico-xlsx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiDLASdeXmeI2dMOpgmpOfbiKiKOgQ4AYeRQICsNWIhrgukplzeg8JEtn3RpPUOGtg+DlYQ2ihwzMzPYcmylLklpAJcjYJanaHoy2EnpYdXLxMREuQOwOQIEQJbHqbsyOMkY4tVLURTgcsQ/oK+iVJ0YPORMyUqoFOWCf9AgFeHkp05tSEqOAAV7gKI1gxyvGM6ESRMHwCz3kbdgCFOxh4uT6wgWciwX4eBn0BNRIjqLUsUBMMv//PvL8OLbO4Z9T84z6AgpUOwIRuS8CWwR/VdWVqZpzXj37l1Qi4iRrtUxzbLhsHAASiKc/Go3AwMI0xhMR2oVY4QAOyMLGBfbRsHZyHx0cZgYDOPTC8JER8GUI6sYcmwgrSgQDeJjAzA5GIbxidFLMA0gG0iMQ2F8YvVSrU2IyxEU5wL04MSlBmYpssXE6MUoCYsPTf/z+/9fZiZgqxk9+LAZCJPHZgk2vf8Y/jOwMjL/7bXLZMHqgOkn19Xc/vmy8fu/XzQpHziZ2P6psovXZ5oHtWB1wEAAgAADABRqMy9QT3WBAAAAAElFTkSuQmCC") -} - -.list-list .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012zDEHrZ4YYiDxHDX7z/JLweQDQZZCuOjiw9YGkB2FE0dgM0iciynWgiQazlGGvjPKfCP8ddXJgZGJpISIro4Tsf8/we0g/8fTgf8UbRoYrl/vI7x+0ecLnjY4EyUGFb7uQT+/lGwaMZZDgwEAAgwANHIuc9Vl0AAAAAAAElFTkSuQmCC") -} - -.list-list .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYxJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabQgHtnhhyCGLIavFph5Sv/8kPhFiswCroTgsJUY9SQURNsPxhQIhyykuCdGDnJwQYCLHQpjBMIzNIWSVhP85Bf4x/vrKxMDIBPcNqUGKV/3/f0A7+P/hzIZ3ZhXWs9w/Xsf4/SNNyof/XAJ//yhYNKuk9TdidcBAAIAAAwBVN9K79PuTHAAAAABJRU5ErkJggg==") -} - -.list-list .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012xgetnjB2WBHQ/nIYujiqPX7T8rLAXSHgNjIYuhqqJ4GYBbi9SmtEyGyI+juAGTfU+oIlDTwn1PgH+Ovr0wMjEwkRwG6Y7BGzf9/QDv4/+HMhndmFdaz3D9ex/j9I03Kh/9cAn//KFg0q6T1N2J1wEAAgAADALuKyAid7COMAAAAAElFTkSuQmCC") -} - -.list-list .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX5JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabSj8hy1eGPLIYoTUM/z5SXo5gG4BhqEUqKcoDcAMR/c11UtCZAuwRQEp6slyALYgxRb3MD7ZUfCfU+Afw/9/tEv+QLP/c/L/w5kN78wqrGe5f7yO8ftHmpQP/7kE/v5RsGhWSetvxOqAgQAAAQYAQvfLEuMZrKwAAAAASUVORK5CYII=") -} - -.list-list .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo0121D4D1u8wGIwGlkcd/3+k/xyANlgdMcgy8McRbM0QIoFVC8J8VkOCxVSHMdCTZ+TEyooDvjPKfCP8ddXJgZGJqISIskW/v8HtIP/H85seGdWYT3L/eN1jN8/0qR8+M8l8PePgkWzSlp/I1YHDAQACDAAtKS/DHmsv9AAAAAASUVORK5CYII=") -} - -.list-list .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHtSURBVFiF7Ze/T8JAFMe/1QYCRKIDIe1AYcAfLJLowL/grCbGydEJTXBw0qiLA4NxNGFlkX/A0UlJJGEBQzoAC3EzxhgTgz0HPXI9jrahxTr4TZrm3r3r+/Tdu2tPIoTAT8lso9lsrgEoAVA8jLEKoMYaMpmMGABASVVVJRKJeBJZ13UAuAawmU6nayKfKa6thEIhGIbhyQUAiUTiBsC1rusrTgA8VzAY3NU07RZARQQxcQAACAQCO8lk8l4EwdeAp4pGo7QOAGDr5/4AQPoVgFgshng8brIxQM4AXisn6PdaYwHI6gJmNo6tfewe0u+1MJcvC/ueL7dH9tF+O7kqwrl82VGQiQHYZWDiAP8ZoBC+AriV7wCudkK38w/8lQxI0ve3odFoDDuoC2MXmqzMC+003gDASnZ7uVv5PgW+A5imIHK1jicA2sUjuvtLEwk49LvLngs6e4uEVaewTHjxtk5h2XTZ+u4tmg4iQ0XYPchCK9bRPcgO2lRasW7y48WOo758m5ejjWjUYKdiwXjZFiEdPOoBboJbArBvrRXrlllg0y3ytRovEUIGO1P7MPcpfbxNQRpvdfI1MPTmxAAJhI3UeXWamkw10E/lTuX23ZH0/iImkIOWADQDAITLmIRnP/vJ3BlQZYw+H8+/AFShLMorUUd8AAAAAElFTkSuQmCC") -} - -.list-list .ico-bt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbxJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMOkTAiAAS4xElQO4QPmJORhivKycDO6yJgymYupY5WGg0yKFcgfgMqjm1AKwA5DlQI4hZCnVcsHvf38ojiK6ZEOqRAG+eKaLA7DFKzUcNeBRQJEDWJlY6BMFzIxMWIPbSkKLYgcwIreIgA2S/8rKyjQN8rt374IaJIzDIw2MOoDqBdGEFzsYGF7Q3tJZeFrFDFzM7DS1/Nvfn7iz4cPPL/8zQWOl99wqhmKjMLgcMh+dDQLIatHFkdUD28sM8rzi2LNh6+mlKBaAaBjGZTm6xTCAy0EwO8hOhDCHobNhlsD4+BxHlANgBsAwNoPRLYGpRfc10UVx4YGpf/4w/GMGlf20AH///wOmeqa//Q7ZLFhzgQqHeMPtHy8agSmVJi7gYmL7B7IDZwgMBAAIMABhJOYNvD7xMgAAAABJRU5ErkJggg==") -} - -.list-list .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII=") -} - -.list-list .ico-apk { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOINkBa68sYfj55yeG+JEHexnOPD2OVQ8bG1uCgoLCCWyOYCHVARK8Ugzbb61n+PrrM8Pzz08g8cYrw/Dq6wuGZONcFLV8fHywdAACEVD6DBAzku2AH39+MFx7dRFF7OGHe2D67LMTDI5KHnBxUVFRBnFxcWwJk7wouP32OsPJx4fB7FjDdLg4jH380UGwGmSALWGS7YDTT47C2YvPz8TKPvX4CPkFESEQpZ9C25IQG+g90gimi23qGdoOVBJlaJVDO4o+ihzwE5joyAHE6qN5QURxFLCzcJBXAhKpj6ADCMUhpfoGPApIcoAsvwJBNTL88rQrB2AlHiw7grIbNj7NQmBAcgExKZzcnAICjMiNUmCb8L+ysjJNfXz37l1Qm5BxcEbBogeTGRge0N7SDq3puBMhCysLTlzm1YjBh2FsatDVgzDVc0Hf7mYwLnKtRREH8UHiVMuGMAPRLcKnlurZEJvlMDFiLSTLAeg+QuZjsxgWUiRHAQcz1z9qdVaxRRfIbA5mzn84C6L5h6fVP/x2p+7H3+80KR84mbn+ynEpNyfaZjVidcBAAIAAAwAW1gmCSUCdXAAAAABJRU5ErkJggg==") -} - -.fileList .ico-bookfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmJJREFUeNrs3E9r1EAYx/HJ/3S3mwTb7a4u7kkPguLBky9A8OrVk9A3ISiK4mvwpNCLHj37GjwJ4smjFNZuQTbrYXe72/hM2lrBS6LuhITvDx7SlsIkn5lkMp22VpZlivx9LAABBBBAAAmAADYEMP3woK7Xsiv1ylBb+1J7Us/dhgyEW1Iv25euKcdvrb2xbHU0mE9GjxbpgdMEwK7Uu43toe94cjnZYv23ra1UEO8oAdy1a47nSL0JkotDrx2bffbZVt55dQd84UfdO0HcrewE6gx4z20lD4O4p59KlZ2EK7OuL8cnUnr6HdRFT/CU39k6u5WqOw+pZ360Iz3ZV5bj1WgAHsvAW1bfkfr9Sc8olp0ZmcGaFv0M7FZ9G9QdkAAIIIAAEgABBBBAAiCAAAJICsfIptJyPlU/vn2SY2rmooJIbfZuyLHTjBFoEu+kw9K8zUaNQB09KsJooLLjlZqOPv7xfZbtqk7/5q/PD7+8z4/bV+8WbmuW7hvtMEPPwJNNH4237phog0kEQAABJAA2A9DbuKAcr81KpHys/JXD3+zJW06mZpOv6mj2HcBCdJalWltX8iXX6RdUmAyVO4u4hYsJ2ud4v/dmmADIJEIABBBAAAmAAAII4H9c9+ro9e66c96Gmd/7NrIW1tuL+U7Zwee8yuZsc6lUm2HcnBGY79GG5v6WTbfV6V1v1ghMLt/mGUgABBBAAAmAAAIIIAEQQAABJBpwnK2WSJTMqdlYA76eT8YKxHJ42kyyp38e+HQxPfSl7svHfXgKZST1Vuox/8HyHwMggAACCCABsKr8FGAAiCO50cIM93UAAAAASUVORK5CYII=") -} - -.fileList .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAY1JREFUeNrs3E9Kw0AUx/FM/kwwxSagqdVC9649gwdw60rIJQRFUTyDK4VudOlBvIdQjJvWVWPr9I3o3gaaNOH7g0eyCUM+mcybbKKMMQ4pHxcCAAEEEEACIIDNjF/mounr2SbeSyb1UNFYb1IjqVu/JRPhSOq+c3DoeDpa+2Bm8TWYTcYXxfTdawNgKvWytTvUXiC3Y4q1D6hk4QvjniOAWdPXQE/qKUz2h0EnrnRg5aqfh9d0wDvdTY/DOKULl8iJHyXnYbxnV6X6urB0VC3HKynbWgeN2T5EiaO3d/5epVq3MTe625Mn2XeUFzRoAn7LxJtvxD4wsx1FuaaSDtbGL5G07teATzkACYAAAgggARBAAAEkAAIIIIAEQAABBJAACCCAABIAAQQQQAIggAACSAAEEEAACYAAAgggARBAAAEEkAAIIIAAEgABBBBAAmD1gLlZzJFYMb9muQV8nE1yB8TV8KyZZGT/G3NdfH5oqVM578Pzr4ylnqUuFT/ipokACCCABEAAm5mlAAMAvOdJo4EAKcIAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-empty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABBJJREFUeNrs3MFPE3kUB/D3m+lMp62kpQgEiXDw5CaYuGsUXTxtOOllr3oxcvI/0JiYaEzWv0AvHrzgcU941kQPxuxmEzl42gNshAXRpSC0nbbz871fmdpGBdoK7LTfl/zSMm0ymU/f7/febyZBaa0J0XpYIAAgAAEIQAQAARjNiIVvVv+8GvVrmeLxcJ/O9ZbHIx53Yh2SCD/xuJ/M/kCWk9zzk+nAH/Y3lm6WNpfsTgDs5/F7vGfUteRyAn/PT6h4uMkBYsCpqK+BNo9pNzU04njpfT2xUsr8eFEHvOskBiYlG1CFm49fY/He625qUFalg6vCXH1dfr3F4wqP4ci0D/FechJ94VQ60DbmtpMcNL+kstzo5J8OeJT/F33glKwh5nfchwrWiTuR/oOeBtjKARABQABGM2LShAYrT8ny50jpva/CJTVEvnU00mjKdiiWyG71o+V/yA4WvvnlfKHaayW89u87qMxJ8vrGyYt41ulKiYq5f0kFmmJOsMz9X3GrKW3cEq3nFanBS9X3S4+pJ9H6lkn1niIr+yOfIvq9puKFL54eIH9tmWIq2OCLKtU+rHCDb1uf8dInfjPHc685G1emKeG2iHf4545a+5SlttbAuu2QoPn2CKniPLnDVbzc6xvms3j/BP03/5jKFaJDnqbd9t6diPdFEZEQmLI3RgPnZ6iUmyUnPUbrb+6R5aTJTjEqvw5dnDPH8gv3KRkHXkMbE7N5FGbp498PDJ5knpMZo+ToJYMoqMWVF7TJWbibaayypzseL7yZ0FAwDh27ZrJMc9HYXKxOWW2nKTs+XRUPclTgJTNfVJSI669iGry+s93TSNcXDMk8ybqgZ4JcRzfgfXh52aBtlDKUOTdDBeeEAe5WPAMYrn2CJ5knU9ROjpjpKlCCJ1NY8FLOau2Yqdib86ZidyueARQAgZA1zjtygeKHJ8zfghbiCag3dKEBLwQNq3E34pnrLj8/qvPc2wpOz/HrVNngFoZbFoELoQRPMlRgg1Kuhheufyp7hvHGuw5vbW62ugYKRMZbpcKbauWVKlyfZYXFJ8DbqQpLG+O5mnu8J7QmVZix6tc84O0AaHYbjuTlDKX4qOxYv7bmAW8bwBoiVfu87DngNQ0YhkznAk9naWvq8QROABE7AAqY7Hn7klRrVYDXBKCg1d8wAN4ONxO2/RLwms/Az3hnzS4D0UIGAq8NQOC1AQi8NgCB1wYg8NqowvL8Qh4CIVrIQOC1Aejbx4DXDmDJHoXCXm/lEAAEIAABiAAgAAEIQAQAAQhAACIACEAAAhABwO8CGOhKGRJNxpZZYGmlXhVz7wiIzeEZM6X+kOfCk8WP75/56ysnMaV3HQHj/cWvvyj8I24UEQACEIAIAAIwmvFJgAEAn5Nwqgtf3PMAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlFJREFUeNrs3M1qE1EYxvH3zNcx09ovaUot6FIUNyJ4A+IFeAfRXINQFIS60IV7Vy7cuPUaXEtEkG68ANEYjaZNE+YjM56ZhBIxlIykMVP+D7zMJnCY35wz7zmBRKVpKuTfY0EAIIAAAkgABLCccYp8+Nf7+4t8L3VTL+c01mdTr0w9cc7IRLhp6oW/cU0s1z/1wdIk3AmPmo+iXtM+C4Cbpt7o85c9K7udJDz1AZUpz6+KAayX/R1om3rtLW1fcs+tznVgpVT+8FSRs7BWi+XdbNSeuZXqrl7ezhbW3Mfvtj6WtwsbvLuOXt/1lrb+C97xTPzZuOeZ62NTNVM7pdk+6HVxKxfEnkPTOGkGZk1kz/W38iepLK88UzBNTMULsQ+sZx0lfyWOOlgQxhJFA3bJY3FdW7TnTATcHHWU42R4K1ceoDaWg0/PJwJylJvlUa7XDyUIYjnqBTJot9EZy2Gnl69MrR3xK95kwAxv7fpDWcPrr6zc2ht+H7D/9A9AlvAslnDU/SJxvy16EEm/UUPlpNOYqX7LFaeyMQRUSWr2f6ksX7wqynYRmmYLaiZa0PkqmZ1jDsOiV6s5oqQhOtMc38yLLzMLD74N34HKUqgURlQ0kVkEQAABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBBBAAiCAAAJIAARwIQCTdBAjUTAjs8RKlXoXdFoCYjG83EypRvaL9TtB98fb8PD7DZb01EkM3gdzva34I26aCIAAAkgABLCc+S3AAB9MnrCxlmzHAAAAAElFTkSuQmCC") -} - -.fileList .ico-fromchromefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABCVJREFUeNrs3F1oW2UcBvDn5KQnbdYmTfPRLMnafdCLUcfGIhM3O2WgOPFiu/FCZBY6BooyvJAJiqNTL4ao6IV4oVJERbwYKoKiBTdlaLeVIV3Z/KhzW9O0OUuWpGnaJWnP3vd01LUGbba2W9LngT9Jc5JzyC/vef9vT0MVwzDA3HwsJCAgAQlIQIaABCzPWG/8IX2ivZzfS4eo95foWBFRXaIOWStkIIRFvbsisB6qZl/0gxmT+eDV1PCLuXRMrQRAr6gjNZ4mTa0Sb8fILfoBFTHx2Zw+CMCOcp8DVVGf2OpXNlWtcC7pgRWLYn545Q74qubwPmhzeu+MJlIs7ZEDi3LgruDhW93Fbqu9/gWbs1HOSrcNUEn1PKmJ25ellajgUkAuAB4EHrQ6N6zV9tuGl77QZ47ATs3hE5+kH4pa9d+Ne4HiWB1egL1MiYFXuCPWgR2yoygWY0k62PQ6IIdKiWwi3usdheGvcgQkIAEZAhKQgMsnC3I5q8VVgz13+dDqseO3xDi+P38FRy+lMGVwBP5v1jir8UpbM+Ra/KMzMRNw36aVaN/QyBE4n4QcGk5GR/HWqcjMiOu/nMWBe0L4OTKKs/EsAYtlVZ0N69016NOz+OlSetY2CZoYz+O17c0YyebxzqmhioUsGVCeqk9vDmBH0z9XgLsvJPHe6eisOa/z+EXUV1vxyFoXOu9rwv7uvxAdy1UcYMlz4KPrGrA1UIc3Tkbw2Jfn8HbvELaHnNi70T/reZFMzjyVXz8REaN0DE+0eityBJYMuFMAHvk9juODaRTEkDt2MSWQBvHQahfuDTqKvkZ25I2+WgLKuMVpeT41Meux3uEMvvgjjqc2+eGx//uibCY3BU1VCCgTnyiY674bU6up+OysDl00jufuDmDu5cWwvxYDyQkCynwzkMCuFjfuF02kUYy2h9e48OHOFrMrvynmO7kufFY0mWrr9K7ldlnfisU1u7DI1wKwoaYK+8OBmcc+7o/NnNaHewbx/JYQtoUc5hypqRZ8fk4350oCisilSlffCL76Mw6vgJTdNpObnNn+a2wMz3QPYIs4bSXe6ZGM+RwupOckMV4wq1iSYp787u8kr8YwBCQgAQnIELAcAO1qAds8UTRoE8sK8JauSFsUA2FXDA94I+JWh2aZXlD3pxvwQyyEX+J+ZCetBJybVkcCbd4hbHVHUWvNF90ua9/afvQkGvGjHkDvFd/yBlxlz6DNM4QdvsF5n6ZyRMrXyErmbTgaC+KYHqwoQPkNVcPRvIHd4CYiv6HKLsxlDAEJSECGgAQkIAEZAhKQgARkCEhAAhKQISABCUhAZl6AujFZoESJuW6mS8APrqZ0ELE0PGkm0iX/sH4wN3pZE/W4uO8nz7wyLOpTUS8p/EfcbCIEJCABGQISsDxzTYABAF5pTWwmgxlgAAAAAElFTkSuQmCC") -} - -.fileList .ico-documentfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNrsnE1rE1EUhu98ZCZOTSbYpB9GKgguBHelP8BFodtuuxLyJwqKovQ3uFLoRpeu/Q2CgktxJdgPTChNQtskTTI9N1hw4cKkk5nMzfPCyc3qXu4z57znTi7EiqJIocllgwCAAAQgABEAAZhNufqj9fmpCXupSbxNaK0DiX2J164hibAu8Wbh7iPleMHUF4sGl9Vu8/hZr/XbMQFgReLjrfKa5+RkO1Fv6gtaYnx+uKQEYC3rHuhIvPdLq2u5hTDRhS3bGj28rAPc84qVTT+s0IUn0LYblHb9cFm7UnpdWDqwJ+MLCd2Kq5k5PgQl5RUWr0sp1WPMK6+4JE9yRVlOLkMJOJTE68/EObCmO4plR4l0MBPfRCpplwGvcgBEAAQgAAGIAAhAAAIQARCAAJwbJXqpdHlxos4a31W/04zn6bt5VVxdV26+MB8Z2D76Fhs8rWG/o1pHX2XO9nxk4HDQHY1hdUPlgsUbz9f48UkgXqhO+5fK2/eVm8CdcKoArxUHvL/VOf05GvOFe4mXszFNRENMo5yN6sK6nDVE40s4LpUfbv3TE8lAzoHjddJJsg2AMw6GEgYgHhirB86qBeCBlDAA8UA8EA+khAEIQACirADsndXjn/O8YX4X1teQo5u0wy9TXcPYDLy9/FjZjj+9zcjceg1jM9ALyurOgyd4IAIgAAEIQARAAAIQgABEAAQgAOcbYD0a9CExpv4wq2uA77rNugLiePA0M9G+/j3wZa/d8CR25PsKeP5LxxIfJJ5b/BE3TQSAAAQgAiAAs6krAQYAXiGbDBfBqf8AAAAASUVORK5CYII=") -} - -.fileList .ico-fromphonefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjNJREFUeNrs3E1O20AYxvGxndgQq04ESfhIxaalgjVnQOq2264q5RJIIKoiztBVkdi0S9acoVfox6qoEQkgjNriNLF5J1Cpm6glJHEm/j/SqySbsfLzeGZsTWIlSaLI4LEhABBAAAEkAAJoZnJ/fwg/vjL5u9Sl3o3pWCdSh1J7uSnpCBtSb/3ldeW4hZEfLOn+rkWXje12eOpMA2BF6mi2vOI6efk6SXvkB7Rk4POKVSWAddPHQEfqvVdaWsn7xbEe2LKt3skzHXDfDSqbXrHCLDxAXuQKpS2vuKBHpfRmYZl5XXndldJTcM2Y5UOhpNxH838upVSXMW/coCpnclFZTt6gDhhLx+tMxDqwrmcUy07GMoNN451IJe3LgFs5AAmAAAIIIAEQQNOSzvPAJFY/z7+o6/BExZ3rh/WA3IyaCWqqMPfk9kFdFgB/nH1Wvy6+DueOWE6APhl6i4pffpaNSzgKvxnR5sQCxt22EW1O7hh4l/Lq86G00/p0zCzMMgZAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAGcwqS6tSPNLRlGA9qO23cz0L/2y/RD121mBtALHvfdHzhor/SCWnYA/fmnSv9YMAq/S0+MHtibPcFbkjZXMzQGWrbyy2u9YhZmGUMABBBAAAmAAJoK2Ey6HSTumTuzpgY8iC6bCsT74WkzyaG+lXvdvmq5Ui/l/SI8/5WG1AepHYs/4mYSARBAAAmAAJqZGwEGAN+Yh/QHkxJLAAAAAElFTkSuQmCC") -} - -.fileList .ico-mix { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhFJREFUeNrsnL9KA0EQh/fukjtNNBc08S9oJSJo5TMItrY2CnkJQVEUn8FKwUZLa19BO7ESKwtRTBASEU1Mcs4GtVFRA5u44fvBcOF2YG+/m53Z4SBOFEUKNS8XBAAEIAABiAAIQDsV+8mhdLpsy1pyYrstmutabF9sK9YhgTArtpMcmVKenzA+WVR7GS0Xb1crpTuvEwBmxY66M2O+F5flRBXjEzqS+IJwQAnAnO050BM7CNLDY/Fk2NKJHddpvDzbAW77qexcEGapwk1oIZZIrwThoM5K7avCUmV9ua6L6XI7as3xIZFWfm//+1Zq6zFm008NyJscUo4XtygA6xJ41X9xDszpiuK4UUsqWCd2Itl2bwNaOQAiAAIQgABEAAQgAAGIAAhAAAIQARCANqgl34Vfnu7VY+FCVZ+Lnx+gK1TJzKSKd/cZ9bU6Ah9uzr5cpJa+r8dN+1odgfVauXHNTMx/GitcHn+Mm/QlB1JEAIgAaGkRcWNdql59biT278ZN+1odgT2D08r1gq8fQO7rcdO+puQUT5ai1PgMe7EJla7OyYG0crRytHK0cgiAAAQgxxhaOVo5WjlaOYoIAiAAAQhABEAAAhCACIAABCAAEQANA8xHtSok/qg3ZnkNcK9czCsg/g2eZiba199ENioPBV9sUX4PgedXuhU7FFtz+CNuiggAAQhABEAA2qlXAQYAvkAprthin2kAAAAASUVORK5CYII=") -} - -.fileList .ico-musicfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAexJREFUeNrs3L9OwlAUx/HbFopCBKKAKImJg4OzPoCLiaurkwkvYaLRaHwGJ01cdHR28An0EYwjCREYwMTI33qukcVJVHq59ftLTiAMtP309B5yB5wgCBT5eRwAAQQQQAAJgAD+A8DW/e4kX0tZ6jykY1WkLqVOYhFphDWps9TiqvL85NgPFvS7pXazut9pPXtRAMxL3UznlnwvLpcTdMb/2LpKJTIFJYBl13I8T+oqkV1Yiqcy4a59rvNx82wHPPXT+c1EJm/sBGwG3I4ls3uJzLxelYydREwmqy+vh1J6xJZs0RM85c/MDR8lc+chdeynC3Ini8rx4hY14EAar2f+RurfT3qiOG4QygSLWvQamDf9GNgOSAAEEEAACYAAAgggARBAq2JsS7/xdKeCwfh2U9zYlJpd3ohuB44TT2fQe4t2Bw6TW9n68++sP96yBjJEACQAAggggARAAAEEkAAIIICRTCjbWc3Kg+q+NujAnyaqeKF1YJS72/o10HR3M0RsAPSTuZE+Zw38knRpnQ6c1JjubuunsOnuZogACCCAABIAAQQQQAIggAACSAAE0DLAWtDvITFiPs1qGvCi3awpEEfD02aSS70jfdR5qftSO/K+CM+3UpW6ljrgHyx/GQABBBBAAAmApvIuwAAZ2pRkuq79SQAAAABJRU5ErkJggg==") -} - -.fileList .ico-picturefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+tJREFUeNrsnE1PE2EQx2d3u7vttts3WlqKKYrBiO8Rj179AF49mfAlTDQajZ/BkyZc9OgX8O7BA0ExctGIECmlon2DvnedeaAQA9JW0227nX+YLIGQ7v52npn/PLtBsiwLWP8uiQEyQAbIABkgiwEywBEAmH93d5CvZR7juU2f9R1jAeOJyyGJMIfxzJuYBUUzev5hVqM2Wclt3q/mtxQnAIxivPZEkpqi4uVY1d4vWxlAD4wDApyXhxyegvFSD04kVW/A3tonS+LmDTvAp5o/eksPRPt2AsMM8LbLCN7TAzGqSn07CRd2Vg2PDzGoxU4OCz2EB5o51lpK/TsPjMeafxzvZBwkRR2iBGxi4tX7fyPJP1FHkWTLlg7mNFENjPZ7GQw7QBYDZIAMkAGyGCADZIAMkMUAGSADZICsjtXzh0r1SgGK6WU85u25IN0PvtglcXREBtoJb++G5fEzPzorA0n+xBxo3t4+/KkUUlDYfG/rDbOhBlogya6ewyPp5oTzamBbvFYD6qUsNOolUN0hUDQvN5FO1ajuQD61KI4tuQNJ8I1fYICdiOpVs1be65qaCaVfX6GcWwOXOwBu/3A8Ye2bD6RCT+EJnUZYpwQ0M34FZEXHZrDBRrqD4icO1GAOJMn4peDvmgywbe1w+0F2uXHZru7bDgt2f36GRm0XVCPCNbC9JGwWF7EOLkF27e3BT1VPCIzQGQbYicgbBpM3oVpMoY0pCxvTqZejzt3Ev6GMdemmyGaKkfOBiurBRjLdsSmnJV/KfhPwjs7B5ugB7MYzFtIfoF7OYf0MgieYRNPtE1lH42KjWoBKPsUA/zbjig0CSUKrc/XIMm/tvBjhGdj+8sb5AKs7Gax76T+Gfsom3YyDRh1Yko/srtCI509cP7HOCQvk5Ays7W5DMbOCy614eNGKSm+9C1BkoBXVACNyDnRfXCzN/MYiKLoPAokbA/n+om0Ay9k1AY8ageaLYQ2bQssSPqxxtRKOcevYIFahkFqCImYTZRs1CzN2eWBf/rQHIE4dxcwnHNM08EZnj7Uq1I29mHme0BTsbK1ApbgpvKI3ch6N9dho+0DasiIYtKlKM++JoxHOwubENRCGhEY6abAf29h2dkZ4ui28Y7pCt7fKuQCpOfS+zq4fjImOWMLUGFoqpJdF2HJRNk4kPc1A8mVG+KzwePbt8gTAh13bERlIXdcYmxHhVPGbCQyQATJABshigAyQATJAFgNkgAyQAbIYoN0AM1ajziS61D6zDAF8UcllgCF2B4+YoRZoQ/VRtfBDw7iD38cZT0eiZ66vMB7wf7D8TzFABsgAGSADZDHAfum3AAMAO2tj3VmRR4EAAAAASUVORK5CYII=") -} - -.fileList .ico-videofolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABSdJREFUeNrsnNtuG2UQgGft9a7Ph8R27KQyLVSgCCICBSTENQ/ALVdIeQkkEAjEM3BFpd7AJQ9RiQsqJGgJIIFapS0hjp3E8dler73MjO3QSNheO+na3sxIv7KKvPNrP88/x00Uy7JAZHbxCAIBKAAFoAAUEYACcDlFneWmyr2PFvFZdnB949Be+7ju4PpSdYkh3ML1dWh9E7xa8LlvZnU7G+1y/hOjUvC6AWAK1/eBZE7z+vBxLOO5b6ig49NjaUCAO8vuA724vtXj2ZwvFHN0Y8Wj8Je37AC/0qKp9/VYSqLwDPKBGox/rMfWyCvNLwpjRNXw52e4KLRuLE36EIyDFlkdHqW5pjFfaNE0fpMZULy+JTLAHhqeuRB54A5FFMVjORLB3FiJpOZ9DKSUE4AiAlAACkABKCIABaAAFIAiAlAACsArI4szVLJ6YDSOwagXwGyVwTRq/DsWxQOqFgbVHwMtlAYtuNqf7AhAZGR1oVnag+bpHo0LR8I12xVerfJTbvwG4tchkLgOiuK9ugA7zRLUDh9At9Ps+xPVD3okgxaWYkiqHiV6CK4Kva4BRq2A6xCv29A4/othRjKvgy+QuHoAW5V9hLfLgAhccPUm+KPX/ueTygAkINgkQHqTwdURYM9sQfnvexBeew3v3bg6ANvVA4T3K1/r4QwC2EKXZvcoKuCP5fC+LFTyv0AH/SbpUtAn6pGs+6MwBYjqAF4g/gJEsttTwHsGIx7x2MZbZ1ZLOs3WqdsBWlDN3+egQJYXSm1eUJ+C1vsq+Cgqo85q/gE4PSN2FCD5rm6nwT4vnNm6JK0KRDPbbJGkm/ZwJ0C0kMbxQ74MJV8Zm36cPvkBaoXfOMWxe5yDiRf5unHy6L/80U0AjcYRpx/DVGWsn8S0hSyp/PRHjrR2hPwpgaTPG/WiCwHWj/pRN7LOx85WwMHEuYTWSJF2shl6ODL3v6xj9wEcRkgtlJzu5GN1Ut7/CY/mw4mf9QVXzu3lKoDk4Dnx1CIzRW+qPCoHP6N7G+0XqV5+di8XAbTwwc0zhz+zG8AyrlbYHRNMtEG8Mh1LZxwCeHnv3li2I6ziriPsGVrHqI6LDaGmQXhM8m11jQtb+eIC9PXfnjeN6kz3BzDPi117h9OgkYGKeogoXl/IfQCHLadpczSypuj6LUy+X554LDuNk3N7uQqgFkqddWLsOngCkci9d3bvpEqnXTs4t5cTojpngSvg1ULQNeoMsZ9Qj4AdXgNVj0Bw5SXbwaB5+pj9K+1Be7mymRBI3OCf9aM/x9a50ewbCO+mbXjUrW6UHp3bw5UAqWus+uNcr1YP7l9ak4JaZGR9pNvpzrTD/UAFrWubUxqavtWLf1wc3uEu18qkk3Q7lf/NCWB/cBRdf5PbWeS3KljnjivPJtXI7eo/rIt0jktxXAOQIxceNc7pvDq3uUp7d6FVfmIvOqPV0Rj05PFd6DRPWAfpIp1zeRaYk9CQPJ57l2cZPBgq/M4NVy2c5uG5R9UxIQ5S7QZds9nv82EtTHlkb1BxUCs/srY1F8ubO8DhcY5tvM1W2MDIPBycT2rL05gziIk1jznnLAvxageB0HJJHrAbmAx3WmXo4fXQ0ihAeHwB8NGrHeEsWmYAFkUW6g+uCQzVvAFYHpG3swSgABSAAlBEAApAASgARQTgfAAWra4pJKaUAbMiAbzdLhdBIE4Hj5ih3KFmwudG9UjD9SFeZwSPLcnj+g7Xp4r8I24JIgJQAApAEQEoAJdT/hVgAK5f795U9dlNAAAAAElFTkSuQmCC") -} - -.fileList .ico-sefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/9JREFUeNrs3G9IG2ccB/Dv5ZKLuUSN1miMq3/atVU6aauu615so6ulY+2gK0ihrFhwZRR801LGcLJRcXtRxmAwBhtzyFYHZUPYi/XNBlspdK3rnznHWitpFfvHNkprUqOJJrfnuZYiYiA6tRq/X/jhn3uSI588d7/nzqBiGAaY2cdCAgISkIAEZAhIwKUZa7IDgx0HFvtrqRP19QLt65aoVlFN1hSZCJWivnD6yqBq+rzvzIiNF0SGB96PBu+pqQDoEdXuyCnUVJt4OUZ03neoiBOfPTMXArBuqZ8DVVFtdnd+oc2ZuaA7ViyK+eYtdcBmLcOz3Z7pWbxNpP5c0bzs+GPL1v/7FG9adfd79sw8eVZ6aoDK8PlaTXz9QJRsswWJBjbEf1tMeBB40NJXwJqmPzW8YF+XOQOPaRm54p30QlFtiUdfnzvAjOLKOXiWuJh4E4tiHVgnO4piMRakgz1aB0SRKpFNxPO4ozC8lCMgAQnIEJCABFw+mfPbWT69FNW+d5Cl+TAWC6F7+Cx+H/iGgNNFVWzYln8QG7J34BnnevN3ofEhE6zN/y5ixjgcajqy7D7cj9wm4OR4HWtwcO1XyHOsMn8eivTjzmgP7BYduwsbsDlnD1p6DuHu6HVE42OcgZOjW92oLzsBt+bFucAPaO9rRnjigblNzridK4/g5bxa1Je2ofnvakRiI2wik7Mt/20TTx6qJ/xHn+DJjIrz3o+9x/DrnS/NQ/dVMZZdeEo2Zu80oU7d/CzhmJ/7PzVhN2S9xi48NTlpK3F6oBXHqzq5DpzNg87c/Q7lWduTGpvKDWTWM9Af/NNsEg0XqxAcD3AGzjQVOW/AoqioKWlKOKamuAmfb+lLeqYuK8ASVwX6Hv6FTdmv463Vn8CuOp9ss1nSsKeoEa94a9F1/xezeAhPiVzrfXSlGvtWH8cWT425aO4f6ULciKPQVW5eoXQMtuPkjUZeC0+X2+FuqBYNLdcO4cXcvXgpbz+KXBvNbTdCl8wmIwF5MyFBvvUfRuWKXbg8dAoXBn/CH/dO8m7MTBIY6xXrwF4wvB9IQAISkIBzEodY1lQ418KtutiFk9dXsF4vwWZXKZ7Ti2FTHj1dz9gtnA/9i86wH6PxKAGnZk1aAapc67DJ+Sx0cek23XZZe42t6Bzx48JIN/4J9y5vQK8tG88LtBfSy5I+TOWMlNCygrEwOh5eFXUlpQDlJ1SNjKJydoNZRH5ClV2YyxgCEpCADAEJSEACMgQkIAEJyBCQgAQkIENAAhKQgExSgAEjNkGJGeaxWUACtkSGAyDizPCkmUir/MP6h9HQoCZqn/jeS56kMiDqe1GNCv8RN5sIAQlIQIaABFya+U+AAQBLjB3QxtGdmwAAAABJRU5ErkJggg==") -} - -.fileList .ico-access, -.fileList .ico-mdb, -.fileList .ico-accdb, -.fileList .ico-sql, -.fileList .ico-db { - background-image: url("../img/ico/ico-access.png") -} - -.fileList .ico-c { - background-image: url("../img/ico/ico-c.png") -} - -.fileList .ico-cpp { - background-image: url("../img/ico/ico-cpp.png") -} - -.fileList .ico-cs { - background-image: url("../img/ico/ico-cs.png") -} - -.fileList .ico-js { - background-image: url("../img/ico/ico-js.png") -} - -.fileList .ico-fla, -.fileList .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAz5JREFUeNrsnL1vEmEcxx8OOE1LEUyKthATgqYJ6eKEM8QRZ1+WmrropJs6mLjof6CJL9HFl7nMJY7SmLg0JIaBRBKrkBhDW2Io3Hm/B69eT6D3Qjm4+36TJwcctL1Pvr+3p+F8siwzyLoEIABARxUYdrJcLovK4YGyVpQVd8tFxxOJp8rhltH3nwiHrQFU9DASidyNRqMsEAi4Al6lUmGNev3mfCzGzEC0CnCV4AmCwCRJck3YnV5YYN+3tkYC8bAcOE/w3KZjosghkhOVp09QRByE6OkqPAqInm9j7EJEH2gTIgDahAiANiECoE2IAGgTom/YdpYyC8upVMpVcBqNBms2m6Y+k06nfVZHOddJme1ZKBTqucf3j8ugx7VaDSGsVTAY3N8Y0Uaf1Y1lzzlQhUjqdrv/uW6QEwGwD0QC1Ol0+NEsOM8D5Bf/N5S1W3UT78AfK9dNvf/U61d9P69/3Q5ECmVa+jBGCBuU3+/fd6IZeI4CHJWDRgmRoGmrsRGImES0MATBtAMBsA9EM//GAMB+862urUERsQgRDhwDRAC0mzOBAACdHQed+sWHjXST1mjDgW5z4LQ4DA4EQAAEQAgAARAAARCaqEb6KEfBcTbpcOC0OdCuOyZtBIQDARAAARAAIQAEQAAEQAgAARAAARACQACcIg39tuaXbA53JlO0VFy3/m1NURQ9Da/dbiOEj1Jj3dIP5/MscuVy33P1R4+ZmEzy81+vXhv6c44vL7PY/XusU6+zb7fveAegqkGACKARzVzIMGl3lwViMQ7z9+YmqrDhP3h2loWyWdZcK3AHEky0MSZE8Eg7xSJrlUpsJpPhUD0VwmfevjnwnGD8fPHScPi2PpZ4CNOR8ip3ZKGAHHhoS6XkSFo768Vei1Gt8kVQPQXQcvjmeuF78sYqX3q4BBMAhxQPynfktF/v3h94PfH8GXehEwCnpoioxYLynlZqLqQ86EQxmUgH6ouMCkrNeXq1NkrcgU4Uk+GbCflLXXFvz9PjXjsYlJYKa35LISyfO7tBd7Pw4u3i6Zr5tadSn6yH8OLixY4kf/BVq+fZ9ra3nDg3J8nJ5GeWiOcshzDkwlEOAF2mPwIMAMxjFWM7SdA6AAAAAElFTkSuQmCC") -} - -.fileList .ico-htm { - background-image: url("../img/ico/ico-htm.png") -} - -.fileList .ico-html { - background-image: url("../img/ico/ico-html.png") -} - -.fileList .ico-java { - background-image: url("../img/ico/ico-java.png") -} - -.fileList .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA9lJREFUeNrsnE9IFFEcx9/srlMbq+2fXHZdD8pG0WIHKQ92iP6QYhRFtzyVHiI7FJ2KIAiMTkoeMoSwTnoqhKLQQ0kHgzI8FAaZGIiu7JKKRVuLO9P83jrjpOu4M7Pu6rzfF5aZffNmYT78/nzfWxhOFEWCMi4bIkCABZVD6+Lo6CgvHW5LnwvSJ2SVhw6Vlz+UDi3Zzt9ZUmIMoKQ7brf7hsfjIQ6HwxLwxsbGSDwWu1zq9xM9EI0CbAZ4NpuNCIJgmbQLBINkJhrNCcT1amApwLOatvE8hQiRKH3txCZSQIhMd+FcQGTexpiFiD7QJEQEaBIiAjQJEQGahIgATULktLazpLWwGA6HLQUnHo+ThYUFXfdEIhHO6FLOcpLW9sTlcqWjh1vmstb55OQkprBaRUVFysaIOvuMbiwzF4EyRFAqlVoVdWtFYt4BTsV+5AVGyO8zDBEALS4u0qNecBsO0OiD5VNyKqu36vSCZN7GAES73b4qjVdGJdZADckAIRIz1UIEmCVEgKbuxpjCOgW773ojEAFmgKjnbwxmbYzm+nYp+rIx10zbmGwgYgpvMEQEaLZmIgIEiAC39FJwq9uYQrsCtDGYwggQASJAFAJEG4M2Bm0MCgEiQASIAFFoY9DGYApjDUQhQASIABEgCgEiQASIAFEIEAFuWmluJnQ8GkBCkrraI8YAgnieZxpeMpnEFC5YCudKh2urSP2xg+TW3SdrzvF6iknd0QNk/74KZezbxDQZePORTEX/35wNBX107u7KsnXnWgLgegIgFxvryOzcT9LZ/ZxCAKBnGmpJS9Np0t3TT8YnonRuuDJImhrrKbDWth6S+JOk98tz2zqf0t9hqgtDNCUSSQneCyWCAMLjngEK6mzDIWUunMMYXAN4ILgn/f0vjXamIhCiB1Kx//VwxusfRr6S8+eOkKql1IbI7JdSdaUAZmtbrzVroJY87uJ0FM1krl3TS+Net0sZG5cikKkmspGC2qluJpD6UAeZATg3ny74oYBPaRRqlQXS/+7Nzv9aHgsuz4Xap9THk7UkXFHG1lIOGgA0hZrqvRmv11TvoVH1+ct3mrrQKDLNdW7n8w5v03Rh8G9OJy/ZkFO0qcjNQk7PvldDSqPofTZIvSJcA2hqGwT35Fuaby66dP2+mIulnGykM6nv5RDttGaN9Nt3n+i8emk8lzUQlnJd7dc4QwCv3nyQEkSO6eWejROFjntX7IZSuDzofQ8vp2HxdfHwzPDsoYBn2HAX9nldJwRRHJyJzVf/TiSZisQdTl4I+H0ju3zFxw3XQNQW6cIIkGH9E2AAawxpPqroZK8AAAAASUVORK5CYII=") -} - -.fileList .ico-mht { - background-image: url("../img/ico/ico-mht.png") -} - -.fileList .ico-php { - background-image: url("../img/ico/ico-php.png") -} - -.fileList .ico-url { - background-image: url("../img/ico/ico-url.png") -} - -.fileList .ico-xml { - background-image: url("../img/ico/ico-xml.png") -} - -.fileList .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLNJREFUeNrsml2LE1cYx59MZpKNSTYb2tUsG0trqsX0YrVUL1pvpNSXFgr1poIgpUVEP4FXgvQ72ItCL7wuxStxlQVfSxFBS+lCXSPupl2XTeu62d1uncyL5zmzEyfZ3UlmEpJJzvOHw8mZOSdkfjxv52RCpmkCyb8kQkAAuyrZ7ebk5GSEdedZ+5q10X556NFs9nvWnW12fmpw0B9ApgtDQ0Pn0uk0yLLcF/CmpqagND9/ZnjrVvAC0S/AbxGeJElgGEbfuF1mZATmnj1rC8RGMXAY4fWbopEIh4iWyIYXKYl0EaLQWbgdEIUvY1qFSHVgixAJYIsQCWCLEAlgixAJYIsQQ27HWWwvbOZyub6CUyqVoFwue1qTz+dDfrdyfSe2t4dEImFZT+g1l80+F4tFcmGnFEWpHow4vc/vwbJwFmhDROm6vs7qNrNEArgBRASkaRrvvYITHiB/+DVXdh7V9bwFlsdPgbH0F0jJLAwe/qEjENGVsdW7cTMAA5VEtH/+4PC4VbAex51QOBzmzSu8wAFUn45bDzS0o2bcSYjOeNhTFmhWVqBSvMk/x/ZYp+w4xuudEp6+96wFqjM3wNRVCKfeBvnN93mPY7zeSSFEL39jSEFzX2Vkf02vTl9vav3yrXO8tUP1ZU3gAeqLT0FfmLKyYmZ/Ta8//5Pfb/jQksJbu9QsRDlI1heKJEF+Y7f1w1iPY1Nd4vdjY6ddvyN+4Lu2/66eSCI8zk1PWG6b+RBNyTYpa8zdeILPC6K6DrDy911uZZbb7qt1j7Ux3sd5bnrx01HehANYrfUcFlfdrzosspM1Yc8ANJZnQZv/rSbm1cQgR0zEeTifADqtz1Gi2Fl3XZZzXG+2pBEDoGkwt3wNxK776uW8zuebwXrJqWtlTGXuPhir/1bHS9fPNHZ5Nh/XbQZbKAv0mxSClky6YoHG/wtQmf3VShTRFAx+dglC4YhrrVi+chLMl4t8Ha6XBtLiWqAzlkVzn7vC45DZ/Wjuiw1jp6AAx9eDaSAn6CC5cccBaqXfq/Vc5J0j3IWb2peyeTi/vn4UDmC1lmM7jOjOLz2t5fPtncnMhHgAnafOSvYASPGMtx/L5uM6XgZ1+LQ6EADtU2fUwHtf+foOe103Tqu7D3At+MvbPqj+ceRVuA7XByWZCPd2llcVCgXXt7Po/cBePo0hgCQCSAAJIAEkgCQCSAAJIAEkEUACSACFk+v/wvKd0zB9hwBB/rY/gKioLDbAlxq5sHgxcPjgBcgevwzx3KFab9g2tuF1AuiMKYkMB2VUlmHLWx+TBXoVWhfCW3x4iYNEoATQC8B3D8HK42uwWrReKo9t/4gANqsYc1lJSXB4hrrC+2T+GAFsVluYtWnLc6A+L/Dxf8VfONBYgGNhYKo8jHWx7RYozLT1YFdn7hJAV/ddi3WzP5/g7msrNXYSkruPgRS5SC7cKPvasc8pO5nEc4cJoFvyQBfGmFcvjIfqQoEDlpSBwAF0fbno0Y9H9AisSCEQU0hGhbix65urYV8WaKZ23dMN64tEhMefPbXzvu8kYsZHP9XAuKGXn+yFypJYBw9K0jDTOx6Y8ewnvl2Y1INbOQIomF4JMACqJM9nuTUMmgAAAABJRU5ErkJggg==") -} - -.fileList .ico-bmp { - background-image: url("../img/ico/ico-bmp.png") -} - -.fileList .ico-cdr { - background-image: url("../img/ico/ico-cdr.png") -} - -.fileList .ico-gif { - background-image: url("../img/ico/ico-gif.png") -} - -.fileList .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7lJREFUeNrsnEFIFGEUx9/szu66uOvumCtuSlREkB2ChII6dEgisFtCEQVBetBD4KU8lBAFURejg0HSqYgKjwke8tBBrEPSaYMMCrRW1NpCxTZmdvrep7vsrLObO+vubPO9P4zfOPNWmB//9733fYsj6boOJOtyEQICaKvkQjdjsZiXDQPsuMiOZqc8dHNLywM29G42PlRXZw0g041wONyvKArIsuwIeNPT07AwP98TaWyEYiBaBXgJ4blcLkilUo5Ju6ZoFObi8S2B+K85MILwnCaf18shohPZr0NURGyEKHQV3gqIwrcxpUKkPrBEiASwRIgEsESIBLBEiASwRIiyaHDq2LoWl3Ob1dfZ2Z5Qa2svAVwXW9tDIBDg55IkZa7nO5+ZmaEUzpbH48lsjGRvJlvdWBbOgWmIKE3TNrgunxMJoAlEBKSqKh+LBSc8QP7w66mcvVVXLMiqnwNXJ19A4v45PpYLotvt3pDGua78bwEm348ZxnIIAZpBdMRKxHfghGEsN8Rs5zmiiPiPnOVHJZT+6gJbGsc4sNJCiMV8jUEATbTZAlLVKYyVN1fK5acVhUgOrADEqnVgttvM3Fg1cyb5jAASQAJIAEkEkAASQAJIIoAEkAASQFIxqrrdmEI7L3bvEZIDRXCg3Y4iBxJAAkgASQSQABJAAkgigASQABJAEgEkgATQwSq4H1j78DTMCQ6oFn/c+2AN4FqET2yCapJS2LYULod8uw5C5MJdSIwOwsq70cz1UHsXBLP+H+RP/COsxl7D0sRzw+fN4lamRg1/y9EAzdTYPQSumoAB6rbO6xA63s3PESLeb2Dgc+OUU32gdPTx67mwhajCwaNnwBvdCz9Gbhlc9H3kJiQ/T7H7a26rbevgcYtPrhriEi8H4df4MIctK1HxAPpbj/FUxVTM1cLjKzA/3MvdhSAxTk3EN8SlgSJk4QCiq9Sf8fxFcB2YqyZoChmV+r3M4r6BHBbQgbQSKVHoKnRhvvmxZeAVuPxB5rKlvHGY4rKyvaCTHQsQ5zVsbczg1Oxu46mJabw08YzPl2aFIj332dHK2A4QHxoh1XdeMxQB7BUR7K/xR5k4dGvD+TuGOGxjsAJjJTYrMI7vA7EAYKUNtXfzfg6PdGpjv4cONcZ1mcbZ1UhLhV4486X/kCapSeZSCcSUDrrHn9p5+43bUgpr0f2TkNLwtT4CsmPPzJ5da9o3aTmF9fodJ1WAMffCp8PS8qJQ75jRAw2qFtnzFhlYTmESNdIEsNr1V4ABAKATVWjNArx4AAAAAElFTkSuQmCC") -} - -.fileList .ico-jpeg { - background-image: url("../img/ico/ico-jpeg.png") -} - -.fileList .ico-jpg, -.fileList .ico-JPG { - background-image: url("../img/ico/ico-jpg.png") -} - -.fileList .ico-png { - background-image: url("../img/ico/ico-png.png") -} - -.fileList .ico-psd { - background-image: url("../img/ico/ico-psd.png") -} - -.fileList .ico-webp { - background-image: url("../img/ico/ico-webp.png") -} - -.fileList .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7JJREFUeNrsnE9M01Acx9+27g9jIkOZTMAYiTEZF4melJtwNJ6MHgwx/jlovHhSL0ZOejNGo4l6Mh7wZMSjHDyoB4NycolZNJpFZhiJSmRs0K6+X6FzsNKtLdtK+/0mTbf2tUs/+/5+v/fe8uaRZZlB5uUFAgBsqgS9k8lkMsB31/l2mm/dTnno7p6eB3x3sdb2W9vazAHkGm1vb78ajUaZIAiOgJdKpVh2ZuZCZyzGjEA0C/AswfN6vaxYLDom7LricfYzk9kQiNVyYCfBc5qCgYACkZzI395HEWkiRFdX4Y2A6PpujFWI6AdahAiAFiECoEWIAGgRIgBahOjRm87iY2G5r6/PUXCy2Sybm5szdE0ikfCYHco5TnxszyKRyLJ7PP+5rPc6nU4jhMvl9/tLEyPl0Wd2Ytl1DlQhkiRJqnDdek4EQA2IBEgURWVvFFzTAM4/e8zEb6m6f46wey9rPXFOv81KKJdP1RkF2fAc2Ah4Rj6HIPp8voowXutK24VwZOQS88V7N/y+UibN/j65Z+gaFSA5USsX2hJgPeBZuS9BJGjl1diWIWxn0ey7UQcCoAZEIz9jAKDW+LbGAuLqfmAtEOHABkAEQKs5EwgAEAABEAAhAARAAHSl6jaUmx97yMTvX+BAs3IDvLo6sBGyg8s3dQ60g8tRRADQoQCFPftMnUMRWVHr8TNwoO2/fRu4fFN3Y+zgchQRAARAAARACAABEAABEAJAAARAAIQMSXe1ZubyKfwzGVf89lPzqzUDBpY9OVGLVRYhIoQtqmkTqltGLimzxrmXY6zw4W3puJ8fo1VMayXnc2zh1bjSdr02pOKvWfbnzqizAXqj2xV4BCXQP7AKoCparrX09fMq4OGjJ1cdW9vGNVU4eOBwyVEEkoBWU27iRcmhru/GBA8eYoXJd2zx09RyoerfjxxYqyhkPaEwW0x+VFxIEEODwyz/ZkL3uvDQMWVPIeuLblNea+XBBe7Uavfa1AD9iQEl0UvTy3/msJScUqDSpjpSCw7BpoJD16oA7ZADGwqQch2BIkVv3K0AWw7QDnBsB1DNdb9vXVEcpaqFh2docIg7LIyxcLXqSy4rh6f09nk+VIsLiohO8aAQXpoYrzhH+ZA2ApybfV7zPbWKSKM70rqTCdPXzkvBQt7Vw71CMFTcefORz1QIi1273kscsBunZOiZ6dnFrt5J0yEsdcSG87L8Wpj5MeBZmHeVE+WW1qIY656SOnYcMR3CkE2HcgAIlfRPgAEAIIZMFb61acMAAAAASUVORK5CYII=") -} - -.fileList .ico-avi { - background-image: url("../img/ico/ico-avi.png") -} - -.fileList .ico-flv { - background-image: url("../img/ico/ico-flv.png") -} - -.fileList .ico-mkv { - background-image: url("../img/ico/ico-mkv.png") -} - -.fileList .ico-mov { - background-image: url("../img/ico/ico-mov.png") -} - -.fileList .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/xJREFUeNrsnLtv01AUxm8SO0mbUOKKhj4RaoUqpQsFJuhGOyImHhNCBQYQf0BZECwgJgYQSBQxIIaiTsBIBwZgQIVOREIRCBTRogaBVEjzsmPucevgJm4aX+fhXp9Pct3G17b883fOufe6jkdVVYJilxcRIMCWSqi2MR6P++nqKl3O0qWPl4vu6++/T1eXam2/s6ODDSDV9UgkMiVJEhEEgQt4iUSCpJaXL3ZFo8QKRFaA5wCe1+slxWKRm7Dr7ukhP5aW6gJxqxzYBfB4U8Dv1yCCE+mf97CItBCiq6twPSC6vhtjFyL2A21CRIA2ISJAmxARoE2ICNAmRE+16Sw6FlaHhoa4gpNKpcjKyoqlfWKxmId1KMed6NiehMPhNfd4/nPZ7PdkMokhbJQoiqWJEWP0sU4su86BOkSQoigVrtvMiQjQBCIAkmVZW1sF1zKA6acPifw10fDzCHv3kdCp89XbrIeycarOKsim58BmwLNyHoDo8/kqwrjclY4L4fCZy8TXM1D34ypLSfL38V1L++gAwYlmudCRABsBz85xASJAM1ZjR4awkwWz71YdiABNIFp5jIEAzca3NRYQV/cDa4GIDmwCRARoN2ciAgSIABEgAkQhQASIAF2phg3l0jMPiPztMzqQVW6A11AHNkNOcPm2zoFOcDkWEQTIKUBhcJhpGxaRdYVOTKIDHX/3HeDybd2NcYLLsYggQASIABEgCgEiQASIAFEIEAEiQASIsqSqszHS9A3yy+WAJPhx+wkbQJDfwmtPPCq/xUuIGMKNDOF6Kjg2TtrGjxP5yyfyx+RNItgGbXLv35DVFzNEHBzW3mYql5pdJZmXz7V2xv10ZeaekezrOf4Alk5IwXilXaT4++eGzwOHDpu2h9e2ChS6rh0Uavux09pnABngAXAAGjh4RNumZjIlwNxVYXCQf2T/xjw7Mko8wfYKqGZapQ4DATy4GcpisgRLX/t6B/h1YG7+reYUY5iJsVEttK0qPfuoIk2ACh8X+O0H5uMftBAW15+agfPAgfkaL7qd5jwNkgE43BDp2p1Sji0w3Ixt40AIOVhECg0uFHIfhDUABJDlKi8k0BZynjHcIXRhgZsSOjmpPa0rdyc3AHUXBscmtGrqjx3Q4AGYWopINUE7SBEQyulZjodycJEQugAREj5rzopM3dLC1qxQcT0W1kMWnAKhyJqz9GPo+RRyIaSEfBOLSMv+M6EQX8t5dvprkAvL86TeEW+Wqn710+KVC0ogl3X1cC8XCBZ7b077mEJY7t7zTqGA3fhl8XDNcO1y98A8cwgrndGJrKq+Epa/j3oyaVc5UW0LFeVo34LSufsocwijHFqFESCqpH8CDACOqX4es1WjewAAAABJRU5ErkJggg==") -} - -.fileList .ico-mp4 { - background-image: url("../img/ico/ico-mp4.png") -} - -.fileList .ico-mpeg { - background-image: url("../img/ico/ico-mpeg.png") -} - -.fileList .ico-mpg { - background-image: url("../img/ico/ico-mpg.png") -} - -.fileList .ico-rm { - background-image: url("../img/ico/ico-rm.png") -} - -.fileList .ico-rmvb { - background-image: url("../img/ico/ico-rmvb.png") -} - -.fileList .ico-swf { - background-image: url("../img/ico/ico-swf.png") -} - -.fileList .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABE9JREFUeNrsnDts01AUhm8S59E2lKbQ0PQl1AgVpQsFJuhGOzMhFlQhXhKoCwuPBcEEGwMIJGBCDLDCyMICAyqEpRFV1AqIaFADKqpIX/EDn5vc5CZO09hpXNc+R4qcHD+i+/n/z7Fv5LgURSEYxsONCBDgtoZQa2UikfCpi1vq66z66rXLoHv7+h6riyv1br+7vd0YQDXudHR03AiFQkQQBFvASyaTJLOwcLkrHCZ6IBoFeB7gud1uIsuybWzXHYmQX+n0lkDcrAZ2ATy7hd/noxBBierHR9hEthGio7vwVkB0/GVMoxDxOrBBiAiwQYgIsEGICLBBiAiwQYiuWtNZ6r2wEo1GbQUnk8mQpaUlXfvEYjGX0Vs524V6b0+CwWBePa4Sl43ep1IptDAfXq+3ODHCu8/oxLLjFMggQkiSpFHdRkpEgFUgAiBRFOlSL7htA5h99YyI35JN/x5h/wHSdvpC7W0KVuan6vSCNL0GmgFPz/cARI/Ho7FxpSotZ+HgxCTxRPq3/LhSOkX+PX+oax8GEJRYrRZaEmAz4DVyXIAI0PhubEkLWzlg9l2vAhFgFYh6fsZAgNXub+tsII6+DqwHIirQBIgIsNGaiQgQIAJEgAgQAwEiQAToyGjarVz25RMifp9FBRoNJ8BrqgLNCCuofEfXQCuoHJsIArQpQGFwyNA6bCKFaDt1DhVo+bNvAZXv6MsYK6gcmwgCRIAIEAFiIEAEiAARIAYCRIAIEAFi6IqaT2umr57BfyZTI3L/hfGnNX06HnuyY6xv8hAiWtjqNdAd2ktCtx+QlrGTZfn2S9do3tNTejDGFWituu2uiUma9x85Xsx5B4doLjA6pnXN8AhdB8sdD1Be/E2k+RQdMA+KgePz3mj+vZT+UXYCYHpeWV0uA5Kbm6HH9cUOa77TGxuh269Px+3RhWGw7s49GlCimvdEBop5eA8Dz83OFHOgOsitvH1NQQLQYn1KfKYngs/ByYHjr019sM9lDCiKVx2AAvXk5r5SVcE6pkaAB8CKAI8eozCYmnzDh0oAp79ocgAPjgdwbQOQKYrZFZYwQFAmP2gAzNuXwYVtmSUDo+Oa8sDbGOzL8rYBCIOHAYHyGChWw1heKKiTQa0GI5eI0/35WsjbuGjfT+9N68Km/awJYMCOMHgGlOW9tElky/IAhIGCjlrZJJilwd7QtcHG8uKfvGIL1rYVwHwdHKOXHXyTgDzkvBXNg9W1v/eul9VEgAXbL79ppXl4ifQkHCTySv4EgGptdy/M4ICy+DrH8tBh+Tx0X1AZD49ZljWXUjOJ0/3BvmY1D9MB8vbkLVZpZ9Y8ADTUPI2SC3UTALPuzewMn826fKlrMmH+5kXJv7bq6Nu9NX9A7rn71GNIgWL3wEdJBezEKRkYM4xd7O6fMtxEpM7w+KqivBMWfo64VrKOUqLS0iaL4d641LnvhGELY1ioiSBAjKrxX4ABABrs14sDnYP6AAAAAElFTkSuQmCC") -} - -.fileList .ico-webm { - background-image: url("../img/ico/ico-webm.png") -} - -.fileList .ico-wma { - background-image: url("../img/ico/ico-wma.png") -} - -.fileList .ico-wmv { - background-image: url("../img/ico/ico-wmv.png") -} - -.fileList .ico-doc, -.fileList .ico-docm, -.fileList .ico-dotx, -.fileList .ico-dotm, -.fileList .ico-dot, -.fileList .ico-rtf { - background-image: url("../img/ico/ico-doc.png") -} - -.fileList .ico-docx { - background-image: url("../img/ico/ico-docx.png") -} - -.fileList .ico-pdf, -.fileList .ico-fdf { - background-image: url("../img/ico/ico-pdf.png") -} - -.fileList .ico-ppt, -.fileList .ico-pptm, -.fileList .ico-pot, -.fileList .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABWZJREFUeNrsnF1oHFUUgM/O7272J7s1iVkT2mrAmBRREQUpiFCtCj74pFAoCOJDlIogSJ8K+qL45A8qqPhSEH0VLAit+lKF0tIXiaQBUaLd2m3zs5tkd+fnjufObtIkZmdmZzLb2Z1zYJifPXdm7rfn3HPO3ZlNWJYFJP4lQQAJYHQBzs7OKrg6hctLuIz1S6fHxsc/w9WrXvUHc7m2n0kubd/O5/MnC4UCSJLUF/Dm5+ehfP36zPDICHQC0S/Alzk8QRCAMdY3bjdaLMK1UmlPIAounw9zeP0mqqLYELkl4u6nYQLsW9kriLEFuFcQYw1wLyDGHmBQiAQwIEQCGBAiAQwIkQAGhOhWC1sTExO+b2Tth09AmzvvSVcsFEF98FlQp5/AnfDKxnK5DJVKpaM209PTCb+lXNfEXCrB+k9fQf3id5A+OgPS2FQo18HaHjKZTNN6Ere4tNteWFiA22aBG7L00TF7XXj967Y6xtU5G6B5c8G2wMxzb4J84AFP529UFyEhSCDKKoiK6qqv6zoYhtEW3E6AThYYmTFQumsScsfeBfmeh9EcDdv92frydshaHWEtQW25DGs3rm4eZwhDkhUw9ebnlmk4XkuWZXt2yQ3e1u3eCCIJAdLPnABx5G6w6qug/fZj8zh6SW35Bhi1VTROCdRU+v9fgKpCMpMDCS2wjhCN+roniBugNpat+z2ZxiQkBVKPvWBva1d+bfJjDC1MhmQaAaGbCg5BRk6mYCBXQLM0QUfgjlaPANtZYm9a4BZ3tl2zUm52RBRBVlPevwSuP5BB8CaY6PZuEEXU3+nGXq0wmnmgxVqe638Sl/edQ9dra/YQ4JhCIcDdIPZsJcKqN5s3ly5sEPV1HhHdXpRkMPSGu24L4s6xsCcB6n9carnyfUH4tSAqwHTNmzUJQscWGLlfinj0rV8+0yypph/f5tKOVc9i2SG4i96npxAiz429/twbKYCWocHqmQ9tiPL++0EaP+QJYKowsreZQMv6vECMDECjNA/r5z4Hc/EfELJDMHB0pjUgMtcgEEo6FUUX3ijp3FKY9NOvgTCQb4KzzN3zPTW5rRpxQAHpoWJoECNjgeK+MUg+8jwok4dvuS3mce2sT0ll7MU5G2JQW62Eet9dBeg0mbBtZgaTX17P8vo20AyPqdupTJgSyTSGD96m1gh8HlPTQFSS8QMoYadNE8swQ/cPD9vyc0hxBMjrMCWdBb1R95VD8za8LT8HeIym/QWQWyHWsQKWYXxGxergwSauy9vwtlIHExB9B9COtOkcr8VgvbKEFlVzLwHR6rgub2O37cYXDREXZSBrz6rUq8s4rmFkVhQst8TNOUGG0ZphumNgwODBJ5UfRuvrXrd64qnJBMJK5YfAQAsztBqmOHVgrQDDXZXP/0nJNLpssvtDTZTyP/dxMXlbIPXsGNgLQgAJ4G1Ot8I8eaO6HJmOqtl87wEM66bJhQkgCQEkgBSFKQpTFCYXJoAEkAASQBICSAAJIAEkIYAEsAfF8V25v96Yov+FQjnwwe8B3taU1HjTMxrkwmFKVx/tGD91dtfj1V++gZWzX7rqrF36HkZPnHa8xrWPj4OxVOpPgFxWzn0B1fPfbu5nD78Ig0desTvNAbnp/P3Ok5vHh4+/b6/Lp9+KbxTmoFi9Ckrx3kA6lMZQHuhPuHsKySxopSuBdGIRRLjwsYwvuwWITnRiC3BngPCrQy5MYyAJAQx7MuHPk4+aCaMh8Dce4ykWWJLKDr53QfRlgWbx0AX7jUmwYgmP952NTl30HYWtffufMi32s/Dv3EOJ2kqs3N1KDTJ25+RldsfBI75dmCTgGEhCAEOX/wQYAOhdFmkKB8pMAAAAAElFTkSuQmCC") -} - -.fileList .ico-pptx { - background-image: url("../img/ico/ico-pptx.png") -} - -.fileList .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2JJREFUeNrsnN9LU2EYx5/98KgxbXM6tuZFMSgaeiHmhV4IGSVFkX+C1FV20W0hBF1IVwVeaAQR/QNBERR5UeGFg1K8MAyySBCbbP1cP6zhzul9js6t1NP5oW6e8/3C4WznbBfvZ8/3fb7vOzguRVEIMi83EABgSeXVujk9PS2J02Vx9IojapdBRxsbb4hTn97P766tNQdQ6Irf778YCATI6/XaAt7MzAylU6lzDaEQGYFoFuBZhud2u0mWZdvYLhyJ0EIyuSkQ/zcHNjA8u6lSklSIXIni7TCaSAkhOroLbwZEx8cYqxCRAy1CBECLEAHQIkQAtAgRAC1CdGltZ4m1sBKLxWwFJ51OUyaTMfSdeDzuMruUs53E2p58Pt9y9bgKXDZ6PTc3BwsXq6KiYnVjpNh9ZjeWHVeBeYisXC63puo2qsRtBzif+rgtMKKhoGmIDGhpaUk9GwW35QDNDmw7lbdy8VadUZCOjzEM0ePxrLHxv1WJOVBDeYBcievNhQCoEyJDK+7GsLBB8e670QoEwHUgGvkbw7ExRnN9u1J9esK1o2OMHoiw8BZDBECrcyYQACAA7uil4E6PMaVOBYgxsDAAAiAAQgCIGIMYgxgDASAAAiAAQogxiDGwMOZACAABEAABEAJAAARAAIQAEADLVpqbCYO3RkBI6Ob1uDmALEmSHA0vm83CwiWz8GaqrWU/9Zzo2PD+18wP+v5jkeoCNXRt6C4t/ir88n1nTlJ1VaV6T0v3Ho7Ri8nX9gTIAyse3EB/Lz1+Mk6jiZer16KRoIB1irq7WgWMhHqts71JXK+n4dsPaD5Z2OXm691dh6h/4A66cF4MiKG2tRyg2L6ICpQhjSam/oLnSAvrFVdk08G91HO8Q9j4twD3QUCdQA40ovuPEup8x9bl1wjSBhXwF5rFnnB5/zlVdhaurpJEt26nqVez6ntuKG9nk/Tp8zdUoB6dXok6I08n1IN17HArLKw3KzaLBsJNgyuOj9GxKfUaxxYA1NByZGlVrVucF7krv3n3njo7mtVoU27SfPTThUtDOVlxOXq553Yp8uDV8x5TFdgYqXvOD6dx4uPiecw89mg4MG66CwfrfEdlRXm2kPrS8nMx66hK3FUtyeFQcLI+WHPEtIWhHRqkAdBB+iPAAD3HE9ONhnJoAAAAAElFTkSuQmCC") -} - -.fileList .ico-xls, -.fileList .ico-csv, -.fileList .ico-xlsm, -.fileList .ico-xlsb { - background-image: url("../img/ico/ico-xls.png") -} - -.fileList .ico-xlsx { - background-image: url("../img/ico/ico-xlsx.png") -} - -.fileList .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABAFJREFUeNrsnF1IU2EYx5+znW3aNvcR6qZS0shwXmUQVFAXKgTrKoSiiwjMiwRDb0ToA/q46E5JsCDpoiAKuuhCIajsAyqQspua5IgKq6mLmV+zyc45nfcs15pnunO2M7ed5w8vZ+Ocs5fz4/9/n/d956Q4jgOUfFEIEAHmLkCv16vnD+f5doJvlYXy0JVVVdf4Q1uq11tKSpKeo9e594LVau222WxA03RBwPP5fBCYnj5VWlYGUiDKBdhC4Gk0GmBZtmBi53A6YdLvzwhEzTrnSwm8QpNBrxcgEifyb/uVBFiwyhRE1QLMFERVA8wERNUDTBciAkwTIgJMEyICTBMiAkwT4nprYc7lchUUnEAgAHNzc5LucbvdlNylXMGJX9uDyWSKuof6xyXZ64mJCYxwvHQ6XWxjJD59crf1VOfAFYhEDMOscl0yJyJAEYgEUCQSEY5SwakeoPDwf6Mcv1WXcw6MTH2CxaEeYBeCWQekMdnB6OkEuty1JkQSZdISY5wKQMWLSGh4YEPgCc7i+w09ur7udVqtVmhS4WXFgczP6DTAeLAd9DV7Ur5v5uqx2Gvb6TuS+10efw2LD/uACX5P6XoCkECLr8Y54UDgouOLFHiZkJz+yO67VAfiUk4EopSvMXK2CsfHNt04S9WK+1KZXNPoubUhYoQVhpizDsx2bGWPmegzBLixy0GswuhABIgRVrgKowMxwqi8XMolVtxcjHVOA8yHcRAjrIaJNDoQq3AGxGX5r/yz1J/yAKloF+H3w1nlF+uPUvYRFR8DaYcLIn4fhJ7eFFrWB3mHK78daGxqA7qqbmMqJN8v6T+vHaixloP58BksIigEiAARIAJEIUAEiAARIAoBIkAEiABRRGv+WvNrRy3+XyheW3vH0vi1Jm1QN71IGCOspDb8a03boU4w1ntEz3272AiO9ltA2ypEz4c/j0Lgdpe6Ac4M9ggtFokiE5S19sPS2Avh/WTf8VX3mPcdAUtDKyy8HcIqLOZI9vcCzD4eED1f7D4gwJt/dReWvM8RYCIc0n4N9orHxebkAXfAsn88KWBVA7Q0tMDi6JAASEz25nPALs1D8P4lnEiLuY8Ui9AH8VhaGk+C3lnDw7sMkRk/AkzUJvd+HswPobImyrjLA+a9R2H2yY2k7lQ9QMO2+ljljRdxnc3TKRSM+Zf3cC0sJgJJU2ReFU0ypbE3nxWcGT/VwYl0gnQV26OrpuD/AHU82JVJdEXXg9WrLB6s2DwxZzYTvnTvZqhImHcpBeoUBxxtYKuvjGhlRZhx1o0AywgfpEZ45NlZR+0b2RHm7FuaGI59ppn6uJNamlXVxgNXbGHZ8h3v2M3VDbIjjEpzDEQhQMX1R4ABANtbXMt+ZZZBAAAAAElFTkSuQmCC") -} - -.fileList .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAP3SURBVHic7ZzBa9NQHMe/L8k6pd3WTra1rDihoGy7zJueZSB48+hJ8LSBMmEHQRC8C4qCepAd/Q8EQdi8CSroQSpaPJQhc61MHFtl0iQeutSsbV6T/vqS1/Z9Lm2T9/JLPvv98l6ypsy2bSg6R4t6B3odJZCIwVuZz+djAO4AuApgOowdCoPpbPYJgGW/7cdGRz3XcQUCuJtMJm+lUikYRrumvUGhUEC5VFqamJwEAkj0op2Va6lUCpqmwbIsaixpSGcy+LG11RWJ7c6BE5rWf6fJ4VgM6UwG5VJpCcBjyrb6z45PuiVxYAUC3ZE40AIBusSBFwjQJCqBh3QqUQl00YlEJbCBoBKVwBYEkch4t7Py+bydy+W6vX+RUi6Xsbu7G6jP3Nwc81rXHxe4AUgmk0gkEgAAxv578Xq/ubnJ3d7AlfDQ0FD9xoi7+jq9sTxwGQjUJAKAaZr1ZU7WeWWiFwMpEKhJZIyhWq2CMRZYnMPACgRQL2X3rTrpMrC6/Q37L+7D2tsRHaoJLTGO+KWbMKa8ZxKGYcA0TZim2VTGfgQKH0Qq688ikQcA1t4OKq+etm2n6zp0XQcQTB4QQgaaP2vTgPjF64idPu+736+HV+rvUzeeB4779+sb7L98BHPnu6/2uq6DMXZkNJYiA2HXzi9B5HWDTuJpmhY4AwduHtgOTdMQ5N8Y0o7C7rKllnNQnOzzM7mWVmDUqBLuAlLMAzsl7LLtFJWBRJRAItKWcJSjcBBUBhJRAolIW8LuspUZlYFElEAi0pYw0DziyljWUguUUVgjqoSJSJuBsk2YvVAZSCQ8gXbI3/IPKZ54gawW4uDTuvBQburxmNhDFH4ONNI5VLcKqGysobKxJjpcy/giEZ6B8cVlGNl50WFaYmTnEV8kP4zEjyF06wC05BRGLt8WHSYy1ChMRAkkogQSUQKJKIFElEAiSiARJZCIEkhECSSiBBJRAokogUS4T2sWV2bVL5MBmHnwmfC0pjHc1Z3pOaoH3NXCS3jm3kfRISJFnQOJCL0j7WSf81pcXTjy2cFZ7qwrri409Wm1XWd9Y59GWm2jWwgV6D44h8bPrZa1asNr647HayuC0EvYzwEFOWi/okUR2Vc7+mVwiUQgrwxFbF8koZdwmAcXBpFPY6jZ19g/7D9QKCXsnpI0Tjd40w93f0cKr3/jq7uNKNpfC/fApZzQrKsecK+FuSVsHxuxALnuJ4Q7etuHDrzhlrCZmX9rFN+dg6YD8P9bKiJpVfJiss8GLBNWevY9rxVXoD1+ctG0rdfa9pez7M/vyAcch+LKrPAY9vExy5o688E6ceoCrx33HOiXIL/0IxvU45cmq3oVJZDIP8OiU3PHSqspAAAAAElFTkSuQmCC") -} - -.fileList .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLVJREFUeNrsnF1oU2cYx59zepp+JTSp1CZaNl3AYXo1hQ0V9EIFobsYQ5gMBMFVWGFjvRHBWZjbhXjTMaEKftwIoqCXBWGrzsI2lM3dzAwt4kbR1GZr1takq5wP3+dtczxJTr5OkpP0nOcPh3ydnJfzy///PO970lTQNA1I1iUSAgJYV0mFXoxGox52M8y2Q2xb75STXt/be/a/4fcGzV5LHrmR81wkErEGkOkrv99/LBAIgCRJjoA3OTkJ8ZmZT7tP3oV8EKvmQKbDCE8URVBV1TGxC4ZCMB2LVQVisRrYjfCcphaPh0NEJ/pP3h2lJlJHiK7uwtWA6PppTKUQaR6YBZFNcUYJoI0QCWCFEAlghRAJYIUQhUKXs9haWAuHw46CE4/HYX5+vqz3sLWwYHUp5zixtT14vd5l9wivueS7PzU1RRE2qrm5Wb8wYkyf1QvLrnNgGiJKUZQc1+VzIgE0gYiAZFnmt+WCcz1AfvIrUTZeqms4B8rPH0NybATUF7O2AxK9XdDRPwRST7ggRIwybtkxLgVgzZtI6taFusDjzmLjpr4/V3S/pqYmvpULzxYHKv8sTwM69n0Gnk3bSn5f4ruP9fuBz6+UPe7LR79A8uYZUGaflrQ/AkRoxm7cEA4Ebbm+lAOvGrIyHl59L9eBtJQzgVjO1xgN24WNsa00zuUq7b5SJtcSea4wRIpwjSE2rAPtjq3lmkk+I4D1XQ5SFyYHEkCKcI27MDmQIkxalUu57I7biLFuaICroQ5ShN0wkSYHUheugjSb/8rfpvFqD1BYHmLpj1u28tPHE2p7ijWvgVIwDHJsElK3L/HN9iIfDK9uB3bsHQSpt68+HZKNi+OvageK/h7wfXicmgiJABJAAkgASQSQABJAAkgigASQABJAEqrgrzX//mIz/Wcypje//bOCX2tKLe6mJy9RhGsp27/W7NzzCfi2H9Afv4w9gsXoHVj46VrOvt0HT0PLxi2QGBuB5G9j+vP4HL5mprnx86bHcgTAtQOjILZ6M4Cs2X8COncP8PvGE5cCIQ5K/X8B2iO7MgCmFb98FJae3NcfB94f4seSE9P8Q3FUF/bt+Ag8oU0we/2bDBj/Xv+aQ/DtOJCxf8fWfg5vbvwCB4lAiyn14M4K/KDzpjFtzEXoCoysmZNmzg9yd+oAt/RD8v4YLK5AaYvsLDpGe98uDn0xOuG8CKP7Fgxxy2l2iVgGbLHVx+BNMCAvOHh0aHZtM6uDWAONx3JcEylF7cxtcuKZ7tYUcxRCTbs4Xw3E2Af6h3LqqSMAIgx0Yb76iMV/+sxB3YGo3uEfcsAWag5YWzH6rW9tdR5APHGEhBCz6yCeMDoOo4cwUc9Of8Djmz39EVtHii+vDLXUMU0E3YGQuvZ/yaOWPdebG7+oNw+EbYTHP4AHE3pM8wlfww8Im4/jaiACwU7buWeA16l0rUI34rwQoWF0pcA6HWZ2CcANAcuJi3mbyMLPV03njHW5mPDXsXcVQV5iLhXAndJAk1rUDafuNVmKsBLquweqwg/kRnh47mpw86+WI6x1vbFX0dQfxecP3xEW51x14UFr61TVnrd/V9ds2G05wqQG6sIEkGSqVwIMAG021uWJKtY8AAAAAElFTkSuQmCC") -} - -.fileList .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABH9JREFUeNrsnEtoE1EUhs9MJmlLUpu0tk20+CCotK5UEMSFCxWEuhARFFERbBcWFN1oQRTUjbpRFFRocVMRBRduCoJvQQTxsdGIFlGpNm1TW9qa1pZ5eM9NZ4w1jZlJ85xz4DLp5N7czpf/v+fMTVNB0zSgsB4iISCAOQ0p2ZOhUMjFDidY28va/GK56Pl1dVfYoSXV/hVz5lgDyOKk1+tt9fl8IElSUcDr6uqCSH///uqaGjAD0SrAfQhPFEVQVbVobOcPBKA3HJ4ViP9bA6sRXrFFicvFIaIS2Y+XKYnkEKKts/BsQLR9GZMuRKoD04RIANOESADThEgA04RIANOEKCTbzmL3wlowGCwqOJFIBEZGRkyNaWhoEKzeyhVdsHt78Hg8MfUIf7jM9Li7u5ssHB9Op9PYGIl3n9WNZdspUIeIoSjKP6qbSYkEMAFEBCTLMj+aBWd7gPzip6wcv1WXdwqU+z5BtPM8qD8Hsw5I9FSCu/EwSLXBpBDRytim2zgVgBlPImMP23MCjyuLzTt27+p/+zkcDt7MwsuKApWBWBng3nQAXEvXpDxu6OJO47Hv4A3T805+fA7Ru5dAGfyeUn8EiNDis3FeKBC02PpiBt5shJX5cPfdrALpVi4BRDMfY+RtFo63bbp2Nhu6+lIpriXSXHKIZOEMQ8xbBWbbtpbXTNIZAczt7SBlYVIgASQLZzgLkwLJwhQFeSs3PePmo63zGmAhrINkYTsU0qRAysKzEFqW/8o/S/NlHqAQm2Li7cOs8jPmEzJ7iRlfAyV/EORwF4w9usZb1hd5f7CwFeje2AJS3fLcZEg2L85f0AoUvbVQvvUYJREKAkgACSABpCCABJAAEkAKAkgACSABpMBI+m3Nr4fq6T+TsVh44X0a39aUSuxNT54gC2cycvKxZvXuc+Dw+qH30p7YL+ELQMX6JihrWGf0GQ89geEH7SAPhY1zJYtXQsWGJnAFlhrnoq87Yfh+G6i/ftoziYilHpi76yw4GMSec1vg26kNEOk4As7AEn5eD/eqRg5eYUCn96tpvszfBFsCdDI1Sb55TEXthoomPr/mysLzqDaEjArFcz9un/6r3wCDKJaVQ/naHfYEqE3BQGsiKD1Gn93iKpsMf+TqE0vLIfqq85/xCBPBulc2/jW+qNfA+EBAww/amMKaYd6RO8a6Jg/1cIj8XWYK0/smTJRT6yQqdqY+RQtQVxs2VJqLWzrAFYVt4PpRysLJImZPDweIFo1CzKYIEpMDPq+OjxrnEilMTyCoWltmYbTv9PULQSEQfe1Tf41ymInGo1LR9rkoZXIOcDz0lMOZy0qU+PrOt/kwX9PG3j3hYLAmRFBV244bsLEuxHGo0NFnN+1pYUwAAx1HWRmynVs2XoFY52GpwhMLU6E8GObZWk82+VBIJ91M+NK6WhHkCaZSAewZGmhSibrozAuHJQsrgeUvQFX4C9kRHl676q9/adnCWuWCjYqmPhb7PqwQxodttfGglVWoau2yN2rVovWWLUxRAFmYANo8fgswAF4ssuGz3PXWAAAAAElFTkSuQmCC") -} - -.fileList .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABGNJREFUeNrsnEtoE1EUhs+k07Q1qclUahMtvoJK05UtCCrUhQpCXblRBEFQFxaUdiMFQVA34qaioC6KG0EU3BYE6xNUEI0bqWjxRZWo0UpfaSOZGe+5adIkjdPMpJkkM+fAZSYzk3uZL/85/52bpoKqqkBhPByEgACWNEStk0NDQ062Oc3aIdZWWuWmVzY3X2Wbrnyv9yxdagwgizNer7dXkiQQRdES8IaHhyHy8+exxuXLQQ9EowAPIzyHwwGKolgm7Xx+P3wPhxcF4kI1sBHhWS1qnE4OEZXIXl4hEykhRFu78GJAtP00plCINA8sECIBLBAiASwQIgEsECIBLBCioLWcxZ6F1UAgYCk4kUgExsfHdb0nGAwKRh/lLBfs2R7cbndCPcIcl//tj4yMUAqnR3V1dWphJD37jC4s206BSYgYsizPU93/lEgAc0BEQPF4nG/1grM9QH7zs6mcvlRXdgqM//gAUwN9oEyOmg7I4W4AV2cPiE0BTYiYytiy0zgfgEU3keiD/pLA48pi40bvXVvwuqqqKt70wjNFgfKvxDTAtfs4ODdsyft9fy4dSO1LJ27qHvfv++cwdfcyyKPf8roeASK0dDcuCwWCmqgveuAtRhgZD1ff9SqQHuVyQNTzNUbZunB62haaznojqb58JtciaU4bIqVwkSGWrQLNTlvDNZN0RgBL+zhILkwKJICUwkV2YVIgpTBFRT7KZTtuOaZ1WQOshDpIKWyHiTQpkFx4EUI1+a/8TRqv+ACFxBCxNw9M5ZcaTyjuLRa9Boq+AMTDwxB9eJ0304u8L1DZCnTt6gKxubU0DsnGxfErWoEObxPU7z1FJkJBAAkgASSAFASQABJAAkhBAAkgASSAFBiav9b80t1C/5mMxeqLbwv4taZYY2968RilcDHDtK81a9a2QePBCznPTTy7BWOD/ZmfbK0bfCdusG09fL98EOJ/wqlz9dv2gWfH0Xn9KDMTMHa/H6ZeDVgPYDIiN05C7FMo9drV3glSZw8HlH7jda3bOTyEUhfsgImnt+f19fXszozX0p4e3lfsYygDuKVdGKEhJKd/Q8ZxV1snTA89Zu0J38+3L672dW32m8ZgyiYDYWKLMngzTE2itIKXACN9WR4g1jNM1cmM9O3gqkwo8DHfd7cvrELPziP8WkvXwFxGgiaSURd5+j6ZS83QANRv3c9A9zFAk6njzacH5/X1+865jGssbyJJR535EOLH0VRQkQgxu/bhuXQzyTYR/HCkPd3MRF6ZBrEsTITXvRXr+XZJcDv8Db/ncNIbHqtj57QCywDCx5ppmxqISsG6xdNB8nOzwLqXHXgMjUXLTOTZqYtgJxPhEKcnuLpcs0aRywSSx7TMJPlB1AbMm8ZoLiZ87t0sC/EYgyyAPUMFVaxR1px/UWVIgbK/9QUoMu/IjvDw3hVfy0vDLqw2rNolq8ojx493m4TpMVstPKh1HkVp2vhaWbZmh+EUpqigRzkCaNP4J8AArrWcCArY0yoAAAAASUVORK5CYII=") -} - -.fileList .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+hJREFUeNrsnF1IU2EYx5+zHTdkU7eZuuUocWA4r/QiqC68UCHQqxCKiAiqiwRD74RKyKu6UhIqULoJouhWIcjsAyqQsJtY5IgKq5krze8m56P3PWtruA/Px77P84fDmTvvu5fz2/95n+d958aIoggo9TIgAgSYU7GpLvp8PhM5DZLjDDlqi+Wma93uW+TUI7d9RXm5OoBEV20224DdbgeWZYsCnt/vh+Di4oWq6mpQAlEtwLMUnsFgAEEQiibsnC4XLAQCaYG42xxYReEVm8wmkwSROpH8eROTSA4h6joLpwOi7ssYrRCxDtQIEQFqhIgANUJEgBohIkCNEJlU21lkLSx6PJ6ighMMBmF1dVVRH6/Xy6hdyhWdyNoerFZr2D3Mfy7JHs/Pz2MIx6qkpCS6MRIbfWo3lnXnwAhEKp7n41yXzIkIMAFECojjOOmsFJzuAUo3/y+UY7fq8s6B3I+PsDE5DML6UtYBGawOsHT2A1vjSQmRhjI9doaxHIAZTyKb0+M5gSc5i4y7+fj2ru2MRqN0KIWXFQfyP8NlgOVoL5gaDsnut3zjZPSx/eI9xeNuz72GjUejwC99k9WeAqTQYrNxXjgQxPD8ogReOqRmPLr7rtSBuJRLAFHJxxh5m4Vjw1ZrOCtVxH1yimsWPZcaIoZwhiHmrQOzHbaq50z0GQLM7XIQszA6EAFiCGc4C6MDMYRRBbmU25lx8zGs8xpgIcyDGMJ6KKTRgZiF0yAxy//ln6XxMg+QCQ8RejedVX7R8ZjM3mLG50DW6QEu4IfNp3ekI+uTvNNT2A60dPQA627KTYYk49LxC9qBBlsNlB27hEkEhQARIAJEgCgEiAARIAJEIUAEiAARIIoq5bc1v/Q14i+TEe0fea/h25qsWd/0uBCGcCaVk481WbsLnL13k15feTIGay8fgHtwCjZmJ2F5YhjKjhyHirbzcW23A3Pwe2JEOusGILccgK9D7XHPV3ZfAXN9C2z5XiTtG9uPvhF7Tl0HR/dlWBg9re8sXNF+Dkq9rcRtIxJguW8EdShr3yvB1C1ACq7s8AlYe3WfuO851oFK50N7Vx+EPs3CytS44r6Wlk7ixO+yXVsUc2CsHGTeE7bW4NfDIVntaWJJlER0lYUjsnf1g8nVAItjPSD8WZfVJ1Hy0WUI07KEhh8tWXJVghQsQOo6WtPRhEHrPSykFcpc3xzNvu7B1rjrkeK54DcTPg8c5BkuRFzKgD4lgsiahbprM0ZVIcy7mmZA4KUX0iM8eu+Cs/GN6hAWHfs6eFF4ZvjxoZnZWtHVxoNYWiEINQfeCpV1bapDGFVAa2EEqFP9FWAAVhNMgyXjjfoAAAAASUVORK5CYII=") -} - -.fileList .ico-bt { - background-image: url("../img/ico/ico-bt.png") -} - -.fileList .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNrs3D9PwkAcxvFrKXUhCAMEAhsbL8nJxElfgYPR+CZ0MnHyJbEwMkDoQGAG6h2CNlIo16t/cvd9kktL2g73ye9a7gj14jgWJH88AAH8v4CDwSCUm3vZLmXr2NLpTrf7LDc3p55/Xq0ePBZkXPtYq9Vu6/W6CILACrzhcCii6fS60WwKHcS8gFcKz/d9sV6vrRl2rXZbTMbjQhD9jOMNhWdbzsJwg6gqUX58+klAa1MUorOARSE6DVgEovOApogAGiICaIgIoCEigIaIWXPhuNfrWYUTRZFYLBZa1/T7fS/vVM66yLm9qFQqH9Xjfbkc2h+NRgzhZMrl8ufCSHL05V3Wc64Cd4gqq9Vqr+oOVSKAKYgKaLlcbra6cM4Dbjq/HcrJpTpdSOe/xijEUqm0N4y/VyUVeCQ7QFWJafdCAE9EVGjJpzFDWDNq9V23AgFMQdT5GQPAtPntiQ8Q7oEZiFTgLyACaHrPhABAAAEEkAAIIIAAEgABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBNBywCj5bhXXsu17ZAL4MpvNhIuIqs+q7zKvx87L+sP1w3w+D2W7kPstxwwnsr3JdnfsJF4FbxgAAfzbvAswAK/3kejP2oZLAAAAAElFTkSuQmCC") -} - -.fileList .ico-apk { - background-image: url("../img/ico/ico-apk.png") -} - -.fileList .ico-css { - background-image: url("../img/ico/ico-css.png") -} - -.ico-cmd { - background: url("../img/soft_ico/ico-cmd.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.btn:hover .ico-cmd { - background: url("../img/soft_ico/ico-cmd-hover.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.fileUploadDiv { - padding: 10px -} - -#file_input { - display: none -} - -#up_box { - background-color: #fafbfe; - border: #e4e5e9 1px solid; - margin: 0; - padding: 10px; - clear: both; - height: 360px; - overflow: auto -} - -#up_box li { - list-style-type: none; - height: 30px; - line-height: 30px; - font-size: 12px; - width: 100%; - margin: 0 0 5px; - padding: 0 -} - -#up_box li .filename { - display: inline-block; - height: 30px; - margin-right: 20px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 240px -} - -#up_box li .filesize { - color: #999; - display: inline-block; - height: 30px; - overflow: hidden -} - -.fileUploadDiv button { - float: left; - border-radius: 3px; - border-style: solid; - border-width: 1px; - width: 75px; - height: 30px; - color: #fff; - margin-bottom: 10px; - cursor: pointer -} - -#up { - background-color: #5cb85c; - border-color: #4cae4c; - color: #fff; - text-align: center; - margin-left: 10px; - position: absolute; - bottom: 0; - right: 10px -} - -#filesClose { - position: absolute; - bottom: 0; - right: 95px; - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -#opt { - background-color: #4592f0; - border-color: #367fa9 -} - -#up_box li em { - font-style: normal; - color: #06F; - float: right; - margin-right: 10px -} - -.cancel { - float: right; - cursor: pointer; - color: #c00 -} - -#textBody { - min-height: 450px; - _height: 450px -} - -#filesClose:hover { - background-color: #c9302c; - border-color: #ac2925; - color: #fff -} - -#up:hover { - background-color: #449d44; - border-color: #398439 -} - -#totalProgress progress { - width: 180px; - height: 10px; - border: 1px solid #ccc; - background-color: #e6e6e6; - color: #0064b4; - opacity: .9 -} - -#totalProgress p { - color: #666 -} - -.setchmod fieldset { - margin-left: 15px; - margin-bottom: 15px; - border: 1px solid #ccc; - float: left; - width: 114px; - padding-bottom: 10px; - border-radius: 3px -} - -.setchmod legend { - padding: 3px; - border: 0; - width: auto; - margin: 0 6px; - font-size: 14px -} - -.setchmod fieldset p { - margin-left: 10px -} - -.setchmod fieldset p input { - position: relative; - top: 2px; - margin-right: 5px -} - -.setchmodnum { - clear: both; - margin: 0 15px -10px -} - -.setchmodnum #access, -#chown { - width: 60px; - margin-right: 5px; - height: 28px -} - -.setchmodnum span { - margin-left: 10px -} - -#chown { - margin-left: 5px -} - -.shellcode { - margin-bottom: 5px; - height: 490px; - overflow: auto -} - -.shellcode pre { - background-color: #111; - color: white; - padding: 5px 10px; - border: 0 none; - border-radius: 0; - min-height: 490px; - margin: 0 -} - -#mExec { - width: 89%; - margin-top: 0; - float: left -} - -.editmenu { - text-align: right -} - -#filesBody { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none -} - -.folderBox input { - display: none -} - -.fileList>div:hover, -.fileList div.ui-selecting { - background: #f6f8fd; - border-color: #d3dfec -} - -.fileList div.active, -.fileList div.focus, -.fileList div.ui-selected { - background: #eff3f9 none repeat scroll 0 0; - border-color: #bcccde -} - -.fileList .file { - background: rgba(0, 0, 0, 0) none repeat scroll 0 0; - border: 1px solid #fff; - border-radius: 2px; - display: inline-block; - height: 136px; - margin: 0 5px 10px 0; - text-align: center; - width: 112px -} - -.fileList div.file { - transition: all .2s ease 0s -} - -.fileList .file .titleBox { - color: #595c5f; - display: block; - height: 23px; - line-height: 23px; - overflow: hidden; - text-align: center; - text-overflow: ellipsis; - width: 100px; - margin-left: 5px -} - -#DirPathPlace { - display: none; - padding: 0 -} - -#DirPathPlace>input { - border: 1px solid #ccc; - height: 28px; - line-height: 28px; - padding: 0 5px; - width: 280px -} - -#DirPathPlace>input:active, -#DirPathPlace>input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -#PathPlaceBtn { - background-color: #f3f3f3; - border: 1px solid #ccc; - height: 28px; - line-height: 26px; - padding: 0; - width: 290px; - cursor: text; - overflow: hidden -} - -#PathPlaceBtn ul { - display: inline-block; - height: 26px; - width: auto; - position: relative -} - -#PathPlaceBtn li { - background: url("../img/ico/ico-ltr.png") no-repeat right center; - padding-left: 10px; - padding-right: 18px; - float: left; - height: 26px; - line-height: 26px -} - -#PathPlaceBtn li a { - display: inline-block; - cursor: pointer; - height: 26px; - line-height: 26px -} - -.backBtn { - color: #666; - border-radius: 0; - height: 28px; - line-height: 8px; - margin-right: -1px; - margin-top: -1px -} - -#tipTools { - background: #fff url(../img/ico_line.png) repeat-x 0 55px; - height: 110px; - border-bottom: #ccc 1px solid; - position: absolute; - top: 0; - left: 0 -} - -.re-head { - border-bottom: 1px solid #ccc; - float: left; - padding: 15px 20px; - width: 100% -} - -.re-con { - float: left; - height: 500px; - width: 100% -} - -.re-con-menu { - background-color: #f0f0f1; - float: left; - height: 100%; - width: 120px -} - -.re-con-menu p { - cursor: pointer; - line-height: 40px; - padding-left: 30px; - position: relative -} - -.re-con-menu p.on { - background-color: #fff -} - -.re-con-con { - height: 500px; - margin-left: 120px; - overflow: auto -} - -@media only screen and (max-width:990px) { - .pr8 { - padding-right: 0 - } - - .pl7 { - padding-left: 0; - margin-top: 15px - } -} - -.bt-ico-ask { - border: 1px solid #fb7d00; - border-radius: 8px; - color: #fb7d00; - cursor: help; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px -} - -.bt-ico-ask:hover { - background-color: #fb7d00; - color: #fff -} - -#RecycleBody .tname { - line-height: 30px -} - -.menu-sub { - border-bottom: 1px solid #ccc; - height: 40px; - line-height: 30px; - margin-bottom: 15px -} - -.menu-sub span { - display: inline-block; - font-size: 14px; - height: 40px; - padding: 0 25px; - cursor: pointer -} - -.menu-sub .on { - border-bottom: 2px solid #20a53a; - color: #20a53a; - font-weight: bold -} - -.bt-progress { - background-color: #e2e2e2; - border-radius: 8px; - height: 16px; - line-height: 16px; - position: relative; - margin: 5px 0 -} - -.bt-progress-bar { - background-color: #5ab76c; - border-radius: 8px; - height: 16px; - max-width: 100%; - position: absolute; - text-align: right; - transition: all .3s ease 0s; - width: 0 -} - -.bt-progress-text { - font-size: 12px; - color: #fff; - padding: 0 10px; - position: static -} \ No newline at end of file diff --git a/route/static/css/index.html b/route/static/css/index.html deleted file mode 100755 index e69de29bb..000000000 diff --git a/route/static/css/install.css b/route/static/css/install.css deleted file mode 100755 index fa853aabc..000000000 --- a/route/static/css/install.css +++ /dev/null @@ -1,181 +0,0 @@ -html, -body, -div, -span, -p, -a, -table, -input, -ul, -li, -* { - margin: 0; - padding: 0; - font-size: 12px -} - -body { - background-color: #444; - height: 100%; - min-height: 768px -} - -.main { - height: 900px; - width: 100%; - background-size: cover; - position: relative; - z-index: 0 -} - -.warp { - background-color: #f6f6f6; - width: 700px; - height: 370px; - position: absolute; - top: 50%; - left: 50%; - margin-top: -340px; - margin-left: -350px; - box-shadow: 0 0 10px #666; - border-radius: 3px -} - -.warp .logo { - background: url(../images/logo.png) no-repeat; - height: 37px; - left: 50%; - margin-left: -52px; - position: absolute; - top: -67px; - width: 103px -} - -.warp .title { - height: 70px; - line-height: 70px; - text-align: center; - font-size: 26px; - border-bottom: #ababab 1px solid; - margin: 0 20px; - color: #444 -} - -.warp .form { - padding: 20px -} - -fieldset { - background-color: #fff; - padding: 10px; - margin-bottom: 20px; - border: #ccc 1px solid -} - -fieldset legend { - padding: 0 5px; - font-size: 14px; - color: #000 -} - -fieldset p { - line-height: 40px; - color: #555 -} - -fieldset p .tit { - display: inline-block; - margin-right: 10px; - text-align: right; - width: 100px -} - -fieldset input { - margin: 0 7px; - height: 28px; - border: #ccc 1px solid; - width: 200px; - padding: 0 5px; - border-radius: 2px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -fieldset input:focus, -fieldset input:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.text { - background-color: #f3f3f3; - border: 1px solid #ccc; - border-radius: 2px; - display: inline-block; - height: 26px; - line-height: 28px; - margin: 0 7px; - padding: 0 5px; - width: 188px -} - -.submit-btn { - background-color: #20a53a; - border: 1px solid #20a53a; - color: #fff; - cursor: pointer; - font-size: 16px; - height: 42px; - line-height: 35px; - text-align: center; - width: 330px; - margin: 12px 164px; - border-radius: 3px -} - -.submit-btn:hover { - background-color: #10952a; - border: 1px solid #10952a -} - -.success { - background-color: #f6f6f6; - border-radius: 5px; - box-shadow: 0 0 140px #556789; - height: 140px; - left: 50%; - margin-left: -150px; - margin-top: -70px; - position: absolute; - text-align: center; - top: 50%; - width: 300px -} - -.success p { - font-size: 20px; - line-height: 90px -} - -.success a { - color: #3385d6; - font-size: 16px; - text-decoration: none -} - -.copyright { - color: #9d9d9d; - text-align: center; - margin-top: -50px; - position: relative; - z-index: 3 -} - -.copyright a { - color: #9d9d9d; - text-decoration: none -} \ No newline at end of file diff --git a/route/static/css/login.css b/route/static/css/login.css deleted file mode 100755 index 97116e984..000000000 --- a/route/static/css/login.css +++ /dev/null @@ -1,539 +0,0 @@ -body { - background-color: #444; - font-family: 华文黑体, "Microsoft YaHei", 微软雅黑, STHei, "Helvetica Neue", Helvetica, Arial, sans-serif -} - -input { - -webkit-appearance: none -} - -.main .login { - background-color: #fff; - border-radius: 4px; - height: 290px; - left: 50%; - margin-left: -220px; - margin-top: -180px; - padding: 35px 40px 50px; - position: absolute; - top: 50%; - width: 360px -} - -.main .login .rlogo { - text-align: center; - font-size: 26px; - color: #444; - cursor: pointer; - height: 40px; - margin-bottom: 40px; - overflow: hidden -} - -.main .login .title { - color: #444; - font-family: "微软雅黑"; - font-size: 28px; - margin: -10px 0 46px; - text-align: center -} - -.main .login .line { - height: 42px; - margin-bottom: 20px; - line-height: 42px; - position: relative -} - -.main .login .line span { - width: 70px; - float: left; - color: #757584 -} - -.main .login .line .inputtxt { - background: transparent; - float: left; - width: 338px; - height: 30px; - line-height: 30px; - border: #d1d1d1 1px solid; - padding: 5px 10px; - position: relative; - z-index: 1; - box-shadow: 0 3px 5px -4px rgba(0, 0, 0, 0.4) inset, -1px 0 3px -2px rgba(0, 0, 0, 0.1) inset; - border-radius: 3px -} - -.main .login .line .code { - width: 230px -} - -.main .login .line .getcode, -.main .login .line .picgetcode { - width: 100px; - text-align: center; - height: 38px; - line-height: 38px; - border: #cacad9 1px solid; - background-color: #dbdbea; - color: #757584; - font-size: 14px; - float: left; - margin-left: 17px; - cursor: pointer -} - -.main .login .line .getcode.no-drop { - cursor: no-drop -} - -.main .login .line .getcode.pointer { - border: #47acff 1px solid; - background-color: #47acff; - color: #fff -} - -.main .login .line .picgetcode img { - width: 100%; - height: 100% -} - -.main .login .line .codemsg { - background: #e53451; - border-radius: 3px; - color: #fff; - font-size: 12px; - height: 21px; - line-height: 22px; - overflow: hidden; - padding: 0 6px; - position: absolute; - right: 127px; - text-align: right; - top: 5px; - z-index: 0 -} - -.main .login .line .inputtxt:focus { - border: #70c3ec 1px solid; - box-shadow: 0 0 5px #eee -} - -.main .login .yzm .inputtxt { - width: 64% -} - -.login_btn input { - width: 100%; - height: 44px; - background: #20a53a; - background: linear-gradient(#20a53a, #189d32); - box-shadow: inset 0 1px 2px #30ad42; - color: #fff; - text-shadow: #00851a 0 -1px 0; - border: 1px solid #20a53a; - text-align: center; - font-size: 16px; - color: #fff; - border-radius: 3px; - cursor: pointer -} - -.login_btn input:hover { - background: #10952a; - background: linear-gradient(#10952a, #088d22); - border: 1px solid #10952a -} - -.login-link { - text-align: right; - margin-top: 22px -} - -.login-link span { - color: #cacad9; - padding: 0 8px; - text-align: center -} - -.login-link a { - color: #757584 -} - -.copyright { - color: #9d9d9d; - text-align: center; - margin-top: -50px; - position: relative; - z-index: 3 -} - -.copyright a { - color: #9d9d9d; - text-decoration: none -} - -.pwinfo { - bottom: 19px; - color: #999; - position: absolute -} - -.resetpw { - bottom: 20px; - color: #20a53a; - position: absolute; - right: 40px -} - -.Validform_checktip { - color: #999; - font-size: 12px; - height: 24px; - line-height: 24px; - overflow: hidden; - padding: 0 6px; - position: absolute; - right: 10px; - text-align: right; - top: 14px; - z-index: 1 -} - -.Validform_wrong { - background: #e53451; - border-radius: 3px; - color: #fff; - float: right -} - -.Validform_right { - padding: 0 10px; - color: #71b83d; - background: url(../images/right.png) no-repeat right center/16px auto -} - -.main .login .line .Validform_error { - border: #e53451 1px solid; - box-shadow: 0 0 2px #ea5d74 -} - -.main .login .yzm .Validform_wrong { - background: #e53451; - border-radius: 3px; - color: #fff; - font-size: 12px; - height: 24px; - line-height: 24px; - overflow: hidden; - padding: 0 6px; - position: absolute; - right: 127px; - text-align: right; - top: 14px; - z-index: 1 -} - -.main .login .yzm .Validform_right { - color: #71b83d; - padding: 0 10px; - right: 130px -} - -.main .login .line .inputcode { - width: 208px -} - -.zun-form { - background: #fff none repeat scroll 0 0; - border-radius: 5px; - color: #888; - font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif; - margin-left: auto; - margin-right: auto; - max-width: 90%; - padding: 20px 10px; - text-shadow: 1px 1px 1px #fff -} - -.zun-form-new { - background: #FFF; - padding: 20px 0 0; - font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #888 -} - -.zun-form-new .line { - padding: 5px 20px -} - -.zun-form-new .line:first-child { - padding-top: 0 -} - -.zun-form-new .line:after { - display: table; - content: ""; - clear: both -} - -.zun-form-new h1 { - font: 25px "Helvetica Neue", Helvetica, Arial, sans-serif; - padding: 0 0 10px 40px; - display: block; - border-bottom: 1px solid #dadada; - margin: -10px -30px 30px -30px; - color: #888 -} - -.zun-form-new h1>span { - display: block; - font-size: 11px -} - -.zun-form-new label { - display: block; - float: left; - width: 80px -} - -.zun-form-new .info-r { - margin-bottom: 5px; - margin-left: 80px; - position: relative; - color: #666 -} - -.zun-form-new .info-r label { - margin-bottom: 0; - padding-top: 5px -} - -.zun-form-new .info-r label input { - margin-right: 5px -} - -.zun-form-new .info-r p { - margin-top: 10px; - color: #999 -} - -.zun-form-new label>span { - float: left; - text-align: right; - padding-right: 10px; - margin-top: 8px; - color: #565656; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: bold -} - -.zun-form-new input[type="text"], -.zun-form-new input[type="email"], -.zun-form-new input[type="password"], -.zun-form-new input[type="number"], -.zun-form-new textarea, -.zun-form-new select, -.webEdit-box input, -.webEdit-box textarea, -.webEdit-box select { - border: 1px solid #ccc; - color: #888; - height: 30px; - line-height: 30px; - margin-right: 6px; - margin-top: 2px; - outline: 0 none; - padding-left: 5px; - width: 100%; - border-radius: 2px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.zun-form-new input:focus, -.zun-form-new input:active, -.zun-form-new select:focus, -.zun-form-new select:active, -.dataTables_paginate select:focus, -.dataTables_paginate select:active, -textarea:focus, -textarea:active, -.webEdit-box input:focus, -.webEdit-box select:focus, -.webEdit-box input:active, -.webEdit-box select:active, -.dlt-text:focus, -.sp-text:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.zun-form-new select { - height: 32px; - padding: 0\9; - margin: 0\9 -} - -.zun-form-new textarea { - height: 100px; - padding: 5px 0 0 5px; - width: 80% -} - -.zun-form-new .submit-btn, -.getfile-btn { - text-align: right; - padding: 7px 20px 10px; - background: #f6f8f8; - border-top: 1px solid #edf1f2; - margin-top: 10px; - clear: both -} - -.zun-form-new .submit-btn button:first-child, -.getfile-btn button:first-child { - margin-right: 4px -} - -.zun-form-new .button { - background: #FFF; - border: 1px solid #CCC; - padding: 10px 25px 10px 25px; - color: #333; - border-radius: 0 -} - -.zun-form-new .button:hover { - color: #333; - background-color: #ebebeb; - border-color: #adadad -} - -.btn { - display: inline-block; - padding: 5px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: 400; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 3px -} - -.btn-sm { - border-radius: 3px; - font-size: 12px; - line-height: 1.5; - padding: 4px 10px -} - -.btn-danger { - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19 -} - -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925 -} - -.btn-danger:active, -.btn-danger.active, -.open>.dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925 -} - -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c -} - -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625 -} - -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439 -} - -.btn-success:active, -.btn-success.active, -.open>.dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439 -} - -@media screen and (max-width:640px) { - body { - background-color: #eee - } - - .main .login .rlogo { - background-color: rgba(0, 0, 0, 0.8); - color: #fff; - font-size: 20px; - height: 2.8em; - line-height: 2.8em; - margin: 0 -3.3% 25px -3.3% - } - - .main .login { - background-color: #eee; - border-radius: 0; - height: 330px; - left: 0; - margin-left: 0; - margin-top: 0; - padding: 0 3%; - position: absolute; - top: 0; - width: 94% - } - - .main .login .line .inputtxt { - background-color: #fff; - border: 1px solid #d1d1d1; - width: 93.6%; - padding: 5px 3%; - font-size: 1.2em - } - - .main .login .yzm .inputtxt { - width: 57% - } -} \ No newline at end of file diff --git a/route/static/css/site.css b/route/static/css/site.css deleted file mode 100755 index 358ac8c93..000000000 --- a/route/static/css/site.css +++ /dev/null @@ -1,5211 +0,0 @@ -body { - line-height: 1.4; - color: #333; - font-family: "微软雅黑", Arial, Helvetica, sans-serif; - font-size: 12px -} - -input, -textarea, -select { - font-size: 100%; - font-family: inherit; - outline: none; -} - -body, -h1, -h2, -h3, -h4, -h5, -h6, -p, -ul, -ol, -form { - margin: 0 -} - -h4, -h5, -h6 { - font-size: 1em -} - -ul, -ol { - padding-left: 0; - list-style-type: none -} - -fieldset, -img { - border: 0 -} - -a { - color: #333; - border: 0; - text-decoration: none -} - -a:hover { - text-decoration: none -} - -a:link { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-touch-callout: none -} - -:-moz-placeholder { - color: #999 -} - -::-moz-placeholder { - color: #999 -} - -input:-ms-input-placeholder { - color: #999 -} - -input::-webkit-input-placeholder { - color: #999 -} - -body, -html { - height: 100% -} - -.f12 { - font-size: 12px -} - -.f14 { - font-size: 14px -} - -.f15 { - font-size: 15px -} - -.f16 { - font-size: 16px -} - -.f18 { - font-size: 18px -} - -.f20 { - font-size: 20px -} - -.cw { - color: white -} - -.c0 { - color: #000 -} - -.c3 { - color: #333 -} - -.c4 { - color: #444 -} - -.c5 { - color: #555 -} - -.c6 { - color: #666 -} - -.c7 { - color: #777 -} - -.c8 { - color: #888 -} - -.c9 { - color: #999 -} - -.cbt { - color: #20a53a -} - -.bgw { - background-color: white -} -.move_class{ - float:right; -} -.bge6 { - background-color: #f2f2f2; -} -/* .bge6 { - background-color: #e6e9ee -} - */ - .btn{ - vertical-align: inherit; - } - .file-box .btn{ - vertical-align: middle; - } -.file-box .page{ - border:none; -} -.file-box .Pcount-item{ - border-right:1px solid #ececec; -} -.file-box{ - padding-bottom: 1px; -} - -.plr10 { - padding: 0 10px; -} - -.plr15 { - padding: 0 15px; -} - -.plr20 { - padding: 0 20px; -} - -.ptb10 { - padding: 10px 0; -} - -.ptb15 { - padding: 15px 0; -} - -.ptb20 { - padding: 20px 0; -} - -.pd0 { - padding: 0; -} - -.pd15 { - padding: 15px; -} - -.pd20 { - padding: 20px; -} - -.pr8 { - padding-right: 8px; -} - -.pl7 { - padding-left: 7px; -} - -.pb15 { - padding-bottom: 15px; -} - -.pb55 { - padding-bottom: 55px; -} - -.pb70 { - padding-bottom: 70px; -} - -.mt10 { - margin-top: 10px; -} - -.mtb10 { - margin: 10px 0; -} - -.mtb15 { - margin: 15px 0; -} - -.mtb20 { - margin: 20px 0; -} - -.mlr15 { - margin: 0 15px; -} - -.mlr20 { - margin: 0 20px; -} - -.mb15 { - margin-bottom: 15px; -} - -.mr50 { - margin-right: 50px; -} - -.mr20 { - margin-right: 20px; -} - -.ml33{ - margin-left: 38px; -} -.ml45 { - margin-left: 50px; -} - -.ml5 { - margin-left: 5px; -} - -.ml10 { - margin-left: 10px; -} - -.mr5 { - margin-right: 5px; -} - -.mr20 { - margin-right: 20px; -} - -.ml0{ - margin-left: 0; -} - -.mg10 { - margin: 10px; -} - -.va0 { - vertical-align: 0 -} - -.ico-font-ask { - border: 1px solid #999; - border-radius: 8px; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px; - cursor: help -} - -.btlink { - color: #20a53a -} - -.btlink:hover { - cursor: pointer -} - -.btn-btlink { - border-color: #20a53a; - color: #20a53a; - vertical-align: 1px -} - -.btn-btlink:hover { - border-color: orange; - color: orange -} -.input-edit{ - cursor: pointer; - border:transparent 1px solid; - min-height: 20px; - /*min-width: 150px;*/ - display: inline-block; -} -.input-edit:hover{ - border:#ccc 1px solid; - background-color: #fff; -} -.baktext{ - height: 20px; - border:#ccc 1px solid; - min-width: 150px; -} -.b-shadown { - transition: border-color .15s ease-in-out 0s, box-shadow .15s ease-in-out 0s -} - -.b-shadown:hover { - -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) -} - - - -.info-title-tips { - background-color: #fbfbfb; - border: 1px solid #eee; - line-height: 46px; - margin-bottom: 15px; - padding-left: 10px; -} - -.bt-input-text { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 2px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.bt-input-text:focus, -.bt-input-text:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bt-submit { - background-color: #20a53a; - border-radius: 3px; - width: 140px; - height: 34px; - line-height: 34px; - text-align: center; - color: #fff; - cursor: pointer -} - -.cursor { - cursor: pointer; -} - -.help-info-text { - margin-top: 15px; -} - -.help-info-text>li { - list-style: inside disc; - line-height: 24px; -} - -.relative { - position: relative; -} - -.ico-copy { - background: url(/static/img/ico-copy.png) no-repeat; - height: 14px; - width: 12px; - display: inline-block; - vertical-align: -2px -} - -.zclip embed { - vertical-align: top; -} - -.webDelete .options { - padding: 20px 0; -} - -.webDelete .options label { - width: 30%; - float: left; - font-weight: normal -} - -.webDelete .options label input { - float: left; - margin: 0 10px 0 0; - margin-top: 1px -} - -.webDelete .options label span { - float: left; - margin: 0; - line-height: 16px -} - -.webDelete .vcode { - background-color: #f0f0f0; - clear: both; - font-size: 14px; - height: 40px; - line-height: 40px; - margin: 10px 0; - padding-left: 12px; - text-align: left; - color: #444 -} - -.webDelete .vcode .text { - margin-right: 10px; - margin-left: 10px -} - -.webDelete .vcode #vcodeResult { - display: inline; - height: 26px; - line-height: 26px; - margin-left: 10px; - width: 50px; - color: #444 -} - -.btswitch { - display: none -} - -.btswitch+.btswitch-btn { - outline: 0; - display: block; - width: 3em; - height: 1.8em; - position: relative; - cursor: pointer -} - -.btswitch+.btswitch-btn:after, -.btswitch+.btswitch-btn:before { - position: relative; - display: block; - content: ""; - width: 50%; - height: 100% -} - -.btswitch+.btswitch-btn:after { - left: 0 -} - -.btswitch+.btswitch-btn:before { - display: none -} - -.btswitch:checked+.btswitch-btn:after { - left: 50% -} - -.btswitch-ios+.btswitch-btn { - background: #cdcdcd; - border-radius: .9em; - padding: 2px; - -webkit-transition: all .4s ease; - transition: all .4s ease; - border: 1px solid #e8eae9 -} - -.btswitch-ios+.btswitch-btn:after { - border-radius: .9em; - background: #fbfbfb; - -webkit-transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08); - box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08) -} - -.btswitch-ios+.btswitch-btn:active { - -webkit-box-shadow: inset 0 0 0 2em #e8eae9; - box-shadow: inset 0 0 0 2em #e8eae9 -} - -.btswitch-ios+.btswitch-btn:active:after { - padding-right: .8em -} - -.btswitch-ios:checked+.btswitch-btn { - background: #20a53a -} - -.btswitch-ios:checked+.btswitch-btn:active { - -webkit-box-shadow: none; - box-shadow: none -} - -.btswitch-ios:checked+.btswitch-btn:active:after { - margin-left: -.8em -} - -.bt-warp { - position: relative; - min-height: 100% -} - -.bt-warp>.container-fluid { - padding: 0 -} - -.main-content { - margin-left: 180px -} - -.sidebar-scroll { - background-color: #3c444d; - width: 180px; - z-index: 100; - height: 100%; - position: fixed; - overflow: hidden -} - -.sidebar-auto { - overflow: auto; - height: 100%; - margin-right: -18px -} - -.mypcip { - display: block; - padding: 0 10px; - position: relative; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease; - width: 100%; - cursor: pointer; - margin: 1px 0 -} - -.mypcip:hover { - background: #20a53a; - opacity: 1 -} - -.mypcip span { - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAYAAAAS7Y8mAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGPSURBVDiNldQ9ixRBEAbgZ3Zn1fVOuQU/wK9AMDDS1MvuRxj5CwTBP2CiYmYmGBsYGRsaa2qkkSiCgRd4y3ri3XqzbdDV7Lgs7uwLRc90V1dXv29VVymluxiiRoVkNaoYG7wN+9chpTTBBn7jKA5YhYR+JPQEDxYd6rAPeIwDnOgQuMEIz3F8mUMdC9/wqkPAxb1P0Yv/6ziLX/hTy9c6jXPYXSPwBQywF+Mz7GCCSRGMbqK1Ufwbmb4beI9TuNZFqFWoZCH7Mp0XcaX33y3dUDKfmRdD1bM+BYuoWtaEKeIdyqSvg5+RZSWX6Ri3ZUqmRbxjcqnsx3cyF7Utbpmf4YxcapXcWFNcjiSbOk7Yxhu5BocRaGBeoyXYNII32MTJsJ7cvR/D92aN1xFsFA6HYZ/iiiWjTVyVGyrhBz7jXSQxxq3wPahxJ7JuX30sd9IoshviC77KzdSmag+XYm4QNq0tF+0e7sc1SwPs4xFeLPHvt2ibsfwl24rFl/KLV/jdCho2ZC3a+I6HOB9rR38B10ZjDE49T6kAAAAASUVORK5CYII=") no-repeat 0 center; - display: inline-block; - line-height: 46px; - padding-left: 30px; - white-space: nowrap; - max-width: 146px; - overflow: hidden; -} - -.btpc-plus { - line-height: 40px; - color: #aaa; - font-family: arial; - font-size: 26px; - cursor: pointer; - padding-left: 80px; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease -} - -.btpc-plus:hover { - background-color: #20a53a; - color: #fff -} - -.mypcip .btedit { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNndO/ahRRFMfxz+zOav6goKC1S54gBBUECy0lIKTeyrXwFcROyBuENCHdNhaCoCgiWoggGPAJxFgIsbEJJBmTrCfFnBFZNjHrDy4z5849935/Z84tIsJ/6gneFhFR4AHuYHjM4jN4iZWM13ALSyWWcR+rOERrzAYFNvP9Ka7jKrZExKeIWI8IpxivIuJrRFxo5lqJXfzDbwfvcQU3cAnTUOIsfif6zeYDfuEDzuM5LuIatrGBu/hcInCAy1mH2STawyLmknI+56bykFZDsJsUP7CAdhIMUeELXo9YGmbOn4o3Nah6vd5Ot9vd6ff7lfr3dXKMVZnIVVp4MRgMZtLW6LpneJhxpB1lJnfwE49yblRtfPsrLhoLpbqA7fT15jjUERXNQeO6biK11DXYnyCnymdIjI+4jXuJdlJXHqq7cArf620izkXEu5hMj5u7cAQCwbENi6a1WQAAAABJRU5ErkJggg==) no-repeat center center; - width: 16px; - height: 16px; - display: none; - position: absolute; - left: 156px; - top: 14px -} - -.mypcip:hover .btedit { - display: block -} - -.task { - position: absolute; - right: 6px; - top: 14px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold -} - -.softnum { - position: absolute; - left: 154px; - top: 12px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold; - display: none; - color: #fff -} - -.cmdlist li { - border-bottom: 1px solid #dbdbea; - line-height: 48px -} - -.cmdlist li .titlename { - padding-left: 12px; - position: relative -} - -.cmdlist li .state { - float: right -} - -.cmdlist li .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: 0; - position: absolute; - top: 6px; - width: 5px -} - -.cmdlist li .cmd { - height: 200px; - background-color: #424251; - overflow: auto; - line-height: 22px; - color: #fff; - padding-left: 10px; - font-family: arial -} - -#remind td { - vertical-align: middle -} - -#remind .titlename { - position: relative; - text-overflow: ellipsis; - height: 20px; - overflow: hidden; - width: 300px; - white-space: nowrap -} - -#remind .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: -10px; - position: absolute; - top: 6px; - width: 5px -} - -.btn-default[disabled], -.btn-default:active[disabled] { - background-color: #f7f7f7; - color: #bbb; - opacity: 1 -} - -.table-page { - height: 32px -} - -.table-page a { - border: 1px solid #ccc; - float: left; - height: 30px; - margin-left: -1px; - outline: 0 none; - position: relative; - width: 34px; - z-index: 1; - color: #666; - vertical-align: middle; - text-align: center; - line-height: 30px -} - -.table-page a:hover { - border: 1px solid #20a53a; - color: #20a53a; - z-index: 3 -} - -.table-page a.disable { - background-color: #f3f3f3; - cursor: not-allowed; - color: #bbb -} - -.table-page a.disable:hover { - border: 1px solid #ccc -} - -.table-page-select { - float: left; - position: relative -} - -.table-page .table-page-num { - width: 60px -} - -.page-select-ul { - background-color: #fff; - border: 1px solid #ccc; - bottom: 29px; - left: -1px; - line-height: 22px; - max-height: 150px; - min-width: 100%; - overflow: auto; - position: absolute; - top: auto; - width: 60px; - display: none -} - -.page-selected .page-select-ul { - display: block -} - -.page-select-ul li { - padding: 0 12px -} - -.page-select-ul li:hover { - background-color: #f0f0f0 -} - -.sidebar-auto .menu { - background-color: #353d44 -} - -.menu li { - margin-bottom: 1px; - position: relative -} - -.menu li a { - font-size: 15px; - color: #d6d7d9; - display: block; - line-height: 44px; - padding-left: 52px; - background-repeat: no-repeat; - background-size: 16px auto; - background-position: 25px 14px; - border-left: #404040 2px solid -} - -.menu li.current a, -.menu li a:hover { - background-color: #2c3138; - color: #fff; - border-left: #20a53a 2px solid; -} - -.menu .menu_home { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII=") -} - -.menu .current .menu_home, -.menu .menu_home:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKTQAACk0BtZPkxgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNpdO9atVBFATw3/UjBEGidkJSRFFIiKJBsAmaBxDFQptUIoJPYCc2Wmpjo4haRUFMnsEmnXUIKQXFRlEvaiIxY/HfheXCNUUWht2dM+fsYXe2l8Ruxp4d4jN4gDNlvw/70dMQw8ZRXMdFbOAvpjGJ93iHrWEdHMdDfMMcPuMpjiC4hYPDOpjCPazgceGeYS8m8BYj2AZJWpxOspTk9gBfcTPJSpJrlRtMXk5yY0hyxUKSF0lOtQWuJHmV5NIOyRWXk7xOcrVe4iSW8BXj/3mZ+jpfiv5ErThS5udJzjcnHUgyXubKnU3yUrcerR38wSH0sVm4C3iDO+Xm5wu/hX44jI3WB4OePoaPeIIPOm9U3XYVtQV6JfC77PtYxyrW8KPwP9vDWiNFZ5BzOtvO4Fejm8ZJzGqM1BbYLHiE77pPc7fEPuE+FjCGxaLV2+13/gdXJgTGYi2BZQAAAABJRU5ErkJggg==") -} - -.menu .menu_web { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHuSURBVDiNpZO/S9ZRGMU/5xZl0NJQDf2ybAgrGgylGloiCR0jDAO3+4WQlqA/oiHkpUGvgYtFRENL5g+CCEIiqSjMoFKsaJFqScMXfU9DV3vVluiZDs997odzuM+Vbf6n1v+tmVJqA9okHba9CXgF9McY+1fPqtpBSmkH0Al8B45Iema7DDRlyHagK8b4ZQ2gp6dnn6QB2+2SdgFvJW2xPQeUbdcCM5L6gNYY4/QyIKW0AbgGPAb2AueALuAQMA9MApeAu7YnJZ0ALscYyyE7aZf0JsZ4R1I/MAKMAwFYl+0/AW4URXFb0gTQTh4AOGP7Xta7gbEY43NJw8BA1hPAHoA821wdYUxSCZi13QhslPTI9kmgLGkUOG37m6QXwGbbF2OMTUvPuGD7JzCbM2N7FigD87bnJC3r7HyhOsK0pNEY46CkoRxhRNJDYDjGOGz7fdYPsqNP1YBB2y0Ai4uLH4GjKaUG281AS0qpAThQqVSmsrsWYAj+bOIt4Gpvb+/ZEMJ+4BTwLp8ZaASOhRA6UkofgHrgyupFqpN0H7gA7ATGJW21/SNvY10IYcZ2n+3WoiimVgAAuru7a0MIncBXoF7SU9sLwHHgJbDN9vWiKD4v3VkBACiVSqqpqekAzgMHc/s1vz/TTVbVGsC/1i9dw/hm1FHr2QAAAABJRU5ErkJggg==") -} - -.menu .current .menu_web, -.menu .menu_web:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGCSURBVDiNpdLPaw9wGAfw13fExWVq1IzYJEkc5kezgws5zM2S+irOWi7KH+EgB0e1m9ZycMGWm0jLRYqUWNNyIDkw2lq9Hb7P5rsfDvKpT717Pp/n/byf53k3kvif0/GX+AXcx3t8wgQurvexsUrBDozgGw7jBRZwHK+wHbeKdA1BLx6iiZ14i078LJLd+IJRnMVMO8Em3MQT7MH5qnQQ8/iAq7hXeBDXsLCxqjfxBuPVRide4xA2lPxnuIPP2Fo5o5JIMp6ku/BAkuHCp5MMFr6S5Gjh7iRjSSwp6MUpzOEYNmMRJ6v/rmqnG7uwpVpdJljEryKYr9hcJc+3DXIJd1TOMsEMnmMW36vSYzQq4Sn21Vqn0INz/DHSBIYKf8QR9ONMxfuxH9P1ZwiT7Qru4gaGsbfm8a7eUnMZwCUtdx7AdVYaqQ8PtCzbo7XGLvyo/vusNNL0agJabhvB16oyVcM6gZfYhts1q9LX2mv7bSS5nGQyyWzdR0ma6/xdo+Cfz28JnsxkWP6vVAAAAABJRU5ErkJggg==") -} - -.menu .menu_ftp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH6SURBVDiNpdM/qJZlGAbw3/1+n31o/0BJrMFaGmoRRBsM14M4iINLixBxHs6pxSxBmhpCqEk5CL2Pp5ZAdBD/UIJNhdBQzTqJYKkoB8HEY+r53rvB99hnuXlNNzfXfT3X9dzPE5npWTB8WrPWuhvvYwvGOI+vSykn/8uNSQfz8/MvdV33DRaxBlMInMZ9LI7H449mZ2cfLM80y0Xbtiu7rjsZEecz8ws8xBEc7l3swfXBYHB8bm6u+Z9A0zSfYmtmXoyIg/gKV3AJLQ7hV+wcjUYzT0Sota7C2Yg4kJm7sQM/YQOWcAFb+ygn8HHXdVMzMzMPlx1swML09PSPEXEIpzCNc/gBH+D7zPyylHIGt5umeWsywou4B5n5BhZKKTdwFddKKTdxJyLe7PmLWD0pcB2v9PVlvN627WZsxKZa6ya8hj96zsuZuUD/DobD4YWlpaUVtdbP8A62RcRNrO+38SG2I2qtv2AUERcfXyLUWnfhu8zcGBEFv+PVPtp9vI2j+A3vlVKOPSHQi+zHrcz8NiLebZrmr3yE1ePx+OfBYLAfK0opnz9eY9u2WzJzHRJ/Yy3uRsTePnfgSmYexAu4gZX9/NUh9kXElH8xxgjPTfTWR8RmPMBg+XCcGOKTiHh+gtxFxKqu685gXd/7MyJ29g6bCe7teNbv/A8ZE8Q3GMBOFwAAAABJRU5ErkJggg==") -} - -.menu .current .menu_ftp, -.menu .menu_ftp:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGVSURBVDiNpdLdaw9wFAbwzw+jzVspIxdIuXHhQqbGHVmSRLlx4w9YKbkhpVz4A6SkKOWlldA0uXA1Um645motL7NImsnYNI+LnZ9+W63IqW+dzvc8zznPOaeRxP/YgnnixzCISUzgEQ7/DcEK3MEefEMDbfiKI7iKxfMRtKMfT3EePwtwCdM4gVHcnoVL0nxnk0wl2ZvkYZLuJKeSHE+yO0lfkgOZsd4mrgnuSPI4SU+SW0nGkwwkeZ1kKMmDJGNJric5mGQwSVsrQXeSu+V3JbmRZE2SK0kuJuks4i2Vcz/J1iR/tCzH9/I34hM+YATv8bEGublyJrCqdYijWF3+MDagC9uwvd46vK2clVXEogq8rHWdwQ7sq6rraxu92F9rfYYleNVKMI3LuFlVh/ECQyVtEmPow3McLYzGnFM+jc+4hl0YR0rvk/pvw7kmoJFkJ9ZW4g90mrnCk6W7gTe4gGU13PbCjzSS9KOnpYvp0jjrZEvGFBY2i+NeI8kmLG1J/IUODFRn8A6HqsPW8/8ydwb/bL8B1eb4OuOuSusAAAAASUVORK5CYII=") -} - -.menu .menu_data { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHISURBVDiNpZPPi81RGMY/z+3mpmasGBOZEgthQ5SFTDM2svMjOyt9X1eUEhshRbGx1e1cO5M/YVIzSWbjx4RidTeKbl3XgoyYW5rHwvlO35TZzFtn8fac5znPc855ZZvVVL3apJR2AGeAw5JGbI9m6Iukvu0ZIEVEp+TINimlOnANGAc6wCvbHUm/AAHrgZ3AHmA7MC3pVlEUS6WDWaALHIuIb/9xOw3QarVGa7XafduzwGQtg+PA+xXIy9VsNnvAHDBRvYMBcDyldEDSvO1nkj7lCNhea3uLpEO29wOjmbMsUAfOAYu2C+CC7XW2hzL+A1iw3QWu5/0vqgIAD4A7EXFxpQgppRPA1bIvBX4Dt4GJlNIV4DPQlTTIERrApryeA3eBh1WBBkBEnM+n7AO22R7O+ALwISJeZvxkySn/wQD4CDyV9Nj2XET0/7G+ATgIHAEmgbGIaJQO1gCngF22A7iRUloCfgLOpwnoA4+AFvC6GmEGOA1ciogpgHa7vRkYygLfi6LoVdzcI79CGUHAZf5+jh4wb/udpK/5EoclbQX2AruBt8DNiFhUdRpTSmPAWeCopI22R7KDfh6mJ8BURLwpOVrtOP8BlJPKP95zNKgAAAAASUVORK5CYII=") -} - -.menu .current .menu_data, -.menu .menu_data:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF3SURBVDiNpdI5a5VhEAXg58qFRDBi4RIiBkSJoiIoihZi0EpsFWy0FAtbSwvB0uUn2LiUliIWMWgT90i0sRFcYxqFiCYQcizufHK9hUQcGIZh3jnvOYdpJfE/sayn34pLeI4PWKj8jJe4jJHuhVYxaOM8RvEGT6r+RAursQ27sBl3cBGLkkgynuRmklXV/y0Hk9xOMpbkt4RRvMK3JciexkMc6pYwhyl8wlM8wPuSAMuxAQexF4PYif4GYAH7C+g0hrASKwrgO2bxEdfKs0doN7oWkkwmObEED44leVY7f0g4Wbr24Uv9Nl8M+orVECZwH9fR3+56AGer7sEmDFQ/i7d4XP3xZqdhMI93GMfdcnmmx/01OIAjOIxh9DUAwW5sx6lyeRE/atanc1AzuIXXOtfaaoy5l+Rqj1nrk2xJMlLH0z27kmSi28QWzpWJ0zq3MIWvRX8AG4vlDkziAuYagCaGcQZHsQ5rS8JM5Rhu4EWz0Avwz/ELJiL9PSckq44AAAAASUVORK5CYII=") -} - -.menu .menu_set { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH3SURBVDiNpZI/SJZhFMV/90kicsv+EdEQRUNQYC1S0R+ag2hxMKPgvc/r0Ba2FFEpJARhiF/P9QsaGippimgoG4QgI6egICcJEkoIEyI/9L0NvV98llLQmS6c5x7OPc8Rd+d/0LQcUa1W1xZFcR9ocvfOGOPkXwXMbDvQoqov3b0NuA00i8gBYNLM9gBfVXWiviP1E8xsB3AZmANmgHVAt4jMu/sl4DuwFfgI9KvqO4DQYKAFmFXVU8Bj4JqqfsiybAroBZ6r6jFglbtv+MNB6aIHeKKqL4aGhra4eycQRORelmXvU0pHgPYYoy4SSCltFJGDwEngCjANXHT3WyGEeXfvAq4Dze7eKyJ3i6IYyfN8KgCIyAjQDFxQ1VfAaaA/xjiWZdm4iPQBHao6LiLdwJoQwsiiX3D3z0VR1NNd4e5zDfnUACnnWeATsNB4wjYR2Q8cdfcbIYSau58D+kRkwd273X1QRGaAAWC4KIrRPM8nfg/xqrs/jTGOmtlOoKN08zDGOGZm+0SkPcuys7+W3J3Sxd6U0s1ybk0ptda5SqWyOqW0u+QepJTa6lxjD2rASjO7A5wBBszsEEAI4TzQZWaDwBfg23I92AWsV9VnZnYC2OTur0Vks6oOm9lhYFpV3ywp0IiySI/42bzjMca3S71bVuBf8QODpRL9eTmkdgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_set, -.menu .menu_set:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF0SURBVDiNpdJNiM5RGAXw38ukkJVvMYqZRsqYZmyYDTZWbCxsKLG3pCSaWNmNJU2NmhU7pQjFRhYsJCTWykfJRz5eH8fmefn39lLy1FPn3nPv7Zzz3FYS/1Oz/sItwg3cwuo/HWp1KRjEQtzBTizAfHzCDMbwDk97PTCECXzBWyzGYXzDCXzGGjzHJB6DJJ3ekuRc4R1JNja4VUl2FZ5OsrXDNTO4jRcYx1W8wTEcx1xcwna0cbM7xGXYgxF8LakTuIbLZWVdWevHXixvWniY5ECSkVqfbGBJBpOcKjyc5FCSR90WXjXSnV1hdqqNVuH3eInvTQUDSfYnmUkylmRDkvNJ1icZSjJV+wNJriQ5WKr01avPqtfW3O/hNPaVmrO1N14qp35pa/jclORM4dHqDjevMdYLSTZ3uD6/q405mMYHjOJojewIluJHjfdj51L3Vx7GElzHbqzAXazERWzDazzoZaG7+5PcT/Kkwux5rlvBP9dPgIpDWf6ENxgAAAAASUVORK5CYII=") -} - -.menu .menu_folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE3SURBVDiNpZO9SgNBFIW/OySiaBCtbCwE8ReUgGDpE9gExNJmd6O+hM8gmGJn3yKxt7WyEARBUUSbIPhTGFAwxyarSwoZ2AO3mjkf98y9Y5IoI1fKDVQA0jRtOOd2B8BRST5Jkk4IwCSRZVkbOAYeACdpD3iX9DrUpQO+gHaz2XwuRnCSLuM4vpU0AnSAvplNmdlkocbNbN3MTrz3de/9WKVA/07TdN85twbcA5/A8AsLuAO6wI6k2UrhoGZmy3EcH4Zk995vAAd5hA9gBXgJMQ+0ZWanOaAH1IGrEGer1TJgxszOckAFWATOQwDVanUO6EVR9JYD5oGJJEmeAtvfBG7gb4xLwEWgGWA1v59PoQs8Zlm2ANg/RgETwLRz7voXIOnIzBrAdgCgBvgoivowWOUyKv0bfwCvBmEVd9ynHgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_folder, -.menu .menu_folder:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADuSURBVDiNpdO9SkQxEIbh56ynUHQbQbCxEMR/BEGw9B4s1m6t9W5svQgL3d7Wyk4QLES0EcGfQkGbsTgJuywIwTMQEsh8b+ZLJlVEaBOdVmrUad7HQQJO4hQXJYAqWTjHCR4S5BAfeBursoOflP8CIkJEDCJiLq03ozymcgUD9NHDFu7xjfEbjmR7Oo2FemSjizUcl3jHDo6yv0+s47VQDHsYZMAXtnFTKK4wj8sMqLGCq0LAYjr0PQOWMIOnQsAu7hi+8SquC8WwkfPzKzzjEcsaf39FpEpnccuwE3uadp4oAHQ1rX42Cvh3tP6Nv5Cebn/RRiyLAAAAAElFTkSuQmCC") -} - -.menu .menu_day { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNpZMxSFxBEIa/ed49tQsBQdEqgrVgithFW1sr62PhKttXpjy0vWb2WjkQrk6rjWUgjY3ICorYGCFV9C7vxuJ24fE4Y8SBZf/9d3b2n/1ZMTPeE1l10ev1Frz3Wy8lq+pXVf1Y5cTM6Ha7jTzPd4BtYBUYAHO184/ALnAJnJRl+b3dbo8aAM1m8xB4AO6BT2b2C5ivFxCRv8AtsJ5l2SZQYGao6kBVFyNeNjOmDVVdifOSqh6Z2aQF7/0xcGNmF4BMkV9tw0RkDfjgnGs14kYJXInIOTALjGOhukUZ8AQ0gc+JIJI/nHNnIYRWCME5505CCC7i04hbzrkz4Gc8Q1IwEwedTmcvXfcSruZnNZKiKPpFUfT/hZnYX1YVyGu31hRk6X2SgnEi/lPBb6JTSUFuZsM3KPiTWk4FbkXki/d+CORMbK3aKEzsTXhDRK6rBb4B+2bWAkbVN5kSDRG5E5EDiJ/pPfEM08DH4VH64rEAAAAASUVORK5CYII=") -} - -.menu .current .menu_day, -.menu .menu_day:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEPSURBVDiNpdO9SkNBEIbh58RoFKwEwT8QFOwEQQvt1NbW+7Cx8B7svAZLa1tbG0sbEQQh2KigjcZoxsI9YT2EGMnCMt8us+98s8sWEWGYUausp7HTJ38bU/lGPYt72MUyFjFeOfyOfdziAudol4BjPOMRS3jCRA/AJ5pYwxaORISIOIuImaTnU+w1F1KcjYjTiOi28IVD3KDoYT93EVhJ+hfgDtdooJNA1SeqoYVRbOSAFq5wmR0q+mhYzQEjaeYJ/XQ3v1bZlCrFH7rw03bXwSBVc10rYaWDzgBVc/0ivVTpYAwf/3DwJrVcAprYTJCx1F/15huZXsc9FOk3TuIAc2hXqlVHHQ84wWsx7Hf+BgvadUGnT3fcAAAAAElFTkSuQmCC") -} - -.menu .menu_logs { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAApJJREFUeJztm71y4yAQx3dvzn4t41dIm8pzAV+6K65LIblId8WVMeReIa9g/FaZcbFXBGdkjBD6iBAyv5kUktB6+QcW7bAAZDKZWwZDGimlGBEV5pJ9nTuDoBFRc853IY2/hTQiogN8dJx192s0GBGVSqmiuWmAAKGGpgYRlSHtGqeAlJIuXkAMMhyLascRcc05177239sYR8QydG7FQCnFrFsrANC+d4JiwJzJAsR2IDZZgNgOxCYLENuB2LT6DnAhpXwFgB8D+BIEEb0tl8v7zWbzPoS9XiNgv98/wYidBwBAxLvT6fR3KHs3PwV6CbDdbp8B4N9AvgRBRG+LxeLXUPZ6xwAhxAMAPAzgSxTyFIjtQGyyALEdiE0WILYDsbl5AXIu0OflnAvMgJwL9DWQc4HEyQLEdmBIOOe6sncZtEXeOwZMDdPp4P3LWY2ALiQ/As71C0RU2rvXSqnCbJdrRNy5tspb1QdMbXu80kEbDY5qFpf/U8oFXoQQjy1+90BErOax874pnYGqCFPKBX5KKX+HNkZE3fF3jtWLmw+CU8oFXoQQf0Ibe4a/rvy5WFUvkguCJvAxuJ7nV/WBpr7x4DDzuSokNQVM4CvBHeF39j/HfBmuHaYYER2UUkVSAkB9oaauK4ezPo8vIKIyNQGcIGLnaTkLAcAKbG2YiwBePCtGWgL45nJdTbO5z2pMprUKcM53Jqpr+5lLhLpcARFLRFwLIdbJfQec8SRCTWghxOfSmNQIsDg2N7nGziFSFqBz5K+SsgBduRg5ITHgfFzm44WRD0z4Yo4rDhj/jgCwsp45zxK1FmBsQgJvNfo7kqHCd4iqUQBPRjUKX73yNMYAzrkWQmCks0IaOkb7TCaTCeE/v6cw3cuvVicAAAAASUVORK5CYII=") -} - -.menu .current .menu_logs, -.menu .menu_logs:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABHNCSVQICAgIfAhkiAAAAf9JREFUaIHtmUFOwzAQRccWmyaN2HIEOAXpIag4BnsWcRfsOQZSD9EgNhyBI3SLnMnSw6IpciI7iUMdB8lv0zTxKDPyOF/6AxCJ/AnW9xARCyLKGWP5TPm0IKKSMVamabqzrbEWgIgFAAgfibmilNpkWVaanl31xInzBREZg30zZuf7CgCAU/Lr9XpzkYwckFL+ti7n/B4AStM6PmNOXogFhCYWEJrBr5AOEV0j4idj7PbCeezTNN1OCXTagbqunzwkDwDwUNf145RApwKSJHkloq8pLxpgnyTJ25RApxZijH0DwN2UF/ni3x/iWEBoYgGhiUJ2IaKQjSIK2ZwgIiEiVVV1CJhDMfn9SyhgDItsISll3vhSxv86TofYN10zDRFBdwYRUQCA0J26PmeOANq+0B+E7Mg5365Wqw/bAillzjkf1a66UzeXkN0opZ4nxA0yl5AdOecvvYmc3LcWRFQO2ZrBhayxEAsAyPX7XUO3qqqDZjUemvMQ/hAzxgqDiSu6bjQR7TrrBMBCP6OmeUCWZcZ2WmQBNkx2+yILMImWTciCF9BojOjcFl0l7q4holIptXESMp+4CBloahx8B87YZmBDLKYAF5RS7+drawvpwnHpIZ9tdGoY6wr9t1Hmnb5bIceswjb/bQ5tq62klLmpzQYH3eChiDED7EhkJn4A1qwofp3F9mcAAAAASUVORK5CYII=") -} - -.menu .menu_control { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF5SURBVDiNpdM9ixNRFMbx3yRZ3/ANbCxEcBubpNJSLIQFaxs7D4JfQSz9CpYWItyPYCOIFtqvhQxopSyCb1gpEuNukrHImewQY7UHLsOce85z/vPMvVXTNA4SAyilfMI2Zpn/n2rV6RtGxOYgE9sITLK5Qa8jVGGezwpH8VgWwRTjiJigj6vYwrVcW5nrZ824pW0Jeh0xSVJhr4M86ez32/qBf6OH04nZCmzk1HbI0qN1AhO8zuYfOJ5i8xWKZq1AREzxpZRyAlfwJiI+r5RVrUCLNOvgtnER93FpDeV0laBnYcwMSimHcQpP7Z+V2xZelA7FkuA8opRyK9/P4gIeYq+Ucj2/fwM3Lc7Mua7AVzzH91LKHZxBExE/s/EudiwOz0u8wLeuwDgidvAMf3APr3LvHR7gfUT8joiPw+HwQwqrmqZRSnmSWL9wEpt4a/Hr5qPRaLeu60OdgcfwKCJutCZeTnNmuXZxpDWqruvG/j1o0vDhkuAg8RdE7nuSY6nc+gAAAABJRU5ErkJggg==") -} - -.menu .current .menu_control, -.menu .menu_control:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFaSURBVDiNpdM7a5RREAbg59uLJOgSkRQhfYSIhWWsQgj+gfgLTJEmBJLa0tZW0MbawtbaUkQEQ8Q/YCWIIAqb7G1S7JzNYdlUOfAxlzPznndevmkiwk1OJ+03nGKMBpNr6lsIdLGBrQJwimP05wCatFEBTHAHL2sGY/zHEMt4jBWMKqZ/8RkXVc8MoJX+MF8bJJth3nczV5i0s2cGUJ82eslgkLlbOU57vngRwAA/sqmftp35AhiFzSKAIX6mv4+3C2pa887YlTjlPMAJtjNeMtVC1k5qgA5Wa2Q8xQvsZnyI51jD3cK+jLCOA/xLyn1TET/m3U4+0M+xRpmfAfzGBzzBM/zB17Rf8B57+ISHpsI+AhEhIt6kbSLiKCLOImIzc72IOIyIbsblex0RMwZLuGf6t73Dd/zK/AVepYDLKV4v7zS5jfUyjXCO25Wok/TLnnRwH1vNTdf5EpkVg1v2kuagAAAAAElFTkSuQmCC") -} - -.menu .menu_soft { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF7SURBVDiNrdPPi41hFAfwz72uoVsUWZOFJbKThYkVe1mpx13YjcmU/NwrZCKRLNRZsrSZErvJ1h+glFgiYxC6Pyze8zSvOwsLzuZ5T9/n/T7n+z3ndCaTiX+JHkTELlzHHozQxQrOl1JeRcReLGIbxtiAN7jcS6JrCT5OcIx9mMcAZ/EBz5N8hCO4UQl240kp5XYtLSJmsZDpdjwspTxr4V9xppv5EP0peVuzEnlumcL7GFaCDn5OXfj+l/wHulXCBIci4rXG2BFmMZP4RhyLiBmNR0McxqgSLGBOY1iNIW7l96LGyNMtfBXznf81B/vzhR0pB37hbillOSIO4hw2JdbBJ9yrEu7gIx7504OLWMYFvMcLax6cwv1K0MPLUsrTWlr2eS7TEZZKKUstfCcGtY3jVnk1+i05E+vnZDPGlaBnfZ+/aMZWnqtT+Df0qoR3ON7qc92FlcQ/YxARB6ztwlG8rQRXNdt4Mn/u5AuXEn+AmziRcroaU6/8BgTXdRpxDzi5AAAAAElFTkSuQmCC") -} - -.menu .current .menu_soft, -.menu .menu_soft:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFsSURBVDiNrdO/jw1RFAfwz4xnVzYIydZeY0tEKETChkIoNGrNtkKyhQL/gUg0YgudQiciGpVEIRsdPdlkQ0usH4vNvHcUc27e9bbkm9zMmfnOPfP9nvneJiL8CwZ5PYS72I8xdmANN/EeQ9zGAkZosYHrTSp4gD14m+QIZ/ATl/AI83iRzcc4jC0RISIeR8S5rMu6EhFvsl6NiOUpfjEinrZpYZwKasyhy7rL+xp7MW6rB5tTL/xKO9Dg9xS/WQ9xJ85jJj12OJ2zgMBJvMs9IyxipgzxFK5lo4JvWMFrHMNV7Kv4DivN/8rBkVQwn3JhC/fwCiewjNnkGnzG/aLgJT7h4ZTHBVzEE3w0yUGHyxgWBQOs4lml7rvet2z4PFfBASzVOZj1N+YqO2F7DnapcjCwPQdfTXLQ6v9KjR8YFAsfcMEkByXrG8l/wRKOmpyVs1gvQzyoP23D3NzkF27oc3Acd7A77bT6od76A2AskgeNVoIQAAAAAElFTkSuQmCC") -} - -.menu .menu_firewall { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAG0SURBVDiNhdNPiI5RFAbw352ZsGBDMhkpYyE12cxOyYIFayW7SY2zkCxIlkrZKIrJjPe+X1FksrQQo/xp2IhZiPXUxIoaGxa+mGsx78dn+vDUvXVu53l6znO6qZSig6mpqbX9/f37cRB7sDOl1C6lzOIZHkXEK11IpRR1Xe8upRzFKNqYxzt8xGpsxy6swwCeYjIi5gcaoTt4i8BcRBQ90Gq11i8tLY3gCjZibABKKUu4FxGvexE7GB8fX8RsVVVPUkqDGjtQGqv/RM75CJ6nlH6gT+dCwvf/kE9hGlvwrZfAqrqu+3LOQz3Ix3EJpyPipeUwdQt8RbuUshkLjdUOeQzXcD4iLnfptvmdwXtsjYgPOefrmM45f0Y/buJiRJzrIm+wvOJfDl5gBCLiBFp4iPuYiIizK6YaSim96Ra4jeGc86ZG5BhyQz65Io9hDJZSZkApRSlFVVUTVVXd7dR/O1VVPaiq6mqn7jiQUjqD0Zxzd1B/IOc8iW1N7zKv+zM1I8zgE8YjYqF534EbTegHImKxp0DTnHABh3ALa3AY0ys20Vugg7qu95ZSHuML9kXEXK++ny1tzgEddf2OAAAAAElFTkSuQmCC") -} - -.menu .current .menu_firewall, -.menu .menu_firewall:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNhdMvaJZRGAXw36viFrRoENxw4IJYLDMJalkxC8OkrC5qNAwEi+CC4lAsikGMY0FQmGIQ/AMKyjDIUGYyqMXgmB7Dd9/P63j37cAN73OfczjPc+7bJFFhFyZxCsdxGGt4hqd4hFc1oSkCxzCNiUJYwXt8xRDGcQS7sQNPMI8VSST5lGQxydEkTal1nT1JTiR5k+RuEtuKkz9YwGv8N9MGfCvjLBUnfYEUq1vhDEbwu+W2Ag3WtyCfx32M4leXwM7yPdJBnsFVXMALvWWqBX7qbX8/PherLc7hBi5hrqqvURaBVRzAF9wsVr9jO+7gCmYr8l69iLXxXEyyUMV1O/9wrSPOpSRnk/QFxpK8S7Kvarq1CflgkuW2t764nuTBgEfUnoe1cH0xnORjkrkB5PkkH5IMdQkott4meVzGauuHkjxP8rI85z6n/ZlqNLiM07iHYUyVZGY3Ng+a9WSS9SQ/kkxs1vcXeVqZSyUF+yoAAAAASUVORK5CYII=") -} - -.menu .menu_exit { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEYSURBVDiNpZOtTkNBEEbPFCQIUHgEBoJAYFDgERgSCBjSvU9Q0/AkX5+ApB6PI6lAYPhJUCgEgkApkHyI/nBpb28DPWozu3MyuzsTtpmG2f5CUs32BlABouBsAK8RUU8pPY4IImIfOLd9k4/n+IyII2AZGBUAM8BZlmXX48qVtAksFl6hx/y45B5N4C4fqExI+EVK6QJYkLT9L0GPJ+BU0hYUP9YAScfACtDh52c6wAPQlHRQKoiIN+B5WGC7DXzYfi8VVKvV5nCs0WgsRcSh7b0sy1qlgiJszwG1lFIL/viIknaAdkrpsh8bruBlgmOXbq8UduIXcCLpvkDc31+j20wDBgdty/Y6sMroMJnuda+A2/xGTDvO32OQXrvPg7l3AAAAAElFTkSuQmCC") -} - -.menu .current .menu_exit, -.menu .menu_exit:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADvSURBVDiNpdO7LkRRFAbg7wwlBZVeoSEKEtHSayUSGs+g8RwqbyDR6ycaiYRC45KoVAqFuAySpdhzODn2mWMyf7KSvdflz7oWEWEUjFfee1hCB0XGt8AL9vGQI9jECa5r+hKf2MZsE8EYjnA1IOMVTDeVAJMDguEYt1VFpyWgji6msNaUwX/wiAN84LSNYAdz6PmdTA/3UjlbbQSveMoQvPUzeBcRpVxExGrl3yQzEdGNiOWIGLqJMCEt3TnDT2G9n/5Zqaj34LmFYEPalewmfmEXdxni0r4gdf8HVcdDLGLe32MKqdxL3FQNxajn/A0ZS19hUhhlTwAAAABJRU5ErkJggg==") -} - -html .menu .menu_home:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPPz8+np6d3d3c/Pz8vLy8XFxb+/v729vbu7u7e3t7W1tbGxsa+vr62traurq6mpqaenp6WlpaOjo6GhoZ2dnZubmwrPOpmZmQzLPJeXl5WVlRLFPhLDPhy5RI+Pjx63RImJiYeHhyypSiirSIODgzKfToGBgTabUDibUDiZUH5+fj6TUnx8fECRVEKPVHp6ekSNVkCPVESLVnZ2dnR0dEyDWnBwcFR4XlZ2Xlh2XlpyYGZmZgAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJHgA7ACwAAAEAEAAOAAAGhsCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFDOkMkBUQ2B0g1HyIdNUUGX0OTOy0jNyMrRQecOzYIKjsyKDspMDsvpkQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAkeADsALAAAAQAQAA4AAAZxwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hKJgZfQ5FUJgeWOzYIKlQvm0QGFEM6QxUGSQgAARYcIhsWAQAIQkEAIfkECR4AOwAsAAABABAADgAABnfAnXC4MyVIxORwphAsTBWGR7ljERw7iSDSINiSKgJkGDkukqRBJDkRXIjpCZVSMAk1BA31TsjsHhcgM1QzIBcNQwUhVCQFRDYHSEomBl9DkVQmB5Y7NggqQzpDL5tEBhQ5NR8iHTU4FQZJCAABGC47LhgBAAhCQQAh+QQJHgA7ACwAAAEAEAAOAAAGfcCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFUJAVENgdISiYGX0ORQzpDJgeWOzYIKjs1HyIdNTsvnUQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAUeADsALAAAAQAQAA4AAAaCwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hCOkMmBl9DkTs1HyIdNUUHmDs2CCo7Mig7KTA7L6JEBhQ5JSc7KCU4FQZJCAABGC47LhgBAAhCQQAh+QQFHgA7ACwGAAkABQADAAAGDsDdTiesfUSdWmt0G62CACH5BAkyADsALAAAAQAQAA4AAAYVwJ1wSCwaj8ikcslsOp/QqHRKdQYBADs=") -} - -html .menu .menu_web:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAPHx8e/v7+vr6+np6efn5+Xl5eHh4d/f393d3dXV1dHR0c3NzcvLy8nJycfHx7+/v729vbu7u7m5ube3t6+vr62traurq6mpqaenp6WlpaGhoZ+fn52dnZubm5mZmZeXlxTDPpOTkyqrSiqpSoeHhy6nSiynSoWFhS6lTDCjTDKhTjSfTjadTjadUDSdTjabUDyXUjqXUjyVUj6TUkCRVHp6ekSNVkaLVkSLVnh4eEqHWHZ2dkiHWEqFWE6BWlB+XFJ8XFJ6XFR6XFZ4XlZ2XlpyYFxwYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgBHACwAAAEADwAOAAAHbYBHgoIdCgIACRyDizsTFQ0XGA4WEjuLNQMnH4shJwU1gw8di4sdD4IaGKSkGBpHC5argzsMRwYbsoMbB0cHHrmCHghHDDnARzm1GhnHGa5HEL+yHxCDmJqcJASg1hEUDRgZkxLGqxoJAQGJi4EAIfkEBQ4ARwAsBAADAAgAAgAABxGAR0dDPkBDgi0gNCgqNCAsgQAh+QQFDgBHACwEAAUACAACAAAHEYBFKUAogyNGPjZHPT5HMD+BACH5BAUOAEcALAMABwAKAAIAAAcWgDcoJjclKDciKTc6Kig9KCo6Jis6gQAh+QQFDgBHACwEAAkACAABAAAHCoA+Nkc6PkcwP4EAIfkEBQ4ARwAsBAAKAAgAAQAABwqARShAJSlAI0aBACH5BAUOAEcALAQACwAIAAIAAAcRgCwgMyUpMiAvR0dDPkJDioEAIfkECTIARwAsAAABAA8ADgAABxaAR4KDhIWGh4iJiouMjY6PkJGSk5SBADs=") -} - -html .menu .menu_ftp:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/f39+3t7evr6+Pj49/f39nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7W1tbOzs7Gxsa+vr62trampqaenp6OjowDZNp+fnwLXNp2dnQTVOATTOJubmwrPOpmZmQrNOgzLPJeXl5WVlRi/QBa/QJGRkY+PjyC1RI2NjSSxRomJiYeHh4WFhYODg4GBgX5+fnx8fECRVESNVkSLVkaLVkiHWHR0dE6BWnJyclB+XFJ8XFR6XlR4Xlp0YFh0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJMgBJACwAAAEAEAAOAAAHvYBJgoIhCAICCiuDi4IOEQsBAQ4QE4yCCR06DRUWD0AYDYwaATALNBweMA8sAB6LCC8QAwwEBQwDESwIgzQOSTURQBMVQBA5SQw3grFJKBdJGRtJFCxJDzKCOApJNg01DA42DDZJCziDCRsMAhQGBxIBDBsJiygCORYjqB8XNgAmjDZ8SCLjxg0YSTRkGBSkh48fO3i0UKGiRY8dP3z4GBKDhMcSIkCIBCGihEcSM5AUWVlkyAmRKYiwLHIkEAAh+QQFDgBJACwAAAEADwALAAAHj4BJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBQIACH5BAUOAEkALAAADAACAAMAAAcIgEE9MSRIRYEAIfkEBQ4ASQAsAgAMAAEAAwAABwWAPiRFgQAh+QQJDgBJACwAAAEADwAOAAAHIIBJgoOEhYaHiImKi4yNjo+QkY8/OzyMJCUijEVFQ4mBACH5BAkOAEkALAAAAQAPAA4AAAepgEmCgiEIAgIKK4OLSQ4RCwEBDhATjEkJHToNFRYPQBgNixoBMAs0HB4wDywAHoMILxADDAQFDAMRLAiCNA5JNRFAExVAEDlJDDdJsUkoF0kZG0kULEkPMkk4Ckk2DTUMDjYMNkkLOIIJGwwCFAYHEgEMGwmDKAI5FiOoHxc2ACaLNnxIIuPGDRhJNGQQFKSHjx87eFgaFIOExRIiQGgEwQhJkY9FJg4KBAAh+QQFDgBJACwAAAEADwAOAAAHsYBJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBSkh48fO3gs6rHjh6AYJDKWEAGiIwgRJUgIQlKkpKUkJpMEAgAh+QQFDgBJACwNAAwAAwADAAAHC4A+PkMkJDNFRUeBACH5BAUOAEkALAQADAAGAAMAAAcOgElJLSoqLYKISUVDiYEAIfkECTIASQAsAAABABAADgAABx2ASYKDhIWGh4iJiouMjY6PkJGSk5SEJyAgKUSFgQA7") -} - -html .menu .menu_data:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/Hx8e/v7+np6ePj4+Hh4d/f393d3dfX19XV1dPT08XFxcPDw8HBwb+/v7u7u7m5ube3t62traenp6OjowDZNgLXNp2dnZubmwrPOpmZmQrNOpeXlxLFPhTDQBTDPhbBQBTBQBa/QBq9Qhi9Qh63RBy5QiC1RCC1RiKzRiSxRouLi4mJiYeHhyirSCynSoWFhS6lTIODgzKhTjSfTjadTjKfToGBgTibUDiZUHx8fD6TUnh4eHZ2dkiHWHR0dEyDWnJyclB+XFZ4XlZ2Xlh0YFxwYGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFMgBJACwAAAEADwAOAAAHr4BJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYSQEyOTErKCQkKCoxNDIBvAYaqYIcB8oCHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi/PAEkCBQ4cQaBBHg4F4AA0GQsGBgkJB6xloMFOQQRHP3jwkCcoAgJBFBg0qNAiRw4XGSIskADKxwQDAwIEIGAgAg1BgQAAIfkECQ8ASQAsAAABAA8ADgAABxaASYKDhIWGh4iJiouMjY6PkJGSk5SBACH5BAkPAEkALAAAAAAPAA8AAAe0gEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhigMSsoJCQoKjGgATI5qYI0MgFJAQYayUkcB80CHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi+CHABJAgUOHEGgQR4OBeQANBkLDBhIkOCAtgw04imI4OgHDx73BEVAIIgCgwYVWuTI4SJDhAUSQPmYYGBAgAAEDESgISgQACH5BAkPAEkALAAAAQAPAA4AAAevgEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhhJATI5MSsoJCQoKjE0MgG8BhqpghwHygIcEAcMDhAQDgwGDxwCSQAcgjIaFhYaL88ASQIFDhxBoEEeDgXgADQZCwYGCQkHrGWgwU5BBEc/ePCQJygCAkEUGDSo0CJHDhcZIiyQAMrHBAMDAgQgYCACDUGBAAAh+QQJDwBJACwAAAAADwAPAAAHtIBJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYoDErKCQkKCoxoAEyOamCNDIBSQEGGslJHAfNAhwQBwwOEBAODAYPHAJJAByCMhoWFhovghwASQIFDhxBoEEeDgXkADQZCwwYSJDggLYMNOIpiODoBw8e9wRFQCCIAoMGFVrkyOEiQ4QFEkD5mGBgQIAABAxEoCEoEAA7") -} - -html .menu .menu_control:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAPcAAOTk5N/f38bUxsnQycTTxMfOx8XLxcPKw8bKxsfHx8TIxMbGxsXGxcXFxcfFx8PGw8TExMXExcbCxsfCx8PDw8PEw8PCw8PBw8HBwcW/xcHAwb7AvsK/whzVHBzTHCDOICLLIjC2MDO0MzOzMzKzMjWxNTOyMzWvNTavNjirOE2hTTqpOjunOzylPEmhSUqgSjqlOoaGhj+iPz6iPoSEhD6hPj6fPj+fP4ODg0CfQDqhOoKCgkGcQUObQ4CAgEWXRUaWRkWWRUqQSkiSSEiRSEyNTFuHW3p6eluGW3l5eU6KTliFWE6JTk2JTVaFVlWFVWB/YFGGUXV1dVGEUVWAVVSAVHRzdHNzc3FxcVV9VXZvdlp4Wm9vb2hyaGF0YVt2W1x1XFx0XGFyYWNwY15yXl1zXWtra15xXmNtY2FuYWpqamBvYG1obWJtYmlpaWJsYmhpaGNqY2hoaGJrYmRqZGRpZGdnZ2VpZWVoZWRoZGZmZmVnZWdlZ2VlZWdkZ2djZ2hjaGxdbG9ab2ZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCACDACwAAAEADwAOAAAIhgAHAUgAYcGCBggbGISQIMCgBlwG9dFjp6IdPX0GYaEwCMKgQWqO4NixA8cRNR89LvjoJglJkkncpBy0EqSPGDRoxPCBsiPNj3KwSLlyRQoWOTNrflzK1GeDplAHcWQQtWmFQQOqMi0wCIHWpQoGWfj6cSzEQXC63IG6UWCCBhA2KDS4sGFAACH5BAUIAIMALAIACQACAAMAAAgJAMUMcsHjSZSAACH5BAUIAIMALAMACgADAAQAAAgQAGGUGbSCxaBBJ4IM8gIlIAAh+QQFCACDACwEAAoAAwAEAAAIEAAHhTkzKAeVFiCoDDIyKCAAIfkEBQgAgwAsBQAIAAMABAAACBAABzFJM6jElj0pwAwaMiggACH5BAUIAIMALAYABwADAAUAAAgSAAfpGFSlQ5FBQEIMGpRiYZiAACH5BAUIAIMALAgACQADAAQAAAgQAAcN2jJIiIwpHogMujEoIAAh+QQFCACDACwJAAcABQAFAAAIGQAHCRw4kI2gHiK+iBlEwoQNFYOaBMqyJCAAIfkECTIAgwAsAAABAA8ADgAACBkABwkcSLCgwYMIEypcyLChw4cQI0qcSDEgADs=") -} - -html .menu .menu_firewall:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP////39/fv7+/f39+3t7evr6+np6efn5+Xl5ePj4+Hh4d/f393d3dnZ2dfX19XV1dHR0cvLy8nJycfHx8PDw8HBwb+/v729vbu7u7m5ube3t7W1tbOzs7Gxsa+vr6enp6WlpaOjo5+fnwDZNpeXl42NjYuLi4mJiSitSCqpSiypSoeHhyirSC6lTIWFhTKhTjiZUDyXUnx8fECRVEKPVHp6enh4eEqHWEiHWHR0dFB+XHBwcFpyYGZmZgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAwA9ACwAAAAADwAQAAAGdMCesEeiHAACR2c17J0oiUXEE/pgHAsFptYjQFzN4c6UmPQKoXAYIzl/1M1M5AyCDzdzwtveu8wRdXwXbQ8afD0SGD0dEIcNIj01CDl2NQeUPRZzcA8WTQWKYRUGYTkJDlw9MgwLO3AaBh0bBhyHJQEDYE1BACH5BAUEAD0ALAMABgACAAMAAAYHwB6PN+qlggAh+QQFBAA9ACwFAAcAAQADAAAGBUDaCBUEACH5BAUIAD0ALAYACAACAAMAAAYHwBtuNGKpggAh+QQFCAA9ACwIAAcAAQADAAAGBcDZSBUEACH5BAUIAD0ALAkABgABAAMAAAYFwNioFQQAIfkEBQgAPQAsCgAFAAEAAwAABgVA2OgVBAAh+QQFBgA9ACwLAAQAAgADAAAGB0Aab6SD9YIAIfkECTIAPQAsAAAAAA8AEAAABhXAnnBILBqPyKRyyWw6n9CodEqtJoMAOw==") -} - -html .menu .menu_day:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAANPT09HR0c3NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7m5ubW1tbOzs7Gxsa2trampqaenp6WlpaOjo6GhoQDZNpubm5WVlY+Pj42NjYuLi4mJiYeHh4WFhYODg35+fnZ2dnR0dHJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAlACwAAAEADwAOAAAGesCSUKQRGkua0RGjAFQm0EkFoMAIFY9HAHPpXjABiINREohKoaMwHSKUBgvKJEKvTygLw1vCyWj+gBkcEnoGHCUWFmpCHAglBYeLRhsHjxuSRhqOBJeYSI4FnZgeegMdniUgBSULER8dHrCxG7QRDUIPCQq7vLsJDyVBACH5BAUKACUALAcABwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAcAAgABAAAGBECLJQgAIfkEBQoAJQAsBAAJAAIAAQAABgRAiyUIACH5BAUKACUALAcACQACAAEAAAYEQIslCAAh+QQFCgAlACwKAAkAAgABAAAGBECLJQgAIfkEBQoAJQAsBAALAAIAAQAABgRAiyUIACH5BAUKACUALAcACwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAsAAgABAAAGBECLJQgAIfkECTIAJQAsAAABAA8ADgAABhTAknBILBqPyKRyyWw6n9CodEoNAgA7") -} - -html .menu .menu_soft:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAOPj4+Hh4d/f393d3dvb29nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw7+/v729vbu7u6+vr62traurq6mpqaenp6WlpZmZmZeXl5WVlRTDQBTDPpOTkxLDPhbBQBi/QBTBQBq9Qha/QJGRkRi9Qhi9QI+Pjxq7Qhy5RB63RBy5QiC1RiC1RI2NjSKzRiSxRouLiyKxRiavSCitSCSvSImJiSatSCypSiirSCynSoWFhYODg4GBgTadUDadTjabUDSdTjibUDiZUDyXUn5+fjqXUjyVUnx8fHh4eESLVkaLVkaJWEaJVkqHWEiHWEqFWE6DWkyDWk6BWlB+WlB8XFJ8XFJ6XFR6XlR4Xlh2Xlp0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFHgBeACwAAAEAEAAPAAAHtoBeXTEdIR0qWF4/BgECAQtKXjIrQkBAMjVeEAwVFBUHCl4iRV6lUy5eCyalXhgAXidArE6oCxqsFQGiRKxStR+sGLonLk5MTTwsXgoSHBscCwJeWDUu1jFUXjMLCt0OOKzh4uI+DwndDDNeOAzdCQ89XgUKzhwTCcsR9dFeAxasKUItAFZKmBcCFFhxEHirVK5+F1jBwKeKVYZXDA50qtDAgZcHnDwdwKdEQaMABcD1KHAywZFAACH5BAUeAF4ALAAAAQAQAAcAAAdhgF5LDAECAQY+XloqHSEdMVxeDQcVFBUND147MkBAQisuXgMXXqUwCV4uUKVeRh1eBBSsHApeMUysRK8EFqwptTGrpa6wChwbHBMIqTxNTE4xIV49DwrWCzBeUzEu3TVWgQAh+QQFHgBeACwJAAEABwAPAAAHWIA/BgECAQtKEAwVFBUHCgsmXpIYAAsakl4Vhh+YGAEKEhwbHAsCMwsKqQ44mK1WMS6xNlguOk1MTjEhMVCYRh0xTJhEHS69kkkdNjJAQEIrLlckHSHFWoEAIfkECR4AXgAsAAABABAADwAAB3CAXoKDhIWGh4iJiouMjY6PiVg2LpQxVV44DAoKCQ89XiQuTkxNPCxeChEcGxwLAl4iRINSLl4LH4MYAV4nQINOtQsagxW7IkWDU8EmgxkAXjErQkBAMjVeDwwVFBUHCV5cLh0hHSRWXj0FAQIBCUeBADs=") -} - -html .menu .menu_logs:hover { - background-image: url("data:image/gif;base64,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cENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA2LjAtYzAwMyA3OS4xNjQ1MjcsIDIwMjAvMTAvMTUtMTc6NDg6MzIgICAgICAgICI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIKICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcERNPSJodHRwOi8vbnMuYWRvYmUuY29tL3htcC8xLjAvRHluYW1pY01lZGlhLyIKICAgIHhtbG5zOnN0RGltPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvRGltZW5zaW9ucyMiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZmYyZWY0OTItM2JkZC05OTQ3LTk5YWMtMmQzMzI3MDVhMzIzIgogICB4bXBNTTpEb2N1bWVudElEPSJmZWI4MmYyNS1mZmZiLTNkYjgtY2EzMC0wNjE4MDAwMDAwM2YiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxYTVlZTUyOS03NWE4LTg0NDctODMzMi01NDRiZTAyMTFlYzUiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjItMTItMTVUMTc6Mzc6NTErMDg6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIyLTEyLTE1VDE3OjM3OjUxKzA4OjAwIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAyMi0xMi0xNVQxNzozNzoyMSswODowMCIKICAgZGM6Zm9ybWF0PSLliqjnlLsgR0lGIgogICB4bXBETTp2aWRlb0ZyYW1lUmF0ZT0iMjUuMDAwMDAwIgogICB4bXBETTp2aWRlb0ZpZWxkT3JkZXI9IlByb2dyZXNzaXZlIgogICB4bXBETTp2aWRlb1BpeGVsQXNwZWN0UmF0aW89IjEvMSIKICAgeG1wRE06c3RhcnRUaW1lU2NhbGU9IjI1IgogICB4bXBETTpzdGFydFRpbWVTYW1wbGVTaXplPSIxIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9IjlkMWU4YTJjLWE4NGUtM2U1Ni04ZDI0LWRjZjEwMDAwMDA2YyIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0xMi0xNVQxNzozNzo1MSswODowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciAyMDIwLjAgKFdpbmRvd3MpIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249ImNyZWF0ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MTJjMmZmZmItZGM3NC02ODQyLWIxOTEtMDhhMzgzM2RhMDgyIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE0OjUwOjAyKzA4OjAwIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmYxNDI3ZWYyLTQ1ZjEtMmY0Zi1hNDY0LTU4MzgyOGMxNjJlNyIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0xMi0xNVQxNDo1MjoxNCswODowMCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL2NvbnRlbnQiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NTZjODkxYmQtMzQ4YS01OTQ3LTk2MDItMjFkM2JlZmRiZjAwIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE1OjQwOjE5KzA4OjAwIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvY29udGVudCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJkZXJpdmVkIgogICAgICBzdEV2dDpwYXJhbWV0ZXJzPSJzYXZlZCB0byBuZXcgbG9jYXRpb24iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NThkMTQ4M2QtZjAyMy02ZTQ4LWE0NTYtZDg3YTA1YWEzMWQ1IgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE1OjQwOjI2KzA4OjAwIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmRmMzhmYzNiLTE5ZGEtNGY0MC1iMjU2LWVhYzIxOWFjZGNmMiIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0xMi0xNVQxNjowMzo0NiswODowMCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL2NvbnRlbnQiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NzZjOTFlOGYtZTkyZC1iMjQ1LWJkYWItODdiYThmMDU5ZjkwIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE3OjM3OjQ4KzA4OjAwIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvY29udGVudCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiNDJmZDUyMi00NGM4LTdmNDUtYTJjNS1iMjU0ZDdiNDdjMDciCiAgICAgIHN0RXZ0OndoZW49IjIwMjItMTItMTVUMTc6Mzc6NDgrMDg6MDAiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MThjMjc4NjktMDAyMS04YzRmLThjMDMtMzVlZTU3MzAwZGQ5IgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE3OjM3OjUxKzA4OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIDIwMjAuMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZmYyZWY0OTItM2JkZC05OTQ3LTk5YWMtMmQzMzI3MDVhMzIzIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE3OjM3OjUxKzA4OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIDIwMjAuMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii9tZXRhZGF0YSIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgIDx4bXBNTTpEZXJpdmVkRnJvbQogICAgc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2ZGUxZmEzNC0wNzNlLTJiNDgtYTNmYi0yY2Y3ZjFhMzczNjAiCiAgICBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjZkZTFmYTM0LTA3M2UtMmI0OC1hM2ZiLTJjZjdmMWEzNzM2MCIKICAgIHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxMmMyZmZmYi1kYzc0LTY4NDItYjE5MS0wOGEzODMzZGEwODIiLz4KICAgPHhtcE1NOkluZ3JlZGllbnRzPgogICAgPHJkZjpCYWc+CiAgICAgPHJkZjpsaQogICAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjg1NmYwYzA0LWU0MzUtNzk0Ny1iMmQwLTkwMDliMjlhYzdmNiIKICAgICAgc3RSZWY6ZnJvbVBhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgIHN0UmVmOnRvUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgc3RSZWY6bWFza01hcmtlcnM9Ik5vbmUiLz4KICAgIDwvcmRmOkJhZz4KICAgPC94bXBNTTpJbmdyZWRpZW50cz4KICAgPHhtcE1NOlBhbnRyeT4KICAgIDxyZGY6QmFnPgogICAgIDxyZGY6bGk+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24KICAgICAgIGRjOmZvcm1hdD0iYXBwbGljYXRpb24vdm5kLmFkb2JlLmFmdGVyZWZmZWN0cy5sYXllciIKICAgICAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NWI1MGU1ZTktMTRlMy0zZDQyLWEyMmMtMDY5NjNjNGY3ZWJjIj4KICAgICAgPGRjOnRpdGxlPgogICAgICAgPHJkZjpBbHQ+CiAgICAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij7mt7HoibIg5ZOB6JOd6ImyIOe6r+iJsiAxPC9yZGY6bGk+CiAgICAgICA8L3JkZjpBbHQ+CiAgICAgIDwvZGM6dGl0bGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgIDwvcmRmOmxpPgogICAgIDxyZGY6bGk+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24KICAgICAgIGRjOmZvcm1hdD0iYXBwbGljYXRpb24vdm5kLmFkb2JlLmFmdGVyZWZmZWN0cy5sYXllciIKICAgICAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N2U1M2FmMjctYzQzNS1iNjQ5LTk4MTAtNTYzYjE4ZmI5ZDI1Ij4KICAgICAgPGRjOnRpdGxlPgogICAgICAgPHJkZjpBbHQ+CiAgICAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij7nn6nlvaIgODEucG5nPC9yZGY6bGk+CiAgICAgICA8L3JkZjpBbHQ+CiAgICAgIDwvZGM6dGl0bGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgIDwvcmRmOmxpPgogICAgIDxyZGY6bGk+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24KICAgICAgIGRjOmZvcm1hdD0iYXBwbGljYXRpb24vdm5kLmFkb2JlLmFmdGVyZWZmZWN0cy5jb21wIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4NTZmMGMwNC1lNDM1LTc5NDctYjJkMC05MDA5YjI5YWM3ZjYiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPuWQiOaIkCAxPC9yZGY6bGk+CiAgICAgICA8L3JkZjpBbHQ+CiAgICAgIDwvZGM6dGl0bGU+CiAgICAgIDx4bXBNTTpJbmdyZWRpZW50cz4KICAgICAgIDxyZGY6QmFnPgogICAgICAgIDxyZGY6bGkKICAgICAgICAgc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1YjUwZTVlOS0xNGUzLTNkNDItYTIyYy0wNjk2M2M0ZjdlYmMiCiAgICAgICAgIHN0UmVmOmZyb21QYXJ0PSJ0aW1lOjBkNzY4MDAwZjI1NjAwIgogICAgICAgICBzdFJlZjp0b1BhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOm1hc2tNYXJrZXJzPSJOb25lIi8+CiAgICAgICAgPHJkZjpsaQogICAgICAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjdlNTNhZjI3LWM0MzUtYjY0OS05ODEwLTU2M2IxOGZiOWQyNSIKICAgICAgICAgc3RSZWY6ZnJvbVBhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOnRvUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgICAgc3RSZWY6bWFza01hcmtlcnM9Ik5vbmUiLz4KICAgICAgICA8cmRmOmxpCiAgICAgICAgIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6YWM5NGVmNzAtMmVkNC03YjQzLTg5YTktNzU0NTkwMjExOGVmIgogICAgICAgICBzdFJlZjpmcm9tUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgICAgc3RSZWY6dG9QYXJ0PSJ0aW1lOjBkNzY4MDAwZjI1NjAwIgogICAgICAgICBzdFJlZjptYXNrTWFya2Vycz0iTm9uZSIvPgogICAgICAgIDxyZGY6bGkKICAgICAgICAgc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpiMjY3NDgyNS05MjE4LTQ1NDYtYjA4ZS01ZTI3M2QyNDAyMjgiCiAgICAgICAgIHN0UmVmOmZyb21QYXJ0PSJ0aW1lOjBkNzY4MDAwZjI1NjAwIgogICAgICAgICBzdFJlZjp0b1BhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOm1hc2tNYXJrZXJzPSJOb25lIi8+CiAgICAgICAgPHJkZjpsaQogICAgICAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmNiYmJlOTY0LTM0MmYtYTM0ZC1iM2Q2LTI3ZGUyOTE3MWFhYSIKICAgICAgICAgc3RSZWY6ZnJvbVBhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOnRvUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgICAgc3RSZWY6bWFza01hcmtlcnM9Ik5vbmUiLz4KICAgICAgIDwvcmRmOkJhZz4KICAgICAgPC94bXBNTTpJbmdyZWRpZW50cz4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICAgPHJkZjpsaT4KICAgICAgPHJkZjpEZXNjcmlwdGlvbgogICAgICAgZGM6Zm9ybWF0PSJhcHBsaWNhdGlvbi92bmQuYWRvYmUuYWZ0ZXJlZmZlY3RzLmxheWVyIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphYzk0ZWY3MC0yZWQ0LTdiNDMtODlhOS03NTQ1OTAyMTE4ZWYiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPue7hOWQiCAxNS5wbmc8L3JkZjpsaT4KICAgICAgIDwvcmRmOkFsdD4KICAgICAgPC9kYzp0aXRsZT4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICAgPHJkZjpsaT4KICAgICAgPHJkZjpEZXNjcmlwdGlvbgogICAgICAgZGM6Zm9ybWF0PSJhcHBsaWNhdGlvbi92bmQuYWRvYmUuYWZ0ZXJlZmZlY3RzLmxheWVyIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpiMjY3NDgyNS05MjE4LTQ1NDYtYjA4ZS01ZTI3M2QyNDAyMjgiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPuefqeW9oiA2NS5wbmc8L3JkZjpsaT4KICAgICAgIDwvcmRmOkFsdD4KICAgICAgPC9kYzp0aXRsZT4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICAgPHJkZjpsaT4KICAgICAgPHJkZjpEZXNjcmlwdGlvbgogICAgICAgZGM6Zm9ybWF0PSJhcHBsaWNhdGlvbi92bmQuYWRvYmUuYWZ0ZXJlZmZlY3RzLmxheWVyIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpjYmJiZTk2NC0zNDJmLWEzNGQtYjNkNi0yN2RlMjkxNzFhYWEiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPuefqeW9oiA4MC5wbmc8L3JkZjpsaT4KICAgICAgIDwvcmRmOkFsdD4KICAgICAgPC9kYzp0aXRsZT4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICA8L3JkZjpCYWc+CiAgIDwveG1wTU06UGFudHJ5PgogICA8eG1wRE06dmlkZW9GcmFtZVNpemUKICAgIHN0RGltOnc9IjE2IgogICAgc3REaW06aD0iMTYiCiAgICBzdERpbTp1bml0PSJwaXhlbCIvPgogICA8eG1wRE06ZHVyYXRpb24KICAgIHhtcERNOnZhbHVlPSIyNSIKICAgIHhtcERNOnNjYWxlPSIxLzI1Ii8+CiAgIDx4bXBETTpzdGFydFRpbWVjb2RlCiAgICB4bXBETTp0aW1lRm9ybWF0PSIyNVRpbWVjb2RlIgogICAgeG1wRE06dGltZVZhbHVlPSIwMDowMDowMDowMCIvPgogICA8eG1wRE06YWx0VGltZWNvZGUKICAgIHhtcERNOnRpbWVWYWx1ZT0iMDA6MDA6MDA6MDAiCiAgICB4bXBETTp0aW1lRm9ybWF0PSIyNVRpbWVjb2RlIi8+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAOw==") -} - -html .menu .menu_set:hover { - background-image: url("data:image/gif;base64,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") -} - -html .menu .menu_folder:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPX19efn593d3dXV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcHBwb29vbm5ube3t7W1tbOzs7Gxsa+vr62traurq6mpqaenp6WlpaOjo6GhoQDZNp+fn52dnZubm5mZmZeXl5WVlZOTk4+Pj4WFhX5+fnx8fHp6enh4eHZ2dnJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgAsACwAAAIAEAAMAAAGYEAWCHEohFjIpJJVGJlKD0oGQ8VoVEkCdslFajEMiuVCJlskjwTz1OgmSQsWQhRxIysg1mLysbMcKywKCih2J3UsAgZ+Hh1IABJ+ECVIARwlmJmZJAxJHkQHoaKhA3ksQQAh+QQJDgAsACwAAAAAEAAOAAAGGUCWULgpGo/DpHLJbDqf0Kh0Sq1ar9isMAgAIfkECQ4ALAAsAAABABAADQAABmZAllC4KRqPQhDiUAgNn8/CyFR6UDKYLEajGhK60LDwi2FQLJd02iJ5JFiFU0M8JC1YCFGELqyAWAsTH3wsDissCgoofCd7LAIGhB4dQgAShBAlQgEcJZ6fnyQMQx5LB6eopwN/LEEAIfkECQ4ALAAsAAACABAADAAABmNAFghxKGyOyCSLVRiZSg9KBkPFaFRLFgGb7Xq1KgyDYrmYzRbJI8E8Nb5d0oKFEEXg2QqItZh88EsOKywKCiiAJ3csAgaALB4dSwASjhAlSwEcJZucnCQMWR5EB6SlpAN7LEEAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQFDgAsACwAAAIAEAAMAAAGZUAWCHEohFjIpJJVGJlKD0oGQ8VoVEkClrXper9ILYZBsVzOZ4vkkWCeGsslacFCiCJxZQXEWkw+eUkOKywKCiiBLCd4LAIGiSweHUgAEpAQJUgBHCWdnp4kDEkeRAemp6YDfCxBACH5BAkOACwALAAAAgAQAAwAAAYUQJZwSCwaj8ikcslsOp/QqHQqDAIAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQJDgAsACwAAAIAEAAMAAAGY0AWCHEobI7IJItVGJlKD0oGQ8VoVEsWAZvterUqDINiuZjNFskjwTw1vl3SgoUQReDZCoi1mHzwSw4rLAoKKIAndywCBoAsHh1LABKOECVLARwlm5ycJAxZHkQHpKWkA3ssQQAh+QQJDgAsACwAAAEAEAANAAAGZkCWULgpGo9CEOJQCA2fz8LIVHpQMpgsRqMaErrQsPCLYVAsl3TaInkkWIVTQzwkLVgIUYQurIBYCxMffCwOKywKCih8J3ssAgaEHh1CABKEECVCARwlnp+fJAxDHksHp6inA38sQQAh+QQJDgAsACwAAAAAEAAOAAAGZkCWULgpGo/DpHIJQhwKoaWyMDKVHpQMZovRqIaEr3QZxjAolotabZE8EqzCqTEWkhYshChSZ1VALAsTH30OKywKCih1J3wsAgZ9Hh1CABJ9ECVCARwlnp+fJAxDHk4Hp6inA4AsQQA7") -} - -html .menu .menu_exit:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAOPj4+Hh4dnZ2dXV1c3NzcvLy8fHx8PDw8HBwb+/v729vbu7u7W1ta2trampqaenp6Ojo6GhoZ2dnQzNOgjPOpeXlxq9Qhi9QI+Pj4uLi4mJiSatSCqpSjCjTDSfToGBgTadTjKfTjibUDqZUDqXUDyVUkCRVESNVnZ2dkyFWnR0dE6BWlB+XFB8XFR4Xlh2XlxwYF5uYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCAAyACwCAAEACwAOAAAGQMBGgUAsEgyoAEMSaTYlgwzgI6taZYkKQHO1ZlDb7jUsrpLLZ3G6ux5zxRgwVayoBBQPh17/EGQgCQiCgwcLKkEAIfkEBQgAMgAsBwAHAAEAAgAABgRA0ykIACH5BAUIADIALAgABwABAAIAAAYEwI4oCAAh+QQFCAAyACwJAAcAAgACAAAGBkCPhzQKAgAh+QQFCAAyACwLAAcAAQACAAAGBEDPKAgAIfkEBQgAMgAsDAAHAAIAAgAABgZA0KbECQIAIfkEBQgAMgAsDAAFAAIAAgAABgZAVypmCQIAIfkEBQgAMgAsDgAGAAIAAwAABgfAlYySmqyCACH5BAUIADIALAwACQADAAEAAAYFQNiFFQQAIfkEBQgAMgAsDAAKAAIAAQAABgTAFysIACH5BAkyADIALAIAAQAOAA4AAAYUQJlwSCwaj8ikcslsOp/QqHRqDAIAOw==") -} - -.pos-box { - height: 50px; - width: 100%; - box-shadow: 0 3px 2px 0 rgba(0, 0, 0, 5%) !important; - border-radius: 4px; -} - -.conter-box{ - box-shadow: 0 1px 2px 0 rgba(0,0,0,.1); -} - -.position { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII="); - background-position: 17px 14px; - background-repeat: no-repeat; - background-size: 20px auto; - line-height: 50px; - padding-left: 30px; -} - -.search { - width: 306px; -} - -.ser-text { - border: #20a53a 1px solid; - height: 30px; - width: 250px; - padding: 0 8px; - margin-top: 10px; - outline: none; -} - -.ser-text:focus { - border-color: #20a53a; -} - -.ser-text:focus + span.glyphicon-search { - color: #20a53a; -} - -.ser-sub { - width: 38px; - height: 30px; - border: 0; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxAAAAMQBz4pYTAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE2SURBVDiNndJBS1VRFMXx9UzNQUQD0caFk6YhEUFCOPETiJQfIygQbNQgCCeOGjYLGjRJkPoKoSGIRKADs1nQoEIEfw3cry6v+3oPN2z2ufuu8z/7nruC9ORNvMRXHOIFbrTogjQfOniKX87iR2P9HSv/A1zA+xK/wRwmMYV5vKt3r/sBnpTgcb9R8aw0D3sB1/GzTum3uZsfSnupCVgr8vwQgKXSLnR7I0nuJJFkN4PjY9WZbmMkyXGtx4YAjFY9aQI2knSS3B4CMFd1508HV3CE3QHfP17G2sdo7298UJfzChMtmy9jszTLbT4InpfgCx6Vme5hFd/8jW1cbQME93Hg39jDYmOKT5hGOmi7rNkk15KcJvmcZKv6E0k2k9xNsp1keZBx2vIi3tYkG+cBxJmV13HrN6Szg7ZCfX3NAAAAAElFTkSuQmCC") no-repeat center #20a53a; - cursor: pointer; - margin-right: 2px; - margin-top: 10px -} - -.title { - height: 50px; - line-height: 50px; - border-bottom: 1px solid #eee -} - -.title h3 { - display: inline-block; - line-height: 50px; - margin-right: 12px -} -.tablescroll { - overflow: auto; - border: #e6e6e6 1px solid; -} -.divtable .table { - border: 1px solid #ddd; - color: #666; - font-size: 12px; - margin-bottom: 0; -} -.divtable{ - position: relative; -} -.table-fixed-box{ - overflow-y:auto; -} -.ui-selectable{ - padding-bottom: 0; -} - -.divtable .table thead th { - vertical-align: inherit; - background-color: #f6f6f6; - border-bottom: 1px solid #e6e6e6; - color: #666; - font-weight: normal; - padding: 8px; -} -.divtable .table_toolbar{ - left: 8px; - bottom: 6px; - position: absolute; -} -.table .btlink{ - word-break: break-word; -} -.table .btlinkbed { - color: #666; - padding: 1px 5px; - border: 1px solid transparent; - white-space: nowrap; - overflow: hidden; - float: left; - display: block -} - -.table .btlinkbed:hover { - border: #ddd 1px solid; -} - -.dataTables_paginate { - display: block; - height: 35px; - margin-bottom: 0px; - margin-top: 5px; - position:relative; -} - -#filePage{ - margin-bottom: 10px; -} - -.page { - /* line-height: 16px; */ - text-align: right; - padding: 8px 0; - /* margin-right: 1px; */ - height: 44px; - /* border: 1px solid #e6e6e6;*/ - border-top: none; - margin: 0; -} - -.site_type { - height: 30px; - line-height: 30px; - bottom: -9px; - position: absolute; -} - -.site_type span{ - display: inline-block; - height: 30px; - line-height: 30px; - padding: 0 10px; - font-family: "Arial"; - font-size: 13px; - color: #666; - float: left; - border: 1px solid #ececec; - border-right: none; - /* border: 1px solid #ececec; */ -} - -.site_type select{ - border-radius: 0px; -} -.site_type_table{ - padding: 15px; - min-height: 100px; -} -.edit_site_type{ - padding:0 15px; -} -.edit_site_type .line .info-r{ - margin-left:0px; -} -.edit_site_type input{ - width: 80% !important; -} -.edit_site_type .btn{ - margin-right: 0px; -} -.page div { - float: right -} - -.page span, -.page a { - display: inline-block; - height: 28px; - line-height: 28px; - padding: 0 10px; - font-family: "Arial"; - font-size: 13px; - color: #666; - float: left; - border: 1px solid #ececec; - border-right: none; -} - -.page a.prev { - border-left: #ddd solid 1px -} - -.page a:hover { - background: #e8e8e8; - color: #20a53a; - text-shadow: 0 1px 0 #fff; - cursor: pointer -} - -.page spanold { - font-size: 12px; - padding: 4px 10px!important; - border-style: solid; - border-width: 1px; - border-color: #ddd #ddd #ccc; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - display: inline-block; - line-height: 16px; - background: #f5f5f5; - color: #333; - text-shadow: 0 1px 0 #fff -} - -.page .Pcurrent { - color: #fff; - background: #20a53a; - cursor: default; - border-color: #20a53a; -} - -.page .Pcount { - color: #333; - padding: 0 10px; - margin-right: 0px; - border-right: 1px solid #ececec; -} - -.system-info-con { - width: 100% -} -.sys-li-box{ - height:100px; - background-color:#f9f9f9; - border:#f0f0f0 1px solid; - margin-left:20px; - max-width:230px; -} -.sys-li-box .name{ - line-height:40px; - margin-top:8px; -} -.sys-li-box .val a{ - font-size:26px; - font-family:arial; -} -.sys-i-c-box { - box-sizing: border-box; - max-width: 350px -} - -.sys-i-c-box .siteinfo-box { - height: 110px; - position: relative; - width: 100% -} - -.sys-i-c-box .siteinfo-box h3 { - background: #20a53a; - color: #fff; - font-style: normal; - height: 110px; - left: 0; - line-height: 110px; - position: absolute; - text-align: center; - top: 0; - width: 56px -} - -.siteinfo-box-right { - border-bottom: 1px solid #ddd; - border-right: 1px solid #ddd; - border-top: 1px solid #ddd; - height: 110px; - margin-left: 56px; - overflow: hidden -} - -.siteinfo-box-right h4 { - font-size: 32px; - height: 68px; - line-height: 60px; - overflow: hidden; - padding-top: 8px; - text-align: center -} - -.siteinfo-box-right .sbr-btn { - color: #ccc; - display: block; - font-size: 14px; - height: 27px; - line-height: 22px; - padding-top: 5px; - text-align: center -} - -.siteinfo-box-right .sbr-btn a { - color: #20a53a; - font-size: 16px; - padding: 0 10px -} - -.circle-box h3, -.circle-box h4 { - line-height: 30px -} - -.circle { - width: 100px; - height: 100px; - position: relative; - border-radius: 50%; - background: #20a53a; - margin: 0 auto -} - -.pie_left, -.pie_right { - width: 100px; - height: 100px; - position: absolute; - top: 0; - left: 0 -} - -.left, -.right { - width: 100px; - height: 100px; - background: #ccc; - border-radius: 50%; - position: absolute; - top: 0; - left: 0 -} - -.pie_right, -.right { - clip: rect(0, auto, auto, 50px); - transition: transform 1s ease-in 0s; - -webkit-transition: -webkit-transform 1s ease-in 0s; - -moz-transition: -moz-transform 1s ease-in 0s -} - -.pie_left, -.left { - clip: rect(0, 50px, auto, 0); - transition: transform .4s ease-in 1s; - -webkit-transition: -webkit-transform .4s ease-in 1s; - -moz-transition: -moz-transform .4s ease-in 1s -} - -.mask { - width: 88px; - height: 88px; - border-radius: 50%; - left: 6px; - top: 6px; - background: #FFF; - position: absolute; - line-height: 88px; - font-size: 18px; - color: #20a53a -} - -@-webkit-keyframes shineGreen { - from { - -webkit-box-shadow: 0 0 10px #999 - } - 50% { - -webkit-box-shadow: 0 0 15px #20a53a - } - to { - -webkit-box-shadow: 0 0 10px #999 - } -} - -.shine_green { - -webkit-animation-name: shineGreen; - -webkit-animation-duration: 3s; - -webkit-animation-iteration-count: infinite -} - -.mem-release { - cursor: pointer -} - -.mem-re-min { - background: url(/static/img/ico/rocket_min.png) no-repeat center center; - height: 35px; - left: 35px; - position: absolute; - top: 2px; - width: 20px; - z-index: 9; - opacity: .9; -} - -.mem-re-con { - background: url(/static/img/ico/ico-rocket.gif) no-repeat center center; - width: 100%; - height: 100%; - position: absolute; - z-index: 9; - display: none; - top: 15px -} - -.mem-release:hover .mem-re-con { - display: block -} - -.soft-man .col-lg-3 { - border-bottom: 1px solid #ececfb; - border-left: 1px solid #ececfb; - border-right: 1px solid #ececfb; - margin-right: -1px; - margin-bottom: -1px; - height: 148px; - cursor: pointer; - padding: 0 -} - -.soft-man .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .08) inset; - -webkit-transition: all .25s ease; - transition: all .25s ease -} - -.soft-man .dashed-border { - border: 1px dashed #20a53a; - z-index: 1 -} - -.soft-man .no-bg:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} - -.soft-man .image { - height: 40px; - margin: 30px 0 20px; - text-align: center -} - -.soft-man .sname { - text-align: center; - color: #999 -} - -.soft-man .col-sm-3>div { - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.soft-man .spanmove { - background: url(/static/images/move.png) no-repeat; - width: 30px; - height: 30px; - display: none; - position: absolute; - top: 5px; - right: 5px; - z-index: 3 -} - -.soft-man .col-lg-3:hover .spanmove { - display: block -} - -.soft-man .text { - background-color: rgba(0, 0, 0, 0.3); - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - display: none -} - -.soft-man .text a { - background-color: #fff; - border-radius: 4px; - display: block; - height: 30px; - line-height: 30px; - margin: 59px auto; - text-align: center; - width: 100px -} - -.soft-man-con p.status { - line-height: 40px; - margin-bottom: 10px; - font-size: 14px -} - -.soft-man-con .sfm-opt .btn { - margin-right: 15px -} - -.soft-man .sname .glyphicon-True { - color: #20a53a; - margin-left: 10px -} - -.soft-man .sname .glyphicon-False { - color: red; - margin-left: 10px -} - -.soft-man { - position: relative -} - -.soft-man .rowbg { - position: absolute; - top: 0; - width: 100%; - z-index: 0; -} - -.soft-man .rowbg .col-lg-3 { - cursor: default; -} - -.soft-man .rowbg .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} -.bw-info{ - height:80px; -} -.bw-info > div{ - float:left; - padding-top:25px; - text-align:center; -} -.bw-info > div p{ - margin-bottom:7px; -} -.bw-info > div a{ - font-size:14px; - color: #666; -} -.bw-info .ico-up{ - width:14px; - height:8px; - background-color:#f7b851; - display:inline-block; - margin-right:3px; -} -.bw-info .ico-down{ - width:14px; - height:8px; - background-color:#52a9ff; - display:inline-block; - margin-right:3px; -} -.bw-info ul li { - height: 56px; - line-height: 56px; -} - -.bw-info ul li.bi-line { - border-bottom: #ddd solid 1px; -} - -.bw-info ul li span { - display: block; - margin-right: 15px; -} - -.btn-zhm { - border-radius: 0 3px 0 0; - display: none; - position: absolute; - right: 42px; - top: 0; -} - -.ellipsis_text { - width: 220px; - text-overflow: ellipsis; - overflow: hidden; -} - -.ssl_state_info{ - padding: 15px 30px; - background: #f5faf2; - margin-top: 5px; - border-radius: 4px; - border: 1px solid #EAF1F2; - font-size: 12px; - position: relative; -} - -.ssl_state_info .state_item{ - width: 49%; - height: 25px; - line-height: 25px; - display: inline-block; - vertical-align: top; -} - -.ssl_state_info .state_item span{ - height: 25px; - line-height: 25px; - vertical-align: top; -} - - -.ssl_state_info .state_item span:nth-child(1){ - font-weight: bold; - color: #666; -} - - - -.ssl_state_info .state_item .switch{ - display: inline-block; -} - - -.ssl_state_info .state_item .btswitch-btn{ - margin-bottom: 0; - height: 1.9rem; - width: 3.2rem; - position: relative; - top:2.5px; -} - -.custom_certificate_info{ - margin-top: 15px; -} - -.custom_certificate_info .state_item{ - width: 48%; - height: 50px; - display: inline-block; - margin-right: 4%; -} -.custom_certificate_info .state_item:nth-child(2){ - margin-right: 0; -} - -.custom_certificate_info .state_item span{ - width:100%; - margin-bottom: 5px; -} - -.custom_certificate_info .state_item textarea{ - width: 100%; - height: 250px; - line-height: 25px; -} - -.line { - padding: 5px 0 -} - -.line .span_tit { - display: inline-block; - text-align: right; - width: 64px -} - -.line .tname { - display: block; - float: left; - height: 32px; - line-height: 32px; - overflow: hidden; - padding-right: 20px; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - width: 100px -} - -.line .info-r { - margin-bottom: 5px; - margin-left: 100px; - position: relative -} - -.placeholder { - cursor: text; - left: 20px; - line-height: 24px; - position: absolute; - top: 27px -} - -.bt-form-submit-btn { - background: #f6f8f8; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bt-form-submit-btn .btn:first-child { - margin-right: 4px -} - -.btn-danger { - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -.bt-w-main { - height: 555px -} - -.bt-form { - height: 100% -} - -.bt-w-menu { - float: left; - background-color: #f0f0f1; - height: 100%; - width: 110px -} - -.bt-w-menu p { - cursor: pointer; - height: 40px; - line-height: 40px; - padding-left: 20px; - position: relative; - text-overflow: ellipsis; - overflow: hidden -} - -.bt-w-menu p a { - display: block -} - -.bt-w-menu p .spanmove { - display: none -} - -.bt-w-con { - margin-left: 110px; - position: relative -} - -.explain-describe-list{ - border: 1px solid #ececec; - border-radius: 6px; - margin: 0 auto; - margin-top: 20px; - margin-bottom: 20px; - background: #f7f7f7; - padding: 15px; - list-style-type: inherit; -} - - -.agreement-box label{ - font-weight: 400; - margin: 0 0 0 5px; - vertical-align: middle; - cursor: pointer; -} - -.agreement-box input{ - height: 15px; - width: 15px; - vertical-align: middle; - margin-top: 0; - cursor: pointer; -} - -.label-input-group input { - margin-top: 0; - margin-right: 5px; - vertical-align: -2px -} - -.label-input-group label { - margin-bottom: 0; - font-weight: normal -} - -.bingfa .line { - margin-bottom: 10px -} - -.bingfa .line .span_tit { - text-align: right; - width: 120px; - display: inline-block -} - -.bingfa .bt-input-text { - width: 100px -} - -.ssl-con-key { - width: 47% -} - -.ssl-con-key textarea { - height: 240px; - line-height: 18px; - width: 100%; - margin-top: 5px -} - -.ssh-item { - float: left; - margin-left: 10px; - padding-top: 5px -} - -.ss-text em { - color: #555; - float: left; - font-style: normal; - line-height: 32px; - padding-right: 2px -} - -.view1, -.view2 { - margin-bottom: 15px; -} - -.searcTime { - position: relative:z-index:999; - margin-top: 11px -} - -.searcTime .tit { - float: left; - padding: 5px 10px; - display: none -} - -.searcTime .gt { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left; - line-height: 16px -} - -.searcTime .gt.on, -.searcTime .gt:hover, -.searcTime .ss .st:hover, -.searcTime .ss .st.on { - background-color: #20a53a; - color: #fff -} - -.searcTime .ss { - display: inline-block; - position: relative; - float: left; - line-height: 16px; -} - -.searcTime .ss .st { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left; -} - -.searcTime .ss .time { - background-color: #fff; - border: 1px solid #ddd; - display: none; - padding: 10px; - position: absolute; - right: -1px; - top: 27px; - width: 306px; - z-index: 99; -} - -.searcTime .ss .time span { - margin-bottom: 10px; - display: block; -} - -.searcTime .ss .time input { - border: 1px solid #ddd; - height: 22px; - padding: 0 5px; - margin-left:5px; - width: 227px; -} - -.searcTime .ss .time input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); -} - -.searcTime .ss .sbtn { - background-color: #20a53a; - border: 1px solid #20a53a; - color: #fff; - line-height: 24px; - text-align: center; - cursor: pointer; -} - -.plan { - padding: 10px 20px; - margin-bottom: 10px; -} - -.plan .typename { - line-height: 34px; - width: 120px; - font-size: 12px; -} - -.plan .textname { - height: 34px; - line-height: 34px; - float: left; -} - -.plan .planname input { - height: 34px; - width: 260px; - padding: 0 12px; -} - -.plan .dropdown button b { - font-weight: normal; - font-size: 12px; -} - -.plan .dropdown-menu { - min-width: 100px -} - -.dropdown-menu>li>a { - padding: 3px 12px -} - -.plan-submit { - margin-left: 141px -} - -.plan_hms { - position: relative; - height: 34px -} - -.plan_hms span { - float: left; - height: 32px; - line-height: 32px; - position: relative -} - -.plan_hms .name { - border-left: 1px solid #ccc; - width: 44px; - text-align: center; - background-color: #f6f6f6 -} - -.planSign { - margin-left: 10px; - height: 35px; - line-height: 35px -} - -.planSign i { - margin-right: 8px; - font-size: 12px; - font-style: initial; - color: red -} - -.plan_hms span input { - float: left; - width: 48px; - height: 32px; - line-height: 32px; - border: 0; - text-align: center; - font-size: 12px -} - -.plan_hms span:first-child input { - border-radius: 4px 0 0 4px -} - -.plan_hms span:last-child { - border-radius: 0 4px 4px 0 -} - -.plan .dropdown button { - width: 94px -} - -.txtsjs { - width: 450px; - height: 80px; -} - -.sl-s-info { - width: 210px; - border: #ccc 1px solid; -} - -.important-title { - display: flex; - color:red; - background-color: #fef3e2; - line-height: 46px; - margin-bottom: 15px; - padding-left: 10px; - box-shadow: 0 1px 2px 0 rgba(0,0,0,.2); -} - -.important-title>p { - flex: 1; -} - -.rec-box-con .onekey { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - margin: 20px auto 15px; - cursor: pointer; -} - -.rec-install { - padding: 16px; - width: 100%; - float: left; -} - -.rec-install .rec-box { - width: 290px; - float: left; -} - -.rec-install h3 { - font-size: 20px; - margin-bottom: 5px -} - -.rec-box-con { - border: #ddd 1px solid; - border-radius: 3px; - padding: 10px -} - -.rec-install .rec-box:hover .rec-box-con,.rec-box-con-hover { - background-color: #f9f9f9; - border: #20a53a 1px solid -} - -.rec-box-con .ico img { - width: 22px; - margin-right: 10px -} - -.rec-box-con ul li { - line-height: 26px; - margin-bottom: 5px -} - -.fangshi { - line-height: 19px; - margin-top: 10px; - display: block; - margin-bottom: 15px -} - -.fangshi label { - font-weight: normal; - margin-right: 44px; - float: right -} - -.fangshi label input { - vertical-align: -2px; - margin-left: 5px -} - -.zun-form-new .fangshi { - padding: 0 30px 0; - line-height: 30px -} - -.zun-form-new .fangshi label { - height: 20px; - line-height: 20px; - margin: 4px 20px 0 0 -} - -.zun-form-new .version { - padding: 0 30px -} - -.zun-form-new .version select { - height: 28px; - margin-left: 30px; - width: 120px -} - -.sec-install-btn { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - cursor: pointer; - margin-top: 10px -} - -.rec-box-con .onekey { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - margin: 20px auto 15px; - cursor: pointer -} - -.rec-box-con .onekey:hover, -.sec-install-btn:hover { - background: #10952a; - background: linear-gradient(#10952a, #088d22) -} - -.phppz { - margin: 14px 0 -} - -.phppz span { - padding: 10px -} - -.phppz textarea { - width: 500px; - margin: 0; - vertical-align: bottom; - padding: 0 5px; - height: 38px; - margin-right: 20px -} - -.soft-man-con .user_pw { - display: none; - margin-top: 30px -} - -.soft-man-con .user_pw_tit { - margin-top: 30px; - padding-top: 30px; - border-top: #ccc 1px dashed -} - -.soft-man-con .user_pw p { - margin-bottom: 15px -} - -.soft-man-con .user_pw_tit .tit { - float: left; - line-height: 22px -} - -.soft-man-con .user_pw span, -.soft-man-con .user_pw_tit span { - width: 75px; - display: inline-block; - text-align: left; -} - -.soft-man-con .user_pw span { - margin-right: 10px -} - -.btswitch-p { - margin-left: 10px -} - -.soft-man-con .user_pw input { - width: 200px -} - -.soft-man-con .conf_p { - margin-bottom: 10px -} - -.soft-man-con .conf_p input { - width: 100px -} - -.soft-man-con .funarea { - width: 100%; - height: 80px; - line-height: 22px; - margin-bottom: 10px -} - -.soft-man-con .ver .btn { - vertical-align: 0; - margin-left: 10px -} - -.webEdit-box .soft-man-con .phpmyadmindk { - width: 100px; - display: inline-block; - margin: 0; - padding-right: 0 -} - -.softlist td img { - width: 24px; - height: 20px; - margin-right: 5px -} - -.softlist .glyphicon-folder-open { - cursor: pointer; - color: #efd566 -} - -.softlist .btswitch+.btswitch-btn { - width: 2.4em; - height: 1.4em; - margin-bottom: 0 -} - -.wafConf_checkbox label { - font-weight: normal; - margin-right: 20px -} - -.wafConf button { - vertical-align: 0 -} - -.wafConf_cc { - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; - margin: 10px 0; - padding: 15px 0 -} - -.wafConf_cc span { - margin-right: 5px -} - -.wafConf fieldset { - border: 1px solid #ccc; - border-radius: 3px; - float: left; - padding-bottom: 0; - width: 240px -} - -.wafConf fieldset:nth-of-type(2) { - margin: 0 10px -} - -.wafConf legend { - border: 0 none; - font-size: 14px; - margin: 0 6px; - padding: 3px; - width: auto -} - -.wafConf fieldset input { - margin-left: 4px -} - -.wafConf fieldset .table { - margin-top: -1px; - margin-bottom: 0 -} - -.wafConf fieldset .table tr td:nth-of-type(2) { - width: 42px -} - -.wafConf fieldset .table-overflow { - height: 210px; - overflow: auto; - margin-top: 10px; - border-top: #ddd 1px solid -} - -.wafConf-btn { - border-bottom: #ddd 1px solid; - margin-bottom: 12px; - padding-bottom: 15px; - height: 45px -} - -.wafConf-btn span { - float: left; - margin-right: 8px; - line-height: 33px -} - -.wafConf-btn .btn { - margin-right: 10px -} - -.wafConf-btn .ssh-item { - margin-right: 50px -} - -.gzEdit { - padding: 13px 20px -} - -.gzEdit button { - margin: 8px; - width: 140px -} - -.setting-con p { - line-height: 30px -} - -.setting-con p .set-tit { - display: inline-block; - height: 22px; - margin-right: 10px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: -2px; - width: 90px -} - -.setting-con p .disable { - background-color: #ededed -} - -.setting-con p .inputtxt { - width: 260px -} - -.setting-con p .modify { - margin-left: -40px; - vertical-align: 0; - position: relative; - z-index: 10; -} - -.setting-con p .set-info { - margin-left: 10px; -} - -.set-submit { - margin: 20px 0 10px 100px; -} - -.changepath { - height: 500px; -} - -.changepath .path-top { - height: 50px; - line-height: 50px; - padding-left: 10px; - border-bottom: #aaa 1px solid; -} - -.changepath .path-top .btn { - margin-right: 10px -} - -.changepath .path-top .btn span { - -moz-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - -o-transform: scaleX(-1); - transform: scaleX(-1); -} - -.changepath .path-top .place { - display: inline-block -} - -.changepath .path-top .place span { - color: #444; - font-size: 12px; - font-weight: bold -} - -.changepath .path-con-left { - width: 130px; - height: 450px; - float: left; - border-right: #aaa 1px solid; - padding-top: 5px -} - -.changepath .path-con-left dl dt { - background: url("../img/ico-computer.png") no-repeat left center; - height: 30px; - line-height: 30px; - padding-left: 23px; - font-size: 14px; - font-weight: normal; - margin-left: 10px -} - -.changepath .path-con-left dl dd { - line-height: 30px; - padding-left: 12px; - cursor: pointer -} - -.changepath .path-con-left dl dd span { - color: #666 -} - -.changepath .path-con-right { - float: left; - height: 450px; - overflow: auto; - width: 520px -} - -.changepath .path-con-right .default li { - width: 25%; - float: left; - text-align: center; - margin-top: 20px; - cursor: pointer -} - -.changepath .path-con-right .default li span { - font-size: 40px; - color: #666 -} - -.path-con-right .file-list { - display: none -} - -.path-con-right .list-list td { - padding-left: 10px; - cursor: pointer -} - -.list-list span.glyphicon { - color: #666; - margin-right: 10px; - font-size: 18px -} - -.list-list span.glyphicon-folder-open { - color: #edca5c -} - -.list-list span.glyphicon-cloud-download { - font-size: 12px -} - -.newFolderName { - border: #ccc 1px solid; - height: 30px; - padding: 0 5px; - width: 90% -} - -.path-con-right .list-list td .delfile-btn { - display: none; - float: right; - font-size: 14px; - text-align: center; - width: 18px -} - -.path-con-right .list-list td .delfile-btn:hover { - color: red -} - -.path-con-right .list-list tr:hover .delfile-btn { - display: block -} - -.path-con-right .list-list td .btn-xs { - margin-top: 4px -} - -.getfile-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - padding: 8px 20px 10px; - text-align: right; - width: 100% -} - -.warning_info { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; - height: 40px; - line-height: 20px; - padding: 10px 15px; - border-radius: 4px; -} - -.success-msg { - width: 100%; - padding: 30px 50px; - font-family: "Microsoft Yahei"; - float: left; - position: relative -} - -.success-msg .pic { - position: absolute; - top: 50%; - margin-top: -100px -} - -.success-msg .pic img { - width: 158px -} - -.success-msg .suc-con { - float: right; - width: 260px -} - -.success-msg .suc-con h3 { - font-size: 16px; - margin-bottom: 15px -} - -.success-msg .suc-con p { - line-height: 23px -} - -.success-msg .suc-con .p1 { - border-bottom: 1px dashed #aaa; - color: #000; - height: 30px; - margin-bottom: 8px -} - -.success-msg .bottom-btn { - margin-top: 60px; - width: 100%; - float: left -} - -.success-msg .bottom-btn a { - width: 200px; - height: 40px; - line-height: 40px; - text-align: center; - display: inline-block; - border-radius: 5px; - margin-left: 30px; - color: #fff; - font-size: 16px; - cursor: pointer -} - -.success-msg .bottom-btn .close-btn { - background-color: #cbcbcb -} - -.success-msg .bottom-btn .blue-btn { - background-color: #5cb85c -} - -.tasklist { - padding: 15px -} - -.tab-nav { - border-bottom: #cacad9 1px solid -} - -.tab-nav span { - background-color: #ddd; - background: -moz-linear-gradient(top, #f6f6f6, #ddd); - background: -webkit-gradient(linear, 0% 0, 0% 100%, from(#f6f6f6), to(#ddd)); - background: -ms-linear-gradient(top, #f6f6f6, #ddd); - height: 32px; - line-height: 32px; - padding: 0 12px; - border: #cacad9 1px solid; - color: #444; - display: inline-block; - margin: 0 -5px -1px 0; - cursor: pointer -} - -.tab-nav .on { - background: #fff; - border-bottom: #fff 1px solid; - color: #444 -} - -.tab-con { - overflow: auto; - padding: 10px -} - -.tab-con ul.cmdlist { - list-style-type: decimal -} - -.tab-con ul.cmdlist li { - position: relative; - list-style-type: decimal; - list-style-position: inside; - line-height: 40px; - border-bottom: #dbdbea 1px solid; - margin-top: 6px -} - -.tab-con ul.cmdlist li .com-progress, -.tab-con ul.cmdlist li .state, -.opencmd { - float: right; - margin-left: 20px; - color: #535362 -} - -.tab-con ul.cmdlist li .line-progress { - position: absolute; - bottom: -1px; - left: 0; - height: 1px; - background-color: #20a53a -} - -.tab-con ul.cmdlist li .cmd { - border: 0 none; - border-radius: 0; - display: block; - width: 570px; - height: 200px; - line-height: 22px; - padding: 0 10px; - background-color: #333; - color: #eee; - overflow: auto -} - -.yuandian { - width: 10px; - height: 10px; - border-radius: 5px; - background-color: #20a53a; - z-index: 999999 -} - -.DrawRecordL i, -.DrawRecordR-T i { - background: url(/static/img/Detailsbg.png) no-repeat -} - -.DrawRecordCon { - font-size: 14px; - height: 422px; - overflow: auto -} - -.DrawRecord { - background: url(/static/img/DrawRecordord.png) repeat-y -204px center; - margin: 0 auto; - overflow: hidden; - line-height: 22px -} - -.DrawRecordlist { - padding-top: 6px -} - -.DrawRecord .DrawRecordL { - float: left; - color: #9a9a9a; - margin-top: 9px; - padding-right: 22px; - position: relative; - text-align: right; - width: 110px -} - -.DrawRecord .DrawRecordL i { - background-position: 0 0; - display: inline-block; - height: 5px; - overflow: hidden; - position: absolute; - right: 0; - top: 9px; - width: 16px -} - -.DrawRecord .DrawRecordR { - margin: 1px 1px 30px 112px; - color: #666; - padding-right: 5px -} - -.DrawRecord .DrawRecordR h3 { - font-size: 14px; - font-weight: normal; - margin-bottom: 5px -} - -.footer { - bottom: 0; - position: absolute; - text-align: center; - width: 100%; - line-height: 50px -} - -.bingfa { - padding: 10px 0 60px 0 -} - -.bingfa p { - margin-bottom: 10px -} - -.bingfa p .span_tit { - display: inline-block; - text-align: right; - width: 120px -} - -.bingfa p input { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 3px; - -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 -} - -.bingfa p input:focus, -.bingfa p input:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bingfa .submit-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bingfa .submit-btn .btn-danger { - margin-right: 5px -} - -.transfer .backupdata { - height: 150px -} - -.selectdata .slabel { - font-weight: normal; - margin: 10px 41px 10px 0 -} - -.selectdata .slabel input { - margin-right: 5px; - margin-top: 0; - vertical-align: -2px -} - -.backupbtn { - margin: 20px 0; - width: 100% -} - -.neice_con { - padding: 7px 20px 20px -} - -.neice_con .tit { - font-weight: bold; - line-height: 30px; - color: #777 -} - -.neice_con .nc_con { - background-color: #fdfdfd; - border: 1px solid #ddd; - line-height: 28px; - padding: 10px; - border-radius: 3px -} - -.nc_opt { - padding-top: 6px; - text-align: right; - height: 24px -} - -.neice_con .nc_opt input { - margin-right: 6px; - vertical-align: -2px -} - -.neice_con .nc_opt label { - float: right; - width: 150px -} - -.nc_con_user p { - margin: 15px 0 -} - -.nc_con p span { - width: 80px; - display: inline-block -} - -.nc_con p input { - background-color: #fff; - border: #ccc 1px solid; - height: 30px; - line-height: 30px; - padding: 0 5px; - width: 280px -} - -.nc_con p input.disabled { - background-color: #eee -} - -.nc-tips { - padding: 20px -} - -.nc-tips p { - line-height: 30px -} - -.nc-tips p span { - font-size: 14px; - font-weight: bold -} - -.nc-tips p a { - color: #20a53a -} - -#BarTools .glyphicon { - color: #666 -} - -#BarTools .glyphicon:hover { - color: #fff -} - -.list-list .ico { - background-position: center center; - background-repeat: no-repeat; - display: inline-block; - height: 30px; - margin-right: 10px; - width: 33px; - z-index: 1; - float: left -} - -.fileList .ico { - background-position: center center; - background-repeat: no-repeat; - display: block; - height: 80px; - margin: 12px auto 4px; - width: 84px; - z-index: 1 -} - -.list-list .column-name .text { - color: #595c5f; - display: inline-block; - max-width: 85%; - overflow: hidden; - text-overflow: ellipsis; - line-height: 30px -} - -.list-list .column-name .cursor { - display: inline-block; - cursor: pointer; - width: 200px\9 -} - -.table>tbody>tr>td { - vertical-align: middle; -} - -.showpicdiv { - max-height: 400px; - overflow: hidden; - width: 400px; -} - -#BarTools { - font-size: 16px; - float: left; - margin-right: 20px; -} - -#BarTools>button { - top: 0; -} - -.comlist { - float: left; - line-height: 28px; - margin-right: 10px -} - -.comlist>span { - margin-right: 20px; - font-size: 14px; - float: left; - padding: 0 10px; - border: #fff 1px solid; - border-radius: 3px; - cursor: pointer -} - -.comlist>span:hover { - border: #ccc 1px solid; - border-radius: 3px; - background-color: #f3f3f3 -} - -.PathPlaceText { - position: absolute; - right: 0; - top: 0; - background-color: #fff -} - -.PathPlace { - color: #666; - float: left; - font-size: 16px; - line-height: 30px; - margin-right: 5px; - width: 170px\9; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.PathPlaceInfo { - font-size: 14px; - line-height: 30px; - float: left; - margin-right: 20px; - color: #999 -} - -.onPath { - float: left; - line-height: 30px; - font-size: 14px; - color: #666 -} - -.editmenu span { - display: none; - text-align: right; - width: 300px -} - -.table tr.hover, -.table tr.on, -.ui-selecting, -.ui-selected { - background-color: #f5f5f5 -} - -.table tr.on .editmenu span, -.table tr.ui-selected .editmenu span, -.table tr.ui-selecting .editmenu span, -.table tr:hover .editmenu span { - display: inline -} - -.ui-selectable-helper { - background: #6bb0c9; - border: 1px dotted #072246; - opacity: .25; - overflow: hidden; - position: absolute; - z-index: 99999 -} - -.list-list .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMpJREFUeNpi/P//P8NAAiaGAQYD7gAWdIFPpxIoMS8ZiOcQqfY5SD0LFT1jAsRTuaU0GZjZuAgq/vPto+S3V3fmUssBokC8llNEjp2ZFWjk/1+Eg56TE0RJsgCD3BPImAvikGs7IzMLAxuvKAMrNz9ZaWAul5iKJAsXPwUBAMzK//9AaDIcIAkODiKCbbQcGHXAqANGHTDqgFEHjDpg1AGjDqCVA57/+f6Z7hZD7XwBahOmfHv1ANQqlqCzG54CcRrjaOd0oB0AEGAAscwsxMSUtNsAAAAASUVORK5CYII=") -} - -.list-list .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//P8NAAiaGAQYD7gAWcjR9OpWASyoZiOcQacxzkHoWKnrGBIincktpMjCzcRFU/OfbR8lvr+7MZSQnEbIzosbc65NxokDqDKeInBwrNz/xIfnwMgPjx5PxnkD2XCCWJNfrjMwsDGy8ogzs/CKkRSXQAaAomMslpiLJwsVPQegDQ/H/HwhNRiKUZOHkZPjx4xvD799/6ZLyWVmZGTjYWVBzAchybsUKujjg6/0OuAMGvBwAJcL/A14Q8SkYD4jlnx6chaaB/79GK6NRB4w6YNQBow4YdcDIdsDfP98/0d1iqJ1/QdVx47dXDxsGIDRADdBGxtHO6UA7ACDAAIASR69Q0kB9AAAAAElFTkSuQmCC") -} - -.list-list .ico-access, -.list-list .ico-mdb, -.list-list .ico-accdb, -.list-list .ico-db, -.list-list .ico-sql { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwZJREFUeNrMV0trE1EU/jKJkzTTR4oJCX1lqq6q+GwriiLarS4VfCwqFEFxKShYUKE+Kv4GcVkquBE3glq0Vqt9iGg3qfZhSVttU0tNJk2aGc+9dGSSJmlaJqkfHG7OvZOc757v3HMnFk3TsJGwGZ2hoaEjNNwnazQxxh6yT8aJurq69AQIHT5CSUmJKZEDgQAbHpOdTCWhQ0jxfZIkQVVVU4zB7/c/XyaxOxcCpkMUxUtEojsTCaEQhUYkmmVZHkhHwpbPwKWlpXodMJxaHgfJLOsiEB4eRqi7B+HvI1CjUUQnp/i8o7ICgihC2rYVmw8fgrNW5vMejwderzddYa49A7NvujHzsgvlBw+gnIJYHXbYfT6+Fg0GocbiiAx/w/jDR/CdOA5XYz1f04sxpz6QDTMvXqHy7Gk4t9SuWHNUVPDRKft5NqafPvtHYE2NKBvUpSVYbDaEet5B2CRCS5BvoRoWBKjxGKx2OxKLi7C73dDo2XV1wmxQxsZRVFPNLRsSigJlYsJ8Aq76fZjvH4CtrIzLYBGSTzDbdYSKMzo1BVdDg/kEfvf1Q758EbFfM5j/2M/TzqRgEiSUCEnggJNOgaO6CsGOTvhbzptLgOnPdil63NwyShAOr8iOSRLsJQkGYS2WYKX7wkFHUKCjqOu+OP0TSwsLiM+G1iRBzlTn3n+Aa38DP2oMf6ihzHa95sa0Z5CoNtgzc7295mdAl4Dt3klWcAk8TUcRaLvLOyHLgoVaLxs16nTK+A/empXRMd4n3E3HzCdQde4MIiOjCL3twWQfOwVx7rPdFvlrIFAjYneAfKGF+6YT4K2WAugXjVkoyPvAf00gSYJQ602EChH1SWfmGrDa8vqShETKTZkx2s57t/H52vUkXwebN/pG6N/R143PGn8vp1Ogk0hHJlc/dS1vRcgCZdp1pmyZSoAFMu40VxJJEgjFxQlNUay72u+sSOFqNbDa7tln9keYxUi6Y4z/jvvaH7TGvny9pYbDeekPgiSp4o7tN+qvXmlLS2Aj8FeAAQC23WzgeNtzYAAAAABJRU5ErkJggg==") -} - -.list-list .ico-c { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuKz0K4QDTjbbc0N4qrvP7+p0yABWY5sKTqf7mmAHMuHVy5ATg9k5wJ+VoZ/n/79Z2JkZCQqyElNhKCOMB8Lwz+cDjAVYGw69f5P3ac/xIWMy4rLJPkW6MG/QDuaUeqXge6eAwQYABvvATpIEEu4AAAAAElFTkSuQmCC") -} - -.list-list .ico-cpp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuxZaFeIBpzttuYGSWIo1fef36RnQ5ChyIYh85EtxidG1TRAyFCy0wA5ABbk6CFErENZyLUUPbjJDSEUB/CzMvz79O8/EyMjI4aBhBIXMQDUEeZjYUBppTIi947LdlypP/X+f92nP7QpH4Ae/GsqwNjc5aHTiNUBAwEAAgwALWAgpic4qfUAAAAASUVORK5CYII=") -} - -.list-list .ico-cs { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFvxZaFeIBpzttuYGhjiyGNbq+89v8rMhyBJ0S0F8ZHF0NTRNAzCLYI4gJgRolgiRHUE3B8AsRPY9qY5gRO6cOq64/PfTPxYmRkZGmiRCkF28jH/+HYjUZcaaCE0FGJtOvf9T9+kP7pBxWXGZJHFkwM/K8BdoRzPOEBgIABBgAMrJHxYdaBS2AAAAAElFTkSuQmCC") -} - -.list-list .ico-fla, -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMMkLgZf1fQx/37yD85lFhBjEG4sYPizfCOYLRPpj6IElRqLKAWIAyBIuG1OqhRITwwCDAXcAyVEAim9YnLNrKDMI5ybS1wGjaWDA0wAu8O3IaTCGAVDaAKURQoARuUECbA/8V1ZWpqmP7969C2oPMI6mgVEHDM5syNI+k+EhkXn0DyUWLp6I2w5cliq1laDw71X1gMVANDa12MSxOZykggiXoXRLAyCfoYcELt8TUkfTECDWcqo3StHjHl9awB4F3Fx//5NhKbE+/g+1A2dldGPCrBqm6/caGb59Z6JJNuTi/PdPU6leoyCtBasDBgIABBgAWP2ttPWI30cAAAAASUVORK5CYII=") -} - -.list-list .ico-css { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALdSURBVFiF7ZfLTxNRFMZ/M/RBnwik0FYpoJIYhJioLFSIupCoWzVqotGNCeEPcGPiI+50Q9xAYli5MaEbFyTiI0EWhhghGkohlgVREVzQhpfals64qB067ZRpoaALv6Rp73dPzvnuud/cOxVkWeZvwpA+CAaDZ4BewFPEGoeBkXSisbFRWwDQ6/V6PTabrSiVQ6EQQB9woaGhYUQrRswYeywWC5IkFeUD4PP5BoC+UCh0KB8BRYfZbO6ora19A/i1RGy5AACTyXS9rq5uWEtEpgeKCqfTmfIBwKU/3+8BYVsEuFwuqqurVVyaoK0XAChmzIVNe0CS4drrGeLSxg60ggRMRKJMLcRU3Mf5X+yvMGMUBaYWYkxEosUXEEvIdAfC9IxHKDeXqOYGPi9zqsYOQLm5hJ7xCN2BMLFEfh3RFRAIR+kcmqXKaqCr1U1l6ZqAuCQTDEc5UFkKQGVpCV2tbqqsBjqHZgmE9buha8Jbw9/pOeHFY80OfTv3kyNuC6KwxgnAud1OjrqtdAx+49lZ37r5dTtQYzfSP72kabKXX5Zp99mz+Lgk0z+9RI3dqJdeX8CjNg8Ok0jn0KzKYMtxibkfq+xxmlTxE5HkljlMIo/a9C9V3S0QBbi4t4xjbisPP8xzo7GcpgozgzMrHPdaVbGBcJTHwQh3W1zstOmvHgp4DHfZjXS1uql3JBO/+rqiuD+FekcyJt/iUOBJKAA2Y1JzV6s7az41Vwi25Tb8L+CfFqAy4ZWRBIx8UsaiIX835wNpNQ7A6NpLcfZToFX0xfl9yu92/+SGedFgVETkFKBVPDN5u3+yYD4XhPR/RgefjMmZHVgvQWqlmfO5eEhuw+jVZuX62pQJ2/2Tyqrz4bVQsIBU0sx91uNzQbUFJ5+OJRYlgygIgiqoWCaUZRmHsCoNXm5W3mpUAm4+D9x5F5FvL65uzflQZiTRskO4/+B00z1NAQqZ0YFiI73mb2NmSwowsJSgAAAAAElFTkSuQmCC") -} - -.list-list .ico-js { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALFSURBVFiF7ZfLTxNRFIe/GTotffJKoa1SQCUx1cSFslBLdKFNdKtGFya6MSH8CybG6NJNwwYS496EblywwEeCLAwx1mgshVgWREVwQRteamk746K2dNopM5WCLvwlTXvPPTn3u+f+5lFBURT+pkylg3g8fgF4BHjruMYJIFoaCAQC2gDAI5/P57Xb7XVZOZFIAIwCV3p7e6NaOWLZ2Gu1WpFluS4fAL/fPw6MJhKJ40YA6i6LxTLQ1dX1EohoQew6AIDZbL7Z3d09pQVR7oG6yuVyFXwAcO339xtA2BMAt9tNR0eHKlYCtPsAQNGM1bRjD8gK3HixQEb+sxtaTQAzqTRzK5uq2PvlnxxptSCJAnMrm8yk0vUH2MwpDMeSjEynaLE0qObGP61zvtMBQIulgZHpFMOxJJs5Yx3RBYgl0wxOLtJuMxEOemhr3ALIyArxZJpjbY0AtDU2EA56aLeZGJxcJJbU74auCW9PfWPkrA+vrTL11dIPTnqsiMJWTAAuHXBxymNjYOIrTy76t62v24FOh8TY/JqmyZ59Xifkd1TEM7LC2PwanQ5Jr7w+wFC/F6dZZHByUWWw9YzM0vcsB11mVf5MKn9kTrPIUL/+Q1X3CEQBrh5q4rTHxoN3y9wKtHC01cLEwgZnfDZVbiyZ5mE8xd0+N/vs+ruHGi7D/Q6JcNBDjzNf+PmXjaL7C+px5nOMLg413gkFwC7lmcNBT8V8Ya4W7cnT8D/APw2gMuH1aA6iH4tj0WTczUYkZzMAvN16Ka68Cowu+vTyYUKR2eLvggoxLYkmqQhRFaBWlYJojfVUFw8Y7YCWdtyBWo+hXDV1oHSR8lgoMlvz7qGsA00S8qqsiIIgVMuvgAhFZg3vXlEUXCZUb6kqgL5m4d7rVPbOala7M+cef9AsXC1eriaJXF+zcL80Jhj5e75dR7aTkdq/AFCYGAYKWHWoAAAAAElFTkSuQmCC") -} - -.list-list .ico-htm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEg8FtzQ04GxsAycPkYGqR1SOL/fvzm7IGCbJjkA2maRoAWYbL97hCglgHMhHra2r4li65ABYKxDoYxQH8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAPufFoHFXwuVAAAAAElFTkSuQmCC") -} - -.list-list .ico-html { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFkgW2hWiweC25gacjQ2A5GFyMLXI6mHyyHL//vwmLxsiOwbZQJqmAZBluHyPKyRIdSATIYOp6Vua5gJs8U2yA/hZgU08GnRWYVEJMpuPhQGllcqIbGHZjiv1p97/r/v0hzblA9CDf00FGJu7PHQasTpgIABAgAEAooAb7fMLYAoAAAAASUVORK5CYII=") -} - -.list-list .ico-java { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvRJREFUeNrUV0toE1EUPfPJTJNp82lNY1PaBGwtpn6q9bNQQRFFpSAKgrgQu3PpXlBEBMGNWwU3LtwoxYULhSoKgkJbawVDta3WT5XWEPpL2iSTifeNjqY6TeNkavDA5c375N1z7z3v8cLlcjmUE2J+JxqNHqDmOlmdjT42k/XlD0QiEXMCzHkwGKxTFMUWz0NDQ6y5RXa0ubm5z2wN/1u/zul0QtM0W4yhsbHxPiNBZNqLIWAZI+PzpuOyLJ8KhUKP6fO2GQlbCLyPpfAhll50XpKkk+Fw+JkZCbFU56qWw72BKRxq9/0x53a7DR0wHPvR9pJxthCYSmbR1RPHnlY32GEei6cRrJZ+7u73+xEIBMyEWXoJBt4ncfXBOLavrkLv2wSVIIX6POcGzIRZMoGZ+Szu9MbRuasW/aMJ+BQB25oqLQViiYDIcwj7ZfirRF0D6az129QSAafEY+uqSnyZzKBjow89IwlEx+b+HQGGNfVOVDg4uJ0C2kIuVMr88hNgwnv+LoHYjKr3fYoIjlS3b71Hzwq7jNJqDk9ez+Bu/yS0IipT9DFkG8dnVYxPZ/DyYxJM0FnyUOHg4RA5NNRIlAkFEn3vaKnCtYcTiH5KYm2Dyx4CbOPddN5Hv6b0aAMex4J5Rkbgvx9ClURpCNW2DBiYoAw8fTMLmerPiLhkQXfICO7f4NXHWIlO7FwBUeDsIZBMaxj8PId6n6Srn1kheFyCnhHbCDyKTuMFXThNKyvgdYkUNdWdNmcOHMKvtCdSml4iNn94S/Xfv4gWw8E2L/au8+jpj02rmJrLIp5QkSFhZsixTOmXSYw1dDF1bPLqp8PSk6wQWKSsBMzsBI8yo+wEFpTgUjddXd3Dy+70RqRABiRZXmCXO1tt7f/fGrh4vAlnbg7rrYH8vjFfaH3JGSi0mZX14lLRFtM3oix2/aIZUCT2btSW/JFVsL1d5CN/jMv/d3yl69W5wQmcTaaXR5wUYLalFhdOH2k9b0qgHPgmwABqjFw0GGyiFAAAAABJRU5ErkJggg==") -} - -.list-list .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmQz1rdMxxGAAWQ6XOAz8+vWL9GxIyEEwPi7xAUsDhCynqQNAvkeODro7gBjfY6QBLk62f79+/2MCAqw+ghkMi290y9DFsTVQQXYgizEi946nzllbf+f+y7pv33/RJGS4ONn/qiiKNWenBDdidcBAAIAAAwBHPa+btF94QQAAAABJRU5ErkJggg==") -} - -.list-list .ico-mht { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEA0y7rbmBVQzEhsnBxNEBst5/f37TtkGC7BhkiylKAzCf4jIYJIbL93RJhCAHEeNbihyAHt80yYb8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAIH+FoEeflonAAAAAElFTkSuQmCC") -} - -.list-list .ico-php { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA1ZJREFUeNrsVktPU0EUPn3dPq73QmmbFlRaAw1Q5KUiYCIkmmjUGEMUdGWMSze6cmViTFwYVv4Aw8KNRhe6kA0SAonRGGNBHpWkBAXFIFAeLS307ZyB1ku90EJvdeOXTO7c6XS+b8755tyRxeNx+JdQCl9cLlcLeXSQdlRCjjrSBoUDDodDXADBUwsBx3GSMLvdbnw8J63NbrcPis2Rp7xbWJaFWCwmSUNYrdZuFEHE1GYiQHIwDHODiHizlQj53zAaEXHNZrM5xUQoc0nM83zCB4j2jecAabJdCVgLx8D5xQ9j06swOR+CGDnCkWgcWLUCrCY1VO3XQlWxDhTy9fVNJhOYzWYxY+48Akj8eniZkOjgdE0+mHgVMMp1In8wBnPeMJ3TM+KFC0f0YLdo6G8JM2ZUB7ZCn8tLCW6esYBG9ds27pk1sns5FOkZYEkEbKShmGfvPOBbjcKhA2zatdOacGI2SMIdhLZGwyZyBI5j+IVAQVebjfB+fIWK3lElFEM/2f3Zunzax0WnF0IgI5E/V6eHmaUwdA8tQa2N1A5S0Ycm/RAhET9RycOp6jzo/+yDSw0F2UVgZjkM5jwV7X+dC0JLBQ+lZg18J0ICJNwXGwzwaTJAhVXs1dL8D5H3YqMapkiEsk6B8GPl8UVAv0cJPxZDNNScVgFoeJVCRsONXvjmCdHxTJFWgJFTUUKaL0L08sMC5OmUEIrEwWpkqOlYzfoyvaNeGJ4KQH0JS71TSARl7YGTB3l45VyC9sYCKCvS0BQkaoKJV9LdN9k56B1ZhvOH9clj2eVchCvHDNlHoITku5Sc6SdvPVBWqE2O44nAhkUH09G8IQzT9Kh3Fo6XczQlWUcAga42kNx39s3SAlNtZYHTyJMEaEDfWhS6Bvy0QrbW66G8SLvzC8l2qLHqwLFPS3P8cWIF5slOkRhNiLnGyohV8nKTIVmKJRWAwHxjdcukwkl2CnKN/wI2eeBBD6l6PeM5J33s2MaEjFr9xx86rpcm+7c7x5NjqX2xeakIBYO7SwEumEqSzTxJ7oSZkkgiIEEmDK0wBdvNk0RApgtmOk/UAywD0XSXyGyAayOHcEwmvHA8fDF6Z+wn3AuEclMfdAzEys1w91Zr5X1RAf8CvwQYAB0rcvQMbg3OAAAAAElFTkSuQmCC") -} - -.list-list .ico-url { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAr5JREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakmgP+/P/LsP/FJYZzb+8wMDEyMchyizD4yVgwcLGwM4iKijKIi4tjS5jkh8DLHx8Ytj45xXDj4xOG59/fMfCwcDBYi2kxBMpZMajxSYPVgMRBDgABWGIkqhwgBO5+fs7QcWUVw4+/vxn42bgZtATkGAwElRjWPDzCsPf5RYZcTT8GIyFlBklOIfIKInwAZOnE6xsZ/gB9FKfszOAqaYgoWIAOmXlrG8P0m1sYuoyTGQTZeIh2ANG54Oirqwzvf31hiFC0R7EcnNe5xRhKtUMY/gGL9d3PzpNfFOMN/i8vGJR5JYHBfZhhyb19eNQ9J8kBRIeAOIcAgzA7Lzgq8IGfBOTJDgE9QUVwYmszTABnNWoBokPg+OvrYHrD42NY5S+9v88Qe6SH4dy7u7RxAMgCHQF5hlNvboFzw+sfH8Hiv/79AWbBC2AxDX5ZcDakSRR8+v2dockglmHu7Z0Mx4ChcebtbXBh8+3PT7A8yHHZGr6UNUjwATEOfgYWRmaGTHVvBlMRNYbTwJB4+u0tg7iAANByBQZHCT3KW0T4QISCHcPBl5eBiVGBwUBIicFEWJX6TTJ8ABS/IExtQJf2wKB2AEoUTHixg4HhBe0tnYXUKiYqBNqdMrDyQTQyxqWeau0BbKBy3wwUi5H5Qy8NkAOQg5tU3w+KKEBxADcT+9+v/34yY7OEVJ/C1KOrBdmBzGdE7h1PPbGm5vaPF43f/v2iSdrgYmL7p8ohUZ9tEdKC1QEDAQACDADzFSd59WqSKgAAAABJRU5ErkJggg==") -} - -.list-list .ico-xml { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57TwhAATCysK3hOhi5UPorHJIcujmwXCJEeB25obDLtCNMBsEA3i0z0NwByBbjk1HDc0cgHMdzDfEhM6ZDmAnxXYxEPrrCIbjssR+ByOrB5kNh8LA0orlRHZwrIdV+pPvf9f9+kPbaIG6MG/pgKMzV0eOo1YHTAQACDAAOGRHaFJlGXVAAAAAElFTkSuQmCC") -} - -.list-list .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAg9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh7SLgvdXNjK82N8K57OysjIwMzMTXw4QC96dX87w7elRBin3HgYmVg64uKCOP5D0R7WAhYXh79+/OM0iOQT+/f4BtpyJjZfh65MzROkBhQITExM4Cih2AMhSNkFVBl5Vb4bvz1DLljen5oCjARuApQGKHQCylFPKmIFTQofh1/vbDL8/vSBaL8UOAFkGspRbxoSBhVOAgUPMgOHbi8sUJVySHACyDGQpLOGxi2owfH2wj3q1IcH4B1r279dnhidbclCj5cU1YJRo0dYBIEtAAD3rgbLk9+cXyXYA0VEAsoRDVA/FchDglNQHZ0tQ9iQHMCK3iIANkv/Kyso0rZju3r0LapAw0rU2pFouGHUAzVvFLEfSGR4eoYOFWodxhwA7C3FYLWweXj4uPDzSgHzQPIaH65LA9IAlQmpYTlaTDOZ7XHzKQoCV798/GvZVwWaz8v7DWRfc2Fxbz/TmbB3D78+0SZxsfH//CRs1a/g2N2J1wEAAgAADAIk82p3e8s5qAAAAAElFTkSuQmCC") -} - -.list-list .ico-bmp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhs1Sx6biiIO46OLIetDVvPt708yygE0S5D5xOjDp4eoNAAyAIaRDUL2KSkOo1sIYIsmqldGsFDAZgkxDkVxABcT2z9gxcPEzMiEMyGSE8zIlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAA9C4L2PHEWagAAAAASUVORK5CYII=") -} - -.list-list .ico-cdr { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Cr/WPhHObj44H0OMGHFk8O3vT+ITIchAZEOQ+aSKUyUNEDKM5pURMQBblFEtBNDjGFcowTAx6lFCgIuJ7R+w4mFiZmSCG4bLR6T6FFapgexAadwg946nnlhTf/vHi7pv/37RpHzgZmL/q8Ih3pxtEdKI1QEDAQACDAD7sQauaYXLQgAAAABJRU5ErkJggg==") -} - -.list-list .ico-gif { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM77raBVTYiax6bChcDsZHlkOXRwbe/P8krCWEW4eLjs5QmaQCbZSBHoYcETeoCmCUDFgLkWERUCHAxsf0DVjxMzIxMWC0lNXixVWogO1AaN8i946kn1tTf/vGi7tu/XzQpH7iZ2P+qcIg3Z1uENGJ1wEAAgAADACWJ9+r79LDyAAAAAElFTkSuQmCC") -} - -.list-list .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAaVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLBSZ/Pc3w+/HVxhYZHUYGJlZMaT5+Phg6QAEIqD0GSBmpIoDfl7Zx/Dt4EIGLvt4BnZ9dwx5UVFRBnFxcWwJkzohwKpoxMD+9gmYxgVgiZGocoBUwMQnysDllEy9gogU8H5SFJzN5ZjEwK7rQp4nyHUAl1MKGFMKyHYAu44TGA+YA6gFRh0w4A5goTQLgsC3/fPAGAQE85bR3gGc1pEDGwIcxr6jiXD4OIARuVn+sECTLm10+QnX8TRIWNixa6rewvCw1QfOhjsaKoZPHAX8+UlZLkB2CDIflzjN0wAxlgzvXIAc7xSXhP85Bf4x/vrKxMDIhDfIsSU2XOIo4P8/oB38/3BmwzuzCutZ7h+vY/z+kSZR859L4O8fBYtmlbT+RqwOGAgAEGAAjiC9hXL1ZWMAAAAASUVORK5CYII=") -} - -.list-list .ico-jpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7hoZa+0SG5oPzwTQMgPgwOWSATRwmBgLf/v6krCREtgDGRrYAXQ4bn+ppAGQBekhgczRV6gKYRciG4rMAm3qiQwCbr0AG4bOQFN9jhAAXE9s/YMXDxMzIRFT8YXMozIHYHA+q1EB2oDRukHvHU0+sqb/940Xdt3+/aFI+cDOx/1XhEG/OtghpxOqAgQAAAQYA3ogNywRusk8AAAAASUVORK5CYII=") -} - -.list-list .ico-jpg, -.list-list .ico-JPG { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7/vaBVTZD07GpYBqeTYF8ZHls4jDw7e9P6pWEMAuQHYXuGGyOoFsaIGQ5xSEAC25sFuGTIysEkOMW2Ye4LCDG9xghwMXE9g9Y8TAxMzLhTYD4ghybQ5ErNZAdKI0b5N7x1BNr6m//eFH37d8vmqQNbib2vyoc4s3ZFiGNWB0wEAAgwADnlgGzkKaAXwAAAABJRU5ErkJggg==") -} - -.list-list .ico-png { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhuRHY9NRRFD5qPLIcsjg29/f5JRDmCxCGvjBU2ekHqK0gDIYGTf4nM4xQURepBTUw8Tsb7FZhC+UCDWsSghwMXE9g9Y8TAxMzLRJFpAlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAADIwGkBp21VIAAAAASUVORK5CYII=") -} - -.list-list .ico-psd { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFCYMKxewyvvv1CZHAJPoYgHSn8FrCwMPz9+5c6DgCBcC1JBn0pfoaXn38wLL/8jOHA3dcMDsqiePWAQgFU4mIrdclOA+K8HAyWMgIMl15+Ajvg5KN3DBtvvYLLt7looKgHxT9VQgAbAIUGyPIMYzkGOUEunOqwOYLsRPjo/TeGPfffMuiJ8zEIcLKCxd5//01ZZUQMWHntORiDgL+aGIO5nBA8bex/8JZhy+1XDC6KwnBxqjsAlgjRAUgMhC8++wh2oCQwjeCLDpqVAwpCXAzcrMwM3//8pU0I4EoPM84+gvPdgFGgLspLlF5G5LwJbJD8V1ZWpmnFdPfuXVCDhJGutSFd08CoAygqiGLOArPO2Vs0t/ScFp5syMTCiqFhmZ8anB216RaGOEwMWR26Whj49+c3eeUAsiUgNoxGFsPmQGyOoEoagDkCl0+pnghBlqH7CNkRNHcAyDJc8U+JI1DSAD8rw79P//4z4Wq9EBMFuBIsCICKfT4Whn8464KyHVfqT73/X/fpD23KB6AH/5oKMDZ3eeg0YnXAQACAAAMAI0n+E64by+4AAAAASUVORK5CYII=") -} - -.list-list .ico-webp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Q5V9PEPbwYUYGmHitfaJKOLNB+eD5ZABTD+yWpC6b39/UqckBBmGDpAdjewJmFqQYyr3zSCcBkAaYT5CppEtABmGHhIgNTCMLQSpWheQEgIwh2LTg9MBsFBApqnpWKwO4GJi+weseJiYGQnnTvTEhRxd6KGBXKmB7EBp3CD3jqeeWFN/+8eLum//ftGkfOBmYv+rwiHenG0R0ojVAQMBAAIMAErgG/MeljhhAAAAAElFTkSuQmCC") -} - -.list-list .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjFg1Cdf0MbxtKULhwwBMHFkMWRwZ/ELrh1CUC5AthrGxWUpxGoBZgO5DXGqJUUdyCGAzFCaG7GtSQ4CFWN9j45NqGUEH/OPk/vfvxzcmSvImtsQJNx9UqQHtwJkNH8zur2e7f6OO8ftXmpQP/7i4//5W0GhWSC1sxOqAgQAAAQYADkDCgkQGXJUAAAAASUVORK5CYII=") -} - -.list-list .ico-avi { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGz48SdqZpE8dHFkfHQTQPNcUoMtYvugWkYQObD5GmaCJEtp0kiJOR7bHxYKJDje4wQ4GYDtSH/0Sy+QWZzAe1AFmNE7h1PWHe1/sYrhrpvv2iTOIEe/KsuxtBcEKTdiNUBAwEAAgwAcCQxq0cx9U0AAAAASUVORK5CYII=") -} - -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA8sBqKR1eDjw8Cvnz/JS4TYDKNrGgD5CD0ksIUKPt9TlA0HPASIdSQpvsdwADcbqA35j4Fa0YIOQGZzAe1AFmNE7h1PWHe1/sYrhrpvv2hTPgA9+FddjKG5IEi7EasDBgIABBgAhSU3wy5Zmw0AAAAASUVORK5CYII=") -} - -.list-list .ico-mkv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAq1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYkayHfD333+GE4/fMVx88ZHhxecfYDEJXg4GfQl+BgtZIQZmJrjZDKKiogzi4uLYEiZ5IfDpx2+GBeceMvBzsDK4q4ozyPJzgsUff/zOcOTBG4azT98zJBjJM/AB5WEAlhiJKgcI+RxkuQ7Qp05KomCxGafugekMMyUGZSFuhn33XjPMB6rJtlBmYEEKCXyA6ER48sk7sM9hlsMAckEKkhMAqjkFVEtWSYgPXHj+ERzsyADkc3RgoyDCsPP2SwYrOWHqOuDpp+8MMnycKMGOjZ1opMDwDKiWWEBWOYAc7OhsRmDUMzEyUj8KpIG+fwL0GXKwY2PfffcVnC2pHgKgfA7KasgAFOywoIcBkBp1ER7qO8AcWMh8AJYDoKyGKzpAcg8/fAOnF2IBI3KDBNge+K+srEywIBLgZGWwlhfBKIhADowzlGNYevExgwgXO4OzsiiDKDc7ihl3794FtQcYyXIAclF8/tkHhldff4LFxICWGEoJwIviP0A1oLLg5OP3DIXWKngdQHJdALLAWl4YjHGmbKAaUDlATFlAl+p41AFEF0QxZ/8C26+3aG7pOS08IcDEwoqCt4ZogzEuMWQ5bGLofJpFweYANQbfDbdokwZABoMswGURuhgh9UMzF8B8hc03yD4mRj1OB/CzMvwjt7OKzRGY7Yj/DHwsDP9wVkZlO67Un3r/v+7TH9pEDdCDf00FGJu7PHQasTpgIABAgAEAZzJTrgcAEqEAAAAASUVORK5CYII=") -} - -.list-list .ico-mov { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAupJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnh7Ob45TAdO2iezjFYHx0MXQ9MP6vnz8pS4TIAJ9FVEsDIANhviTWAlL0DI1cAPMRKcFLrB6UNMDNxvDv579/TEBAkiXoiRAXADVQuYB2IIsxIveOJ6y7Wn/jFUPdt1+0iRqgB/+qizE0FwRpN2J1wEAAgAADAIjpSLqXWcBIAAAAAElFTkSuQmCC") -} - -.list-list .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjCh84Zo+MP22pQirGIyNTw0y+IXWD6FKFIAsQXYMiEYXoygNYDOckHpi1VIlBEAWYbOMaiGAbBg2H8GCGz3+SS4J/3Fy//v34xsTExXSBLZECGqe/gfagTMbPpjdX892/0Yd4/evNCkf/nFx//2toNGskFrYiNUBAwEAAgwArNbTCDlVe6IAAAAASUVORK5CYII=") -} - -.list-list .ico-mp4 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKcEpmsX3cMqBmOjq4GpQxb79fMnbaIAZAm6Y5DZFKcBZMPRfTUoEiHIUcgOI9aRTKQGMy6DYVGALe3giwoUB3CzgdqQ/6iaJpATJshsLqAdyOoYkXvHE9Zdrb/xiqHu2y/alA9AD/5VF2NoLgjSbsTqgIEAAAEGAKRaSLN4Zv7KAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwJJREFUeNrMV01ME0EUftS2221LscBSt0pbjBWsBjGAQRMT48FEL/5F44kYD8ajnjypMVw8cvFkSJSrxMSDMRoSUeNPDARJtKBVo0Sx0CZFC6Wldeu8laWz2y0sMoAvedmZN1/nfe/NezNQls/nYS3FTE/C4fAh8ukiKjL00UJ0gDaEQiF9Aujc6/WKDoeDiedIJIKfO0RPBoPBAT2MSTMXeZ4HSZKYKIrP53uIJAiZZiMEmAvHcef9fv8TMuzRI2FajUKzWq1nAoHAKz0S5qVuNp2R4Pn7JIyMpSGezMk2wWWGbRt52BN0goMrxORyuZQ6QDk99+0nWvZPBN5+m4F7/QnY6bfDsVY3eCossn38ZxaGRlPQ+SAKR1rcsGMT/5eYIIDH49ErzKVnAJ3fH5yEs/sFENdbVGu1VVZZm+sc0P0sLtsUEkoxlhKT0bRj5O37qoucq1qIrCEGsVNpyVBghgi8jExBo89e5LyrLwaJ6VwRCcS++JBkR2D4+ww0kXPXyqfxDNx4NAGRaFplRywWKTMCsV858Lr1U5+aleDW0zj0hQsRiwQbS+bYEVhUyHsmUY8a1t06gzsb6gLs87FEVq50rdgsJjjVVgkNXtu8Dduy0mlmRwAvGexzLYGAwMGJ3W6o0jhDbINoY3cEbVucMPQ1BT8msyr7uQNCkfMowSB279ZydgScNpN8w+EloyVBC67dJhjE4m+YHcH8zUbqrOtxDJoCdthFWk1wWea6JAuDJOo3X1JwtLVwFTMlIJOo5aGuhpMfo57XCfkxwmqvLjdDvcjDxcMbVI8RcwIo6OBgY4WsLGRV/h74rwmojuB6L6my3o8r7rQ7tEANWDkOOto3y+PL3Z/n7bRNGdMY2lbKjrbZTGZ5RajnQBnThLVryvzSzWFjNUBHqt2olCBOmwk90kwyoHdMC5FU8IYzQGdBb2O0GcnKYuRUGXBYQcpIkmm5WaEJao/ETnzQ8zL6v+POu++ujkzAldTsytwPJMDf9TXQceH49mu6BNZC/ggwAJR8VIseM6jsAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvlJREFUeNrMV81PE0EUf61tt9uWYoGlbpW2GCtYDWIAgyYmxoOJXvyKxhMxHoxH/QPUGC4euXhsor1KTDwYoyERNX7EQJBEK1o1ahQLbVK0UPrl1nkr2+5ut7CVAfwlLzP75nXfb2Z+b2ZrKBaLsJYwyR8ikcgh0oSI8RRzdBMblTuCwaA2AUzu8Xh4u91OJXM0GsXmFrGTgUBgVCvGqHrmWZYFQRCoGMLr9d5HEoRMlx4C1MEwzHmfz/eIdAe1SBhXQ2gWi+WM3+9/oUXCVOvL5rICPH2XgonJDCRSBdHHOU2wbSMLewIOsDPlOTmdTkkHiNML7Qgxwz8ReP1tHu6MJGGnzwbHelzgrjeL/qmfeRj/moaBezE40u2CHZvYv8Q4Dtxut5Ywa18BTH53bAbO7ueAX29WjLU0WkTrarVD+ElC9EkkJDFWg1HvsuPM+/Y1VSRXlBAZwxiMnc0Iuiami8Dz6Cx0eG0VyUPDcUjOFSpIYOyz9yl6BN5+n4dOsu9qfJzKwvUH0xCNZRR+jEWRUiMQ/1UAj0t76dM5AW48TsBwpDxjnsTGUwV6BJYEuc8E2aWGulun8826qgDrfDKZF5WuhtVshFO9DdDusZZ8WJYNDhM9AnjIYJ2rCfg5Bk7sdkGjKhnGtvNWelvQu8UB41/S8GMmr/CfO8BVJI+RGIzdu7WOHgGH1SiecHjIqEnIgWM3SQzG4m+obUHpZCM6Cz2MQ6ffBrtIqXFO80KV5GGMzPrV5zQc7SkfxVQJiCRaWGhtZsTLaPBlUryMUO1NdSZo41m4eHiD4jKiTgCBCQ521ItGA6vyPfBfE1BswbUhorKhDyueNBxcRAMWhin1+/s2i+2l8CdNn9SvFqP2I3LZ7PJEqIaUAJNKpNRk1CRq0oB8pku9bDGC1MpQDa1t0jNWUxVoLa18rFoCPaulWAG7BYSsIBgJlqUJuQjlwA9UG8kh9xnk/44Hbr+5MjENl9O5lTkfyAR/tzVD/4Xj269qElgL/BFgADHlS7OoZ6a9AAAAAElFTkSuQmCC") -} - -.list-list .ico-rm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA9FHMbHJYYMfv38SV4iRLcElwV0TwMgh8B8TY7DiA4B5Giglu+pFgWwUCDHYShRwM0GakP+o1nqB5nNBbQDWYwRuXc8Yd3V+huvGOq+/aJN+QD04F91MYbmgiDtRqwOGAgAEGAAnMw5uSNz54cAAAAASUVORK5CYII=") -} - -.list-list .ico-rmvb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08a6EzVROHXLroHppvjlFD46GIgNrocTBwZlM++TjgRYjOIEoCsH92DTKQaBPMRusPwyYH4MExUCCAHG6W+JxQCJEcBtvgmRo6oXMDNxvCPFlkTOUQbF17/NyNDG85nRO4dT1h3tf7GK4a6b79oUz4APfhXXYyhuSBIuxGrAwYCAAQYAPSMU/fnJ99KAAAAAElFTkSuQmCC") -} - -.list-list .ico-swf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUKp4d/3H2D4tm8fw/9fPxlEOjtR5Pj4+GDpAATCoPR5IGakigM+LVrM8PfdWwbBokIGJkFBDHlRUVEGcXFxbAmT8hD4+/Ytw69LlxhEuoC+ZsIdk7DEiAuQnQZ+Xb3KwGFpgddykgsiUsCfR48ZPq9eDY4GEOAJDmbgi4ulnwP+fvrIINLezsCmpUlRCJAdfv+//2Bg5OSgOIuS7QBQ6mfi4hoYB/z/9Yvh78tXDMzAbEZ/BwCz1ad58xg4rawozgFkOeB1UTFQFzMDX0I89dsDxADRCf1UrSfoUhmNOmDUAfgAI3Kz/KaTM13a6Or79uJukACbTxgaFGfPAtP3U9PgfBgbXR1IHKYeBpDV/gKWoiSVA8iW4bIYGyBWHRMxBqGHALIYMo1sKYiPHhJkJ0JswUqMHmJCgYmYKMDmCBgfmaa8LuDj+/vv2zdmJqRaDlsUkAvADVSgHTiz4Y2GxhrG8+cbGT5/pk35wMv777+hYb1GQ30LVgcMBAAIMACmqfNs4ifjnwAAAABJRU5ErkJggg==") -} - -.list-list .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JYw4Q4CNkRGOJWv7Ufi4xEnhD6008LaliEG4pg/MRqZB4jAA48PkselDVk+TEEC2nOJEiC2ekH2JzffE8kHgF9A+nInwHyc3gSYkhQ1UUKUGtANnCDyY3V/Pdv9GHeP3rzRJnP+4uP/+VtBoVkgtbMTqgIEAAAEGAH8AyCqN8wbhAAAAAElFTkSuQmCC") -} - -.list-list .ico-webm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwxJREFUeNrMV81PE0EUf137tbQUCyx1q7TFWEE0iAEMmpgYDyZ68SsaLxKjifGof4Aaw8UjHky8NFGuEhMPxmhIRI0fMRAkwYpWDRothTYpWmgLLVvnrSzdr2JLBuoveZndt2/m/ebNezOzhlwuB+WEUf4SDAYPkSZAhKfoo53IkFzR3NysTwCdu91u3mazUfEcCoWwuUfkpN/vH9KzYVTvPMuyIAgCFUF4PJ7HSIKQaSuGAHVYLJaLXq/3GXns0yPBrEWimc3msz6f740eCWOpg83OCfDyYwLGwmmIJbKijnMYYdtGFvb47WCz5OfkcDikPECcXmwHiRhWRGD0RwoeDMZhp7cCjnU4wVVlEvWTvzIw8j0JPY8icKTdCTs2sX+JcRy4XC69xCw9Auj84fA0nNvPAb/epPhWX2MWpa3BBr0vYqJOIiElYyEwxYYdZ961r1bjXFFC5BvaoO1MWihqYkUReB2agRZPhcZ5YCAK8dmshgTavvqUoEfgw88UtJJ1V+PL5BzcejIFoUhaoUdbTFJqBKK/s+B26oc+OS/AnecxGAjmZ8wT22giS4/AP0HOM0F2qGHerSty5KKqAOs8HM+Ima6G1cTAqc5qaHJbl3RYltV2Iz0CuMlgnasJ+DgLnNjthBqVM7Rt4q30lqBzix1GviVhYjqj0F84wGmcR4gN2u7dWkmPgN3KiDscbjJqEnLgt7vEBm2xD7UlWNrZSJ4Fnkah1VcBu0ipcQ7TYpVkYJjM+t14Eo525LdiqgREEvUsNNRZxMOo721cPIww22srjdDIs3D58AbFYUSdAAIdHGypEoUG1uQ+8F8TUCzBjX6SZf2fV91pb/5SDAb5f8FESshd6f2q6dDdtRlQj60chXRSH/m7XMezjGHFSahHUO1Ez6bwOUIiIMmZm6O5cHIhd/52SNNKz5Lo6dT6Qq3cZ9kjwOgNJl/zksJZgPBy4ygiYDODMCcIy5amPOnUCVcoQvl7gvaeqKiCnvvvr41NwdXk/OrsD2SCC4110H3p+PbrugTKgT8CDACPHLU9lTMolQAAAABJRU5ErkJggg==") -} - -.list-list .ico-wma { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgSmX93C8ODzC6IsVOCVYMjU9oHzf//7w3DgySUGVxkj4soBbABkeadFClEOKD8xB85+9f0Dw9LbexlefHuP1wE0SYRnXt9imAEMOWsJbYJqaeKA1XcPMaRpeTOYiWkMjANAQIJLkCh1dCkHBsQBz76+HTgHhCrbMcy+vo3h3Js7pFXHVKt/RdUYZLhFGZbd3ke5A0CFC3L+xq9WHCUR5ur6Mxx4dokyByCXbKQCViYWvIXQ8M4Fow4gq1U84cUOBoYXtLd0FlKrGCMEuJjZwbjFKQ3ORsYwcRCNrgaXGDJ/6KSBtoMLGars48FsZBokjksNshyyGEzNgCRCfJYTLAlhrkem8anB5ntcfKwhwMXE9u/v/380Cw2Q2SA7kMUYkXvHU0+sqb/940Xdt3+/aBI13Ezsf1U4xJuzLUIasTpgIABAgAEAkufcCgWFyTcAAAAASUVORK5CYII=") -} - -.list-list .ico-wmv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGzg3FnqiacjYxh4iAaXQ2yGDY5ED100kDtonsMzXFKYDYyDRLHpYZYuaGTC2C+QKbxqSFFDqsDuNlAbch/NPMtyGwuoB3IYozIveMJ667W33jFUPftF22iBujBv+piDM0FQdqNWB0wEAAgwAD570Ssrd8AngAAAABJRU5ErkJggg==") -} - -.list-list .ico-doc, -.list-list .ico-docm, -.list-list .ico-dotx, -.list-list .ico-dotm, -.list-list .ico-dot, -.list-list .ico-rtf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAArdJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFcYPnW+ww/f/1lSAhUQRFfsP4OmIaJP3j6hWHGiptgtoQIJ0NBvBYDCwsLw9+/f3GaTVQI6KkLMtx99Jnh959/cDEQ+97jz2AMExcWYGcIcpVn4OFiYdDXEIKrBYUCExMTOArIcoC6Ah/QAEawI2AAxJYU5QJjmDgvNyuDGlDt1+9/gA4QRDEDlgbIcgALCxODhhI/w417H+FiILamMj8YI4tfuf2eQVaSm0GInx3DHLIdAAK6aoIM1+5+YIBVXTfuf2TQVhFg0FIWQBG/fOsDgwFS8JNUGeGNBkU+hh8//zI8e/mNAeQRFmZGBlEhDkhqB4YQSBwUBU9efGWI9VeivgNYYdFwHxLcmkCfwwA4GoDinOzMDEqyvMBEyEq0A0gqB0C5ARTfIKyF4gABsNgVUPBrCpFUVpDkAHUFfoZXb38wvPvwk0FeGlFlK0jzMLx9/5Ph8cuvDDqqAuQ3SAiXbEzgbMYGpJmQUjQzMIuqAdPInz//GdjZmGnnABCI9sWewCK8FMkqrulSHY86gOg0MGHJIyD5iOaWzmrD3SoGNZ9Q+JU5Dij89ikHsMoRIw4Cv379Ij0XoBsO4sNoYsUHLA3gs3xk5AL0NIQ3F3BxMP/79ecfE6j5hMsQWJDC4ptYcRAA9ZZAdqA0UpB7x1MXHKm//eh73bcff2kSMtyczH9VZDmbsxNsGrE6YCAAQIABAHOPGcK/91HrAAAAAElFTkSuQmCC") -} - -.list-list .ico-docx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAphJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiA5ClZsu8/w4+dfhoRAFRTxBevvgGmY+IOnXxhmrLgJyU4inAwF8VpgB4AsBuUKbA4gKgR0VQUZ7j76zPD7zz+4GIh97/FnMIaJCwuwMwS5yjPwcrMy6GkIwtUyMzODMdkOUFPkA9MgR8AAiC0lxgXGMHGQxWoKfAyfv/5mMNAQQrWIiQmMyXIAKwsTg7aqAMONex/hYiC2hhI/GCOLX779nkFOiptBiJ8dwxyyQwAWDTfuIzngPpIDkMSv3v6A4Xt8gGgHgKLh67c/DC/efAdjZiZGcEIDYRAAiX349Ivh8YuvDLpqguTVhqREA7IlWsoQcQ52ZgYlGV5wWqC6A0BAW0WA4ei5V2C2h600XFxPXZBhx+GnYLaprghJZQVJBREovp+9+sbw+t0PcEKDARAbJAaSAzmS7AYJMdEAcgQTMP6ZkFI0iK0KzH6g8gAUDTRzAAhE+ShhFY/wUiSruKZLdTzqAKLTwIQlj4DkI5pbOqsNd6sY1HxiqMxxQBFrn3IAzkaWIyQOEkNX09i3i7hcgK4RxMdmID5xZDl0NTRNA8gW4bN8+OYC5PSAHAXYACNy57Swefefn78ZmavznGiWCHum7/3bX+vKgtUBUxcerbn98Fvjtx9/aRIyXBzM/1Tlueqz461bsDpgIABAgAEAbXE38hMP+G4AAAAASUVORK5CYII=") -} - -.list-list .ico-pdf, -.list-list .ico-fdf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7CQa+jf9x8ZGFlZGJh4cKcXPj4+WDoAgQgofQaIGSl2wJupCxk4tNUYuG1MGVhEhbGqERAQADuCkZERjh8+fMhAeQgAExgoBPgD3IF+YcSpjJWVleHPnz/wBEm9RMjExMAiLsLw/fwVhj8v3+AvaFhYGJiZmYFamMAhQLVEyOfryvBu/mpgGuAiqJYmDuDQVGFgFhJg+H72MlHqYWmAag748+oNOBF+OXya4ffjZ0Q7gmoO+LL/OAOPszWDcGokw7t5qxh+P3lOeWVEdCb4+g2cC76fvsjw6/5jhv/AlP5m2iKGvx8+M3CZGwALib8MrDKSDLxudtR3wO/nrxjeL14HLoQYOdgZ+IBZkVVCFJwdf964y/Dz3kMwn0VCjPoh8PXIaYaPG3cx8Hk7MfA4WGKW+xrKYExSjiZW4ffzVxl+3rzLwGWix8Bjb0G1eoJoB/x5847h/6/fDHxeTnhLP5olQl5XWwYGEKYyoEt1POoAotMAS/tMhof0sHDxRNyJEFuqVGorgbPvVfVgiBEjDs9N5OYCZAtgbGTDcYkPSBoAOQY9JCguB2AGEuM7UkKA5CigaS5g4Ob89//rdyZGEkOFWAeCu8FcnCgtVEbk3vGN/ln1TDfu1TF8+06b8oGb6+8/dcVmjcK0RqwOGAgAEGAASrzyfwJnztEAAAAASUVORK5CYII=") -} - -.list-list .ico-ppt, -.list-list .ico-pptm, -.list-list .ico-pot, -.list-list .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdtJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGYkywHvJ0VhCjKzMLCpmDNwmAYwMAtJo0iJiooyiIuLY0uY5IUAb3Atw+e1zQxczmkMLKLyYLH/v74x/Lp1nOHzimoG3pB6BmYxRRQ9sMRIVDlACDBDLWUWlESxiEVGm4GJT4zh+/FVDDz+5SRFE9USIbuOE8OfZzdJ1kfdXMDINHAO+HXzGDyKyK4LiAV/3z9nYGRhgyTCP78Yfj84z/Dj/DYGHt9S+jjg295ZiFBn42JgkdVm4PEpZmCV06WPA0DZjUVKnSpRR5eieNQBVEsDoAQnmLdsNAqGlwMYkZvlDws06dJGl59wHU+DhIUdU0PVZoQj23yxiiHz0dWigD8/ycsFyBbD2NjEkNnDqxyABTGy77CJ0cwB2CyhxGKsUfCfS+Avw/9/tAtvoNlgO3Blwzuzi2pY7h1rZPz+kSZp4z8n/78/Slb1Kql9LVgdMBAAIMAARLzKcEnwqqcAAAAASUVORK5CYII=") -} - -.list-list .ico-pptx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZyXLA+0lRGGKMrOwMrIrGDBymAQzMwjIocqKiogzi4uLYEiZ5IcATWMXwZX0bA5dzGgOLqDxY7P+fXwy/bh1j+Ly6noE3qJqBWUwJRQ8sMRJVDhACrLI6YJpZUBJokSLCECl1BiZeEYbvx1cz8PiXkxRNVEuE7LouDH+e3iBZHxVzATBd/f83cA74deMQA7O4MmV1AbHg39f3DP8+voIkwp9fGX7dOcXw88J2Bm6fIvo44Ov2SQgOMwsDCzDlc/sUM7DK6dLHAbwh9eCUTw1Al6J41AFUTQOCectGo2B4OYARuVn+sECTLm10+QnX8TRIWNghiqo2IxzW5otVDJmPLg7TA9MH5//5SXwuQLYYxsYmhm4hsiPQ5eiWBoixnGAIwIIYPTjRxWhWEGGzhFiLyYqC/5wC/8hp1eCyHCOxAs3+z8n/D2c2vDOrsJ7l/vE6xu8faZI2/nMJ/P2jYNGsktbfiNUBAwEAAgwA8LXlQoWLfqIAAAAASUVORK5CYII=") -} - -.list-list .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVJJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmRhi9a3TweIgGp86mFoY+PXrF4ocI3LHJL1own9sDkC2ANkwZDFsjkFXC3PAzL4CRqqlAWyWj6xcgBwFFDuAi5PtH6FGJK40QYwjQGaD7MCZCKfOWVt/5/7Lum/ff9Ekarg42f+qKIo1Z6cEN2J1wEAAgAADALEDt/dBJDrPAAAAAElFTkSuQmCC") -} - -.list-list .ico-xls, -.list-list .ico-csv, -.list-list .ico-xlsm, -.list-list .ico-xlsb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBAQYAAlaEZGRjAGgcePH9MvClhZWRlYWBB+hDmCLAeceHmdIWZPO5gmpObYi6uIOAY6AISRQ4GsNGAhrgmmp1zegMJHtnzSpfUMGdo+DFYS2ihyzMzMYMuxlbokpQFcjoBZnqLpyWAnpYdVLxMTE+UOwOYIEABZHqfuyuAkY4hXL0VRgMsR/4C+ilJ1YvCQMyUroVKUC/5Bg1SEk586tSEpOQIU7AGK1gxyvGI4EyZNHACz3EfegiFMxR4uTq4jWMixXISDn0FPRInoLEoVB8As//PvL8OLb+8Y9j05z6AjpECxIxiR8yawRfRfWVmZpjXj3bt3QS0iRrpWxzTLhsPCASiJcPKr3QwMIExjMB2pVYwRAuyMLCi42DYKKx9dHCYGw+hyMExyFEw5soohxwbSmgLRID42AJODYZgeqqQBmIG4LEd3KIxPs7qAmo4gKgRgvscXrDA5WBSQlQu4mNj+/v7/l5kJ0WpGCXr0qEC3lFC8/2P4D7YDZ1E8/eS6mts/XzZ+//eLJuUDJxPbP1V28fpM86AWrA4YCAAQYADJhRMxzVK59wAAAABJRU5ErkJggg==") -} - -.list-list .ico-xlsx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiDLASdeXmeI2dMOpgmpOfbiKiKOgQ4AYeRQICsNWIhrgukplzeg8JEtn3RpPUOGtg+DlYQ2ihwzMzPYcmylLklpAJcjYJanaHoy2EnpYdXLxMREuQOwOQIEQJbHqbsyOMkY4tVLURTgcsQ/oK+iVJ0YPORMyUqoFOWCf9AgFeHkp05tSEqOAAV7gKI1gxyvGM6ESRMHwCz3kbdgCFOxh4uT6wgWciwX4eBn0BNRIjqLUsUBMMv//PvL8OLbO4Z9T84z6AgpUOwIRuS8CWwR/VdWVqZpzXj37l1Qi4iRrtUxzbLhsHAASiKc/Go3AwMI0xhMR2oVY4QAOyMLGBfbRsHZyHx0cZgYDOPTC8JER8GUI6sYcmwgrSgQDeJjAzA5GIbxidFLMA0gG0iMQ2F8YvVSrU2IyxEU5wL04MSlBmYpssXE6MUoCYsPTf/z+/9fZiZgqxk9+LAZCJPHZgk2vf8Y/jOwMjL/7bXLZMHqgOkn19Xc/vmy8fu/XzQpHziZ2P6psovXZ5oHtWB1wEAAgAADABRqMy9QT3WBAAAAAElFTkSuQmCC") -} - -.list-list .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012zDEHrZ4YYiDxHDX7z/JLweQDQZZCuOjiw9YGkB2FE0dgM0iciynWgiQazlGGvjPKfCP8ddXJgZGJpISIro4Tsf8/we0g/8fTgf8UbRoYrl/vI7x+0ecLnjY4EyUGFb7uQT+/lGwaMZZDgwEAAgwANHIuc9Vl0AAAAAAAElFTkSuQmCC") -} - -.list-list .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYxJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabQgHtnhhyCGLIavFph5Sv/8kPhFiswCroTgsJUY9SQURNsPxhQIhyykuCdGDnJwQYCLHQpjBMIzNIWSVhP85Bf4x/vrKxMDIBPcNqUGKV/3/f0A7+P/hzIZ3ZhXWs9w/Xsf4/SNNyof/XAJ//yhYNKuk9TdidcBAAIAAAwBVN9K79PuTHAAAAABJRU5ErkJggg==") -} - -.list-list .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012xgetnjB2WBHQ/nIYujiqPX7T8rLAXSHgNjIYuhqqJ4GYBbi9SmtEyGyI+juAGTfU+oIlDTwn1PgH+Ovr0wMjEwkRwG6Y7BGzf9/QDv4/+HMhndmFdaz3D9ex/j9I03Kh/9cAn//KFg0q6T1N2J1wEAAgAADALuKyAid7COMAAAAAElFTkSuQmCC") -} - -.list-list .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX5JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabSj8hy1eGPLIYoTUM/z5SXo5gG4BhqEUqKcoDcAMR/c11UtCZAuwRQEp6slyALYgxRb3MD7ZUfCfU+Afw/9/tEv+QLP/c/L/w5kN78wqrGe5f7yO8ftHmpQP/7kE/v5RsGhWSetvxOqAgQAAAQYAQvfLEuMZrKwAAAAASUVORK5CYII=") -} - -.list-list .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo0121D4D1u8wGIwGlkcd/3+k/xyANlgdMcgy8McRbM0QIoFVC8J8VkOCxVSHMdCTZ+TEyooDvjPKfCP8ddXJgZGJqISIskW/v8HtIP/H85seGdWYT3L/eN1jN8/0qR8+M8l8PePgkWzSlp/I1YHDAQACDAAtKS/DHmsv9AAAAAASUVORK5CYII=") -} - -.list-list .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHtSURBVFiF7Ze/T8JAFMe/1QYCRKIDIe1AYcAfLJLowL/grCbGydEJTXBw0qiLA4NxNGFlkX/A0UlJJGEBQzoAC3EzxhgTgz0HPXI9jrahxTr4TZrm3r3r+/Tdu2tPIoTAT8lso9lsrgEoAVA8jLEKoMYaMpmMGABASVVVJRKJeBJZ13UAuAawmU6nayKfKa6thEIhGIbhyQUAiUTiBsC1rusrTgA8VzAY3NU07RZARQQxcQAACAQCO8lk8l4EwdeAp4pGo7QOAGDr5/4AQPoVgFgshng8brIxQM4AXisn6PdaYwHI6gJmNo6tfewe0u+1MJcvC/ueL7dH9tF+O7kqwrl82VGQiQHYZWDiAP8ZoBC+AriV7wCudkK38w/8lQxI0ve3odFoDDuoC2MXmqzMC+003gDASnZ7uVv5PgW+A5imIHK1jicA2sUjuvtLEwk49LvLngs6e4uEVaewTHjxtk5h2XTZ+u4tmg4iQ0XYPchCK9bRPcgO2lRasW7y48WOo758m5ejjWjUYKdiwXjZFiEdPOoBboJbArBvrRXrlllg0y3ytRovEUIGO1P7MPcpfbxNQRpvdfI1MPTmxAAJhI3UeXWamkw10E/lTuX23ZH0/iImkIOWADQDAITLmIRnP/vJ3BlQZYw+H8+/AFShLMorUUd8AAAAAElFTkSuQmCC") -} - -.list-list .ico-bt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbxJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMOkTAiAAS4xElQO4QPmJORhivKycDO6yJgymYupY5WGg0yKFcgfgMqjm1AKwA5DlQI4hZCnVcsHvf38ojiK6ZEOqRAG+eKaLA7DFKzUcNeBRQJEDWJlY6BMFzIxMWIPbSkKLYgcwIreIgA2S/8rKyjQN8rt374IaJIzDIw2MOoDqBdGEFzsYGF7Q3tJZeFrFDFzM7DS1/Nvfn7iz4cPPL/8zQWOl99wqhmKjMLgcMh+dDQLIatHFkdUD28sM8rzi2LNh6+mlKBaAaBjGZTm6xTCAy0EwO8hOhDCHobNhlsD4+BxHlANgBsAwNoPRLYGpRfc10UVx4YGpf/4w/GMGlf20AH///wOmeqa//Q7ZLFhzgQqHeMPtHy8agSmVJi7gYmL7B7IDZwgMBAAIMABhJOYNvD7xMgAAAABJRU5ErkJggg==") -} - -.list-list .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII=") -} - -.list-list .ico-svg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII=") -} - -.list-list .ico-apk { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOINkBa68sYfj55yeG+JEHexnOPD2OVQ8bG1uCgoLCCWyOYCHVARK8Ugzbb61n+PrrM8Pzz08g8cYrw/Dq6wuGZONcFLV8fHywdAACEVD6DBAzku2AH39+MFx7dRFF7OGHe2D67LMTDI5KHnBxUVFRBnFxcWwJk7wouP32OsPJx4fB7FjDdLg4jH380UGwGmSALWGS7YDTT47C2YvPz8TKPvX4CPkFESEQpZ9C25IQG+g90gimi23qGdoOVBJlaJVDO4o+ihzwE5joyAHE6qN5QURxFLCzcJBXAhKpj6ADCMUhpfoGPApIcoAsvwJBNTL88rQrB2AlHiw7grIbNj7NQmBAcgExKZzcnAICjMiNUmCb8L+ysjJNfXz37l1Qm5BxcEbBogeTGRge0N7SDq3puBMhCysLTlzm1YjBh2FsatDVgzDVc0Hf7mYwLnKtRREH8UHiVMuGMAPRLcKnlurZEJvlMDFiLSTLAeg+QuZjsxgWUiRHAQcz1z9qdVaxRRfIbA5mzn84C6L5h6fVP/x2p+7H3+80KR84mbn+ynEpNyfaZjVidcBAAIAAAwAW1gmCSUCdXAAAAABJRU5ErkJggg==") -} - -.fileList .ico-bookfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmJJREFUeNrs3E9r1EAYx/HJ/3S3mwTb7a4u7kkPguLBky9A8OrVk9A3ISiK4mvwpNCLHj37GjwJ4smjFNZuQTbrYXe72/hM2lrBS6LuhITvDx7SlsIkn5lkMp22VpZlivx9LAABBBBAAAmAADYEMP3woK7Xsiv1ylBb+1J7Us/dhgyEW1Iv25euKcdvrb2xbHU0mE9GjxbpgdMEwK7Uu43toe94cjnZYv23ra1UEO8oAdy1a47nSL0JkotDrx2bffbZVt55dQd84UfdO0HcrewE6gx4z20lD4O4p59KlZ2EK7OuL8cnUnr6HdRFT/CU39k6u5WqOw+pZ360Iz3ZV5bj1WgAHsvAW1bfkfr9Sc8olp0ZmcGaFv0M7FZ9G9QdkAAIIIAAEgABBBBAAiCAAAJICsfIptJyPlU/vn2SY2rmooJIbfZuyLHTjBFoEu+kw9K8zUaNQB09KsJooLLjlZqOPv7xfZbtqk7/5q/PD7+8z4/bV+8WbmuW7hvtMEPPwJNNH4237phog0kEQAABJAA2A9DbuKAcr81KpHys/JXD3+zJW06mZpOv6mj2HcBCdJalWltX8iXX6RdUmAyVO4u4hYsJ2ud4v/dmmADIJEIABBBAAAmAAAII4H9c9+ro9e66c96Gmd/7NrIW1tuL+U7Zwee8yuZsc6lUm2HcnBGY79GG5v6WTbfV6V1v1ghMLt/mGUgABBBAAAmAAAIIIAEQQAABJBpwnK2WSJTMqdlYA76eT8YKxHJ42kyyp38e+HQxPfSl7svHfXgKZST1Vuox/8HyHwMggAACCCABsKr8FGAAiCO50cIM93UAAAAASUVORK5CYII=") -} - -.fileList .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAY1JREFUeNrs3E9Kw0AUx/FM/kwwxSagqdVC9649gwdw60rIJQRFUTyDK4VudOlBvIdQjJvWVWPr9I3o3gaaNOH7g0eyCUM+mcybbKKMMQ4pHxcCAAEEEEACIIDNjF/mounr2SbeSyb1UNFYb1IjqVu/JRPhSOq+c3DoeDpa+2Bm8TWYTcYXxfTdawNgKvWytTvUXiC3Y4q1D6hk4QvjniOAWdPXQE/qKUz2h0EnrnRg5aqfh9d0wDvdTY/DOKULl8iJHyXnYbxnV6X6urB0VC3HKynbWgeN2T5EiaO3d/5epVq3MTe625Mn2XeUFzRoAn7LxJtvxD4wsx1FuaaSDtbGL5G07teATzkACYAAAgggARBAAAEkAAIIIIAEQAABBJAACCCAABIAAQQQQAIggAACSAAEEEAACYAAAgggARBAAAEEkAAIIIAAEgABBBBAAmD1gLlZzJFYMb9muQV8nE1yB8TV8KyZZGT/G3NdfH5oqVM578Pzr4ylnqUuFT/ipokACCCABEAAm5mlAAMAvOdJo4EAKcIAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-empty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABBJJREFUeNrs3MFPE3kUB/D3m+lMp62kpQgEiXDw5CaYuGsUXTxtOOllr3oxcvI/0JiYaEzWv0AvHrzgcU941kQPxuxmEzl42gNshAXRpSC0nbbz871fmdpGBdoK7LTfl/zSMm0ymU/f7/febyZBaa0J0XpYIAAgAAEIQAQAARjNiIVvVv+8GvVrmeLxcJ/O9ZbHIx53Yh2SCD/xuJ/M/kCWk9zzk+nAH/Y3lm6WNpfsTgDs5/F7vGfUteRyAn/PT6h4uMkBYsCpqK+BNo9pNzU04njpfT2xUsr8eFEHvOskBiYlG1CFm49fY/He625qUFalg6vCXH1dfr3F4wqP4ci0D/FechJ94VQ60DbmtpMcNL+kstzo5J8OeJT/F33glKwh5nfchwrWiTuR/oOeBtjKARABQABGM2LShAYrT8ny50jpva/CJTVEvnU00mjKdiiWyG71o+V/yA4WvvnlfKHaayW89u87qMxJ8vrGyYt41ulKiYq5f0kFmmJOsMz9X3GrKW3cEq3nFanBS9X3S4+pJ9H6lkn1niIr+yOfIvq9puKFL54eIH9tmWIq2OCLKtU+rHCDb1uf8dInfjPHc685G1emKeG2iHf4545a+5SlttbAuu2QoPn2CKniPLnDVbzc6xvms3j/BP03/5jKFaJDnqbd9t6diPdFEZEQmLI3RgPnZ6iUmyUnPUbrb+6R5aTJTjEqvw5dnDPH8gv3KRkHXkMbE7N5FGbp498PDJ5knpMZo+ToJYMoqMWVF7TJWbibaayypzseL7yZ0FAwDh27ZrJMc9HYXKxOWW2nKTs+XRUPclTgJTNfVJSI669iGry+s93TSNcXDMk8ybqgZ4JcRzfgfXh52aBtlDKUOTdDBeeEAe5WPAMYrn2CJ5knU9ROjpjpKlCCJ1NY8FLOau2Yqdib86ZidyueARQAgZA1zjtygeKHJ8zfghbiCag3dKEBLwQNq3E34pnrLj8/qvPc2wpOz/HrVNngFoZbFoELoQRPMlRgg1Kuhheufyp7hvHGuw5vbW62ugYKRMZbpcKbauWVKlyfZYXFJ8DbqQpLG+O5mnu8J7QmVZix6tc84O0AaHYbjuTlDKX4qOxYv7bmAW8bwBoiVfu87DngNQ0YhkznAk9naWvq8QROABE7AAqY7Hn7klRrVYDXBKCg1d8wAN4ONxO2/RLwms/Az3hnzS4D0UIGAq8NQOC1AQi8NgCB1wYg8NqowvL8Qh4CIVrIQOC1Aejbx4DXDmDJHoXCXm/lEAAEIAABiAAgAAEIQAQAAQhAACIACEAAAhABwO8CGOhKGRJNxpZZYGmlXhVz7wiIzeEZM6X+kOfCk8WP75/56ysnMaV3HQHj/cWvvyj8I24UEQACEIAIAAIwmvFJgAEAn5Nwqgtf3PMAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlFJREFUeNrs3M1qE1EYxvH3zNcx09ovaUot6FIUNyJ4A+IFeAfRXINQFIS60IV7Vy7cuPUaXEtEkG68ANEYjaZNE+YjM56ZhBIxlIykMVP+D7zMJnCY35wz7zmBRKVpKuTfY0EAIIAAAkgABLCccYp8+Nf7+4t8L3VTL+c01mdTr0w9cc7IRLhp6oW/cU0s1z/1wdIk3AmPmo+iXtM+C4Cbpt7o85c9K7udJDz1AZUpz6+KAayX/R1om3rtLW1fcs+tznVgpVT+8FSRs7BWi+XdbNSeuZXqrl7ezhbW3Mfvtj6WtwsbvLuOXt/1lrb+C97xTPzZuOeZ62NTNVM7pdk+6HVxKxfEnkPTOGkGZk1kz/W38iepLK88UzBNTMULsQ+sZx0lfyWOOlgQxhJFA3bJY3FdW7TnTATcHHWU42R4K1ceoDaWg0/PJwJylJvlUa7XDyUIYjnqBTJot9EZy2Gnl69MrR3xK95kwAxv7fpDWcPrr6zc2ht+H7D/9A9AlvAslnDU/SJxvy16EEm/UUPlpNOYqX7LFaeyMQRUSWr2f6ksX7wqynYRmmYLaiZa0PkqmZ1jDsOiV6s5oqQhOtMc38yLLzMLD74N34HKUqgURlQ0kVkEQAABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBBBAAiCAAAJIAARwIQCTdBAjUTAjs8RKlXoXdFoCYjG83EypRvaL9TtB98fb8PD7DZb01EkM3gdzva34I26aCIAAAkgABLCc+S3AAB9MnrCxlmzHAAAAAElFTkSuQmCC") -} - -.fileList .ico-fromchromefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABCVJREFUeNrs3F1oW2UcBvDn5KQnbdYmTfPRLMnafdCLUcfGIhM3O2WgOPFiu/FCZBY6BooyvJAJiqNTL4ao6IV4oVJERbwYKoKiBTdlaLeVIV3Z/KhzW9O0OUuWpGnaJWnP3vd01LUGbba2W9LngT9Jc5JzyC/vef9vT0MVwzDA3HwsJCAgAQlIQIaABCzPWG/8IX2ivZzfS4eo95foWBFRXaIOWStkIIRFvbsisB6qZl/0gxmT+eDV1PCLuXRMrQRAr6gjNZ4mTa0Sb8fILfoBFTHx2Zw+CMCOcp8DVVGf2OpXNlWtcC7pgRWLYn545Q74qubwPmhzeu+MJlIs7ZEDi3LgruDhW93Fbqu9/gWbs1HOSrcNUEn1PKmJ25ellajgUkAuAB4EHrQ6N6zV9tuGl77QZ47ATs3hE5+kH4pa9d+Ne4HiWB1egL1MiYFXuCPWgR2yoygWY0k62PQ6IIdKiWwi3usdheGvcgQkIAEZAhKQgMsnC3I5q8VVgz13+dDqseO3xDi+P38FRy+lMGVwBP5v1jir8UpbM+Ra/KMzMRNw36aVaN/QyBE4n4QcGk5GR/HWqcjMiOu/nMWBe0L4OTKKs/EsAYtlVZ0N69016NOz+OlSetY2CZoYz+O17c0YyebxzqmhioUsGVCeqk9vDmBH0z9XgLsvJPHe6eisOa/z+EXUV1vxyFoXOu9rwv7uvxAdy1UcYMlz4KPrGrA1UIc3Tkbw2Jfn8HbvELaHnNi70T/reZFMzjyVXz8REaN0DE+0eityBJYMuFMAHvk9juODaRTEkDt2MSWQBvHQahfuDTqKvkZ25I2+WgLKuMVpeT41Meux3uEMvvgjjqc2+eGx//uibCY3BU1VCCgTnyiY674bU6up+OysDl00jufuDmDu5cWwvxYDyQkCynwzkMCuFjfuF02kUYy2h9e48OHOFrMrvynmO7kufFY0mWrr9K7ldlnfisU1u7DI1wKwoaYK+8OBmcc+7o/NnNaHewbx/JYQtoUc5hypqRZ8fk4350oCisilSlffCL76Mw6vgJTdNpObnNn+a2wMz3QPYIs4bSXe6ZGM+RwupOckMV4wq1iSYp787u8kr8YwBCQgAQnIELAcAO1qAds8UTRoE8sK8JauSFsUA2FXDA94I+JWh2aZXlD3pxvwQyyEX+J+ZCetBJybVkcCbd4hbHVHUWvNF90ua9/afvQkGvGjHkDvFd/yBlxlz6DNM4QdvsF5n6ZyRMrXyErmbTgaC+KYHqwoQPkNVcPRvIHd4CYiv6HKLsxlDAEJSECGgAQkIAEZAhKQgARkCEhAAhKQISABCUhAZl6AujFZoESJuW6mS8APrqZ0ELE0PGkm0iX/sH4wN3pZE/W4uO8nz7wyLOpTUS8p/EfcbCIEJCABGQISsDxzTYABAF5pTWwmgxlgAAAAAElFTkSuQmCC") -} - -.fileList .ico-documentfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNrsnE1rE1EUhu98ZCZOTSbYpB9GKgguBHelP8BFodtuuxLyJwqKovQ3uFLoRpeu/Q2CgktxJdgPTChNQtskTTI9N1hw4cKkk5nMzfPCyc3qXu4z57znTi7EiqJIocllgwCAAAQgABEAAZhNufqj9fmpCXupSbxNaK0DiX2J164hibAu8Wbh7iPleMHUF4sGl9Vu8/hZr/XbMQFgReLjrfKa5+RkO1Fv6gtaYnx+uKQEYC3rHuhIvPdLq2u5hTDRhS3bGj28rAPc84qVTT+s0IUn0LYblHb9cFm7UnpdWDqwJ+MLCd2Kq5k5PgQl5RUWr0sp1WPMK6+4JE9yRVlOLkMJOJTE68/EObCmO4plR4l0MBPfRCpplwGvcgBEAAQgAAGIAAhAAAIQARCAAJwbJXqpdHlxos4a31W/04zn6bt5VVxdV26+MB8Z2D76Fhs8rWG/o1pHX2XO9nxk4HDQHY1hdUPlgsUbz9f48UkgXqhO+5fK2/eVm8CdcKoArxUHvL/VOf05GvOFe4mXszFNRENMo5yN6sK6nDVE40s4LpUfbv3TE8lAzoHjddJJsg2AMw6GEgYgHhirB86qBeCBlDAA8UA8EA+khAEIQACirADsndXjn/O8YX4X1teQo5u0wy9TXcPYDLy9/FjZjj+9zcjceg1jM9ALyurOgyd4IAIgAAEIQARAAAIQgABEAAQgAOcbYD0a9CExpv4wq2uA77rNugLiePA0M9G+/j3wZa/d8CR25PsKeP5LxxIfJJ5b/BE3TQSAAAQgAiAAs6krAQYAXiGbDBfBqf8AAAAASUVORK5CYII=") -} - -.fileList .ico-fromphonefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjNJREFUeNrs3E1O20AYxvGxndgQq04ESfhIxaalgjVnQOq2264q5RJIIKoiztBVkdi0S9acoVfox6qoEQkgjNriNLF5J1Cpm6glJHEm/j/SqySbsfLzeGZsTWIlSaLI4LEhABBAAAEkAAJoZnJ/fwg/vjL5u9Sl3o3pWCdSh1J7uSnpCBtSb/3ldeW4hZEfLOn+rkWXje12eOpMA2BF6mi2vOI6efk6SXvkB7Rk4POKVSWAddPHQEfqvVdaWsn7xbEe2LKt3skzHXDfDSqbXrHCLDxAXuQKpS2vuKBHpfRmYZl5XXndldJTcM2Y5UOhpNxH838upVSXMW/coCpnclFZTt6gDhhLx+tMxDqwrmcUy07GMoNN451IJe3LgFs5AAmAAAIIIAEQQNOSzvPAJFY/z7+o6/BExZ3rh/WA3IyaCWqqMPfk9kFdFgB/nH1Wvy6+DueOWE6APhl6i4pffpaNSzgKvxnR5sQCxt22EW1O7hh4l/Lq86G00/p0zCzMMgZAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAGcwqS6tSPNLRlGA9qO23cz0L/2y/RD121mBtALHvfdHzhor/SCWnYA/fmnSv9YMAq/S0+MHtibPcFbkjZXMzQGWrbyy2u9YhZmGUMABBBAAAmAAJoK2Ey6HSTumTuzpgY8iC6bCsT74WkzyaG+lXvdvmq5Ui/l/SI8/5WG1AepHYs/4mYSARBAAAmAAJqZGwEGAN+Yh/QHkxJLAAAAAElFTkSuQmCC") -} - -.fileList .ico-mix { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhFJREFUeNrsnL9KA0EQh/fukjtNNBc08S9oJSJo5TMItrY2CnkJQVEUn8FKwUZLa19BO7ESKwtRTBASEU1Mcs4GtVFRA5u44fvBcOF2YG+/m53Z4SBOFEUKNS8XBAAEIAABiAAIQDsV+8mhdLpsy1pyYrstmutabF9sK9YhgTArtpMcmVKenzA+WVR7GS0Xb1crpTuvEwBmxY66M2O+F5flRBXjEzqS+IJwQAnAnO050BM7CNLDY/Fk2NKJHddpvDzbAW77qexcEGapwk1oIZZIrwThoM5K7avCUmV9ua6L6XI7as3xIZFWfm//+1Zq6zFm008NyJscUo4XtygA6xJ41X9xDszpiuK4UUsqWCd2Itl2bwNaOQAiAAIQgABEAAQgAAGIAAhAAAIQARCANqgl34Vfnu7VY+FCVZ+Lnx+gK1TJzKSKd/cZ9bU6Ah9uzr5cpJa+r8dN+1odgfVauXHNTMx/GitcHn+Mm/QlB1JEAIgAaGkRcWNdql59biT278ZN+1odgT2D08r1gq8fQO7rcdO+puQUT5ai1PgMe7EJla7OyYG0crRytHK0cgiAAAQgxxhaOVo5WjlaOYoIAiAAAQhABEAAAhCACIAABCAAEQANA8xHtSok/qg3ZnkNcK9czCsg/g2eZiba199ENioPBV9sUX4PgedXuhU7FFtz+CNuiggAAQhABEAA2qlXAQYAvkAprthin2kAAAAASUVORK5CYII=") -} - -.fileList .ico-musicfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAexJREFUeNrs3L9OwlAUx/HbFopCBKKAKImJg4OzPoCLiaurkwkvYaLRaHwGJ01cdHR28An0EYwjCREYwMTI33qukcVJVHq59ftLTiAMtP309B5yB5wgCBT5eRwAAQQQQAAJgAD+A8DW/e4kX0tZ6jykY1WkLqVOYhFphDWps9TiqvL85NgPFvS7pXazut9pPXtRAMxL3UznlnwvLpcTdMb/2LpKJTIFJYBl13I8T+oqkV1Yiqcy4a59rvNx82wHPPXT+c1EJm/sBGwG3I4ls3uJzLxelYydREwmqy+vh1J6xJZs0RM85c/MDR8lc+chdeynC3Ini8rx4hY14EAar2f+RurfT3qiOG4QygSLWvQamDf9GNgOSAAEEEAACYAAAgggARBAq2JsS7/xdKeCwfh2U9zYlJpd3ohuB44TT2fQe4t2Bw6TW9n68++sP96yBjJEACQAAggggARAAAEEkAAIIICRTCjbWc3Kg+q+NujAnyaqeKF1YJS72/o10HR3M0RsAPSTuZE+Zw38knRpnQ6c1JjubuunsOnuZogACCCAABIAAQQQQAIggAACSAAE0DLAWtDvITFiPs1qGvCi3awpEEfD02aSS70jfdR5qftSO/K+CM+3UpW6ljrgHyx/GQABBBBAAAmApvIuwAAZ2pRkuq79SQAAAABJRU5ErkJggg==") -} - -.fileList .ico-picturefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+tJREFUeNrsnE1PE2EQx2d3u7vttts3WlqKKYrBiO8Rj179AF49mfAlTDQajZ/BkyZc9OgX8O7BA0ExctGIECmlon2DvnedeaAQA9JW0227nX+YLIGQ7v52npn/PLtBsiwLWP8uiQEyQAbIABkgiwEywBEAmH93d5CvZR7juU2f9R1jAeOJyyGJMIfxzJuYBUUzev5hVqM2Wclt3q/mtxQnAIxivPZEkpqi4uVY1d4vWxlAD4wDApyXhxyegvFSD04kVW/A3tonS+LmDTvAp5o/eksPRPt2AsMM8LbLCN7TAzGqSn07CRd2Vg2PDzGoxU4OCz2EB5o51lpK/TsPjMeafxzvZBwkRR2iBGxi4tX7fyPJP1FHkWTLlg7mNFENjPZ7GQw7QBYDZIAMkAGyGCADZIAMkMUAGSADZICsjtXzh0r1SgGK6WU85u25IN0PvtglcXREBtoJb++G5fEzPzorA0n+xBxo3t4+/KkUUlDYfG/rDbOhBlogya6ewyPp5oTzamBbvFYD6qUsNOolUN0hUDQvN5FO1ajuQD61KI4tuQNJ8I1fYICdiOpVs1be65qaCaVfX6GcWwOXOwBu/3A8Ye2bD6RCT+EJnUZYpwQ0M34FZEXHZrDBRrqD4icO1GAOJMn4peDvmgywbe1w+0F2uXHZru7bDgt2f36GRm0XVCPCNbC9JGwWF7EOLkF27e3BT1VPCIzQGQbYicgbBpM3oVpMoY0pCxvTqZejzt3Ev6GMdemmyGaKkfOBiurBRjLdsSmnJV/KfhPwjs7B5ugB7MYzFtIfoF7OYf0MgieYRNPtE1lH42KjWoBKPsUA/zbjig0CSUKrc/XIMm/tvBjhGdj+8sb5AKs7Gax76T+Gfsom3YyDRh1Yko/srtCI509cP7HOCQvk5Ays7W5DMbOCy614eNGKSm+9C1BkoBXVACNyDnRfXCzN/MYiKLoPAokbA/n+om0Ay9k1AY8ageaLYQ2bQssSPqxxtRKOcevYIFahkFqCImYTZRs1CzN2eWBf/rQHIE4dxcwnHNM08EZnj7Uq1I29mHme0BTsbK1ApbgpvKI3ch6N9dho+0DasiIYtKlKM++JoxHOwubENRCGhEY6abAf29h2dkZ4ui28Y7pCt7fKuQCpOfS+zq4fjImOWMLUGFoqpJdF2HJRNk4kPc1A8mVG+KzwePbt8gTAh13bERlIXdcYmxHhVPGbCQyQATJABshigAyQATJAFgNkgAyQAbIYoN0AM1ajziS61D6zDAF8UcllgCF2B4+YoRZoQ/VRtfBDw7iD38cZT0eiZ66vMB7wf7D8TzFABsgAGSADZDHAfum3AAMAO2tj3VmRR4EAAAAASUVORK5CYII=") -} - -.fileList .ico-videofolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABSdJREFUeNrsnNtuG2UQgGft9a7Ph8R27KQyLVSgCCICBSTENQ/ALVdIeQkkEAjEM3BFpd7AJQ9RiQsqJGgJIIFapS0hjp3E8dler73MjO3QSNheO+na3sxIv7KKvPNrP88/x00Uy7JAZHbxCAIBKAAFoAAUEYACcDlFneWmyr2PFvFZdnB949Be+7ju4PpSdYkh3ML1dWh9E7xa8LlvZnU7G+1y/hOjUvC6AWAK1/eBZE7z+vBxLOO5b6ig49NjaUCAO8vuA724vtXj2ZwvFHN0Y8Wj8Je37AC/0qKp9/VYSqLwDPKBGox/rMfWyCvNLwpjRNXw52e4KLRuLE36EIyDFlkdHqW5pjFfaNE0fpMZULy+JTLAHhqeuRB54A5FFMVjORLB3FiJpOZ9DKSUE4AiAlAACkABKCIABaAAFIAiAlAACsArI4szVLJ6YDSOwagXwGyVwTRq/DsWxQOqFgbVHwMtlAYtuNqf7AhAZGR1oVnag+bpHo0LR8I12xVerfJTbvwG4tchkLgOiuK9ugA7zRLUDh9At9Ps+xPVD3okgxaWYkiqHiV6CK4Kva4BRq2A6xCv29A4/othRjKvgy+QuHoAW5V9hLfLgAhccPUm+KPX/ueTygAkINgkQHqTwdURYM9sQfnvexBeew3v3bg6ANvVA4T3K1/r4QwC2EKXZvcoKuCP5fC+LFTyv0AH/SbpUtAn6pGs+6MwBYjqAF4g/gJEsttTwHsGIx7x2MZbZ1ZLOs3WqdsBWlDN3+egQJYXSm1eUJ+C1vsq+Cgqo85q/gE4PSN2FCD5rm6nwT4vnNm6JK0KRDPbbJGkm/ZwJ0C0kMbxQ74MJV8Zm36cPvkBaoXfOMWxe5yDiRf5unHy6L/80U0AjcYRpx/DVGWsn8S0hSyp/PRHjrR2hPwpgaTPG/WiCwHWj/pRN7LOx85WwMHEuYTWSJF2shl6ODL3v6xj9wEcRkgtlJzu5GN1Ut7/CY/mw4mf9QVXzu3lKoDk4Dnx1CIzRW+qPCoHP6N7G+0XqV5+di8XAbTwwc0zhz+zG8AyrlbYHRNMtEG8Mh1LZxwCeHnv3li2I6ziriPsGVrHqI6LDaGmQXhM8m11jQtb+eIC9PXfnjeN6kz3BzDPi117h9OgkYGKeogoXl/IfQCHLadpczSypuj6LUy+X554LDuNk3N7uQqgFkqddWLsOngCkci9d3bvpEqnXTs4t5cTojpngSvg1ULQNeoMsZ9Qj4AdXgNVj0Bw5SXbwaB5+pj9K+1Be7mymRBI3OCf9aM/x9a50ewbCO+mbXjUrW6UHp3bw5UAqWus+uNcr1YP7l9ak4JaZGR9pNvpzrTD/UAFrWubUxqavtWLf1wc3uEu18qkk3Q7lf/NCWB/cBRdf5PbWeS3KljnjivPJtXI7eo/rIt0jktxXAOQIxceNc7pvDq3uUp7d6FVfmIvOqPV0Rj05PFd6DRPWAfpIp1zeRaYk9CQPJ57l2cZPBgq/M4NVy2c5uG5R9UxIQ5S7QZds9nv82EtTHlkb1BxUCs/srY1F8ubO8DhcY5tvM1W2MDIPBycT2rL05gziIk1jznnLAvxageB0HJJHrAbmAx3WmXo4fXQ0ihAeHwB8NGrHeEsWmYAFkUW6g+uCQzVvAFYHpG3swSgABSAAlBEAApAASgARQTgfAAWra4pJKaUAbMiAbzdLhdBIE4Hj5ih3KFmwudG9UjD9SFeZwSPLcnj+g7Xp4r8I24JIgJQAApAEQEoAJdT/hVgAK5f795U9dlNAAAAAElFTkSuQmCC") -} - -.fileList .ico-sefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/9JREFUeNrs3G9IG2ccB/Dv5ZKLuUSN1miMq3/atVU6aauu615so6ulY+2gK0ihrFhwZRR801LGcLJRcXtRxmAwBhtzyFYHZUPYi/XNBlspdK3rnznHWitpFfvHNkprUqOJJrfnuZYiYiA6tRq/X/jhn3uSI588d7/nzqBiGAaY2cdCAgISkIAEZAhIwKUZa7IDgx0HFvtrqRP19QLt65aoVlFN1hSZCJWivnD6yqBq+rzvzIiNF0SGB96PBu+pqQDoEdXuyCnUVJt4OUZ03neoiBOfPTMXArBuqZ8DVVFtdnd+oc2ZuaA7ViyK+eYtdcBmLcOz3Z7pWbxNpP5c0bzs+GPL1v/7FG9adfd79sw8eVZ6aoDK8PlaTXz9QJRsswWJBjbEf1tMeBB40NJXwJqmPzW8YF+XOQOPaRm54p30QlFtiUdfnzvAjOLKOXiWuJh4E4tiHVgnO4piMRakgz1aB0SRKpFNxPO4ozC8lCMgAQnIEJCABFw+mfPbWT69FNW+d5Cl+TAWC6F7+Cx+H/iGgNNFVWzYln8QG7J34BnnevN3ofEhE6zN/y5ixjgcajqy7D7cj9wm4OR4HWtwcO1XyHOsMn8eivTjzmgP7BYduwsbsDlnD1p6DuHu6HVE42OcgZOjW92oLzsBt+bFucAPaO9rRnjigblNzridK4/g5bxa1Je2ofnvakRiI2wik7Mt/20TTx6qJ/xHn+DJjIrz3o+9x/DrnS/NQ/dVMZZdeEo2Zu80oU7d/CzhmJ/7PzVhN2S9xi48NTlpK3F6oBXHqzq5DpzNg87c/Q7lWduTGpvKDWTWM9Af/NNsEg0XqxAcD3AGzjQVOW/AoqioKWlKOKamuAmfb+lLeqYuK8ASVwX6Hv6FTdmv463Vn8CuOp9ss1nSsKeoEa94a9F1/xezeAhPiVzrfXSlGvtWH8cWT425aO4f6ULciKPQVW5eoXQMtuPkjUZeC0+X2+FuqBYNLdcO4cXcvXgpbz+KXBvNbTdCl8wmIwF5MyFBvvUfRuWKXbg8dAoXBn/CH/dO8m7MTBIY6xXrwF4wvB9IQAISkIBzEodY1lQ418KtutiFk9dXsF4vwWZXKZ7Ti2FTHj1dz9gtnA/9i86wH6PxKAGnZk1aAapc67DJ+Sx0cek23XZZe42t6Bzx48JIN/4J9y5vQK8tG88LtBfSy5I+TOWMlNCygrEwOh5eFXUlpQDlJ1SNjKJydoNZRH5ClV2YyxgCEpCADAEJSEACMgQkIAEJyBCQgAQkIENAAhKQgExSgAEjNkGJGeaxWUACtkSGAyDizPCkmUir/MP6h9HQoCZqn/jeS56kMiDqe1GNCv8RN5sIAQlIQIaABFya+U+AAQBLjB3QxtGdmwAAAABJRU5ErkJggg==") -} - -.fileList .ico-access, -.fileList .ico-mdb, -.fileList .ico-accdb, -.fileList .ico-sql, -.fileList .ico-db { - background-image: url("../img/ico/ico-access.png") -} - -.fileList .ico-c { - background-image: url("../img/ico/ico-c.png") -} - -.fileList .ico-cpp { - background-image: url("../img/ico/ico-cpp.png") -} - -.fileList .ico-cs { - background-image: url("../img/ico/ico-cs.png") -} - -.fileList .ico-js { - background-image: url("../img/ico/ico-js.png") -} - -.fileList .ico-fla, -.fileList .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAz5JREFUeNrsnL1vEmEcxx8OOE1LEUyKthATgqYJ6eKEM8QRZ1+WmrropJs6mLjof6CJL9HFl7nMJY7SmLg0JIaBRBKrkBhDW2Io3Hm/B69eT6D3Qjm4+36TJwcctL1Pvr+3p+F8siwzyLoEIABARxUYdrJcLovK4YGyVpQVd8tFxxOJp8rhltH3nwiHrQFU9DASidyNRqMsEAi4Al6lUmGNev3mfCzGzEC0CnCV4AmCwCRJck3YnV5YYN+3tkYC8bAcOE/w3KZjosghkhOVp09QRByE6OkqPAqInm9j7EJEH2gTIgDahAiANiECoE2IAGgTom/YdpYyC8upVMpVcBqNBms2m6Y+k06nfVZHOddJme1ZKBTqucf3j8ugx7VaDSGsVTAY3N8Y0Uaf1Y1lzzlQhUjqdrv/uW6QEwGwD0QC1Ol0+NEsOM8D5Bf/N5S1W3UT78AfK9dNvf/U61d9P69/3Q5ECmVa+jBGCBuU3+/fd6IZeI4CHJWDRgmRoGmrsRGImES0MATBtAMBsA9EM//GAMB+862urUERsQgRDhwDRAC0mzOBAACdHQed+sWHjXST1mjDgW5z4LQ4DA4EQAAEQAgAARAAARCaqEb6KEfBcTbpcOC0OdCuOyZtBIQDARAAARAAIQAEQAAEQAgAARAAARACQACcIg39tuaXbA53JlO0VFy3/m1NURQ9Da/dbiOEj1Jj3dIP5/MscuVy33P1R4+ZmEzy81+vXhv6c44vL7PY/XusU6+zb7fveAegqkGACKARzVzIMGl3lwViMQ7z9+YmqrDhP3h2loWyWdZcK3AHEky0MSZE8Eg7xSJrlUpsJpPhUD0VwmfevjnwnGD8fPHScPi2PpZ4CNOR8ip3ZKGAHHhoS6XkSFo768Vei1Gt8kVQPQXQcvjmeuF78sYqX3q4BBMAhxQPynfktF/v3h94PfH8GXehEwCnpoioxYLynlZqLqQ86EQxmUgH6ouMCkrNeXq1NkrcgU4Uk+GbCflLXXFvz9PjXjsYlJYKa35LISyfO7tBd7Pw4u3i6Zr5tadSn6yH8OLixY4kf/BVq+fZ9ra3nDg3J8nJ5GeWiOcshzDkwlEOAF2mPwIMAMxjFWM7SdA6AAAAAElFTkSuQmCC") -} - -.fileList .ico-htm { - background-image: url("../img/ico/ico-htm.png") -} - -.fileList .ico-html { - background-image: url("../img/ico/ico-html.png") -} - -.fileList .ico-java { - background-image: url("../img/ico/ico-java.png") -} - -.fileList .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA9lJREFUeNrsnE9IFFEcx9/srlMbq+2fXHZdD8pG0WIHKQ92iP6QYhRFtzyVHiI7FJ2KIAiMTkoeMoSwTnoqhKLQQ0kHgzI8FAaZGIiu7JKKRVuLO9P83jrjpOu4M7Pu6rzfF5aZffNmYT78/nzfWxhOFEWCMi4bIkCABZVD6+Lo6CgvHW5LnwvSJ2SVhw6Vlz+UDi3Zzt9ZUmIMoKQ7brf7hsfjIQ6HwxLwxsbGSDwWu1zq9xM9EI0CbAZ4NpuNCIJgmbQLBINkJhrNCcT1amApwLOatvE8hQiRKH3txCZSQIhMd+FcQGTexpiFiD7QJEQEaBIiAjQJEQGahIgATULktLazpLWwGA6HLQUnHo+ThYUFXfdEIhHO6FLOcpLW9sTlcqWjh1vmstb55OQkprBaRUVFysaIOvuMbiwzF4EyRFAqlVoVdWtFYt4BTsV+5AVGyO8zDBEALS4u0qNecBsO0OiD5VNyKqu36vSCZN7GAES73b4qjVdGJdZADckAIRIz1UIEmCVEgKbuxpjCOgW773ojEAFmgKjnbwxmbYzm+nYp+rIx10zbmGwgYgpvMEQEaLZmIgIEiAC39FJwq9uYQrsCtDGYwggQASJAFAJEG4M2Bm0MCgEiQASIAFFoY9DGYApjDUQhQASIABEgCgEiQASIAFEIEAFuWmluJnQ8GkBCkrraI8YAgnieZxpeMpnEFC5YCudKh2urSP2xg+TW3SdrzvF6iknd0QNk/74KZezbxDQZePORTEX/35wNBX107u7KsnXnWgLgegIgFxvryOzcT9LZ/ZxCAKBnGmpJS9Np0t3TT8YnonRuuDJImhrrKbDWth6S+JOk98tz2zqf0t9hqgtDNCUSSQneCyWCAMLjngEK6mzDIWUunMMYXAN4ILgn/f0vjXamIhCiB1Kx//VwxusfRr6S8+eOkKql1IbI7JdSdaUAZmtbrzVroJY87uJ0FM1krl3TS+Net0sZG5cikKkmspGC2qluJpD6UAeZATg3ny74oYBPaRRqlQXS/+7Nzv9aHgsuz4Xap9THk7UkXFHG1lIOGgA0hZrqvRmv11TvoVH1+ct3mrrQKDLNdW7n8w5v03Rh8G9OJy/ZkFO0qcjNQk7PvldDSqPofTZIvSJcA2hqGwT35Fuaby66dP2+mIulnGykM6nv5RDttGaN9Nt3n+i8emk8lzUQlnJd7dc4QwCv3nyQEkSO6eWejROFjntX7IZSuDzofQ8vp2HxdfHwzPDsoYBn2HAX9nldJwRRHJyJzVf/TiSZisQdTl4I+H0ju3zFxw3XQNQW6cIIkGH9E2AAawxpPqroZK8AAAAASUVORK5CYII=") -} - -.fileList .ico-mht { - background-image: url("../img/ico/ico-mht.png") -} - -.fileList .ico-php { - background-image: url("../img/ico/ico-php.png") -} - -.fileList .ico-url { - background-image: url("../img/ico/ico-url.png") -} - -.fileList .ico-xml { - background-image: url("../img/ico/ico-xml.png") -} - -.fileList .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLNJREFUeNrsml2LE1cYx59MZpKNSTYb2tUsG0trqsX0YrVUL1pvpNSXFgr1poIgpUVEP4FXgvQ72ItCL7wuxStxlQVfSxFBS+lCXSPupl2XTeu62d1uncyL5zmzEyfZ3UlmEpJJzvOHw8mZOSdkfjxv52RCpmkCyb8kQkAAuyrZ7ebk5GSEdedZ+5q10X556NFs9nvWnW12fmpw0B9ApgtDQ0Pn0uk0yLLcF/CmpqagND9/ZnjrVvAC0S/AbxGeJElgGEbfuF1mZATmnj1rC8RGMXAY4fWbopEIh4iWyIYXKYl0EaLQWbgdEIUvY1qFSHVgixAJYIsQCWCLEAlgixAJYIsQQ27HWWwvbOZyub6CUyqVoFwue1qTz+dDfrdyfSe2t4dEImFZT+g1l80+F4tFcmGnFEWpHow4vc/vwbJwFmhDROm6vs7qNrNEArgBRASkaRrvvYITHiB/+DVXdh7V9bwFlsdPgbH0F0jJLAwe/qEjENGVsdW7cTMAA5VEtH/+4PC4VbAex51QOBzmzSu8wAFUn45bDzS0o2bcSYjOeNhTFmhWVqBSvMk/x/ZYp+w4xuudEp6+96wFqjM3wNRVCKfeBvnN93mPY7zeSSFEL39jSEFzX2Vkf02vTl9vav3yrXO8tUP1ZU3gAeqLT0FfmLKyYmZ/Ta8//5Pfb/jQksJbu9QsRDlI1heKJEF+Y7f1w1iPY1Nd4vdjY6ddvyN+4Lu2/66eSCI8zk1PWG6b+RBNyTYpa8zdeILPC6K6DrDy911uZZbb7qt1j7Ux3sd5bnrx01HehANYrfUcFlfdrzosspM1Yc8ANJZnQZv/rSbm1cQgR0zEeTifADqtz1Gi2Fl3XZZzXG+2pBEDoGkwt3wNxK776uW8zuebwXrJqWtlTGXuPhir/1bHS9fPNHZ5Nh/XbQZbKAv0mxSClky6YoHG/wtQmf3VShTRFAx+dglC4YhrrVi+chLMl4t8Ha6XBtLiWqAzlkVzn7vC45DZ/Wjuiw1jp6AAx9eDaSAn6CC5cccBaqXfq/Vc5J0j3IWb2peyeTi/vn4UDmC1lmM7jOjOLz2t5fPtncnMhHgAnafOSvYASPGMtx/L5uM6XgZ1+LQ6EADtU2fUwHtf+foOe103Tqu7D3At+MvbPqj+ceRVuA7XByWZCPd2llcVCgXXt7Po/cBePo0hgCQCSAAJIAEkgCQCSAAJIAEkEUACSACFk+v/wvKd0zB9hwBB/rY/gKioLDbAlxq5sHgxcPjgBcgevwzx3KFab9g2tuF1AuiMKYkMB2VUlmHLWx+TBXoVWhfCW3x4iYNEoATQC8B3D8HK42uwWrReKo9t/4gANqsYc1lJSXB4hrrC+2T+GAFsVluYtWnLc6A+L/Dxf8VfONBYgGNhYKo8jHWx7RYozLT1YFdn7hJAV/ddi3WzP5/g7msrNXYSkruPgRS5SC7cKPvasc8pO5nEc4cJoFvyQBfGmFcvjIfqQoEDlpSBwAF0fbno0Y9H9AisSCEQU0hGhbix65urYV8WaKZ23dMN64tEhMefPbXzvu8kYsZHP9XAuKGXn+yFypJYBw9K0jDTOx6Y8ewnvl2Y1INbOQIomF4JMACqJM9nuTUMmgAAAABJRU5ErkJggg==") -} - -.fileList .ico-bmp { - background-image: url("../img/ico/ico-bmp.png") -} - -.fileList .ico-cdr { - background-image: url("../img/ico/ico-cdr.png") -} - -.fileList .ico-gif { - background-image: url("../img/ico/ico-gif.png") -} - -.fileList .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7lJREFUeNrsnEFIFGEUx9/szu66uOvumCtuSlREkB2ChII6dEgisFtCEQVBetBD4KU8lBAFURejg0HSqYgKjwke8tBBrEPSaYMMCrRW1NpCxTZmdvrep7vsrLObO+vubPO9P4zfOPNWmB//9733fYsj6boOJOtyEQICaKvkQjdjsZiXDQPsuMiOZqc8dHNLywM29G42PlRXZw0g041wONyvKArIsuwIeNPT07AwP98TaWyEYiBaBXgJ4blcLkilUo5Ju6ZoFObi8S2B+K85MILwnCaf18shohPZr0NURGyEKHQV3gqIwrcxpUKkPrBEiASwRIgEsESIBLBEiASwRIiyaHDq2LoWl3Ob1dfZ2Z5Qa2svAVwXW9tDIBDg55IkZa7nO5+ZmaEUzpbH48lsjGRvJlvdWBbOgWmIKE3TNrgunxMJoAlEBKSqKh+LBSc8QP7w66mcvVVXLMiqnwNXJ19A4v45PpYLotvt3pDGua78bwEm348ZxnIIAZpBdMRKxHfghGEsN8Rs5zmiiPiPnOVHJZT+6gJbGsc4sNJCiMV8jUEATbTZAlLVKYyVN1fK5acVhUgOrADEqnVgttvM3Fg1cyb5jAASQAJIAEkEkAASQAJIIoAEkAASQFIxqrrdmEI7L3bvEZIDRXCg3Y4iBxJAAkgASQSQABJAAkgigASQABJAEgEkgATQwSq4H1j78DTMCQ6oFn/c+2AN4FqET2yCapJS2LYULod8uw5C5MJdSIwOwsq70cz1UHsXBLP+H+RP/COsxl7D0sRzw+fN4lamRg1/y9EAzdTYPQSumoAB6rbO6xA63s3PESLeb2Dgc+OUU32gdPTx67mwhajCwaNnwBvdCz9Gbhlc9H3kJiQ/T7H7a26rbevgcYtPrhriEi8H4df4MIctK1HxAPpbj/FUxVTM1cLjKzA/3MvdhSAxTk3EN8SlgSJk4QCiq9Sf8fxFcB2YqyZoChmV+r3M4r6BHBbQgbQSKVHoKnRhvvmxZeAVuPxB5rKlvHGY4rKyvaCTHQsQ5zVsbczg1Oxu46mJabw08YzPl2aFIj332dHK2A4QHxoh1XdeMxQB7BUR7K/xR5k4dGvD+TuGOGxjsAJjJTYrMI7vA7EAYKUNtXfzfg6PdGpjv4cONcZ1mcbZ1UhLhV4486X/kCapSeZSCcSUDrrHn9p5+43bUgpr0f2TkNLwtT4CsmPPzJ5da9o3aTmF9fodJ1WAMffCp8PS8qJQ75jRAw2qFtnzFhlYTmESNdIEsNr1V4ABAKATVWjNArx4AAAAAElFTkSuQmCC") -} - -.fileList .ico-jpeg { - background-image: url("../img/ico/ico-jpeg.png") -} - -.fileList .ico-jpg, -.fileList .ico-JPG { - background-image: url("../img/ico/ico-jpg.png") -} - -.fileList .ico-png { - background-image: url("../img/ico/ico-png.png") -} - -.fileList .ico-psd { - background-image: url("../img/ico/ico-psd.png") -} - -.fileList .ico-webp { - background-image: url("../img/ico/ico-webp.png") -} - -.fileList .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7JJREFUeNrsnE9M01Acx9+27g9jIkOZTMAYiTEZF4melJtwNJ6MHgwx/jlovHhSL0ZOejNGo4l6Mh7wZMSjHDyoB4NycolZNJpFZhiJSmRs0K6+X6FzsNKtLdtK+/0mTbf2tUs/+/5+v/fe8uaRZZlB5uUFAgBsqgS9k8lkMsB31/l2mm/dTnno7p6eB3x3sdb2W9vazAHkGm1vb78ajUaZIAiOgJdKpVh2ZuZCZyzGjEA0C/AswfN6vaxYLDom7LricfYzk9kQiNVyYCfBc5qCgYACkZzI395HEWkiRFdX4Y2A6PpujFWI6AdahAiAFiECoEWIAGgRIgBahOjRm87iY2G5r6/PUXCy2Sybm5szdE0ikfCYHco5TnxszyKRyLJ7PP+5rPc6nU4jhMvl9/tLEyPl0Wd2Ytl1DlQhkiRJqnDdek4EQA2IBEgURWVvFFzTAM4/e8zEb6m6f46wey9rPXFOv81KKJdP1RkF2fAc2Ah4Rj6HIPp8voowXutK24VwZOQS88V7N/y+UibN/j65Z+gaFSA5USsX2hJgPeBZuS9BJGjl1diWIWxn0ey7UQcCoAZEIz9jAKDW+LbGAuLqfmAtEOHABkAEQKs5EwgAEAABEAAhAARAAHSl6jaUmx97yMTvX+BAs3IDvLo6sBGyg8s3dQ60g8tRRADQoQCFPftMnUMRWVHr8TNwoO2/fRu4fFN3Y+zgchQRAARAAARACAABEAABEAJAAARAAIQMSXe1ZubyKfwzGVf89lPzqzUDBpY9OVGLVRYhIoQtqmkTqltGLimzxrmXY6zw4W3puJ8fo1VMayXnc2zh1bjSdr02pOKvWfbnzqizAXqj2xV4BCXQP7AKoCparrX09fMq4OGjJ1cdW9vGNVU4eOBwyVEEkoBWU27iRcmhru/GBA8eYoXJd2zx09RyoerfjxxYqyhkPaEwW0x+VFxIEEODwyz/ZkL3uvDQMWVPIeuLblNea+XBBe7Uavfa1AD9iQEl0UvTy3/msJScUqDSpjpSCw7BpoJD16oA7ZADGwqQch2BIkVv3K0AWw7QDnBsB1DNdb9vXVEcpaqFh2docIg7LIyxcLXqSy4rh6f09nk+VIsLiohO8aAQXpoYrzhH+ZA2ApybfV7zPbWKSKM70rqTCdPXzkvBQt7Vw71CMFTcefORz1QIi1273kscsBunZOiZ6dnFrt5J0yEsdcSG87L8Wpj5MeBZmHeVE+WW1qIY656SOnYcMR3CkE2HcgAIlfRPgAEAIIZMFb61acMAAAAASUVORK5CYII=") -} - -.fileList .ico-avi { - background-image: url("../img/ico/ico-avi.png") -} - -.fileList .ico-flv { - background-image: url("../img/ico/ico-flv.png") -} - -.fileList .ico-mkv { - background-image: url("../img/ico/ico-mkv.png") -} - -.fileList .ico-mov { - background-image: url("../img/ico/ico-mov.png") -} - -.fileList .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/xJREFUeNrsnLtv01AUxm8SO0mbUOKKhj4RaoUqpQsFJuhGOyImHhNCBQYQf0BZECwgJgYQSBQxIIaiTsBIBwZgQIVOREIRCBTRogaBVEjzsmPucevgJm4aX+fhXp9Pct3G17b883fOufe6jkdVVYJilxcRIMCWSqi2MR6P++nqKl3O0qWPl4vu6++/T1eXam2/s6ODDSDV9UgkMiVJEhEEgQt4iUSCpJaXL3ZFo8QKRFaA5wCe1+slxWKRm7Dr7ukhP5aW6gJxqxzYBfB4U8Dv1yCCE+mf97CItBCiq6twPSC6vhtjFyL2A21CRIA2ISJAmxARoE2ICNAmRE+16Sw6FlaHhoa4gpNKpcjKyoqlfWKxmId1KMed6NiehMPhNfd4/nPZ7PdkMokhbJQoiqWJEWP0sU4su86BOkSQoigVrtvMiQjQBCIAkmVZW1sF1zKA6acPifw10fDzCHv3kdCp89XbrIeycarOKsim58BmwLNyHoDo8/kqwrjclY4L4fCZy8TXM1D34ypLSfL38V1L++gAwYlmudCRABsBz85xASJAM1ZjR4awkwWz71YdiABNIFp5jIEAzca3NRYQV/cDa4GIDmwCRARoN2ciAgSIABEgAkQhQASIAF2phg3l0jMPiPztMzqQVW6A11AHNkNOcPm2zoFOcDkWEQTIKUBhcJhpGxaRdYVOTKIDHX/3HeDybd2NcYLLsYggQASIABEgCgEiQASIAFEIEAEiQASIsqSqszHS9A3yy+WAJPhx+wkbQJDfwmtPPCq/xUuIGMKNDOF6Kjg2TtrGjxP5yyfyx+RNItgGbXLv35DVFzNEHBzW3mYql5pdJZmXz7V2xv10ZeaekezrOf4Alk5IwXilXaT4++eGzwOHDpu2h9e2ChS6rh0Uavux09pnABngAXAAGjh4RNumZjIlwNxVYXCQf2T/xjw7Mko8wfYKqGZapQ4DATy4GcpisgRLX/t6B/h1YG7+reYUY5iJsVEttK0qPfuoIk2ACh8X+O0H5uMftBAW15+agfPAgfkaL7qd5jwNkgE43BDp2p1Sji0w3Ixt40AIOVhECg0uFHIfhDUABJDlKi8k0BZynjHcIXRhgZsSOjmpPa0rdyc3AHUXBscmtGrqjx3Q4AGYWopINUE7SBEQyulZjodycJEQugAREj5rzopM3dLC1qxQcT0W1kMWnAKhyJqz9GPo+RRyIaSEfBOLSMv+M6EQX8t5dvprkAvL86TeEW+Wqn710+KVC0ogl3X1cC8XCBZ7b077mEJY7t7zTqGA3fhl8XDNcO1y98A8cwgrndGJrKq+Epa/j3oyaVc5UW0LFeVo34LSufsocwijHFqFESCqpH8CDACOqX4es1WjewAAAABJRU5ErkJggg==") -} - -.fileList .ico-mp4 { - background-image: url("../img/ico/ico-mp4.png") -} - -.fileList .ico-mpeg { - background-image: url("../img/ico/ico-mpeg.png") -} - -.fileList .ico-mpg { - background-image: url("../img/ico/ico-mpg.png") -} - -.fileList .ico-rm { - background-image: url("../img/ico/ico-rm.png") -} - -.fileList .ico-rmvb { - background-image: url("../img/ico/ico-rmvb.png") -} - -.fileList .ico-swf { - background-image: url("../img/ico/ico-swf.png") -} - -.fileList .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABE9JREFUeNrsnDts01AUhm8S59E2lKbQ0PQl1AgVpQsFJuhGOzMhFlQhXhKoCwuPBcEEGwMIJGBCDLDCyMICAyqEpRFV1AqIaFADKqpIX/EDn5vc5CZO09hpXNc+R4qcHD+i+/n/z7Fv5LgURSEYxsONCBDgtoZQa2UikfCpi1vq66z66rXLoHv7+h6riyv1br+7vd0YQDXudHR03AiFQkQQBFvASyaTJLOwcLkrHCZ6IBoFeB7gud1uIsuybWzXHYmQX+n0lkDcrAZ2ATy7hd/noxBBierHR9hEthGio7vwVkB0/GVMoxDxOrBBiAiwQYgIsEGICLBBiAiwQYiuWtNZ6r2wEo1GbQUnk8mQpaUlXfvEYjGX0Vs524V6b0+CwWBePa4Sl43ep1IptDAfXq+3ODHCu8/oxLLjFMggQkiSpFHdRkpEgFUgAiBRFOlSL7htA5h99YyI35JN/x5h/wHSdvpC7W0KVuan6vSCNL0GmgFPz/cARI/Ho7FxpSotZ+HgxCTxRPq3/LhSOkX+PX+oax8GEJRYrRZaEmAz4DVyXIAI0PhubEkLWzlg9l2vAhFgFYh6fsZAgNXub+tsII6+DqwHIirQBIgIsNGaiQgQIAJEgAgQAwEiQAToyGjarVz25RMifp9FBRoNJ8BrqgLNCCuofEfXQCuoHJsIArQpQGFwyNA6bCKFaDt1DhVo+bNvAZXv6MsYK6gcmwgCRIAIEAFiIEAEiAARIAYCRIAIEAFi6IqaT2umr57BfyZTI3L/hfGnNX06HnuyY6xv8hAiWtjqNdAd2ktCtx+QlrGTZfn2S9do3tNTejDGFWituu2uiUma9x85Xsx5B4doLjA6pnXN8AhdB8sdD1Be/E2k+RQdMA+KgePz3mj+vZT+UXYCYHpeWV0uA5Kbm6HH9cUOa77TGxuh269Px+3RhWGw7s49GlCimvdEBop5eA8Dz83OFHOgOsitvH1NQQLQYn1KfKYngs/ByYHjr019sM9lDCiKVx2AAvXk5r5SVcE6pkaAB8CKAI8eozCYmnzDh0oAp79ocgAPjgdwbQOQKYrZFZYwQFAmP2gAzNuXwYVtmSUDo+Oa8sDbGOzL8rYBCIOHAYHyGChWw1heKKiTQa0GI5eI0/35WsjbuGjfT+9N68Km/awJYMCOMHgGlOW9tElky/IAhIGCjlrZJJilwd7QtcHG8uKfvGIL1rYVwHwdHKOXHXyTgDzkvBXNg9W1v/eul9VEgAXbL79ppXl4ifQkHCTySv4EgGptdy/M4ICy+DrH8tBh+Tx0X1AZD49ZljWXUjOJ0/3BvmY1D9MB8vbkLVZpZ9Y8ADTUPI2SC3UTALPuzewMn826fKlrMmH+5kXJv7bq6Nu9NX9A7rn71GNIgWL3wEdJBezEKRkYM4xd7O6fMtxEpM7w+KqivBMWfo64VrKOUqLS0iaL4d641LnvhGELY1ioiSBAjKrxX4ABABrs14sDnYP6AAAAAElFTkSuQmCC") -} - -.fileList .ico-webm { - background-image: url("../img/ico/ico-webm.png") -} - -.fileList .ico-wma { - background-image: url("../img/ico/ico-wma.png") -} - -.fileList .ico-wmv { - background-image: url("../img/ico/ico-wmv.png") -} - -.fileList .ico-doc, -.fileList .ico-docm, -.fileList .ico-dotx, -.fileList .ico-dotm, -.fileList .ico-dot, -.fileList .ico-rtf { - background-image: url("../img/ico/ico-doc.png") -} - -.fileList .ico-docx { - background-image: url("../img/ico/ico-docx.png") -} - -.fileList .ico-pdf, -.fileList .ico-fdf { - background-image: url("../img/ico/ico-pdf.png") -} - -.fileList .ico-ppt, -.fileList .ico-pptm, -.fileList .ico-pot, -.fileList .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABWZJREFUeNrsnF1oHFUUgM/O7272J7s1iVkT2mrAmBRREQUpiFCtCj74pFAoCOJDlIogSJ8K+qL45A8qqPhSEH0VLAit+lKF0tIXiaQBUaLd2m3zs5tkd+fnjufObtIkZmdmZzLb2Z1zYJifPXdm7rfn3HPO3ZlNWJYFJP4lQQAJYHQBzs7OKrg6hctLuIz1S6fHxsc/w9WrXvUHc7m2n0kubd/O5/MnC4UCSJLUF/Dm5+ehfP36zPDICHQC0S/Alzk8QRCAMdY3bjdaLMK1UmlPIAounw9zeP0mqqLYELkl4u6nYQLsW9kriLEFuFcQYw1wLyDGHmBQiAQwIEQCGBAiAQwIkQAGhOhWC1sTExO+b2Tth09AmzvvSVcsFEF98FlQp5/AnfDKxnK5DJVKpaM209PTCb+lXNfEXCrB+k9fQf3id5A+OgPS2FQo18HaHjKZTNN6Ere4tNteWFiA22aBG7L00TF7XXj967Y6xtU5G6B5c8G2wMxzb4J84AFP529UFyEhSCDKKoiK6qqv6zoYhtEW3E6AThYYmTFQumsScsfeBfmeh9EcDdv92frydshaHWEtQW25DGs3rm4eZwhDkhUw9ebnlmk4XkuWZXt2yQ3e1u3eCCIJAdLPnABx5G6w6qug/fZj8zh6SW35Bhi1VTROCdRU+v9fgKpCMpMDCS2wjhCN+roniBugNpat+z2ZxiQkBVKPvWBva1d+bfJjDC1MhmQaAaGbCg5BRk6mYCBXQLM0QUfgjlaPANtZYm9a4BZ3tl2zUm52RBRBVlPevwSuP5BB8CaY6PZuEEXU3+nGXq0wmnmgxVqe638Sl/edQ9dra/YQ4JhCIcDdIPZsJcKqN5s3ly5sEPV1HhHdXpRkMPSGu24L4s6xsCcB6n9carnyfUH4tSAqwHTNmzUJQscWGLlfinj0rV8+0yypph/f5tKOVc9i2SG4i96npxAiz429/twbKYCWocHqmQ9tiPL++0EaP+QJYKowsreZQMv6vECMDECjNA/r5z4Hc/EfELJDMHB0pjUgMtcgEEo6FUUX3ijp3FKY9NOvgTCQb4KzzN3zPTW5rRpxQAHpoWJoECNjgeK+MUg+8jwok4dvuS3mce2sT0ll7MU5G2JQW62Eet9dBeg0mbBtZgaTX17P8vo20AyPqdupTJgSyTSGD96m1gh8HlPTQFSS8QMoYadNE8swQ/cPD9vyc0hxBMjrMCWdBb1R95VD8za8LT8HeIym/QWQWyHWsQKWYXxGxergwSauy9vwtlIHExB9B9COtOkcr8VgvbKEFlVzLwHR6rgub2O37cYXDREXZSBrz6rUq8s4rmFkVhQst8TNOUGG0ZphumNgwODBJ5UfRuvrXrd64qnJBMJK5YfAQAsztBqmOHVgrQDDXZXP/0nJNLpssvtDTZTyP/dxMXlbIPXsGNgLQgAJ4G1Ot8I8eaO6HJmOqtl87wEM66bJhQkgCQEkgBSFKQpTFCYXJoAEkAASQBICSAAJIAEkIYAEsAfF8V25v96Yov+FQjnwwe8B3taU1HjTMxrkwmFKVx/tGD91dtfj1V++gZWzX7rqrF36HkZPnHa8xrWPj4OxVOpPgFxWzn0B1fPfbu5nD78Ig0desTvNAbnp/P3Ok5vHh4+/b6/Lp9+KbxTmoFi9Ckrx3kA6lMZQHuhPuHsKySxopSuBdGIRRLjwsYwvuwWITnRiC3BngPCrQy5MYyAJAQx7MuHPk4+aCaMh8Dce4ykWWJLKDr53QfRlgWbx0AX7jUmwYgmP952NTl30HYWtffufMi32s/Dv3EOJ2kqs3N1KDTJ25+RldsfBI75dmCTgGEhCAEOX/wQYAOhdFmkKB8pMAAAAAElFTkSuQmCC") -} - -.fileList .ico-pptx { - background-image: url("../img/ico/ico-pptx.png") -} - -.fileList .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2JJREFUeNrsnN9LU2EYx5/98KgxbXM6tuZFMSgaeiHmhV4IGSVFkX+C1FV20W0hBF1IVwVeaAQR/QNBERR5UeGFg1K8MAyySBCbbP1cP6zhzul9js6t1NP5oW6e8/3C4WznbBfvZ8/3fb7vOzguRVEIMi83EABgSeXVujk9PS2J02Vx9IojapdBRxsbb4hTn97P766tNQdQ6Irf778YCATI6/XaAt7MzAylU6lzDaEQGYFoFuBZhud2u0mWZdvYLhyJ0EIyuSkQ/zcHNjA8u6lSklSIXIni7TCaSAkhOroLbwZEx8cYqxCRAy1CBECLEAHQIkQAtAgRAC1CdGltZ4m1sBKLxWwFJ51OUyaTMfSdeDzuMruUs53E2p58Pt9y9bgKXDZ6PTc3BwsXq6KiYnVjpNh9ZjeWHVeBeYisXC63puo2qsRtBzif+rgtMKKhoGmIDGhpaUk9GwW35QDNDmw7lbdy8VadUZCOjzEM0ePxrLHxv1WJOVBDeYBcievNhQCoEyJDK+7GsLBB8e670QoEwHUgGvkbw7ExRnN9u1J9esK1o2OMHoiw8BZDBECrcyYQACAA7uil4E6PMaVOBYgxsDAAAiAAQgCIGIMYgxgDASAAAiAAQogxiDGwMOZACAABEAABEAJAAARAAIQAEADLVpqbCYO3RkBI6Ob1uDmALEmSHA0vm83CwiWz8GaqrWU/9Zzo2PD+18wP+v5jkeoCNXRt6C4t/ir88n1nTlJ1VaV6T0v3Ho7Ri8nX9gTIAyse3EB/Lz1+Mk6jiZer16KRoIB1irq7WgWMhHqts71JXK+n4dsPaD5Z2OXm691dh6h/4A66cF4MiKG2tRyg2L6ICpQhjSam/oLnSAvrFVdk08G91HO8Q9j4twD3QUCdQA40ovuPEup8x9bl1wjSBhXwF5rFnnB5/zlVdhaurpJEt26nqVez6ntuKG9nk/Tp8zdUoB6dXok6I08n1IN17HArLKw3KzaLBsJNgyuOj9GxKfUaxxYA1NByZGlVrVucF7krv3n3njo7mtVoU27SfPTThUtDOVlxOXq553Yp8uDV8x5TFdgYqXvOD6dx4uPiecw89mg4MG66CwfrfEdlRXm2kPrS8nMx66hK3FUtyeFQcLI+WHPEtIWhHRqkAdBB+iPAAD3HE9ONhnJoAAAAAElFTkSuQmCC") -} - -.fileList .ico-xls, -.fileList .ico-csv, -.fileList .ico-xlsm, -.fileList .ico-xlsb { - background-image: url("../img/ico/ico-xls.png") -} - -.fileList .ico-xlsx { - background-image: url("../img/ico/ico-xlsx.png") -} - -.fileList .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABAFJREFUeNrsnF1IU2EYx5+znW3aNvcR6qZS0shwXmUQVFAXKgTrKoSiiwjMiwRDb0ToA/q46E5JsCDpoiAKuuhCIajsAyqQspua5IgKq6mLmV+zyc45nfcs15pnunO2M7ed5w8vZ+Ocs5fz4/9/n/d956Q4jgOUfFEIEAHmLkCv16vnD+f5doJvlYXy0JVVVdf4Q1uq11tKSpKeo9e594LVau222WxA03RBwPP5fBCYnj5VWlYGUiDKBdhC4Gk0GmBZtmBi53A6YdLvzwhEzTrnSwm8QpNBrxcgEifyb/uVBFiwyhRE1QLMFERVA8wERNUDTBciAkwTIgJMEyICTBMiAkwT4nprYc7lchUUnEAgAHNzc5LucbvdlNylXMGJX9uDyWSKuof6xyXZ64mJCYxwvHQ6XWxjJD59crf1VOfAFYhEDMOscl0yJyJAEYgEUCQSEY5SwakeoPDwf6Mcv1WXcw6MTH2CxaEeYBeCWQekMdnB6OkEuty1JkQSZdISY5wKQMWLSGh4YEPgCc7i+w09ur7udVqtVmhS4WXFgczP6DTAeLAd9DV7Ur5v5uqx2Gvb6TuS+10efw2LD/uACX5P6XoCkECLr8Y54UDgouOLFHiZkJz+yO67VAfiUk4EopSvMXK2CsfHNt04S9WK+1KZXNPoubUhYoQVhpizDsx2bGWPmegzBLixy0GswuhABIgRVrgKowMxwqi8XMolVtxcjHVOA8yHcRAjrIaJNDoQq3AGxGX5r/yz1J/yAKloF+H3w1nlF+uPUvYRFR8DaYcLIn4fhJ7eFFrWB3mHK78daGxqA7qqbmMqJN8v6T+vHaixloP58BksIigEiAARIAJEIUAEiAARIAoBIkAEiABRRGv+WvNrRy3+XyheW3vH0vi1Jm1QN71IGCOspDb8a03boU4w1ntEz3272AiO9ltA2ypEz4c/j0Lgdpe6Ac4M9ggtFokiE5S19sPS2Avh/WTf8VX3mPcdAUtDKyy8HcIqLOZI9vcCzD4eED1f7D4gwJt/dReWvM8RYCIc0n4N9orHxebkAXfAsn88KWBVA7Q0tMDi6JAASEz25nPALs1D8P4lnEiLuY8Ui9AH8VhaGk+C3lnDw7sMkRk/AkzUJvd+HswPobImyrjLA+a9R2H2yY2k7lQ9QMO2+ljljRdxnc3TKRSM+Zf3cC0sJgJJU2ReFU0ypbE3nxWcGT/VwYl0gnQV26OrpuD/AHU82JVJdEXXg9WrLB6s2DwxZzYTvnTvZqhImHcpBeoUBxxtYKuvjGhlRZhx1o0AywgfpEZ45NlZR+0b2RHm7FuaGI59ppn6uJNamlXVxgNXbGHZ8h3v2M3VDbIjjEpzDEQhQMX1R4ABANtbXMt+ZZZBAAAAAElFTkSuQmCC") -} - -.fileList .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAP3SURBVHic7ZzBa9NQHMe/L8k6pd3WTra1rDihoGy7zJueZSB48+hJ8LSBMmEHQRC8C4qCepAd/Q8EQdi8CSroQSpaPJQhc61MHFtl0iQeutSsbV6T/vqS1/Z9Lm2T9/JLPvv98l6ypsy2bSg6R4t6B3odJZCIwVuZz+djAO4AuApgOowdCoPpbPYJgGW/7cdGRz3XcQUCuJtMJm+lUikYRrumvUGhUEC5VFqamJwEAkj0op2Va6lUCpqmwbIsaixpSGcy+LG11RWJ7c6BE5rWf6fJ4VgM6UwG5VJpCcBjyrb6z45PuiVxYAUC3ZE40AIBusSBFwjQJCqBh3QqUQl00YlEJbCBoBKVwBYEkch4t7Py+bydy+W6vX+RUi6Xsbu7G6jP3Nwc81rXHxe4AUgmk0gkEgAAxv578Xq/ubnJ3d7AlfDQ0FD9xoi7+jq9sTxwGQjUJAKAaZr1ZU7WeWWiFwMpEKhJZIyhWq2CMRZYnMPACgRQL2X3rTrpMrC6/Q37L+7D2tsRHaoJLTGO+KWbMKa8ZxKGYcA0TZim2VTGfgQKH0Qq688ikQcA1t4OKq+etm2n6zp0XQcQTB4QQgaaP2vTgPjF64idPu+736+HV+rvUzeeB4779+sb7L98BHPnu6/2uq6DMXZkNJYiA2HXzi9B5HWDTuJpmhY4AwduHtgOTdMQ5N8Y0o7C7rKllnNQnOzzM7mWVmDUqBLuAlLMAzsl7LLtFJWBRJRAItKWcJSjcBBUBhJRAolIW8LuspUZlYFElEAi0pYw0DziyljWUguUUVgjqoSJSJuBsk2YvVAZSCQ8gXbI3/IPKZ54gawW4uDTuvBQburxmNhDFH4ONNI5VLcKqGysobKxJjpcy/giEZ6B8cVlGNl50WFaYmTnEV8kP4zEjyF06wC05BRGLt8WHSYy1ChMRAkkogQSUQKJKIFElEAiSiARJZCIEkhECSSiBBJRAokogUS4T2sWV2bVL5MBmHnwmfC0pjHc1Z3pOaoH3NXCS3jm3kfRISJFnQOJCL0j7WSf81pcXTjy2cFZ7qwrri409Wm1XWd9Y59GWm2jWwgV6D44h8bPrZa1asNr647HayuC0EvYzwEFOWi/okUR2Vc7+mVwiUQgrwxFbF8koZdwmAcXBpFPY6jZ19g/7D9QKCXsnpI0Tjd40w93f0cKr3/jq7uNKNpfC/fApZzQrKsecK+FuSVsHxuxALnuJ4Q7etuHDrzhlrCZmX9rFN+dg6YD8P9bKiJpVfJiss8GLBNWevY9rxVXoD1+ctG0rdfa9pez7M/vyAcch+LKrPAY9vExy5o688E6ceoCrx33HOiXIL/0IxvU45cmq3oVJZDIP8OiU3PHSqspAAAAAElFTkSuQmCC") -} - -.fileList .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLVJREFUeNrsnF1oU2cYx59zepp+JTSp1CZaNl3AYXo1hQ0V9EIFobsYQ5gMBMFVWGFjvRHBWZjbhXjTMaEKftwIoqCXBWGrzsI2lM3dzAwt4kbR1GZr1takq5wP3+dtczxJTr5OkpP0nOcPh3ydnJfzy///PO970lTQNA1I1iUSAgJYV0mFXoxGox52M8y2Q2xb75STXt/be/a/4fcGzV5LHrmR81wkErEGkOkrv99/LBAIgCRJjoA3OTkJ8ZmZT7tP3oV8EKvmQKbDCE8URVBV1TGxC4ZCMB2LVQVisRrYjfCcphaPh0NEJ/pP3h2lJlJHiK7uwtWA6PppTKUQaR6YBZFNcUYJoI0QCWCFEAlghRAJYIUQhUKXs9haWAuHw46CE4/HYX5+vqz3sLWwYHUp5zixtT14vd5l9wivueS7PzU1RRE2qrm5Wb8wYkyf1QvLrnNgGiJKUZQc1+VzIgE0gYiAZFnmt+WCcz1AfvIrUTZeqms4B8rPH0NybATUF7O2AxK9XdDRPwRST7ggRIwybtkxLgVgzZtI6taFusDjzmLjpr4/V3S/pqYmvpULzxYHKv8sTwM69n0Gnk3bSn5f4ruP9fuBz6+UPe7LR79A8uYZUGaflrQ/AkRoxm7cEA4Ebbm+lAOvGrIyHl59L9eBtJQzgVjO1xgN24WNsa00zuUq7b5SJtcSea4wRIpwjSE2rAPtjq3lmkk+I4D1XQ5SFyYHEkCKcI27MDmQIkxalUu57I7biLFuaICroQ5ShN0wkSYHUheugjSb/8rfpvFqD1BYHmLpj1u28tPHE2p7ijWvgVIwDHJsElK3L/HN9iIfDK9uB3bsHQSpt68+HZKNi+OvageK/h7wfXicmgiJABJAAkgASQSQABJAAkgigASQABJAEqrgrzX//mIz/Wcypje//bOCX2tKLe6mJy9RhGsp27/W7NzzCfi2H9Afv4w9gsXoHVj46VrOvt0HT0PLxi2QGBuB5G9j+vP4HL5mprnx86bHcgTAtQOjILZ6M4Cs2X8COncP8PvGE5cCIQ5K/X8B2iO7MgCmFb98FJae3NcfB94f4seSE9P8Q3FUF/bt+Ag8oU0we/2bDBj/Xv+aQ/DtOJCxf8fWfg5vbvwCB4lAiyn14M4K/KDzpjFtzEXoCoysmZNmzg9yd+oAt/RD8v4YLK5AaYvsLDpGe98uDn0xOuG8CKP7Fgxxy2l2iVgGbLHVx+BNMCAvOHh0aHZtM6uDWAONx3JcEylF7cxtcuKZ7tYUcxRCTbs4Xw3E2Af6h3LqqSMAIgx0Yb76iMV/+sxB3YGo3uEfcsAWag5YWzH6rW9tdR5APHGEhBCz6yCeMDoOo4cwUc9Of8Djmz39EVtHii+vDLXUMU0E3YGQuvZ/yaOWPdebG7+oNw+EbYTHP4AHE3pM8wlfww8Im4/jaiACwU7buWeA16l0rUI34rwQoWF0pcA6HWZ2CcANAcuJi3mbyMLPV03njHW5mPDXsXcVQV5iLhXAndJAk1rUDafuNVmKsBLquweqwg/kRnh47mpw86+WI6x1vbFX0dQfxecP3xEW51x14UFr61TVnrd/V9ds2G05wqQG6sIEkGSqVwIMAG021uWJKtY8AAAAAElFTkSuQmCC") -} - -.fileList .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABH9JREFUeNrsnEtoE1EUhs9MJmlLUpu0tk20+CCotK5UEMSFCxWEuhARFFERbBcWFN1oQRTUjbpRFFRocVMRBRduCoJvQQTxsdGIFlGpNm1TW9qa1pZ5eM9NZ4w1jZlJ85xz4DLp5N7czpf/v+fMTVNB0zSgsB4iISCAOQ0p2ZOhUMjFDidY28va/GK56Pl1dVfYoSXV/hVz5lgDyOKk1+tt9fl8IElSUcDr6uqCSH///uqaGjAD0SrAfQhPFEVQVbVobOcPBKA3HJ4ViP9bA6sRXrFFicvFIaIS2Y+XKYnkEKKts/BsQLR9GZMuRKoD04RIANOESADThEgA04RIANOEKCTbzmL3wlowGCwqOJFIBEZGRkyNaWhoEKzeyhVdsHt78Hg8MfUIf7jM9Li7u5ssHB9Op9PYGIl3n9WNZdspUIeIoSjKP6qbSYkEMAFEBCTLMj+aBWd7gPzip6wcv1WXdwqU+z5BtPM8qD8Hsw5I9FSCu/EwSLXBpBDRytim2zgVgBlPImMP23MCjyuLzTt27+p/+zkcDt7MwsuKApWBWBng3nQAXEvXpDxu6OJO47Hv4A3T805+fA7Ru5dAGfyeUn8EiNDis3FeKBC02PpiBt5shJX5cPfdrALpVi4BRDMfY+RtFo63bbp2Nhu6+lIpriXSXHKIZOEMQ8xbBWbbtpbXTNIZAczt7SBlYVIgASQLZzgLkwLJwhQFeSs3PePmo63zGmAhrINkYTsU0qRAysKzEFqW/8o/S/NlHqAQm2Li7cOs8jPmEzJ7iRlfAyV/EORwF4w9usZb1hd5f7CwFeje2AJS3fLcZEg2L85f0AoUvbVQvvUYJREKAkgACSABpCCABJAAEkAKAkgACSABpMBI+m3Nr4fq6T+TsVh44X0a39aUSuxNT54gC2cycvKxZvXuc+Dw+qH30p7YL+ELQMX6JihrWGf0GQ89geEH7SAPhY1zJYtXQsWGJnAFlhrnoq87Yfh+G6i/ftoziYilHpi76yw4GMSec1vg26kNEOk4As7AEn5eD/eqRg5eYUCn96tpvszfBFsCdDI1Sb55TEXthoomPr/mysLzqDaEjArFcz9un/6r3wCDKJaVQ/naHfYEqE3BQGsiKD1Gn93iKpsMf+TqE0vLIfqq85/xCBPBulc2/jW+qNfA+EBAww/amMKaYd6RO8a6Jg/1cIj8XWYK0/smTJRT6yQqdqY+RQtQVxs2VJqLWzrAFYVt4PpRysLJImZPDweIFo1CzKYIEpMDPq+OjxrnEilMTyCoWltmYbTv9PULQSEQfe1Tf41ymInGo1LR9rkoZXIOcDz0lMOZy0qU+PrOt/kwX9PG3j3hYLAmRFBV244bsLEuxHGo0NFnN+1pYUwAAx1HWRmynVs2XoFY52GpwhMLU6E8GObZWk82+VBIJ91M+NK6WhHkCaZSAewZGmhSibrozAuHJQsrgeUvQFX4C9kRHl676q9/adnCWuWCjYqmPhb7PqwQxodttfGglVWoau2yN2rVovWWLUxRAFmYANo8fgswAF4ssuGz3PXWAAAAAElFTkSuQmCC") -} - -.fileList .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABGNJREFUeNrsnEtoE1EUhs+k07Q1qclUahMtvoJK05UtCCrUhQpCXblRBEFQFxaUdiMFQVA34qaioC6KG0EU3BYE6xNUEI0bqWjxRZWo0UpfaSOZGe+5adIkjdPMpJkkM+fAZSYzk3uZL/85/52bpoKqqkBhPByEgACWNEStk0NDQ062Oc3aIdZWWuWmVzY3X2Wbrnyv9yxdagwgizNer7dXkiQQRdES8IaHhyHy8+exxuXLQQ9EowAPIzyHwwGKolgm7Xx+P3wPhxcF4kI1sBHhWS1qnE4OEZXIXl4hEykhRFu78GJAtP00plCINA8sECIBLBAiASwQIgEsECIBLBCioLWcxZ6F1UAgYCk4kUgExsfHdb0nGAwKRh/lLBfs2R7cbndCPcIcl//tj4yMUAqnR3V1dWphJD37jC4s206BSYgYsizPU93/lEgAc0BEQPF4nG/1grM9QH7zs6mcvlRXdgqM//gAUwN9oEyOmg7I4W4AV2cPiE0BTYiYytiy0zgfgEU3keiD/pLA48pi40bvXVvwuqqqKt70wjNFgfKvxDTAtfs4ODdsyft9fy4dSO1LJ27qHvfv++cwdfcyyKPf8roeASK0dDcuCwWCmqgveuAtRhgZD1ff9SqQHuVyQNTzNUbZunB62haaznojqb58JtciaU4bIqVwkSGWrQLNTlvDNZN0RgBL+zhILkwKJICUwkV2YVIgpTBFRT7KZTtuOaZ1WQOshDpIKWyHiTQpkFx4EUI1+a/8TRqv+ACFxBCxNw9M5ZcaTyjuLRa9Boq+AMTDwxB9eJ0304u8L1DZCnTt6gKxubU0DsnGxfErWoEObxPU7z1FJkJBAAkgASSAFASQABJAAkhBAAkgASSAFBiav9b80t1C/5mMxeqLbwv4taZYY2968RilcDHDtK81a9a2QePBCznPTTy7BWOD/ZmfbK0bfCdusG09fL98EOJ/wqlz9dv2gWfH0Xn9KDMTMHa/H6ZeDVgPYDIiN05C7FMo9drV3glSZw8HlH7jda3bOTyEUhfsgImnt+f19fXszozX0p4e3lfsYygDuKVdGKEhJKd/Q8ZxV1snTA89Zu0J38+3L672dW32m8ZgyiYDYWKLMngzTE2itIKXACN9WR4g1jNM1cmM9O3gqkwo8DHfd7cvrELPziP8WkvXwFxGgiaSURd5+j6ZS83QANRv3c9A9zFAk6njzacH5/X1+865jGssbyJJR535EOLH0VRQkQgxu/bhuXQzyTYR/HCkPd3MRF6ZBrEsTITXvRXr+XZJcDv8Db/ncNIbHqtj57QCywDCx5ppmxqISsG6xdNB8nOzwLqXHXgMjUXLTOTZqYtgJxPhEKcnuLpcs0aRywSSx7TMJPlB1AbMm8ZoLiZ87t0sC/EYgyyAPUMFVaxR1px/UWVIgbK/9QUoMu/IjvDw3hVfy0vDLqw2rNolq8ojx493m4TpMVstPKh1HkVp2vhaWbZmh+EUpqigRzkCaNP4J8AArrWcCArY0yoAAAAASUVORK5CYII=") -} - -.fileList .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+hJREFUeNrsnF1IU2EYx5+zHTdkU7eZuuUocWA4r/QiqC68UCHQqxCKiAiqiwRD74RKyKu6UhIqULoJouhWIcjsAyqQsJtY5IgKq5krze8m56P3PWtruA/Px77P84fDmTvvu5fz2/95n+d958aIoggo9TIgAgSYU7GpLvp8PhM5DZLjDDlqi+Wma93uW+TUI7d9RXm5OoBEV20224DdbgeWZYsCnt/vh+Di4oWq6mpQAlEtwLMUnsFgAEEQiibsnC4XLAQCaYG42xxYReEVm8wmkwSROpH8eROTSA4h6joLpwOi7ssYrRCxDtQIEQFqhIgANUJEgBohIkCNEJlU21lkLSx6PJ6ighMMBmF1dVVRH6/Xy6hdyhWdyNoerFZr2D3Mfy7JHs/Pz2MIx6qkpCS6MRIbfWo3lnXnwAhEKp7n41yXzIkIMAFECojjOOmsFJzuAUo3/y+UY7fq8s6B3I+PsDE5DML6UtYBGawOsHT2A1vjSQmRhjI9doaxHIAZTyKb0+M5gSc5i4y7+fj2ru2MRqN0KIWXFQfyP8NlgOVoL5gaDsnut3zjZPSx/eI9xeNuz72GjUejwC99k9WeAqTQYrNxXjgQxPD8ogReOqRmPLr7rtSBuJRLAFHJxxh5m4Vjw1ZrOCtVxH1yimsWPZcaIoZwhiHmrQOzHbaq50z0GQLM7XIQszA6EAFiCGc4C6MDMYRRBbmU25lx8zGs8xpgIcyDGMJ6KKTRgZiF0yAxy//ln6XxMg+QCQ8RejedVX7R8ZjM3mLG50DW6QEu4IfNp3ekI+uTvNNT2A60dPQA627KTYYk49LxC9qBBlsNlB27hEkEhQARIAJEgCgEiAARIAJEIUAEiAARIIoq5bc1v/Q14i+TEe0fea/h25qsWd/0uBCGcCaVk481WbsLnL13k15feTIGay8fgHtwCjZmJ2F5YhjKjhyHirbzcW23A3Pwe2JEOusGILccgK9D7XHPV3ZfAXN9C2z5XiTtG9uPvhF7Tl0HR/dlWBg9re8sXNF+Dkq9rcRtIxJguW8EdShr3yvB1C1ACq7s8AlYe3WfuO851oFK50N7Vx+EPs3CytS44r6Wlk7ixO+yXVsUc2CsHGTeE7bW4NfDIVntaWJJlER0lYUjsnf1g8nVAItjPSD8WZfVJ1Hy0WUI07KEhh8tWXJVghQsQOo6WtPRhEHrPSykFcpc3xzNvu7B1rjrkeK54DcTPg8c5BkuRFzKgD4lgsiahbprM0ZVIcy7mmZA4KUX0iM8eu+Cs/GN6hAWHfs6eFF4ZvjxoZnZWtHVxoNYWiEINQfeCpV1bapDGFVAa2EEqFP9FWAAVhNMgyXjjfoAAAAASUVORK5CYII=") -} - -.fileList .ico-bt { - background-image: url("../img/ico/ico-bt.png") -} - -.fileList .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNrs3D9PwkAcxvFrKXUhCAMEAhsbL8nJxElfgYPR+CZ0MnHyJbEwMkDoQGAG6h2CNlIo16t/cvd9kktL2g73ye9a7gj14jgWJH88AAH8v4CDwSCUm3vZLmXr2NLpTrf7LDc3p55/Xq0ePBZkXPtYq9Vu6/W6CILACrzhcCii6fS60WwKHcS8gFcKz/d9sV6vrRl2rXZbTMbjQhD9jOMNhWdbzsJwg6gqUX58+klAa1MUorOARSE6DVgEovOApogAGiICaIgIoCEigIaIWXPhuNfrWYUTRZFYLBZa1/T7fS/vVM66yLm9qFQqH9Xjfbkc2h+NRgzhZMrl8ufCSHL05V3Wc64Cd4gqq9Vqr+oOVSKAKYgKaLlcbra6cM4Dbjq/HcrJpTpdSOe/xijEUqm0N4y/VyUVeCQ7QFWJafdCAE9EVGjJpzFDWDNq9V23AgFMQdT5GQPAtPntiQ8Q7oEZiFTgLyACaHrPhABAAAEEkAAIIIAAEgABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBNBywCj5bhXXsu17ZAL4MpvNhIuIqs+q7zKvx87L+sP1w3w+D2W7kPstxwwnsr3JdnfsJF4FbxgAAfzbvAswAK/3kejP2oZLAAAAAElFTkSuQmCC") -} - -.fileList .ico-svg,.ico-css { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNrs3D9PwkAcxvFrKXUhCAMEAhsbL8nJxElfgYPR+CZ0MnHyJbEwMkDoQGAG6h2CNlIo16t/cvd9kktL2g73ye9a7gj14jgWJH88AAH8v4CDwSCUm3vZLmXr2NLpTrf7LDc3p55/Xq0ePBZkXPtYq9Vu6/W6CILACrzhcCii6fS60WwKHcS8gFcKz/d9sV6vrRl2rXZbTMbjQhD9jOMNhWdbzsJwg6gqUX58+klAa1MUorOARSE6DVgEovOApogAGiICaIgIoCEigIaIWXPhuNfrWYUTRZFYLBZa1/T7fS/vVM66yLm9qFQqH9Xjfbkc2h+NRgzhZMrl8ufCSHL05V3Wc64Cd4gqq9Vqr+oOVSKAKYgKaLlcbra6cM4Dbjq/HcrJpTpdSOe/xijEUqm0N4y/VyUVeCQ7QFWJafdCAE9EVGjJpzFDWDNq9V23AgFMQdT5GQPAtPntiQ8Q7oEZiFTgLyACaHrPhABAAAEEkAAIIIAAEgABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBNBywCj5bhXXsu17ZAL4MpvNhIuIqs+q7zKvx87L+sP1w3w+D2W7kPstxwwnsr3JdnfsJF4FbxgAAfzbvAswAK/3kejP2oZLAAAAAElFTkSuQmCC") -} - -.fileList .ico-apk { - background-image: url("../img/ico/ico-apk.png") -} - -.ico-cmd { - background: url("../img/soft_ico/ico-cmd.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.btn:hover .ico-cmd { - background: url("../img/soft_ico/ico-cmd-hover.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.fileUploadDiv { - padding: 10px -} - -#file_input { - display: none -} - -#up_box { - background-color: #fafbfe; - border: #e4e5e9 1px solid; - margin: 0; - padding: 10px; - clear: both; - height: 160px; - overflow: auto; -} - -#up_box li { - list-style-type: none; - height: 30px; - line-height: 30px; - font-size: 12px; - width: 100%; - margin: 0 0 5px; - padding: 0 -} - -#up_box li .filename { - display: inline-block; - height: 30px; - margin-right: 20px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 240px -} - -#up_box li .filesize { - color: #999; - display: inline-block; - height: 30px; - overflow: hidden -} - -.fileUploadDiv button { - float: left; - border-radius: 3px; - border-style: solid; - border-width: 1px; - width: 75px; - height: 30px; - color: #fff; - margin-bottom: 10px; - cursor: pointer -} - -#up { - background-color: #5cb85c; - border-color: #4cae4c; - color: #fff; - text-align: center; - margin-left: 10px; - position: absolute; - bottom: 0; - right: 10px -} - -#filesClose { - position: absolute; - bottom: 0; - right: 95px; - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -#opt { - background-color: #4592f0; - border-color: #367fa9 -} - -#up_box li em { - font-style: normal; - color: #06F; - float: right; - margin-right: 10px -} - -.cancel { - float: right; - cursor: pointer; - color: #c00 -} - -#textBody { - min-height: 450px; - _height: 450px -} - -#filesClose:hover { - background-color: #c9302c; - border-color: #ac2925; - color: #fff -} - -#up:hover { - background-color: #449d44; - border-color: #398439 -} - -#totalProgress progress { - width: 180px; - height: 10px; - border: 1px solid #ccc; - background-color: #e6e6e6; - color: #0064b4; - opacity: .9 -} - -#totalProgress p { - color: #666 -} - -.setchmod fieldset { - margin-left: 15px; - margin-bottom: 15px; - border: 1px solid #ccc; - float: left; - width: 114px; - padding-bottom: 10px; - border-radius: 3px -} - -.setchmod legend { - padding: 3px; - border: 0; - width: auto; - margin: 0 6px; - font-size: 14px -} - -.setchmod fieldset p { - margin-left: 10px -} - -.setchmod fieldset p input { - position: relative; - top: 2px; - margin-right: 5px -} - -.setchmodnum { - clear: both; - margin: 0 15px -10px -} - -.setchmodnum #access, -#chown { - width: 60px; - margin-right: 5px; - height: 28px -} - -.setchmodnum span { - margin-left: 10px -} - -#chown { - margin-left: 5px -} - -.shellcode { - margin-bottom: 5px; - height: 490px; - overflow: auto -} - -.shellcode pre { - background-color: #111; - color: white; - padding: 5px 10px; - border: 0 none; - border-radius: 0; - min-height: 490px; - margin: 0 -} - -#mExec { - width: 89%; - margin-top: 0; - float: left -} - -.editmenu { - text-align: right -} - -#filesBody { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none -} - -.folderBox input { - display: none -} - -.fileList>div:hover, -.fileList div.ui-selecting { - background: #f6f8fd; - border-color: #d3dfec -} - -.fileList div.active, -.fileList div.focus, -.fileList div.ui-selected { - background: #eff3f9 none repeat scroll 0 0; - border-color: #bcccde -} - -.fileList .file { - background: rgba(0, 0, 0, 0) none repeat scroll 0 0; - border: 1px solid #fff; - border-radius: 2px; - display: inline-block; - height: 136px; - margin: 0 5px 10px 0; - text-align: center; - width: 112px -} - -.fileList div.file { - transition: all .2s ease 0s -} - -.fileList .file .titleBox { - color: #595c5f; - display: block; - height: 23px; - line-height: 23px; - overflow: hidden; - text-align: center; - text-overflow: ellipsis; - width: 100px; - margin-left: 5px -} - -#DirPathPlace { - display: none; - padding: 0 -} - -#DirPathPlace>input { - border: 1px solid #ccc; - height: 28px; - line-height: 28px; - padding: 0 5px; - width: 280px -} - -#DirPathPlace>input:active, -#DirPathPlace>input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -#PathPlaceBtn { - background-color: #f3f3f3; - border: 1px solid #ccc; - height: 28px; - line-height: 26px; - padding: 0; - width: 290px; - cursor: text; - overflow: hidden -} - -#PathPlaceBtn ul { - display: inline-block; - height: 26px; - width: auto; - position: relative -} - -#PathPlaceBtn li { - background: url("../img/ico/ico-ltr.png") no-repeat right center; - padding-left: 10px; - padding-right: 18px; - float: left; - height: 26px; - line-height: 26px -} - -#PathPlaceBtn li a { - display: inline-block; - cursor: pointer; - height: 26px; - line-height: 26px -} - -.backBtn { - color: #666; - border-radius: 0; - height: 28px; - line-height: 8px; - margin-right: -1px; - margin-top: -1px -} - -#tipTools { - background: #fff url(/static/img/ico_line.png) repeat-x 0 55px; - height: 110px; - border-bottom: #ccc 1px solid; - position: absolute; - top: 0; - left: 0; - z-index: 999; -} - -.re-head { - border-bottom: 1px solid #ccc; - float: left; - padding: 15px 20px; - width: 100% -} - -.re-con { - float: left; - height: 500px; - width: 100% -} - -.re-con-menu { - background-color: #f0f0f1; - float: left; - height: 100%; - width: 120px -} - -.re-con-menu p { - cursor: pointer; - line-height: 40px; - padding-left: 30px; - position: relative -} - -.re-con-menu p.on { - background-color: #fff -} - -.re-con-con { - height: 500px; - margin-left: 120px; - overflow: auto -} - -@media only screen and (max-width:768px) { - .sys-i-c-box:nth-of-type(3){ - margin-top: 20px; - } -} -@media only screen and (max-width:990px) { - .pr8 { - padding-right: 0 - } - .pl7 { - padding-left: 0; - margin-top: 15px - } -} -@media only screen and (max-width:1200px) { - .btvipbox{ - margin-top: 20px; - } -} - -.bt-ico-ask { - border: 1px solid #fb7d00; - border-radius: 8px; - color: #fb7d00; - cursor: help; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px -} - -.bt-ico-ask:hover { - background-color: #fb7d00; - color: #fff -} - -#RecycleBody .tname { - line-height: 30px -} - -.menu-sub { - border-bottom: 1px solid #ccc; - height: 40px; - line-height: 30px; - margin-bottom: 15px -} - -.menu-sub span { - display: inline-block; - font-size: 13px; - height: 40px; - padding: 0 25px; - cursor: pointer -} - -.menu-sub .on { - border-bottom: 2px solid #20a53a; - color: #20a53a; - font-weight: bold -} - -.bt-progress { - background-color: #e2e2e2; - border-radius: 8px; - height: 16px; - line-height: 16px; - position: relative; - margin: 5px 0 -} - -.bt-progress-bar { - background-color: #5ab76c; - border-radius: 8px; - height: 16px; - max-width: 100%; - position: absolute; - text-align: right; - transition: all .3s ease 0s; - width: 0 -} - -.bt-progress-text { - font-size: 12px; - color: #fff; - padding: 0 10px; - position: static -} -.conf_p{ - overflow: hidden; -} -.conf_p p{ - margin-bottom: 1px; -} -.conf_p span{ - display: inline-block; - margin-right: 10px; - width: 140px; - text-align: right; -} -.btvipbox{ - border: #ddd 1px solid; -} -.btvip{ - border-right: #ddd 1px solid; - float: left; - width: 40%; - height: 108px; - text-align: center; -} -.btvip-r{ - width: 60%; - float: right; - overflow: hidden; -} -.btvip .t2{ - font-size: 16px; - padding: 10px 0 5px; - float: left; - width: 100%; -} -.btvip p{ - float: left; - width: 100%; - margin-bottom: 8px; - font-size: 14px; -} -.btvip p .price{ - margin-right: 3px; -} -.btvipinfo{ - width: 200px; - float: left; - padding:5px 10px; - line-height: 32px; -} -/*付费插件*/ -.libLogin{ - width: 340px; - margin: 0 auto 20px; -} -.libLogin .bt-input-text { - width: 300px; - height: 34px; - line-height: 34px; -} -.libLogin .line{ - margin-bottom: 10px; -} -.libLogin .login-button,.shuoming .login-button{ - width: 300px; - height: 36px; - background: #20a53a; - background: linear-gradient(#20a53a,#189d32); - box-shadow: inset 0 1px 2px #30ad42; - color: #fff; - text-shadow: #00851a 0 -1px 0; - border: 1px solid #20a53a; - text-align: center; - font-size: 16px; - color: #fff; - border-radius: 3px; - cursor: pointer; -} -.libLogin .login-button:hover,.shuoming .login-button:hover{background:#10952a;background:linear-gradient(#10952a,#088d22);border:1px solid #10952a} -.shuoming{ - line-height: 24px; -} -.shuoming .login-button{ - width: 100px; - height: 30px; - font-size: 14px; - margin: 15px; -} -.sm-content{ - border: #ccc 1px solid; - padding: 10px; - height: 200px; - background-color: #f9f9f9; - overflow: auto; -} -.libPay .btn-group .btn{ - width: 80px; -} -.libPay .btn-group .btn em{ - position: absolute; - right: -2px; - top: -9px; - width: 33px; - height: 19px; - background: #ff7300; - border-radius: 2px; - font-size: 10px; - line-height: 19px; - color: #fff; - transform: scale(.9, .8); - font-style: normal; -} -.libPay .price{ - font-size: 28px; - color: #FF6600; - margin-right: 3px; - line-height: 30px; -} -.libPay .sprice{ - font-size: 14px; - display: inline-block; - line-height: 30px; - margin-right: 3px; -} -.wxPay{ - width: 160px; - margin: 10px auto 20px; -} -.wxPay .ewm canvas{ - border: #ccc 1px solid; - padding: 6px; - width: 160px; -} -.wx-pic{ - width: 160px; - height: 36px; - background: url(/static/images/weixin.png) no-repeat top center; - margin-bottom: 10px; -} -.wx-pay-info{ - width: 160px; - height: 30px; - background-color: #00C800; - color: #fff; - font-size: 14px; - line-height: 30px; - text-align: center; - margin-top: 10px; -} -.color-red{ - color:red; -} -.color-green{ - color:green; -} -.btpro{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAAAUCAMAAAAEAbViAAAAA3NCSVQICAjb4U/gAAABXFBMVEX///+7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAD////+/fv+/fr8+vX8+vP69uv38eD28N3179v07NXz69Lz6tHz6c/x6Mzv48Pu48Hr3rX74jnq27D54Djp2av43zfo16fo2Kj33TX33Tbm1aLl1Z/12Tbl057z1zLk0Znj0Jfx1DDizpPu0DPv0S7fyIbqzCroyC/cxH3oySjawXXZv3DkwyTYvWzivyzXu2jWumXevCDUtVzctinathzSslXSs1bRsVHRsVLWsBjVrSbNqkPTrBXLqD3PpCLJozTOphHIozLIojDIoS/HoCzKoQ7GninGnyrFnifJnB/FnCTFnSbEmiDEmyLFmQnCmBvBlxjBlhbBlRXAlRPCkhy/kxDAkwW+kQq9kAi9jwa9jwK9jQC8jgS8jQO8ihm7jAC2gRWweBKpbw+jZgydXgnhk9RtAAAAdHRSTlMAESIzRFVmd4iZqrvM3e7//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wol0KMAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDUvMzEvMTikOWwBAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAg5JREFUOI2dlGlz0zAQhhUHJ45jST2gXKUVR4BijmKKCwX1CKcKLoVQcCmFYgcSjqiy8/9nWCtOJvlEwusZe3Q80rurlRFCyLDsSi7bMtAkKlSlU602W61o+3VDvrnOxlTGliUxkdFcfRjN0aWm3ByXBdgglaqN7N2pmb2t6edba0e3xmctBxWxjecoPfPh2afTtPGkN+J6/2SLxC6ajrQvLC4uPJXvLs6/aC7rkZD35/Awb8RMMc5FGAc9FktMiJS42W5HX+SP6Nv+n13GlFJdlWmEVfCkYcD9fF9MTNO0iHX15o3aRnTncq0W7WcDQvQmeEHO1rlm1ZBnw6lAvrBJQY+3s9fOPSbCMA21BEvdHnvssa7qCm3Hz3N1gpQRsu1ZSk8drFC60nrAmM/DmGv5LAg06yVM75mF0fFyFpWkiUrEoHTx6wKt/d7U/R2hUZjkppoFBxCrYCrkfd9ZcVSkUcAlOrW+Q8+3X13S/SIzHHeFXiZjlctYwiFevwNrDdgCdsD09OzbtZmPh9d6/QFQbiJGzsgFksM5Zcv0WVSUlumcPHdwdv3Xcp5DF5KVowMWvioW7nHKh1gIuYznlxq35aOhshFJCBvEaqC4ztKkntQ9FQ+xEDK5u3r08+Vo0dVHm3Ayvu9nhr1htuDIvc/y/RU2ifqX2MBSHo59g0ZZZJLv9/8ThXyZE/1tQH8B/ly2lqVFKXEAAAAASUVORK5CYII="); - background-repeat:no-repeat; - padding:2px 0 2px 64px; - margin-right:5px; -} -.btpro-gray{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAAAUCAMAAAAEAbViAAAAA3NCSVQICAjb4U/gAAAA51BMVEX////6+vr4+Pj29vbx8fHu7u7r6+vp6eno6Ojl5eXk5OTj4+Pi4uLf39/e3t7d3d3b29vU1NTT09PNzc3Ly8vIyMjFxcXDw8PAwMC+vr67u7u6urq5ubm4uLi3t7e2tra0tLSzs7OysrKxsbGvr6+srKyrq6upqamoqKikpKSjo6OgoKCfn5+enp6dnZ2cnJybm5uampqZmZmYmJiXl5eWlpaVlZWTk5OSkpKQkJCPj4+NjY2MjIyLi4uKioqIiIiHh4eGhoaFhYWDg4OCgoKBgYGAgIB/f39+fn59fX14eHh0dHT///9HdDDXAAAATXRSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////ANNqEYAAAAAJcEhZcwAADpwAAA6cAQeUU90AAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA1LzMxLzE4pDlsAQAAAeZJREFUOI2VlH1XmzAUxjNbZutxL2hnXeiGA1kiNdspbJlEC9FmLtD7/T/PkkC78tfw4Rw4IfeXPDf3AgIALyBRJxJ4MFgIYLTU+XJZbTYluVnp4jMeKMvOtfTBq84/lAAnlV4OZTECT0ZLAoQB5AuIFhcPX4azQQ5TQYQxgLKrnwhWq3YmjP/LTiWZ+rkmr8bjoyv93UNR1UKc7mIo7wYFVphSxousZYUWUmotKqVKoZ/Kkj8zjJVSW2XVY5W5Gp7RpNtXSN/3Axkcn55OPpZvX08mJbcTjLUBcdaxKXWsOvDs5ZGpsPBtwebE3m6vMeO84U4MN2HLPsZ4q7bM2Um6s3on5wDEUnB3BnC2IRgnlBfUKcFZ5ti4xm5Pm8Y67liYaR9m0vTT+P4IJr/b+q6ZQ01Q2DjWODC5Mqw43fk2LETaG4kZwOUtQoov3HtmDRdb5paxrAoxrqnJN1lT/o8didyZ/naBflRdS2aGCmvWq1FoSGrqZJfZsTDVgZ8DukOXf3YNEZrD6tA9a56qYOFjQw9Yk/JcoJPVG31z0Das5maDQu1VpLip0zqNVXHAmpTl+/OH57zfdGl/aCqTJIk1HB+yo1znv/T9J/wSdazpDq2rwV9QnwVfbr6+DIU9CzN/+N+m1V+TNr+ixpmAWwAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding:2px 0 2px 64px; - margin-right:5px; -} - -/*插件付费*/ -.wx-pay-ico{ - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAMAAAAR8Wy4AAAAA3NCSVQICAjb4U/gAAAAtFBMVEUAzABM2Ezx/PHB8sEazhqf6p+B5IE61TrU9tRm3mYq0Sqz77P///8Lygvj+ePI88iP549U2lR54nlF10Wm7KY31Dcj0CPS9dL1/fW58Lnr++sx0zHY99jD8sMSzBJc3FxA1kCU6JQfzx+t7a1s32zm+uaj66O377cPyw+q7ar////c99zH88fL9Mu177VG10Yk0CSR55Eczxw81TzX9tdp32k41Ti78Lsy0zLF88UhzyHB88EyB+unAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA1LzI5LzE4XO1tXgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAACTSURBVAiZRU8JDsIwDHNXGMcCjHNjMBhnQTAVcQjE/x9G0qHOUtLYal0HJEiiXaQzN4JrU/TAmL2WTgjG8GiyEHxqvj2yEHra0pdYEW7AgwtqTnkBXMF+w35n3aanlWtnEZATpfvq3QRd7jaNV3+jAWI5Gt6Yc0zrb3EqJZjy/Ktd9DdwF7oYZdUuoU0OxpjS7UY/XkQJtfApswsAAAAASUVORK5CYII=") no-repeat; - display: inline-block; - width: 16px; - height: 14px; -} -.libPay-item{ - margin-bottom: 30px; -} -.libPay-item .li-tit{ - margin-bottom: 10px; -} -.libPay-item .li-c-item li{ - font-size: 0; - margin-bottom:20px; - border: #ddd 1px solid; - border-radius: 4px; - height: 42px; - line-height: 40px; - padding: 0 20px; - cursor: pointer; - box-sizing: border-box; - transition: border-color .1s ease-in,color .1s ease-in; -} -.libPay-item .li-c-item li:hover{ - border: #20a53a 1px solid; - color: #20A53A; -} -.libPay-item .li-c-item li.active,.libPay-item .li-c-item li.active:hover{ - border: #20a53a 2px solid; - color: #20A53A; - padding: 0 19px; - line-height: 38px; -} -.libPay-item .li-c-item li .item-name{ - font-size: 14px; - vertical-align: middle; -} -.libPay-item .li-c-item li .item-info{ - font-size: 14px; - vertical-align: middle; -} -.pay-btn-group{ - position: relative; - display: inline-block; - vertical-align: middle; -} -.pay-btn-group>.pay-cycle-btn { - position: relative; - float: left; -} -.pay-cycle-btn { - font-size: 0; - width: 106px; - height: 40px; - line-height: 38px; - display: block; - margin-bottom: -1px; - text-align: center; - vertical-align: middle; - border: 1px solid #ddd; - box-sizing: border-box; - margin-left: -1px; - transition: border-color .1s ease-in,color .1s ease-in; -} -.pay-cycle-btn span{ - font-size: 14px; - line-height: 25px; - vertical-align: middle; -} -.pay-btn-group>.pay-cycle-btn:first-child { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pay-btn-group>.pay-cycle-btn:nth-of-type(5n+1){ - margin-left: 0; -} -.pay-btn-group>.pay-cycle-btn:last-child { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pay-btn-group .active,.li-c-item .active{ - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAA3NCSVQICAjb4U/gAAAAclBMVEX///8fpTofpTogpTogpTogpTrw+fLt+O/i8+be8uLb8d/T7djP7NXR7NbE58u548G34sCr3bWU1KGT05+FzpN+y413yIZ0x4RxxoJwxYFnwnliv3RWu2pUumhMt2FGtVxDs1k6sFE4r1Avq0cjpjwgpTpRW86VAAAAJnRSTlMAEXd3iO7//////////////////////////////////////////0c8mUAAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDUvMjkvMThc7W1eAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHhJREFUGJVtzskSgkAMBFB2AUcR2QTcEPr/fxFmmFiW0znk8CrVac9jE1CNYqoAV8JaXTbq8K7/bBVcwRVcwRVcwbRTIDqcWrjaJ3cJ0VrdjDaHh2Sb2/F43XadPuWlTfjkJersLU2+ufNFnSdb0P/5thQv6R2CzQo9Oh+AlBF9bAAAAABJRU5ErkJggg==") no-repeat right bottom; -} -.pay-btn-group .pay-cycle-btn:hover{ - border: 1px solid #20a53a; - color: #20a53a; - z-index: 2; - cursor: pointer; -} -.pay-btn-group .active,.pay-btn-group .active:hover{ - border: 2px solid #20a53a; - color: #20a53a; - z-index: 2; - cursor: pointer; -} -.pay-btn-group .active span{ - line-height: 22px; -} -.pay-cycle-btn em { - position: absolute; - right: -2px; - top: -9px; - width: 33px; - height: 19px; - background: #ff7300; - border-radius: 2px; - font-size: 10px; - line-height: 19px; - color: #fff; - transform: scale(.9, .8); - font-style: normal; -} -.pay-btn-group .active em{ - right: -3px; - top: -10px; -} -.paymethod{ - margin: 0 auto; - width: 160px; -} -.paymethod .pay-wx{ - height:175px; -} -.paymethod .pay-wx canvas { - width: 160px; - height: 160px; - border: #ddd 1px solid; - margin: 6px auto; - padding: 5px; - border-bottom-color: #00c800; -} -.lib-price-box{ - padding-bottom: 5px; -} -.price-txt{ - color:#FF7301 -} -.sale-price{ - font-size: 20px; - margin-right: 5px; -} -.lib-price-name,.price-txt{ - margin-right: 15px; -} -/*input样式*/ -.bt-label{ - margin:20px 20px 0 0; - display:inline-block; - font-weight:normal -} -.bt-radio,.bt-checked{ - display:none -} -.bt-radioinput{ - background-color:#fff; - border:1px solid #ccc; - border-radius:100%; - display:inline-block; - height:16px; - margin-right:10px; - margin-top:-1px; - vertical-align:middle; - width:16px; - line-height:1 -} -.bt-radio:checked + .bt-radioinput,.bt-checked:checked + .bt-radioinput{ - border:1px solid #20a53a; - background-color: #20A53A; -} -.bt-radio:checked + .bt-radioinput:after{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAKBAMAAAB293L0AAAAA3NCSVQICAjb4U/gAAAAKlBMVEX///////////////////////////////////////////////////////9q+00tAAAADnRSTlMAETNEVWZ3iJmqu93u/wlUiAQAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDYvMDIvMTjAxKOiAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAADpJREFUCJljYACDTAjFcgRC+xpAuFcYGCqAtK0DA4P2BAbmq0A2404G3QSQpPTi62BFjGsTIJpEQQQATscJDpRQCfQAAAAASUVORK5CYII="); - background-repeat: no-repeat; - border-radius:100%; - content:""; - display:inline-block; - height:12px; - width:12px; - margin: 1px; -} -.bt-checkbox.bt-radioinput,.bt-radio:checked + .bt-checkbox.bt-radioinput:after{ - border-radius:0 -} -.loading{ - background-image: url("data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs="); - background-repeat:no-repeat; - background-position:62px 41px; - border: #ddd 1px solid; - width: 160px; - height: 160px; - padding:5px; - background-color: #787878; - color: #fff; - display: inline-block; - margin: 6px auto; - line-height: 200px; - font-size: 14px; - text-align:center; -} - -.cloading { - background-image: url("data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs="); - background-repeat: no-repeat; - background-position: center 45px; - height: 150px; - width: 100%; - line-height: 210px; - text-align: center; - font-size: 14px; -} - -/*重启*/ -.rebt-con { - height: 106px; - padding: 15px; - width: 330px; -} -.rebt-li { - float: left; - line-height: 40px; - margin: 15px; - text-align: center; - width: 120px; -} -.rebt-li a { - display: block; - background: #eee; - border: #ddd 1px solid; -} -.rebt-li a:hover{ - background-color:#20a53a; - color:#fff; -} -/*绑定宝塔微信*/ -.bind-weixin{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAASBAMAAACtCzMeAAAAA3NCSVQICAjb4U/gAAAAKlBMVEX19fXr6+vg4ODW1tbMzMzCwsK4uLitra2jo6OZmZmPj4+FhYV6enr////oR1GpAAAADnRSTlP/////////////////AEXA3MgAAAAJcEhZcwAADpwAAA6cAQeUU90AAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTYvMTgi+WYYAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAK9JREFUCJljuHuXyS1I4C4IMNy9MiNYpF0XzL5Qznv37uUpYPbVWBDF4Qtii9+N8b2y5WIRiB1810L3UuHdEBC7FqTkYlhnqyyUbcJ798rSuwxBQOYVkE5OX6Deu3dVedMulF1oYLgCNDPpbujd1LtJDEDqbhDYCc4Md9WUBIrB7ESGu15pLiUg5oUAhrvKd+9etQWy2WUZwEJAPUCbwey7zCHu04ug7LsMDExL7gIAh3d+c1Jd4JYAAAAASUVORK5CYII="); - background-repeat: no-repeat; - padding-left:26px; - margin-right:30px; -} -.bind-user{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASBAMAAACk4JNkAAAAA3NCSVQICAjb4U/gAAAAMFBMVEX19fXr6+vg4ODW1tbMzMzCwsK4uLitra2jo6OZmZmPj4+FhYV6enpwcHBmZmb///+wFcT2AAAAEHRSTlP///////////////////8A4CNdGQAAAAlwSFlzAAAOnAAADpwBB5RT3QAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNi8xOCL5ZhgAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAh0lEQVQImWP4//+/2KxiIMnw//+X2+1rCsCsuP7/n26CWB+PA6X04oGsbweBrJ/rgawf64GsbxtQWBDZ+TAdNv5gUxL+s74EmfJpz6mOtWdAYnYTNFZP/vb0P8OHU///f/j/P0+f4duF/yDwaz4Ddz2Y9ekQg448mPV/G4M3hPG/g8ESygoHALeIhpF8DGw6AAAAAElFTkSuQmCC"); - background-repeat: no-repeat; - padding-left:24px; - margin-right:20px; -} -/*系统图标*/ -.ico-linux{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAATBAMAAACEi/vCAAAAA3NCSVQICAjb4U/gAAAAMFBMVEX///9mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmambAcKAAAAEHRSTlMAESIzRFVmd4iZqrvM3e7/dpUBFQAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNi8xOCL5ZhgAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAmUlEQVQImWNgYGCQ/GvAAAHz/1+CMBjPvvoOYTFVLXkNZZ2u+isAZjE/4vyvAGaxlgbnJ4BZHAoM3A/BLA8GBpafYNYiIL4PMpt1A5DYPwFIcDWArDkAJPgWAInzF4BE9jMGBvb/QM2SBfEGDFy7UhkY3Bk4CxisGdgCGMwYGI97vGKwSmAQC1G8e+aN2WagDuP0IEGbawIMAPDJKG706GgtAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-windows{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAA3NCSVQICAjb4U/gAAAALVBMVEX///+ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZny8jBSAAAAD3RSTlMAESIzRFVmd4iZqrvM7v/Y8bBbAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA4LzE2LzE4IvlmGAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAABGSURBVAiZY2AAAkbTqrMMDEJuHvfevXvHcPbdu+53IMY7Ihi7d+9O7QACBiB2BTOAol0wNbgY59696wQzGFQqw8GWwpwBAGURWs03JAEWAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - background-position: 0 2px; - padding-left: 20px; -} -.ico-centos{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAPCAMAAADjyg5GAAAAA3NCSVQICAjb4U/gAAAASFBMVEX///9mZmZmZmZiYmJaWlpYWFhmZmZiYmJgYGBmZmZgYGBmZmZiYmJmZmZiYmJmZmZiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmbwNFvuAAAAGHRSTlMAESIiIiIzMzNERFVVZmZ3d4iZqrvM3f93YyfbAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA4LzE3LzE4mkUBfQAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAB6SURBVAiZVY5ZEsIwDEMdUpYQghIv9f1vWpNCp/hH1oz1LKI5if5G5ezuzLff7ibO7KK+H2LaBp1WDMLcYDNf3A31CXMvRCy995rSK0SExhjAkh/XEK5E2YOCN5r75RNGUJgtZJLXoMQjw/ot2LR3baJHSbN8Ll3LrhtQxAfrvVHLpQAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-ubuntu{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAA3NCSVQICAjb4U/gAAAAOVBMVEX///9mZmZhYWFmZmZiYmJmZmZiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZVQ1Z+AAAAE3RSTlMAEREiIjMzRFVmd4iZqrvM3e7/67N/KAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNy8xOJpFAX0AAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAbElEQVQImU2OWQ6AMAhEcW3Z2jL3P6xWjHE+CMPwCESfDOXtFmutAp6OtwC0jUwZVlWHaaaBniyYVq6B/Rn3G3aEjDxRuPwtN6NFzkB97IAlE3ddHTimPUKkd3TlhLb5huvLzAWWE2j0k0elCynoBUe/zXGJAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-debian{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAA3NCSVQICAjb4U/gAAAAPFBMVEX///9paWlmZmZgYGBdXV1mZmZgYGBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYdQ+x6AAAAFHRSTlMAERERESIiM0RVZneImaq7zN3u/ynguF0AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTcvMTiaRQF9AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHJJREFUCJlVjlESwjAIRLFFIRhCaO5/VyHijN2vt+zCABBq87IhT/hK1+zdfOl2Y71r2rPp1UKfAOdFgaTtAWIRWgacMUUquSi7TR6U2LflFuW0nOVjYB7JQBqwHklOe49f9dOAf+HvjRIZ3L3ePTIWfQAgiAODqr7Z+QAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-fedora{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAA3NCSVQICAjb4U/gAAAAPFBMVEX///9VVVVLS0tmZmZaWlpmZmZiYmJmZmZiYmJmZmZiYmJiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYd2jWRAAAAFHRSTlMAEREiIjMzRERVVWaImaq7zN3u/2KENScAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTcvMTiaRQF9AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAGJJREFUCJlFj1kSwCAIQ9N9Lxjuf9faUKf5IPNQiAJAZ4yg9ZCWkMj5pxWwGCqK5skLg8AphMv2dIJC0106nNmu5VLA1bBmfqclF7fZO9HTYqzO4tMuOt5XGLA1enMzQ194ADH/CrSqGfFbAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-mac{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAA9ElEQVQ4T5XSvyvGURTH8dej+AsMyGAykEWSTSkyPFJMyvAsYjFZJWZ/gQyKYjEZLcrkRzZZTXomgyzKj3TkW99u6l63bt3OPe/z43NOQ/6sI+5g6trIsMs4wjUm/gs/og+TuMrBA1jAO06wgn3MohfHeKqC1MvexBY6fz/fcI+xWsZXNHEZtgpu4SCvnRfM4KYOn2O6AF7DXr3syB4ldhXAw3hIe/5ERwEc/d+lcBs9BfAOtlM4RrBUAH8hprKLj0rtRZwWwOES4xupqx3vWMHxggDzOEvhftxmer/A1F8bFrYhbKAbh3jGKOYQoq4ituznfAP/VikQF8NdKgAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding-top: 2px; - padding-left: 20px; -} - -.index-pos-box{ - height: 52px; - box-shadow: 0 1px 2px 0 rgba(0,0,0,.1); -} -.index-pos-box .position{ - line-height: 52px; -} -.change-id{ - margin-left: 8px; - color: #20a53a; -} -.bind-user .glyphicon{ - color: #20a53a; - font-size: 12px; - margin-right: 3px; -} -/*微信二维码*/ -.boxConter { - height: 458px; - position: relative; - overflow: auto; -} - -.iconCode { - padding: 50px 60px; -} - -.box-conter { - width: 100%; -} - -#QRcode { - margin-bottom: 25px; - text-align: center; -} - -.iconCode #QRcode, -.iconCode .codeTip { - width: 100%; - text-align: center; - font-size: 17px; -} - -.iconCode .weChatSamll img { - width: 100%; -} - -.iconCode .weChatSamll { - display: none; - width: 200px; - height: 200px; - position: absolute; - border: 1px solid #ececec; - border-radius: 5px; - bottom: 150px; - right: 50px; - padding: 20px; - background-color: #fff; -} - -.iconCode .weChatSamll:after { - content: ''; - width: 15px; - height: 15px; - background: #ffffff; - border-bottom: 1px solid #ececec; - border-right: 1px solid #ececec; - transform: rotate(45deg); - position: absolute; - border-radius: 4px; - left: 90px; - bottom: -8px; -} - -.iconCode .weChat { - margin-left: 15px; -} - -.iconCode .weChat:hover .weChatSamll { - display: block; -} - -.iconCode .QRcode { - margin-bottom: 15px; - text-align: center; -} - -.codeTip ul li { - margin-bottom: 10px; -} - -.personalDetails .head_img { - width: 50px; - height: 50px; - float: left; - margin-right: 30px; -} - -.personalDetails .head_img img { - height: 100%; - border-radius: 50%; -} - -.personalDetails .nick_name { - height: 50px; - line-height: 50px; - width: 148px; - float: left; - font-size: 15px; - color: #808080; -} - -.personalDetails .userList { - height: 100%; -} - -.personalDetails .userList .addweChat { - height: 50px; - text-align: center; - padding-top: 20px; - color: #20a53a; - font-size: 16px; -} - -.personalDetails .userList .addweChat:hover { -} - -.personalDetails .userList .item { - height: 70px; - padding: 10px 15px; - border: 1px solid #ececec; - margin: 15px 65px; - border-radius: 5px; -} - -.personalDetails .userList .cancelBind { - height: 50px; - width: 60px; - float: right; - line-height: 50px; - text-align: center; -} - -.libLogin .line{ - position:relative; -} -.libLogin .tips { - position: absolute; - top: 13px; - right: 28px; - background: #ff4949; - color: #fff; - padding: 4px 6px; - border-radius: 5px; -} -.change-id{ - cursor:pointer; -} -.red{ - color: red; -} - -select[disabled]{ - border:1px solid #DDD; - background-color:#F5F5F5; - color:#ACA899; -} - -.cicle { - position: relative; - width: 88px; - height: 88px; - margin: 0 auto; - border-width: 6px; - border-color: #ccc; - border-style: solid; - border-radius: 50%; -} - -.cicle *, -.cicle{ - box-sizing:content-box; - -webkit-box-sizing:content-box; - -moz-box-sizing: content-box; -} - -.cicle .bar { - position: absolute; - width: 50px; - height: 100px; - overflow: hidden; -} - -.cicle .bar-left { - top: -6px; - left: -6px; -} - -.cicle .bar-an { - position: absolute; - width: 88px; - height: 88px; - border-width:6px; - border-style: solid; - border-radius: 50%; - transform: rotate(-135deg); -} - -.cicle .bar-left .bar-left-an { - z-index: 10; - border-color: transparent transparent #20a53a #20a53a; -} - -.cicle .bar-right { - top: -6px; - left: 44px; -} - -.cicle .bar-right .bar-right-an { - left: -50px; - z-index: 20; - border-color: #20a53a #20a53a transparent transparent; -} - -.cicle .occupy { - position: absolute; - width: 88px; - height: 88px; - line-height: 88px; - text-align: center; - font-size: 18px; - color: #20a53a; -} - -.mem-action .occupy{ - font-size: 14px; -} -.mem-action .occupy.line{ - line-height: 22px; - padding-top: 22px; -} - -.change-default button{ - width: 188px; - margin: 10px -} -.bt-w-con .line .info-r{ - margin-left:0; -} - -.bt-logs textarea{ - white-space: pre; - margin: 0px; - width: 500px; - height: 520px; - background-color: #333; - color: #fff; - padding: 0 5px; -} -.dir-rewrite-man-con{ - margin-left: 15px; -} - -.domain-ul-list{ - padding: 5px 10px; - max-height: 180px; - overflow: auto; - width: 260px; - border: #ccc 1px solid; - border-radius: 3px; -} -.domain-ul-list li{ - line-height:26px; -} -.domain-ul-list li .checkbox-text{ - margin-right:5px; - vertical-align:-2px -} -.btswitch-btn.bt-waf-firewall{ - width:2.4em;height:1.4em;margin-bottom: 0 -} - - -/*6.0终端样式*/ -.term-box{ - padding:5px 10px 10px; - background-color:#333333; -} -.shell-text-input .bt-input-text-shell{ - width:100%; - padding:10px 10px 0; - border:0 none; - height:60px; - overflow:auto; - resize:none; -} - -.shell-text-input .bt-input-text-shell:focus,.shell-text-input .bt-input-text-shell:active{ - border:0 none; - outline:none; -} - -.shell-btn-group { - height:30px; - position:absolute; - bottom:20px; - right:10px; -} -.shell_btn_close { - margin-right:8px; -} - -.file_search { - position: absolute; - right: 58px; - font-size: 12px; - color: #666; - width: 85px; - border-top: none; - background: #fff; - height: 8px; - line-height: 48px; - top: 0; -} - -.file_search .file_search_checked { - position: relative; - height: 15px; - width: 15px; - border-radius: 1px; - cursor: pointer; - text-align: center; - margin: 0; - vertical-align: sub; - border:1px solid #aaa; - border-radius: 2px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: 400; - line-height: 1; - -webkit-font-smoothing: antialiased; -} - -.file_search .file_search_checked.active { - background: #20a53a; - color: #fff; - border-color:#20a53a ; -} - -.file_search .file_search_checked.active:after { - content: "\e013"; - font-size: 12px; - transform: scale(.85); - position: absolute; - left: .75px; - top: .75px; -} - - -.file_search label { - position: absolute; - right: 5px; - cursor: pointer; - top: 0; - font-weight: 400; -} - -#form_proxy .sub-groud .bt-input-text{ - margin-right: 10px; -} -#form_proxy .sub-groud { - margin-bottom: 15px; -} -#form_proxy .btswitch-btn{ - display:inline-block; -} -#form_proxy .add-replace-prosy { - position: relative; - top: -10px; -} -#form_proxy .disabled{ - background-color: #eee !important; - cursor: default; - border: 1px solid #dbdbea; -} - - -/* site的tab切换 */ -.tab-list .tabs-item:hover { - background: #20a53a08; -} - -.tab-list .tabs-item:first-child { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} - -.tab-list .tabs-item { - display: inline-block; - height: 45px; - min-width: 100px; - padding: 0 20px; - line-height: 45px; - font-size: 13px; - cursor: pointer; - white-space: nowrap; - position: relative; - text-align: center; -} -@media screen and (max-width: 1300px) { - .tab-list .tabs-item{ - padding: 0 10px; - min-width: 80px; - } -} -.tab-list .tabs-item.active:after { - content: ''; - width: 20px; - height: 2px; - position: absolute; - left: 50%; - bottom: 0px; - background: red; - margin-left: -10px; - background: #20a53a; -} - -.tab-list .tabs-item.active { - color: #20a53a; - background: #20a53a10; -} - -.tab-view-box { - padding: 15px; - border-radius: 4px; - box-shadow: 0 0 1px 0 rgb(0 0 0 / 5%); -} - -.radius4 { - border-radius: 4px; - box-shadow: 0 0 1px 0 rgb(0 0 0 / 5%); -} -.radius2 { - border-radius: 2px; -} - - -.tootls_group { - height: 30px; - line-height: 30px; -} - -.tootls_group .search { - display: inline-block; - height: 30px; - line-height: 30px; - position: relative; - width: 230px; -} - -.tootls_group .search .search_input { - height: 30px; - line-height: 30px; - border-radius: 2px; - border: 1px solid #ccc; - outline: none; - padding-left: 8px; - vertical-align: top; - width: 230px; -} - -.tootls_group .search .search_input:focus { - border-color: #20a53a; -} - -.tootls_group .search .search_input:focus + span.glyphicon-search { - color: #20a53a; -} - -.tootls_group .search .glyphicon-remove-sign { - position: absolute; - top: 8px; - right: 30px; - font-size: 15px; - cursor: pointer; -} - -.tootls_group .search .glyphicon-search { - height: 28px; - line-height: 28px; - padding: 0 10px; - color: #888; - position: absolute; - right: 0; - font-size: 14px; - cursor: pointer; - float: left; -} - -.tootls_group .page .page_jump_group .page_jump_btn:hover { - background-color: #10952a; - border-color: #398439; -} - -.tootls_group .search .search-btn span { - font-size: 15px; - vertical-align: middle; - position: relative; - top: -1px; -} - -.tootls_group .page { - padding: 0; - height: 30px; -} - -.tootls_group .page .page_select_number, -.tootls_group .page .page_jump_group { - height: 28px; - line-height: 26px; - margin-left: 8px; - vertical-align: top; - float: left; -} - -.tootls_group .page .page_jump_group span { - border: none; - padding: 0 8px; -} - -.tootls_group .page .page_select_number { - border: 1px solid #ececec; - border-radius: 0; - padding: 0 5px; - border-left: 0; - margin-left: 0; -} - -.tootls_group .page .page_jump_group input { - height: 28px; - line-height: 28px; - width: 30px; - max-width: 50px; - vertical-align: top; - border: 1px solid #ececec; - outline: none; - text-align: center; - float: left; -} - -.tootls_group .page .page_jump_group input:focus { - border-color: #20a53a; -} - -.tootls_group .page .page_jump_group .page-jump-title { - height: 28px; - line-height: 28px; - color: #555; - border: none; - padding: 0 5px; -} - -.tootls_group .page .page_jump_group .page_jump_btn { - height: 28px; - line-height: 24px; - border-radius: 0; - outline: none; - margin-left: 8px; - border-radius: 2px; - color: #555; - background-color: #fff; - border: 1px solid #ccc; -} - -.tootls_group .page .page_jump_group .page_jump_btn:hover { - color: #fff; - background-color: #20a53a; - border-color: #20a53a; -} - -.size_ellipsis { - display: inline-block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - - -.title.tabs-nav { - padding: 0; - position: relative; -} - -.title.tabs-nav span { - display: inline-block; - height: 50px; - min-width: 100px; - padding: 0 20px; - line-height: 50px; - font-size: 15px; - cursor: pointer; - position: relative; - text-align: center; -} - -.title.tabs-nav span.active { - color: #20a53a; - background: #20a53a08; -} - - -.title.tabs-nav select { - position: absolute; - right: 5px; - top: 50%; - margin-top: -12px; -} - -select[name="network-io"], select[name="disk-io"] { - font-size: 13px; - height: 24px; - line-height: 24px; - padding: 2px; - border-radius: 0; - border-color: #ececec; -} - -.tabs-content .tabs-item { - display: none; - height: 100%; - width: 100%; - background-color: #fff; -} - -.tabs-content .tabs-active.tabs-item { - display: inline-block; -} - -.bw-info { - height: 80px; -} - -.bw-info>div { - float: left; - padding-top: 35px; - text-align: center; -} - -.bw-info>div p { - margin-bottom: 7px; -} - -.bw-info>div a { - font-size: 14px; - color: #666; -} - -.bw-info .ico-down, -.bw-info .ico-up, -.bw-info .ico-write, -.bw-info .ico-read { - width: 10px; - height: 10px; - display: inline-block; - margin-right: 3px; - border-radius: 5px; -} - -.bw-info .ico-down { - background-color: #52a9ff; -} - -.bw-info .ico-up { - background-color: #f7b851; -} - -.bw-info .ico-write { - background-color: #6CC0CF; -} - -.bw-info .ico-read { - background-color: #FF4683; -} - -.bw-info ul li { - height: 56px; - line-height: 56px; -} - -.bw-info ul li.bi-line { - border-bottom: #ddd solid 1px; -} \ No newline at end of file diff --git a/route/static/favicon.ico b/route/static/favicon.ico deleted file mode 100644 index 17e918a805f4f1f16613716ef3238518435197cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9662 zcmd5?-Ahzk6yN5QWj0P`_DNbPL_zdYlv!4Ym6U;54;3O36+{okK_Pt4SI`i zT;viVVMh4S7t}*Ngav(QHce&^S%_BZzQ5n8HFwXRea0Detkd1Dz4uvruiyGOYp-+W zDwTnMt5zxeEmD7%C{>`8T84%xRgcDb?<)R5#{2J%9Xs;p&!1m8efspX+1c6GX3w4- zo;h=-4g>;T%gxQz`T6->)22;p%E`$&HE-U$%9@&*(0JoWWxw+B@|;j8R38imZ%m#% z`3u(SR2p>ag)TSH4wROb=A1+_UexuITA>OCDnT+B_&1ha|^JK+14)H z<{WK?7q2k z=hh^P*W_EUU_mWx(C6BBBIib<17n7EPuFvrs{Yg=*;rb-bZHUdJ(8-7bo29hC@wBu z1UbIf@#Dt>I74rMA?qarFjjf|GtY;Wv%AifgFH;^GX?n#Nfd!^<%n@7XfAhwz(n*tdCQj9Tzd z^rKfrMMaL^8AX|oA3xTSNJRJa^yv5R-+Q#XyIbG9c{5JN>eZ|DhYugTMjkd_wJp?M_#TarA)tA2G-7v8#iW)0;nMy(2v<$#>D^n^=myiIH-i48tGAo$U5 z5xh2~4Y(I^%l?At{{8!Lc!;08!Y?{=yl&mPIR1P0?#1X!KCVdue>3s#+O;bWxz)FR zxAB|UXggcqva+%$9$N?Av-yc%d`q8+ALw(x_~9PF+`D)0-VkcyZT6n#nDHa_7u(Q2 zl;dar;o)Hq6LE|Fp62 z*lYakFLn|8lfHWOYDE0ViH&I2Y|Q4E__>$(n6jijc<>-D9{7w>zF;@0-}5UxVtF@9Nd`%hc?d=-hQDWBPT#v?q_==R+P$S0h(l!qt zK8&%y*oHEB-?q6q&dbZwh@p0K&h8UGeJQa+KTrl~Z*Q+&ym)bpjQ#ufM`On1rT&eL zjWN8e+vr>F$0o7H{>zsy<9usl<99yf&wuQBb8OpR{2=l!U%srF1IWi3_13Lh9uDeD zJf;qei4`kWcy=X!eSLitvtTD>bLD0e7yHcp$f1q)yg3&9+*7biJcz7k&z|Y#=4So$ z>C>nk1wVOt9#dcH^6}$G-P+o!Z{NPH`}+E#{N&?)q-+;2UX1aTiAnnK-)Ww~GImkH z&pnArbRgxJxMW_a_(tw?EwKxgwZzMLQsHHv>uYOkN5qeBB-`*kGsIfRg`IQ6Z=S!D zBf5}^JdrQ=MF+VrdUM{?MaHDE_Q{hc8g)%V{H!@qd*%b9f*)9mI{8ie6R+T6-s8F8 z(nV}4*cn@wE?x3;73`v~@XGk}=g)f6rcF*PCPqAStogkQ&%r))oNI`m`G;62gOr#n zD=YQ#<;%V2%*?4K9`aqeaz!)G@jdbU`SV`Pi=D|U_NRaN?zMC0&Jpttu>ya+2OfVm zY}k+ue{^x))tD0x9XjN-!-o&+y1F`z_h^ro`G3!zJ-Vr>NxyjU!h08d{`|SVdiAQ_ zzJ0sLM;mZo(v>S$>I)Yx=tqwp>9=p+>To!$SwoyVcTVFskfgBV+zJD8%sV`OFX20} z6MwSv)P?WeiOzJ7K(Mh#4m`Oetgp}8wt00f6kma)yR=wi2YaMCwAo3S`%Bc zed>VqQ9oeagxa6j@w|=-yXeSRf**X(wsh%)Gm5`gq!crP*`|esg$r40!yZG#o6dOR z-u#}K?6YcP^&9gyWz^aS;EQzre%*t2s)G#;4GD9kAI4rYvv{{ei&Y%u@; diff --git a/route/static/images/ico-ts-cpu-active.png b/route/static/images/ico-ts-cpu-active.png deleted file mode 100755 index 1abf4f009e9fb10342b560060fb502da88b7647a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 780 zcmV+n1M~ceP)mnt=I5S;Kst?7`km7{4I*g4AHGIb?=tWi&z)-@se&3cNNWVf>OK=OoLX zq+0K&u`rtvar+)uFj5~@ff?uz3e?eV<(Sg~Iek@_-;rx-pq6~c zyd6(urmb!MBghPN_$gjZJChXC=Dz`a0iVF9Fm|Vr6Nqp6-y0>mls1C+QQY55t_E6rMbp<$ z?vZgxJu;u~zGW68ljH<2_OguJR%hB5HF3O0P1^|OAvWu5yN+yBq@jws>yu7oF^{il z79-PmBAP~yixHXANOi;?!=jkFgHXq_{;)K4xt zDXB*m@oJi7xtgiG8pp)PbVc@&=hEc zridRjmENFf1`KG!Xs|aG>EC!B?A;0acd!P3mvi7d;RrlBJ^}#jY=k=dMk3z;0000< KMNUMnLSTY=qjOaN diff --git a/route/static/images/ico-ts-cpu.png b/route/static/images/ico-ts-cpu.png deleted file mode 100755 index 5c9d3eb38c5bdf58aebc87cff15efa2b97e2747e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 645 zcmV;00($+4P)n*>vvcNi zXYRf&5k=8=UAS<0hJ27*>c;X$)|qP-NF+(rY4TK-dHbW-1sNyrr3rN1@Wk(IbiR0@fIB*Nz8f zV4NIhYYLoa!zcM59~~bUe`LaMmB@^LqF!KvykMV0zEj8|mA z#nc&W&1Tljjk!&SIA>%kL9#tTMsNq(n;8z00000NkvXXu0mjf-p($g diff --git a/route/static/images/ico-ts-disk-active.png b/route/static/images/ico-ts-disk-active.png deleted file mode 100755 index acaca696eecc89b7795976e2d6775a0a713b2ac9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1072 zcmV-01kd}4P)MH0000B`NklDa)TS>OZ_Q__FYumPM+qSLUOmb}7{-1iEx1DZsZ&P>s!}HnkcV5h!t!QXy zXv7KglO3%#z;QTL22Q{xC-UYrYnH@wp&u}L41R!j%fPpAEEjp_w-9IIde{%+q~Fs_ zDtf~>dv8vw+ggf8{)0^JfidL}x3oWmvv6oXqBe6-EYk$)ZQw%OW`;a=L8!i`R( zzT@{O`=54JFRp5Jf9T!Ax*e<=pNPkPuJIX!>V`IMEkdVlth7V_d^=Ll(qE9c)bmXt zd9e-g%b7onm!>7++Mg#I89Xa<({i`JWU3XZ#}U1a_S0NqQpVv+q(WMu?`NHZam%FI zv2JlnTq!tBLdnDboucZSBo|}UVlH%XXWlZ2 zIM-PB#KbrllAd$oppFo4r_el^7Jr$VyLxh@YNRjOXk#&4*_ASiC!e>QF0g#!a&J`g z+|)H^ROO<^C;E0e=AW|orJRHIXdy$sQ$LeQ6+Va5)z#_|2xpbctnn^l81R*sX4oam;-OhrOmS$m`pA7Xdxf6)!EX2PRE1h^*O_Fr} zWnAC{29K5)I8ATd3;z4Q)$KA4hhRS(=n%=;lagF8 zsyD>Z844>g`W4B(59t&qhxvZJB3?M77QH{=+X@i|Q($@r5vG=`m4Xt}dDAMmgPnpq z3PzWezn2b1?=@IcVdA|9w!%)>Rc1tIFKid_1El|NQe@z^9^fjt0d9pW;1<)r+RWdG z-Y@(f6!DYrOpnDg^p`rZ8gF8IVIO^6rKN-Mv=^mf{t%q5X!Czp=WDrqq_q4|gmI=i zPBC8in@@FLBmQKw_(^!8$1IQL)t1_SxdRhx=hdVvzpq(*H>`nodx$sTEz^Dj-ZA|* qQ)^bj5vjF*)7S^6dXfF;7XAh38QMMH00008LNkl|2}a%}u4PBu`T3>gyCv+M&O{|DT$g?s+Id zH~MU#2W42U$rQu4qYDvmwSiD{VZXNU^JvBtxwbaW9fL+((-!^^4VYx3@KLD4D{bLl zQHy~#3U@*!T(yOVpuh&fVn!-rwS}i5(u9LtFajD8@)Jd;%)))WGI*s6rO*;wUa644rrlCq4V`paxUL@HuG21)UoSf`s?j zkGEJY%x^;vKI%E+INGsL3}1;J?9#DmC@S$B*YFxMw5(f?Zfw(W&PMcMofy6q5|(J$ zFc;yd#0@+$@Mv5Ss$*^@B{u%jjQR=Da#7Q&T=zB=W#spQDOT}+7+!s;8qBAJL zU<p?bJ2pC z!q`33V3b^pMJq0>VksvVSXG7cAU%?eK80Uf(If4cQ6HQn2)PS#$aXLOHFt$URd)}60U~}*J6LarwOk|0bH%*Cw{>dKk!GX of5KIfiw+0nb~K^Sj+&AB7eFLbfUXKcQ~&?~07*qoM6N<$f|(6Xr2qf` diff --git a/route/static/images/ico-ts-mem-active.png b/route/static/images/ico-ts-mem-active.png deleted file mode 100755 index d71de28ed34f05c9196efe729f9d7167897bb67f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 503 zcmVbM7=VBXq!B9Fy4R5{G0)>*S_90~UveIg1GV!s8$!GvObZ zABy0D3%~Ibdjih5&|s4X&%Xt81)b4niFE?gpU6!hCWAdL!w(G}dI~PxUXV=+-C+h@at$p=X|5QYZ4zy2`pp+CKZ#a#}#%BT;XFO z*~w=DCav7yvW7o@9l7olrrM%TG{+P^5n~KoiecS=^>=!0P0uhG1CzLbrg^AHe4Un= zgx(08r8NBZ@4)|HF2aD0lg*y0R_SQ)Ry6O5HI}$(1(&bFO4>V@64ITUHr@(0RY=^| zuG{PhJjf@}Vo_}^xnhkK)Mm5Cre^}0T;i$COyY^DwjFR+tg(QG6-+~rwr4b8w2dj> z!TRp0ZfzH+5}Q|rUmC?bV#%~Sv)f}P;W$Olnl#l^IrZBeAAT-_Kt8t8AtjDM*=jV7 t2fUIPwRqHGcsG@v7}T}~+5UeZFaWFL;Gcy`#=Zam002ovPDHLkV1f><-ZKCI diff --git a/route/static/images/ico-ts-mem.png b/route/static/images/ico-ts-mem.png deleted file mode 100755 index d12b654389b21843e76dab8c8ed48d17f707a3e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmV-t0hs=YP)NnLj`0b>Q?Rh#*~A9~FW3zd_InR8f#4N;LBf9bKHeah zC|~u;;}OP?zu_YYMz~v}`LD2#XDIN#MpGXSJ2W3Q7p}tme*&jyBf<`xL8~At>H{Q-#=`r=Al~X5 zUm;gysH1^_;r)dcZvOMe-Rj25RN*jFg$uaA#y?@%gm1f%8N#hDVom@6002ovPDHLk FV1k+Pn@<1$ diff --git a/route/static/images/ico-ts-refresh.png b/route/static/images/ico-ts-refresh.png deleted file mode 100755 index b292189d8160308ffd92f9ee4e980211eb854e41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1185 zcmV;S1YY}zP)&eaehN zvsjteqv`hR`~Cds_Uyst$TO}>cgTm~^W<^Hfkm`izU9V8vsv5l-_PvSFsw(I)uiqC z@9p^Tq}s1LuTr<-zW4n2pxLW5txUM%z`5hVT)uF?=EwW~{G-~gHLXp-=Eu$J(>t(J z`~Caq_UkaKN4Da=NVHm))uZ|S`g6yGXu^8G<;F&|THEp8G_6Z?#)bC#_}A~;;Pd4E z|NmCIYW)BG%<9s$;J#A1X7l>>Ij>Or{{5NOr7o&Q^ZN8;z;~(Kv%}}gIImCe`SN4H zcKrSQ?Dy>{s6ukbg8lydkkg&E;l8`$!{zkoi_nU0**;U zK~#7FVi=iV6fhF45CWjQ{}4b;OE`e0qget6Kd54fFwFe#T#S$5qO$+Lal-5Yur^PJ z5k&zM08dcC`(4|%ZQC|lPY)?zbu|slA2=yhYK>N>m++LNH)u6#qY^w!W{cHkci;_& z-R88IO<<*TxjkMzKGJ(VK9>?K9R5Hs6b6xSBpC2JKrI@JC+r|*PsC$UP)erKUQqF- zQ%TT>WPY8QK5Gq%yH881FD`gO=H<~gqlC>IjqNCmEf|;z- zCRlp?fdd>IgMJSm3`eaoxQtt)A>NuyXA(F`v*`ry%oj^=TQ25!W3?^<{EF)pUfOJT z5MsC8;Dzu$4MEcTFrM=tju7N{@Z+gd;Cz7~mvi8RXRd`Cgt-;2c;fDkTp-LP;{GSj zuJIZO!yt@L^!sHS+}#?a&k1q_Xj8@A-4&0X7I$dydGqhrx0=TXK7Rmw!H^Sl;fNi4 zd(jx^CgTav9VBPKx06bPZYg5{U3OjrojaF@fRGM!O9egXiX{W+R?0@ut&E$%H(>^u zssIY8C=gY(+Mcr2v?`eDjizcf>L6>YGG3Hb8)WrHOEnvHkiU;j9p8T+`9F=oJOMt_ z_zLKZ* z=$xS#_yT^=@W%rF?7<&!_*0bds3t!T0?q##I-By7tsWm-Utj-ye7--)S2KFoia)V- zj}C=`z!!S`zom_SPdw7_a}z(D@e`nY%#_cv@`3r@>frmSe6Tv&=>N6h-Q=gwn2B%s z!ps@d)uz#XAqM43N@SoSPY_E=26S=EC>R9*qL$No@JmEa00000NkvXXu0mjfNNkT# diff --git a/route/static/images/ico-ts-score.gif b/route/static/images/ico-ts-score.gif deleted file mode 100755 index e4c8a65cadb083c0fc6ce2e2be22130042bea7cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9773 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 diff --git a/route/static/images/ico-ts-score.jpg b/route/static/images/ico-ts-score.jpg deleted file mode 100755 index 8eb5d62e5d72fa1315b96637724c2a6b0ff2b1de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3961 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 UHgDO~RXV{!b`xU>H0FHrKQq!lz5oCK diff --git a/route/static/images/ico-ts-score1.png b/route/static/images/ico-ts-score1.png deleted file mode 100755 index 8219d32af1c21cf368b6cca5c93db0bc74db6b04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2061 zcmV+o2=e!dP)DQm-C}ji0X=ul@3N5a+}-`$rS{z2-?>W_Id>P%{qm;n?q!;!@c_H(RtEg0-qf2bpjF~bT!w$+Uc7?U*ovK|{fJ;Uc47-w;bnyI zFIEU@G&#S>)3}jbfO0_=rBx)EDY#~p&OlO!ydefYw;OWszBM*;JbJZ z`_YN0B}5q4Vm5qmLo!57lEDohX5(6fGs1OZAD+h7QK13>!|???gBC;)hk=JN+Zv)5 zbd&Uw8?*7SC0Gegl5XCxt4`a$9&}7WV zR>WZ-jB`;90g4p}KsC-q7zSe4fU7ZaFb1l?rPzcR3@m^T0>D0jtbABt3AG;QV$5I( zbQ<16)Dp%C0qhYBrT!UsrajhK# zO~R`%uo-ShnQ>zabUc9?y9AnvRWQ(qYDk%?Mk91ALrt**)nY3Q#Lyl9T1tl(4><1|!Q_ zBavmTVU6?x#^f{5-H4;Hm+Rwh)I$cTdUW@4yZc|nac#Z=osTHCpt_f@uKF@KlTeS7 zd-=7S(Sxt%I?$PDL-T+RLKQcf(1N3L6{rTwVdUhbr*aJvcoP-52=q_H@i0WmiH8xx zF9#aPjdnEUbX%fSqY2G8W*~vy&D*L?xdu93>TjU$BaSd6FO)E%_;OzZjloVB@ImrI z@xj1)jP6UI`G{cwB(IbOh~nbj2AYWNFyMsbmEwefb*So1peqqoOHV;9Ko7o$tbwYq z8oJu{6qFA-KESAKfj*D!qU^aTVRS;v7U&VgaV|tDsB;m=fcZqZ>LpupT$yY>a^$I$jjst4VfHq(2BV z)fv(>rWZ3G!fqr9lw_LHiWZmO6DQC?!oFo*7`z{M@UdSL?P5Ou1;U?Q>#sk;5ojQy zXDXks%=leHA?3haX!fNBF(e@ab=YEUBvTF7;6WTf(h1k0wV~ad?!-HI8h+f2pR;6M zfG_fMPOA-Q(;UINNr(GAla5&G83jpmEj@E6WKbXwT2g~Jl8~GVHB|DXeu42n9e(X4 zXKI(ypCuHK$e1_Kjt(5;XKcWmjGs6>;JNE?mOkdsa|G(R8|IwPIhUSuWNpY>=?pfo z`m8(L6|5T$DM9H#&8b0C9s&WO|1VKhY=@3bZLe6Bg^0AlT6S<7LDS5eJ?g zrtdO;7w*9G*opnvg|@6qAP9GWrR%cVpm#K+l$dLeonraF4y3?p@k88+_t1r;37$@# z;AM=j#~0wh;W)>fXYWvJG$Wf~0O&}V+rG`Np!ZxzNudcgW*n#%)DR3u4Nhfgnae8b zS;l+NiKOWuR+u5r#c{0KMnW9?gIvqk#tHH?es6wl2a={{d@SR4i>+A>RNsLPH>6mI zGnND3W0-#1+O_fG_A!MkP_fV94}Z;milb? zM_z(^u@lJ*E!Nc{fu5brk_BPD8h=C5q6ZJ*IG5i$9AaCvKn!=KW^O?Yvmv&#@Qewp z{yxuv?~TmAiakzq$;i-w8_<9&unfse@Vk~aOoG@JEzpBsr7zg^;97`H4R_qDO=fx$ zyX+Se=sw1q(UG|)2pwH8VA-QfFLj>BcoL3<*p|6%#pg@(l6T!yrk}zu@RXCk-x31| zdeUJ!Q!&}L{gtts-S>&ErS&5rwnYoHV@mo#A3y?9f~NDte~al&Sb=VK&p8+9+0A$n zpZhc>K}tpX7|428&s!ya+tu({0na$XoaG3d^9%g0Hl#H`tT<2}0X6-WhAR3PPlBGf)h&pIs*00000NkvXXu0mjf6!*%A diff --git a/route/static/images/ico-ts-score2.png b/route/static/images/ico-ts-score2.png deleted file mode 100755 index 1eff7f3b3837425b5e39f28fff46b16ad70b3873..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2191 zcmV;A2ypj_P)hG_5LMtiMBjl+gB&OiZzG9qxE9h(T!U>$ z;F%x>>Vo&-VmZQ)W+sdkaPTK|2uh&dmlB|KDSeM=f;cecvy zXz?h!JIfqM)7p!5)<~FzR!tqK9X|DMIhlMvO=qmd100Um7#Vo_q#KY!ZS@3=zJuw0tI_{L{o-> zEQHB{U;w7GB|QW|E~KgGx|QwaEK#5#>+qw49q4RSVOx+-Lahq>n*`dyzTpW-(CFB0 z#daKfumW|!Y`C}v5@0_kP&J`mWZ4B0G&+W9e2lgSBhV8_VKOA(ep6Io*C7T8Djmxt z?l{muVSI(HhFrq=H9U;5rrLRsAkwkiifuUVKmvVk#?*!iJD=sv@R;J>4~?}8Awi{M z>fk^78|XJk;T?zp@^Q5Wn#M8$3AlHVzz_B{P%Es3i~f+Hvp%+S^*qhPLW}3^AcY)) zLxM=h*2Q8Rxi5ikL=wXxfi}j@fiT4t>NDFO*7k|pHYDhDj1#zcZv%D4YPhfPx1*^A3O5 z&ViUf$KJvC%s_2W2^V6Zju-*Yr;eTPjS!P)gY}t#2BH>|AqMJ$*WkcY)vWNF))W2m zAg0g;H8|NH=vkypeV&>SA)TVV3=(=;w1bf+&$41lp;QT>Kbfi0(L7TO{yf z;oA#vd}^=&FCmK7kbt`x9%`(3>3@jxcYR-l-$4SZHKKS)$GirP&qJagHgfPjD&Z&> zDQrL-eIbGK_8ECB?}w`%#}oRx29H7lrZ3{yfD~LfsuCZ-K}_@#w~H`Zp#;xkF?PZ6 zKf)V0vC?*Wn&4pv&cnr62M;gz9dEaS1cmRBVMoV!v5t9(HVDJPzlCoL8##ira2;+$ zZ%ELD%Iyw%72d-N{18Ln)wB5QQiwl0Bq($&Z`851Ajsrkdi{%4NJC5%8e^YqRoHVi z$Oa5R7zqvZ&q9udASP+}Tv7dtO(qA@s|cUM!#tdZ-dF+;({L6zgtc=ZCeT2u8%|O^ zh=~fB&MZAiMKvpD>8<;}!YhtPb{-v%4uP0NH=0eN=Rn2=3hjAdevz)jr#P0s)=@9n z6UbiOH)j7BViFCsUGy-|*g&`dweYYUFB9kvqJ61?Zp3zY zs*V-h*)TJ3;_8LsrT2D>pOyzPg$CLvdJQWLG1O(KgO^DIu`DewikEbSn8S9lR(!wf zKupt*r@Z z^*BU*2W#P>n*Gp2RxElLBmOg@bu;v_tAF?d!FWB{irzv)oay*Bj)a)X z|HgdfrNt~4p&GZK4WuPdU_|n98{#+-Vmf_s2zle1`9-7qcZa}u0sjNG_%G>%KN?By R{$v0E002ovPDHLkV1kjr11A6g diff --git a/route/static/images/ico-ts-score3.png b/route/static/images/ico-ts-score3.png deleted file mode 100755 index 43cf9600f8461adc021672336eb2f404d8349764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2119 zcmV-N2)Or&P)DP*b$5?I>Ke|IY?8-wclUFbdi&g6&RwWMa+H`76hO|(}iNtpdx@~G@=ft@D^^x=P}$26x%5L z059V#8sRsD@ZeS~gadX+D}J4{Vuu3@aVtEn!ZqRyUck373}zto!C7k#g#3+t|TKgKDsDuVT_Tz^boDPA; zV<{>Sga!{T#YhN{s6YTl;!=2^A%KIp8Do26pkcTchY^4VCmav}$rH%P0jD9<0bGi~ zy(Q2E*a*KNj1>YTdmsQ-LnuGq#q3lM^b0g11P>gLIP&WxjvVknLmd{SVxalh4i%NS z1=0h4p_3l45LHm|A!epRpuSjwI%p`vNJuX=5@pa(gKLvHP+vTP0IIMMk|u6J75un0 z83T>OThQQz9g=43D2Iw?F)CRCWnecnY{5uKni`2MP_Z4O5*;WL70?hw6LKMGDi=)% zLc>9fO;n(nI0g-mpa~&(Q3OddMerhoCO8qoLComsKx1(j8k`X1!V3*PTnI@M7s3Y( zUgSdHgob?>+f#vtV;@3rLJW%FLm0j^Ig?`josE&UV{p6!4aW!2;EeU#gpl#Ksh7%y z7oiyU8pcSkVsJbIJ&Yi>wDbPB8O4xZsu<1foOib(h+E?o=yLc`j*;#B?J7=#XA+8$ z+s*N1?$Ai7S&88unvEdjj1Ezj^lbC?{I*!D2p7HF_TObKO&hPRA?s?7N(sc1MI4K&ky_8{qa(16L2jcX9V23&@T z5K}@-#HH8(KfW2+c_&nZChW!CD8dj(T$qNS2zO&InxGo|N%-SgsCWxLs5%WnoPrxu zA#E^~Di+AOSl20iA#?9l`sCcnv5Y0$IE5fIs5&3sh6i)Z6%9CmJCKLLutUXb!XNvf#dx)E5tbqY(%UhJ z78}4eOF&x7C{rvDaF*~yXNi)5=qKENh+h%q8SKJ#wjX7AgKe!cMIXzhSQO(`YZY4} zD@PE5q#+BWBM3Lkwnmwq<5Fg3xsLH zn}Oee_gH@w6$m2;mIf}k3!(V10gn^q3Tvq{6=KA(7D&5s(%?*FC4g`*our4eoQ&y& ziQFAs$-c`O#CzDc9?!vxa#TY_7=DCfA`WV8uEL!}`X&GV3n#nsheHTrfp8XCAe}-W zR7X~VsU9f9eetL)cOH{-K`z_pV+C%-AMgY=;Sh1^Z9w=OLI}6RA>6@P_BETU=scV2 zp3LvQnAKfig=A#$}b{UuSjaJw>2O_SK-W9fRG7omh`m#CwS|;skr?t-v2c98$#F*tDx zK7mdkaW0|&2nZbaqwlYy$ zOA4cddK?>Y3^fQNgtsvZq8p7b;;-;PGsLOHJGh^HU$L%H#zT6N-sDh>2dYVLI&mIA z9qSsx_z*w9EcRc52lY88#QLTzo_w^iA4~BLYTKAw{)1&q{PS!j9~%fs1mgqM523=I zVpnLtX*JK(;E!z_Q;jg1*sjteR3eNP_BF8Ffd}vfOuzt00@)G7tHviw9gyT#n91GF zA_n%OHYWX-Fbxx!Xg>*eG-6baj(<-i*oz#ql;uqQndLzcFzH`pfh2<7B7lb?8}}f9 zg^;AS&cPrS-5BFixfU#M;Uw-#!z1TjX-?6eSUsMN!|d65oeSIl1%g#^_UR-i+_X=B*pXr z%i=6sN$~>alP`0Y+{@W=iv8>Hbw~=*a2yid@qTZ4+f~NndHroA1-Z;EnHU6_u{OuT x85AK&j|R~2ax_qqG9@yQ$qZx$GMRx){{yC7T6R>r3FH6(002ovPDHLkV1l5Vzuo`< diff --git a/route/static/images/index.html b/route/static/images/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/images/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/images/logo.png b/route/static/images/logo.png deleted file mode 100755 index 4d96d6b6e76c2294a7e9b38fcae6bd13310d2810..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1939 zcmV;E2WP)b+qP}LI@`8wbm(Td#JU&AN zU-Kd*72DAs{UfDCx$$F~avpO1l+IixmJ^&dPIAaN#R<-E+I$VLlR7BdBz%;YVl3w! zVpzi?EH(~vhUMH((NGST`H7$T)%J@Szw;m4+0S`&XchT_EGUPQz z8d|liCbXa_O=u{icuoq7k`bKaGH{7i=6z);?jV-+?58ctowE*=tl{{bSwD~$<-sgx zeiZe*%=hPzuLyF23$k~8O!3)l;NNz)UFvpo6LwzsvgIQ_B%`~N%(S)bajHU@@NttmgHBpYSMC#I7 zF^RoIN>68L&qvrI6N81k=5!~A%~oKW3I%~F>c}~e&k!e;d8d3`JIpWl#lt2H>nnuQ(baPLs1lyaf~v? zFw~#NucVS>OMT9eE0oiKS9zN^j2BshHc`v@jQ4oWe650VOu$Dmls^k&xPvK7woPH4 zbQUGbYfcxU(Z)q?y%IStqY)~gMmJ9f90|l#e?hqlg8xF%We{ zmQHc1P=>OUF$!`QnzrO6A0y=jDN7M@qpbQeS}HjEvKpLbJXy$LWFsq2uqBWyVa|H; z8u`gbUh+5!Q^9J->%=>A7Nf36PA1+Uf&&~h_OpjeXpXX-eQacfd49|UsxVd9%Q@Dt z$;?>KS}yTRAZNmyC|2+vt6AlojI1<|w{Z|(=p=MG*{@hmJ0>!g@x}xuF`S;}(KMzo zmFdi64lPk(s&Pm@WF#|;=}f_FMWLnRL99#_<()exqe)J=CE-?Vm9=h*&J^u22Yh_Y zMANT4@s@K1gmSQm;1ZV|k%kYyE9)n!qCC0gIlJUGX)PV3y>v7>n9)W0GmR(smIySU zjcruDF>+R+l98QOtVIK$<^4dmE4;F0BEMD4Di+M0pCiG~eGhFsfeKRxZ8XMvP9Q7)I5T%k-Ycwr&4C;EHRH7aRSXlX~TS4yz3@XnBh>I624Bo*ZNuB~TtMMB9Qi3fq6hhj#X^ zV^=UcAE7xUU3{5+$}p^2{9>oq3tQQq3k#ka`rz!G6&&UWM>%G~PH>9ToH5RFj`N)1 zFx&Z^x0z;Du#|V)jdnCgh4ex@PE#Ef&MM4@XybbxlDTpRZ*vaK4sLU8J4;L>nnD%B2YX=|O(f Z{{U1^9{XkihzkG!002ovPDHLkV1f~NmxTZT diff --git a/route/static/images/move.png b/route/static/images/move.png deleted file mode 100755 index 85763763ef0642efd96511e4647bae1576ad5b47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^av;pe3?v!<**pYNYymzYuK)l4PfJVddzZ5SBvBIN y7tG-B>_!@pBk1Yk7*fHQtiXKbzhYF64+BGlJWG*9PS6CPB!j1`pUXO@geCwh+8eb1 diff --git a/route/static/images/right.png b/route/static/images/right.png deleted file mode 100755 index 86e3aa594c11e3be98e6d492a33b0358112a4ea2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 396 zcmV;70dxL|P)~mCAdVmw9RI$K9rh0`1u&}ay<&QDu zcE;`DgE4czAOD@XsPB8PvxhWBhPlWPhy2zTTA)UU220VPlf7c7Y*qLS8WnORjx&ku zRnVAu3pg}Ii53f}fbl6g+oHsDsG!6rQb(AtD3#xq&H@=r>kappwC1?X52~?{jkU?R zG-9)$Kn1md0wr06Sx_S%5qgMw9LPFH{a+yC>5!}$egus>DB}uuh$DHG&B*XuOsu^I zStL(?X>D#{;t4;-7j9f<<;Ble6`bQ0d!F!X`(oGy&JnTR#S7l-64od9#+5VNF(T(j zX1#-FykkE+-1^8v*HlWL@PSkO;BqM3yr$lVCVSg>#4j$O!XH}TUxk$X%?G5Jl%j&B qg&iG=Yx-9>lvkMpQt8vY*#ZDhT8!vo{i4bM0000g9CMJdp#qxQy!;P#trpwXjj(+z0`(aLr+LGq`6 zZqncglWGmL@y!+QwBaL+)4vb_SSja5Y4~yDc3lf-W1Gw0K5P5d7!ocZA^3GAK+R$+3tRElzSiqZ`<(x`!A{*31A1W9tC9`R zGgf~&vg%;t^c792cL2c_pt|s04u%Zw4J2fc08KH3E>KMa$O;CPVFUx96_7wC2DPbI z%FoS153;F&VyiEV%Jk*c3(_74&t|JX%4v9igXafYlxl6PdLjNk2m4zOuzBkwKB$axg@=GUx!+aIm;vGL87w?=3kdo)Xm9!(PbX;HqOirm}mB z*GY{6#RGLA1ghs*Rc3WA{KPo@B2>Z~0ZHsyH;kOx7g>FJZUOXF?9^7EDq?^Wpc;WX zxL{ze<2l_iWDO(*m1jBifJ$^_wQCkAr|xW=KC`gLNf*QL$H;|e%&|sq2$y(6elQ&haqH; z2yH@`o>viy0~oZ2v2ESgq@ z9)TLdK+OLPB!ZFTw&k~L1bfAYKNzbRguQhd*!A3$i}%u#m1jmVMOJ^RVo=ZFG<+fu zJ9Sm=tM}tR1!rvP52mkPfy#0-gr)@~8Q0oooVo{&{Sut*w>G7o2UKRB;4QDq_ly1tyc~A6B2*>pp^X2|$I}6&RvhT1l8tds8lbGta>T}brGOx*tITUTyP`; z_@^1ysooCcL!Non_;!wyA7)$;l%D9hRupJ%5u&@?91S3!{82x1y-X55>3 z^i2oP*bB&@TnD;kl|T$YvdR@oR$nf8AESx3XH!oyP!-SV7bs5kb88In8SM0ZfuzMt zC$4oU_WusDWpp+Ie<`AZ-@&k7Y!nNkiG)7i?OVb#}>RV9aEAp1XW#evE#7W-Bx z8{TDcI`gj>?+04c4+6Ft>r>%ZYxPL}74hc?&4oOsy8-Z&Kqa0H>t4}&+FVPg>p-$72p5yEsByWoz-skdX>JB)Q9EYC4cZ3ikN z-fU5zidN4%xocUY25UfxtdwO$c&R4YOm~2xE1s8DAW-*)xaP&--A}4nVa|q=AX_nM z_y>gc>Oj8g1WJ_iwwN}%%Mhq|)-@bY*Aamnir3965h%$&Q2iYjHCFz7WsSKoW2a7F ztF{37$_GkJWh7)`K=w{KLyOmWpja+Q$|-Q&1PaJFeLD>1-`7K+Oscp$)-{>?K6YOnv5davm%fk7@f@;$|}!sf!(f$J0Vg|BBy`MR^JwARPba?Tj7%!s~%{a{`HCnDn+1D^5boxTFl+~!fZ!#EwlyYs)^1P5P0 zAby6wCu2eMJdgiD;XkNGKR`(L>#)x zJ9RaL>R!eL&x}(264-}hJ~4*vk03a2!v5#r*e6Hn_CN-F>GrDXa~^T|ChoY?9{ywj=k8vwXu3DP?fp4{{l$8D>Iik RXFmV{002ovPDHLkV1h(z21x(_ diff --git a/route/static/img/Detailsbg.png b/route/static/img/Detailsbg.png deleted file mode 100755 index 28f5277d941061a10f57f0017eaa1e78dc182ce8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~i!3HGN^yhQ|DN|1u$B+ufWQ)TG9xxqw-|)kJ zO4(0;ri6xUw+H1*q&~>Y+E;#RiSXIUt-;Ui^>GE`VzveYhCH72_=~saA44$rjF6*2UngDbD8%zKI diff --git a/route/static/img/account.png b/route/static/img/account.png deleted file mode 100755 index 8755ebff16f9bf0ca75bb486e3777b6ec84a1ddb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 414 zcmV;P0b%}$P)C*Qj8X*yZ|9A78|1%;7*EUJA;xdQcA4P{XXT^ zzw4gsu-@~l&vH87;XLOo$EB2#Lg?cnoKb;K-YX+7X0TezD(v9VGC#v0wl=~sJTgq; zY9m}BEW>x4Y=lz;Wa!2cdTkU@zA2$64@ibu3VI8~#PtZ~I3OxRDKDP&h&}X7B;c|N z4aAYd2m_??2G?()4^3>aKo?ciFocOU3NrL#0=uij&|w45E=%~*b~=2C6FQ+2I-wIf zp%XgczX(4*RS11tgflAe$$Mqw#SF?q%PQcqFo~;;aD}i8-*K`L zP7#oy8%rp(QAGKsgq}E{yTeIAVS$*q9>E+3M6KRqUOej&dnlMl{Ph7Ch$Dv)21w)n z_u&@!(8LA{bWueOLzq~j@Ez*Nj|r4bbr?EqKs_m|L-m5)7mNG>Ida{3@c;k-07*qo IM6N<$f@!X>*#H0l diff --git a/route/static/img/alipay_zz.png b/route/static/img/alipay_zz.png deleted file mode 100644 index 98d99a533275efa448841105b1d36f5de459dc94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7307 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 diff --git a/route/static/img/ico-close.png b/route/static/img/ico-close.png deleted file mode 100755 index d5b8a22c979644c0d3cd4fd8314561c64283bda3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260 zcmV+f0sH=mP)kdg0002XNklhfas7}|wg}pK#&;Yes5HbQ+d<{Ml>Yi8##ci&b@Zw(|@+HND=O(SpgyPO&hG(YJ zNEv{~TOsj*)MlWQ!U-3OL?{}GCoXFh)ANd+*ljNZIw9x?-0()+-ity4S3H)5*eO2v zS1-&Jzqqwxj$0{~abtxVH&jsR*G4KTwV<>DlrqYB=luHSJ&4Y+_xeXBEFp#s wJV12vf6To(7O~(4B@}RhGiL!M_g44r1Lo+QdwA4rX8-^I07*qoM6N<$f|rqFssI20 diff --git a/route/static/img/ico-copy.png b/route/static/img/ico-copy.png deleted file mode 100755 index adede38d2a59e136e93dcee7cf7370b51311e9dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 281 zcmV+!0p|XRP)LoZ${Q{jEoG7(*1ZY;w0>#s;hz42`E&T; rpSuG^wa#8?aWXWBmpV7Sbp-=Md1XYeslY3cw-`KK{an^LB{Ts5^hjVH diff --git a/route/static/img/ico-success.png b/route/static/img/ico-success.png deleted file mode 100755 index ba677b2c3b3471674c687b6c6005da8a1aad1584..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 585 zcmeAS@N?(olHy`uVBq!ia0vp^S|H593?x6vT4n;N$pJnguK)l4=i1H6v73!+4;vSd z1S5{!91t#uv6~G@a_;5iLK1{4VB5*YxtEoD4>!klu*#im96LF$`W|1Uk>KB*-tA!MJ?p?z``1-u-Vp z^PO>r@hX?yyIquw1(iBNU5v}i&xD@o5KI$PIup7oO;Au!$=LX-!~s(V21XN47srqa z#wk}=%MKd|uv{?o_+)bC%$c44pPtGSZG33|O?rJrBF7;Ov7jysF6I=D5GUV7H7~b! zEm;_~^58?kyB;AiB4))-e5R6 zNBPly;Ra)VXLnyCr;NDM+;cqJjveeXVTCG~G)@UpN5DdeUH}Z@|BcxR7 zAV{y(8gx3N!2n5Qvh?dZjRsy^H0bqHzZEUM>Y2(fFla6DI(X`>D4HfozPxNOXcrf! zOUjfjt(v(xcws@W*Xw3y#ngl`=6Rz|C$HqqH8ko~3*xxbqjaW0tu{cAW_}*5sRMbx zgVnW`Q}px7xkvoz(sCoL9Xpda-}G!e@gh7iscd~YnVT=U$kB*L7dpCRnOV}y8Pe3W zrM`YxrD`XJcZHvtD63pqQiJ?PQTjFY;7~s^QPI-6(A6!yRrI#zJ}i+M6kCc;VxJ(K1Z;kin{WSODcY?3fd5fz-DaVE z-x~3CLn#1Qxdt6QOyVHqeQgLoKS%4sXc-^=mTbK?{=N=it~^ebj-;?SF|;Od7Q%>& zi@OW$Jo0_yLqFqs9FJ}NTY*QX+43CXbnZ95d>43s!(vmMw|zV9-GIp>r~RBguN}9( zu({RjcE`t=?y#%!MHfDwXo8OgZ<1}3Y-Ut>=brg)udDb^+uSTcu7{J5?o$+8J#seO z8()a<>b>WC+v}s`XfGFdw^PEYAQBSg6Y2v*Ht~9Wo3hF|>Q3i2r=ngGf*YQE-XGP% z6i!kon;v{s`Y@uAh?Mks22$EiC)yG@`}Q^9qq)bi0x`DUSOO}D*(O=N*c3b!i*vP_ z8Das)jaxcfpJ_z`yU@0~!`4+9*Ov#?#zVE4jVtkX6JwoMZC;X>ZGzr$tGadttSFQ6CiUeT%GZ%)+{iXQag@mV94veavEIL2s{p#<`!eg@=s!^K9DLXrA z+SaUYeLWsv$oROz#z8nzrFMB3 zn4wK{zO?R8lIrGwdoqFXIlU6v=%sL~2zd@vAYzD^iXJv<-lhD~H;x>9zsvq7rR?hu z&UZy1z3y+IMnAXyfj3lJ4@QQta=40OTj04Hj>+}j6wBeH?!6sAOBD%-RspD+_t0E7 zbf`P`?zKI@5#g>qv5vO)kXRdsmci8w@{SXsIjX1xQJIf05-1G*V>_2vtxl1qhWBn` zmH1`~m4P%|4w!YTdC-;QGUGWOFv|0olGBm0IuYH^Z6lT<5ajb+oeG*%h{!LJR;M1} z-59tCY9>V5w|eLx+a}0JFhI7oGZnKDR+94!%WVF97NQ6^oG^A?(C<6Gc7Tw!)IxjNRlM7IkTYu z?LQPu)+;u0_YRPMq3Sa}R>1s3WePd>=F>{@_B)&3uY2)V-tdFsywkA+&xBcUwhB{1 k4MY1QcJ6+~K=T1$z?a6r#DCvx`B5OqpK$cik=P&q1w_CDdjJ3c diff --git a/route/static/img/ico/ico-apk.png b/route/static/img/ico/ico-apk.png deleted file mode 100755 index 27880ca2be0507cc054dfb51fb14ebf3d31ddc4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1010 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPVEzz^EMH6XN>+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|#V2RKe*1d%;fZU{F73LpWzUVR>n|;T^!CB0ub&p3 zp85Fk(#YW4tLh=D6HREJo8xprpv3Ao|$v+)$PxpKfizf{=)~L4Ie*! z`tAS}>j`dAD+VkM`-36znx9l!I^YHlbdxtvr)%6@`oOr0M z|6ohU-kS2Q8P&UTW}oi=^yA~B5BD#$47SG-aPqO`>i+E zR$pG&eW-5n`5ATF3#RVxuH2qAWq;SQqw|1d{f@$ue_D%yp_W(@T2%L;8RlKtEzRG7|;7iiI1)6RObY#Bd#(k4^QmBnBh~pqp(}4%wVF=(ua|1 zvrmh6Y)V=gqBmW8s@K-2RQBx3AMtw=6+Sh#s%f15arIIB`V(AkQ4G14mPyE#?Q)4+ z@3T|Mcm`{OMkmuB>2K^=AK!kJaD2}#HAzvUO?gq)pD$H=4V5hzYfBdi#>;72m@-%! zFFI!^*DuSVAs{NdMnS^0f2qEtySP9fWQE?$w;me^=!HQuzhS_imrn=HHZN=qPM*(7m#x@q&SX zOX7^zrN7-?fBZe4N$ud=$UXIU_T9*52)}B}=ACNKvd(d1f{Tu;;B2O?tfSpTy}wd{TT(%W^0*s7ydTSf7InGWpd^% z*XOt97GkYxjvsT+Y~0I#Z2J*CcDZTX=6d{&g_C$B;`OZ@oeBVDT96BJ_dvJ4Z9eDLs4|$Ur<1hE=eR3wOYNqyUXM8bWy9- z!U!S~iC`F(%jF7%?h8^Xl~TECbY6CEL{i=lQagp@W?)GKRStp@8B+JFIHQ&S?5}P9 zOXd2u3PF&CRVb@n@O_(rIkyR`G!u(kX%B#=-z0LSS^=w7D%BuMIy^1CJtV4sES?7B z8jXg*-mDk^#hVDFh2OxGbpN@bL^MB+uF)Q?a3#v-v9*EcQk7Z*O5w8J)s+o+oFjW7 zRCGLDe>}U%SyBAHu*KrZb325a3e^;MJNuy^riv3^$1CX*D^!2;*EKCqq`94|-BYrc z@}2ZnA$@i`rHMbj2u%U-ZTecn$T|m9CDd@@YqY|3QP#5|4%GMMuR7mzS3T0N`@T7&?W?aixR{W`;jc)0?*`y7mH( zjCfvfZ*YQ%&4hpsfn(z$JQuk8E}^48GIgxya!=nER$y*Vih);D3dOREm(W>C z7&%L100Y>QE^0^H-YZsMEZdM3X>LE8ctti@NbI4Lu!lCrBO0|I#qHqx@J=+P z+9K1=*J7RvD=Kyr=aYmJ-PL|_!2fN?V2zkjP=!!z6D`jTU*9fPlZOAmeq!5O6JiJs zUGuqHoHBCEvBbNdj~j%&c8Vq(1n{d2Q+5-NZm}k8#@u-2@!?HyK&wh6`xKezpCAv< zLcTC&e?5sB^S!wwqcQgI4wA4EJ2N&x$_Ky(W!Vy9kd!;TpZQfe)itSBW zJ;SM_kCz|nXX$*vdAik|T3ANIzmXS77{^ml$7BniBo?Xibkf1Dz@Rc4*@*`QsK?h= z4SyP({qAGQ4Uk209L0U*eEf&Qr#zut08cyaq($EhOFCG1{tPW<@=|%h{SJ|S?>p|E sh|xEV31-vtWX#faw%HbM7wXT(8gw6xue|g;L^pp-FfKH(JAg#}2clZ8g#Z8m diff --git a/route/static/img/ico/ico-bmp.png b/route/static/img/ico/ico-bmp.png deleted file mode 100755 index 83f1278290b6917f3937d1100de6441903284248..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1027 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPVEzz-Sua6XN>+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|$kD` z$fHY9+gf6_w|@Ql_1%|`C!SnA^zg#>@83%fP7m5#ci`dqoP86QUEA~F>!%gh_g;B< zZ{DSCMF*x<9iIK(ufy&u%Qb zvh&l|FSlMldiCjD#MY*L56+eynh~<4zF_~98?PTceE0m?tNVHTCik6Lwczshj6Ho@ z?i`zSar2)0XSUrrzUB5Y|BW?qJK9tC_U0d$vijD+k3YY@`1)?*`E}=B-M;?z;o|GN z%a6>u|M5lV>E(MLo!fHn#L;J0W?kOWcw%Ah{z-WUCU+iPlDTg}$I&HoPj2iuvN(VL zWUjqT8-U@MUJ~RN%)r+2n{{BDy|0K_uzs)y`Z!j=0iFvv>hEy;n zN$hNx*eQ8H%QnGMm^D$tuH*C36*so!7XLr})9|u+`i(W^jOSgadR9NX@7R4c?fRUP zA9ugl;#jSA{rw`&Du>+yHyso=H9Dm*x1M0*n#e1nB(LFO7tm3&K)}*r`%7n;1;<~1 z{aI7D-(G-YLxa;3=7)mw+Bsb=d_Ptxaxh><3b%X9k9Vw74`^tLG>80|FV1sJ^Z-ks zkkQf(A^ng0mov4*bn+|Ye5|;0jD_psF>a-`az1IBW-d|c*b-&n^8d7ElgDb7&mj(t zpH3-+G(~DH@`$uE@K>E;kuM=we}YwM>Ovo`pr>A@ZV#hY<}v&USnL^eGi)|vPsxhg zubG=qOwh=Ykx9+8Iaqz!Mv^VPIO;)Y;YxlV-p^a2!p#_>t{iUPR(0U+yd`|Qm9O%w z_sw!$UMzbxX%^%adD{z#K#-T&c>ZtNXKxd%X0eBkocxy?H^9dB;5 z5p&Qy(5djANaaNiy*q(iG zzU`F*oLZ~$H$ItjWWtdZL50oEj!p+1F5meRX;i=(VG$H~@TBj7Ij{IzG>>0haNN*t zLemqc4Vi_Zxy5W{e?yaQ^QS+sIJ;bX*7A?J+xIdX$_N>m-J0qGOq2|su6{1-oD!M< DduUS? diff --git a/route/static/img/ico/ico-bt.png b/route/static/img/ico/ico-bt.png deleted file mode 100755 index 387293f80940ddd49241d107cfdbf530f66e212a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1166 zcmcIjdoUXY98N^cD{7;}tHnBFdTMQrP1m7urt_*fQ|;E$m38y%Qo+0;h#2ZshamA7 zrKPl*(jZBbAZ_MRkEqNmi6BDWkGfeF|Lv}S@4N5&e)rvde|>j9Ojt+&#L(Ih002M& zzlMctZ`h&qL0TW0(!l`$Kv-B%guh0kng0*O5{Wi#Y-}i%%KiO)Hk+-DYPDJ>mvgyX znM@`UiNs=UOCps@<#IWdGm}BXCX;Ir)CPq@!Q?N_ZZC@!Qk6=TMQ?(?s8gy`EpvmZ zlzXFVlaH3DGNrtL(S~`|nMrS45UdWaFo>(K#&~mgsP#iDFWExw!#9+5(N@R8@NG(M z*WzgV0+BD-L5{aH&k__WCHD2ej!aagGsU(>y`)R~g4Qs?nM6*suy)rgr+Vr9xq+35hS`BcQdJVU8bfctHCjj6X4+?-vaasc$!+Yc2qI^NmOc z#zKekDlt~%Prg6lWdgbcT{){VqW%)s;r?q;{a$XZ&*$xZXBEjzTwj z!(O~{a!*7ag{;P=g51`6iyQEV?0$#V_}m+g5O-7KBEOatFIx=EoLqTd-_c#JbJ4!l zVO?hj+3&S!e%GpX)1RfYnaKk>c?&aUw$`KVGYaa<1M6Z}uUMX||IH%K8;wSLXX1;! zpWBB#^=*vK6Cx}?BYc$CQ^1VKR-9&;7X?1Xus%nEx|reNw+x#5S@RzJJkOe>U9$HI z_4n8MHAy0MO3Zubn`H)2S0{DQDA|O@b;VgCLwPm{z9%@tKtA(y)o=;r7)(&QZDdc) zdGmgH`NKZ%1^&rk+^`kiT;_PWgao zXU;HwEXu`$IijPC+-XoMJS9aJG5tqb5)po9!`<`(5Kc`0DCj~txs>vcM#U63GclyJ zAG{v-qCco3$5)Yc+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|ra#TOsHfB$~#_2;4k4_|!zYP$S-;O09Sdmh|*^LfJA z=N2n&*ss31^y;gb7hX&`|9sE=cU~KAAAS5je#gE0Z@(OV^xl8d9iNT2_dR%*zyG1t z${VvUzWDs@d*aUf)6c(n`0mTf>#tvZ`WC$9&bj9w-+%cQw&iZy$*0@zzI9x4v-ap? z=e4(Py!yQ1)|>icPuAUhleYUo<>ANGM;`Z{d3OBChs!TNJ^k=CW!L=$mtW4i^fGGO zJ;OzpQg%N)_Uy~+Z@(8_d+W3DPT|4FYj3~5^!nTVk3Tz4zlhj&ulCqe+hv#1HeEJa zawTQc<>uX&fu#MiOH3Q?YyyU9c1e(5Faul5lmFA&fnoap6^Nh(_4BxoUZ*4#TDi zGBX8wrk@w%3jK8aoROl0f4r91sy+XMPK9tBJAAxl^UtJ}_g)sAlNDTdO?v5p8*gu| zT>Yh`QG@G1EW`S%-Me@1w!TtWe&avWFU6T0R=j-QQ+0kOr+<>4!2YMyyJ@cDLXOg- zLNB*WJQ?0?-RRG?l#BPuf|+IuuGp;=DrYRMnjm%I1jn+>^6BcEL$LzUAD! zPan)HkC9`)vG>M;(4dtA%W_{&rlkjaBWgfOp_AGmqfu!JGx$hRKsV9jDo- zvroxaIOC+#Jm0x~-W?IecDcV9%Ph}ye=O?QJOZg=8=4mVWYWe#mR~<1)IOJZ+Mu#A<>_C5im(I Nc)I$ztaD0e0s!h=ZrA_- diff --git a/route/static/img/ico/ico-cdr.png b/route/static/img/ico/ico-cdr.png deleted file mode 100755 index 2e16c24e06556aae8e9c67bbead175dc24e4e123..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1035 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPVEzz-Sua6XN>+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|$kD` z$fHY9+gf6_w|@Ql_1%|`C!SnA^zg#>@83%fP7m5#ci`dqoP86QUEA~F>!%gh_g;B< zZ{DSCMF*x<9iIK(ufy&u%Qb zvh&l|FSlMldiCjD#MY*L56+eynh~<4zF_~98?PTceE0m?tNVHTCik6Lwczshj6Ho@ z?i`zSar2)0XSUrrzUB5Y|BW?qJK9tC_U0d$(s*Lw(Pvja{``9G)$NJr*WLg4V)d-(X;1lJj(N45?sD zlGxcWu~YJZmTiKiFl(ZOUB~C6D{gGdE&hM{r{QJu^c!o+8PB^;^{jq&-?95@+Vwdn zKkk09#j#rL`ujzkRSvraZaOG#YII6rZau-qHIY|DNnXRnE})}kfqEu_)`B-u17z@|MW86w><$TgM&0M0?u_emD<^O5TCXdxDpFzY`Dwsocjhl7ImAnJW{pVf5^bYyylFB1&eXTbY|r!M;niY8e1AyF5mOr zCzIv>HgEAx(KZ8thU!D+1`!W_=6~Ps*n36x)!Q{`UlmWz{>=Zc`Tqm^AEpmhS$}sd zd8X}<>}kbsF^Na;){G^bCYGE&iw;;a7)DD-CS7YFD10Hrf4G8w&a>336I18;{mVJW(9+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|ra#TOsHfB$~#_2;4k4_|!zYP$S-;O09Sdmh|*^LfJA z=N2n&*ss31^y;gb7hX&`|9sE=cU~KAAAS5je#gE0Z@(OV^xl8d9iNT2_dR%*zyG1t z${VvUzWDs@d*aUf)6c(n`0mTf>#tvZ`WC$9&bj9w-+%cQw&iZy$*0@zzI9x4v-ap? z=e4(Py!yQ1)|>icPuAUhleYUo<>ANGM;`Z{d3OBChs!TNJ^k=CW!L=$mtW4i^fGGO zJ;OzpI#0j2^!nTDZ@=$<{5kdFtF^b^*B*PCvio7gwtEY&z1{Wj)3Ik?d^X;(U3MvL z(`BP2S5h`zZr*(vNZK#Ed6AB|(0{3~Vh={!eQM2J8PEakt!T97%tKcCA1{MdEU+XIV=RV?Vzw;+!TZ1g`^$Mf4|H2<{$lU!e zf9iq7Z?62;>OJmdn8@#D$D%C%Xd>Um6YU94oDDuHO8gY)so`+bGPyceXkqEOYI&y) z!=?!`GX;94pBLi_{dD}Ck)niuyq4IiJ^zDFg>W1@e7t4z&!m<2UKX8`6Y3U)wx0{X1Ubd-o`2OL$bZUVnALs^Hza8{8^?D;F@AH1J(uv^vn5`m%Au3VY_5 zbryZ%3D!Uux?IvwaJ;}1= zZ2*g1Mcf7(Lxz+!vZrpTDY&f6Wx2t6WYY5;SA~`}NJm_8W54q*!E^Usz6y(ZVqzs$ fa_{a&)-o-4TlM)6=Lue58fEZw^>bP0l+XkK9nW@v diff --git a/route/static/img/ico/ico-cs.png b/route/static/img/ico/ico-cs.png deleted file mode 100755 index 6588319769571ec47bb20c22fbb5830aa4ebd523..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1085 zcmcJN{ZrBh9LK*pQOCm4Yg9TfbLN)0O6SZ|r{yCS5*a%3=G4yX+%0qxnVofbT0V2> znyE{tu6$NzXp#?8919;J6;Syq1R^4!BJ!+QF#2u(z}|PC*XQ1M@1NfHE_N-*N2wx(8am`h)K+~@zrgPTrJtwr40%jIr87x^2C0D z&mZh6*7}BikFlmLc`aja=Uoa?A3st+w=lrGxMZya~)|mL)7WQ=t$M@M> z8ii9Z#IZ26h25}>{oc|nju3~s0@&VPH9s!0G;3b}fd5DD$IX-bnD1S7$`b&H&J~}e{xD^3>@+nqy9oj+ zn%GhSKu41pHOSE~O|AiTsUuiCwJ`pMH}@vfKW!PWYQmAeB4XfY%Z|gqKU3K8?qUXN z_sCwz$KZ{*`D7vunf*|dRhjoi%hZwIoC8>Sf#vm^!rE8~>9#GwX*TgZ^*9k4jpBJG zKvuw)fx#^C)6;Q~{eu@9=t*vmR|1r(GS51CW5kp2kRW*rn1d*I7>ux1SC`CCN?XzG zLtRjL|D96}Sg+F%#wmcIE+F;yi0dbbv-$A5Fd1jS<^j`X49N5EycXu>=hsF@coaGo#Fijt-L4YZNH!sf zQe&fYn3&J{`CxI15D$~N*E$@$99cP5(TKWz@oI+Mq43z=6QYbRN4x_@WCWkJd0@+8 zYA90lrvRZZ2>eOw-vjs6LBPH`ZuX&+(DIPVELr{N8>d^=1mvR#8_+UdASMzOK@LmF F{}+dGkH`Q3 diff --git a/route/static/img/ico/ico-doc.png b/route/static/img/ico/ico-doc.png deleted file mode 100755 index ca27a9666d17fe07a524285e1180f62fe9285dda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1158 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 diff --git a/route/static/img/ico/ico-java.png b/route/static/img/ico/ico-java.png deleted file mode 100755 index 20affd3205723d6dd7a7a3e32271a628f1d8c5ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1417 zcmcgrS634T7!3g;rocfQ1Wv4ZP!WnaQQJ}nqe8TREEUB@P@up;84W^3AP9oYSRnx_ z0c1p`Bw&~t5Qzq4h8aQvTF@7uVL-6iTI7t{$8d zPc6y_)aAMlOW7@~zuzpf1d6D#xkAz+hr_|Na~dfEg+d{bNTgETC7E0!Q7toyjR)cS>#x&@6! zGs~6b64)IhE9_-OTqV7KO0+CiO|YZ|t?U;)+VGo*+XjG22}OG7-L4#Lc( z)zFHGG*k^O6hk|d2yD>}gNpO^@Bx4xbG1cJbi-&@zRCZFEiFZ>{bW3}m0)4q1OVtC zb$7Yo`#>)U(vgDBvLwR;!yKCWqoL&O8;o+YlK~uqJY|JJvq~cA%E`NI2)ur9^H4Hs`!QK`X zt0;n5Op?=GBMP$NtZ@Q>TM2A;I)x?VKZNVW73{3J8AYOSWQjdzah`fgADGb(S9Tbu z+n|~|7924rA-Pwe-3DhdO2=6HKaTD;tz)*5VTZN;DY%Pv+gRk-xLrfZA5DZ9qM8)2t-v~feA?X@eyrOB5sJb81T{-9 zF-7EuIh=%=efH+@JR_We=u3K7>j21}%>GAOrwC$N+Ux_<=DkqlF&O?x7!Fyl+2LLl zd1<)7u|3Ac1AD@o61it%ApZb(WDu1LQPm$q;$@LJEq(!8mJbtT7F%c0251?s2*MFw z3jOkdzkzqjETO)97zFv%h(DagFC-f`5Ydg5z%q%|YunbuXrSTIE_mvA8JZoR8?yOq z@4L0cx7+b4#2a_&NFOU^`}~{}s)ZXCt=|n`YXUDmwPwAD+kF)-4KYORBCm^zWB2tb z8=x~YdFXw8{38Lx7z7d%Ru~v2bEBO}wP9!#dH#~SS^^su8=pghj|B34L3e9Aw#Z^Z zL?fkk^!(4r>GphO*JSHg)jt*Wd4%?JR!*Qit25yU&<6m6d>P!E0x^Q+8WUg!w;_qQ z4nIcicKo{kJ`ii~&DhlVM0JH83>m1lNoV_O_58yeA~qil6>rasMPO^Gh9E`|LG%mg z>DUf)KTa=%XZrCpBZd-aCTlMqCbb1=xTyrrV-c0UkMM91CjjpBT-`(%K-tA|UI69V-w)%;FR diff --git a/route/static/img/ico/ico-jpeg.png b/route/static/img/ico/ico-jpeg.png deleted file mode 100755 index f28f52905241b4ad533f959bafbbe5378f2c4491..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1018 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPVEzz-Sua6XN>+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|$kD` z$fHY9+gf6_w|@Ql_1%|`C!SnA^zg#>@83%fP7m5#ci`dqoP86QUEA~F>!%gh_g;B< zZ{DSCMF*x<9iIK(ufy&u%Qb zvh&l|FSlMldiCjD#MY*L56+eynh~<4zF_~98?PTceE0m?tNVHTCik6Lwczshj6Ho@ z?i`zSar2)0XSUrrzUB5Y|BW?qJK9tC_U0d$^6}@_)wd2_fBUfW^zyxr&XpgTwfOq( z#uE$ge|&ND*_9Vx-<^APd&|8O6VI=kb$Ltf{z-WUCU+iPlDTg}$I&HoPj2iuvN(VL zWH~we$-r<-FA4GsW?*Z1@_$-8FdYBC0ufXOfBzrmBVg} zS+2{AWpBuayZp|%Z?I!gw@J$*Rjd7n3_Q$h&RAHm7*|YZR*rJC@mQ#_rE%r*J>Px) z=1!LrP-tml;3+?p7Psy7Kl2mY{=R?q>ycAIq5Y3$AiCc0<=HKToHtwooB}>cH)_}{ z8RmIk<5+#LLdz@2YOU=PY}j@#brgf-dXj zt&fxfuCLPeS)Dg?NpPZ~#|Ckq`)O3#%^}2LPm#&*7CoJr{Ab#L~YPK tozu8&v*#9>?W|RG4*41%Y+r45)q5eB`C2ZK2bdNaJYD@<);T3K0RSG7P|yGX diff --git a/route/static/img/ico/ico-jpg.png b/route/static/img/ico/ico-jpg.png deleted file mode 100755 index 989551b668646b76e9a7532f97de30b330330a91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1007 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPVEzz-ScU6XN>+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|$kD` z$fHY9+gf6_w|@Ql_1%|`C!SnA^zg#>@83%fP7m5#ci`dqoP86QUEA~F>!%gh_g;B< zZ{DSCMF*x<9iIK(ufy&u%Qb zvh&l|FSlMldiCjD#MY*L56+eynh~<4zF_~98?PTceE0m?tNVHTCik6Lwczshj6Ho@ z?i`zSar2)0XSUrrzUB5Y|BW?qJK9tC_U0d$a_-gbiRagS{Q338*LRDr@4o)_;oe8* z%8$%yJh5=~t%F^eRlU2$VuZt?%qKMgONr{7pp&UoH+s%Q1H`;OgL)2`1s`EmD) zEsoV{*WWMVta8{baMM9?Q=?M~bL$Bmb^#qV3j{13w!d_iS#bRI*Pk_Y z`|SldHZ(XrVSXq$ubtE7!uMm9A_oIzq;R{p{CLMY^?-(^NOQ=a`QkjsL=Uh83K=c! z5Yqp+e>qc2OeeoW&c}*7$5^;79^+P8E9aB8Y3358jxA9JF8@z!HhHXO`5fZV`012F zNK>TNB9BNr1Ao;i7Woo_^(R=BrY`j13VP~g>h>^dWgf$yfW@9cH^XK#_LQu+{hGP? z!~~5T8JW~fn}gMtZ6w*!i=!Td7Ov#?;r+ZND%^}A>dN8vZB+;E&RfE_Tlp%_dfzP9 z<;Ai$WW!y4=iE2gv8dam<&mn@{zC>H<~3(5ELe;yrZX!?IofzE)Y#Iva`~R`KA9~4 zw|R@R3mgg%ab14s=aYwdp8r)J>^nGj_d)&%DP{6M8iD9}!~d(ij!`!w7pMq2&s&q? z-o@o`IlNitexsw>8lJ6heI3}l7$ei>1GQy)NsAw}SDLUkG0Zra=ZzS3j3^P6x1G z#z4lHZph(a(s0QgDHO`MifzG`w$PSuFM81Xpc{7OxBUZq^1h!pPx2;j^5n%Q#KW8& zJsbf5a7MtR6Rk60za8wX99^;f4FGJ?6JnDuSS*&c|45})Tb)oSG#CtKvzg6iTdm1t z(&}^|2x_%jrBbOGgWG-Wb}wev!a37)`G@3TP+e(V__RnopIejYcC& zrp+hvC2IW_FBn$_I9U@sj$B)}EDrvI8UE*b$C|YHjig~kJi*1EG?%Jh*m`eH zAOEZNEOVB5r=`7Nkslj!eed2$ss-rXzp@5x!;u)`Hn`spR~VHB{p1%)ml= z-dzKGv&Y}%HWJ(9s}}#7xtJj2V;E${=fw1nPHK1s);B7&-rH;5^)b2%=6`PO>EYHQ z%Kek)tAeKMj*YrjH?6okrfB9U5btA`)BWAMCoG8z$qr=F9ByFuX4mF+KE>0G@b*Lp zkr~9Vt-lQ)I8`-u^k;>tcnHJJ;#dN-uN)OQ-8Vl$w3nuCGwq>i_9D$e@XV73pOc}c z6hiXx#HbBZLiU5*v}#oE`uW!7UY9X4Lxu{}xz&Ej^hAa>Lk4lp2V=#kYIk%Q9e)W^ z>sQb?V?@V5nE@GZ8cy#m#Q2lP;vlI#4MJLO;9+VHHK2a#>6x}L7YGst#m;2=ZB2^1 zKU7O`KJ&b9E5HvP0+=8`mIZSp2N)6Ad#xEZP~lU z^Hvf88xmdahOkcyyn6OgLd{upQ#hP)aZ&pz&C*eF!w(@uRu!IxH09fo8Zdx@R_<1s z0aPH3rjIkcdCI{ML-@*0JDB3(1-X%T4MB9zncA=FXSkN Rx>#QqKwOBACY;YG{TG8Wrj!5x diff --git a/route/static/img/ico/ico-ltr.png b/route/static/img/ico/ico-ltr.png deleted file mode 100755 index dc6d48718d317609272ccfb2506d33321ad1f559..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233 zcmVH*!WKYKfI7q03HA!9OC+FWb%6*01W}*^#6pT9bf@VX9)SMO8vFv^qcv|P zYnsmn(-1xwy+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|ra#TOsHfB$~#_2;4k4_|!zYP$S-;O09Sdmh|*^LfJA z=N2n&*ss31^y;gb7hX&`|9sE=cU~KAAAS5je#gE0Z@(OV^xl8d9iNT2_dR%*zyG1t z${VvUzWDs@d*aUf)6c(n`0mTf>#tvZ`WC$9&bj9w-+%cQw&iZy$*0@zzI9x4v-ap? z=e4(Py!yQ1)|>icPuAUhleYUo<>ANGM;`Z{d3OBChs!TNJ^k=CW!L=$mtW4i^fGGO zJ;OzpQg%Ol{q6Vtk3S0!KECw&Tj%K)wa1?NY`nAf_WNBAKTW;(>e#a{3$MM6*mlo$ z*`>5imyMQON!fI{dG}=?X}|1Jq7Zu#Fl2K}g8YIR*jk?apVkfx+5fLV1Z}|IZt^eZ<#(=TFAA23g+g6-I0Sg+Jbq zx%*%K)B}s(T=}ond)&(~k>AaZMOpsQM81nB+7q5Q8+=lf_$km+!{Mf7a&@lI!qRiq z@=hIwO%r5h3iM1rFUA%6>G(M#MG60SEwNR5{s)~3;W&2qc+2LWNh|NYEIKDExbB+t z(gQaPAFi9b%cDTL?GVEq=91_??K2ObIiqPlQ95nc|6O4YZ3~jbkLN3Ivg_tA+_LUv zMf!&)Q&P{L)6XcX+!}RSC=0}Bf zM&Y5S-!?oF-Ltvp`17XA=XtB{tje=vxv`$_K1 zqL+sYPIU|Jij7GKFC-bGu6x4g zk|%pB=zrC8*v772sZ#Xkxk1DfJ=Y^&kH|??@pY%qc|Kw8+@HQD9~@rvNqO}X_Lm81 z)s>z8CN~SF#~1B2n}2?3@RwFy9s3iHwD+CtzIE{Z^SW9`F}92b5c=`_;G|~(%@-u< z8FYjnPFrU0!Kx5CYwg6U)-#K?@msvU8qmP7D&@VP8RMNvU3m%J-_~>K-aW8M!RW2u z)TYfBxVn;GPbv!CFIcvGS>FPS_=T&qBUF_Zs#kc=x*`0=G5JeKLj8iX(mbILGHf(6 hJ5E-7d35ubzfwd?+oOVM#=u0%;OXk;vd$@?2>^R=gggKM diff --git a/route/static/img/ico/ico-mkv.png b/route/static/img/ico/ico-mkv.png deleted file mode 100755 index 00abc0be98406277e2b9e7c04d705cf8d03d2c85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1247 zcmcJO=~Gh&6vbZviBQ65iEK@+f}v^oo7H=^b}LLGI>CR-ik5yo`UCgEO)z zIk>1ar!^B&MfQu)tkzKmPojq5H&YAg%_H{)1)y51)M(ALOw3pH^M7j@X`K+2_wsJ{ zOqO(WDgFF+^KzX|N9T%*yEtNn>h~6A?OWmfA>pOAvH4|H^Y}b%Y!1@GT3ELLLT!^` zws^TqAicsE8<#A@I$gJLK>=xo=j0dZtUNkXT~NOK{=PVqEKV8B0E+GTi4Vo(RBSyF|Ht5t^@wbBGfA<8T$-!+2fCp*Sc@(Xdx^Uj4yYyR)awewa_?-A z$tV;GkH@3aeG&UQX004)&S{QPm!nqJ2To(rEuq{h$mXhh(0F05L+Q2tYR?TevByhr zOkwGfU1eb&Rq6m~lM6MrTtAP^%{N$F$L@*ssv|#aC2Vd_YMMvlH$~@uUJ>68`?;ae zG39!(0nu}YTknx~vPF~I2%X9UBe5JtcHF;;T9_VmOsPo!Ojd9(6 zmgy5nTpu}Y46(X(S$D=}DCtIf*QUqPN7?5a<76AMNs)2xUQ2Oq-+UX4-0fJ@b?ee+ zlimv_1v$buS24r2KNqbywidCx+})9g3m-x^Vnw*obEp@z)bTXrm4w4z@YPHsqoLtP zMRxsTM8)$F<^f{3-7ju$pO$()|I+%5i;=T-q4`fgd*^7n0OR%*Qii*}{71$(A#cvk zHR0@JkO%%|_jL?aWKr%1_h8Y*HI-BTw=SjIqKA@_AFi){y{3`i?b(@Vr-x5?&DmM` zi-sX%>Itub;P}jT-xI9w)>aMDS#1I%^FB}*vo@6+ZkzSuS;VZhPkzLLBGNwI0CxH) zQ8||9jK!#FEoBA~kQR2VU}MNubWoKzpeJk<+!|Ceb+J!N>>L=ga#Y+sRaQ_mcB5gv0uL*H|a-;=lV%rIZv*f{ zkw~P~YE>!~1X;e+8jVJ#($i-ZFQ}6H6k%}-@8&Dcyh#19Qz%jBp1u|5G;+s2DY+{e zsZy`g>E;CLyw^*)O-t{le9OXnVesZZJC94(~ZY#5S(LVXQYN*Y(JsMfhs zV_}QnN-ejCsjTW*t{{uE8hHaOb@~%9wR*AiHHah5U#MOzX#$^hfW=K9z8bvug+^*= z^YSfh+!?^ORBV1yZfVmj`z-%Aw#_4E^O(cLHiu5!0067{lac6z9GHT&D~pXmlnKJW z3w}P@o1SiW^eEn3bBOR>qt2_nx#^PP?0~jSd|!xka~bg)x6c?xtkj zwCizj%Yz=`y&j_I*Lk&Srn?XMP?+-U+j;v_22VJgf~0{YFC)~I-$Wk?MZLqW4V}9| zyiNT(WV{CD^rHO6+U=b8orR5He0b82f8q_E-8m)Ft8dC`b=mtf5a(E@mF3Gg3d1Q^ z-cEFNV*KVSymXb4Mess%9_f+AYx83v4vxam$?_1h;y5)Ax8ZJwVDpgW(Wvy7v{)xL zrYH>k@z_4*3mTS;4;{gtZ}R`%MfrotW9!SER6gMZoOXC~7e5-iYW1lIQBg15t3owQ z=NRav{=_Ou!UKV;4g+`|hNWO*ue-Da%8j zLERBW&2<8sBbf^%N6Z9NoZ{R~pjNHhLs=`}@AUjT4^{}yQ}Xp4ps@N$tsovY8WW%D aLI!}y$o)k~aj30jBEZS0xJXhsuJ|8|54}GC diff --git a/route/static/img/ico/ico-mp4.png b/route/static/img/ico/ico-mp4.png deleted file mode 100755 index 9dd90f5c46b84d8c31ea34820dc9cb1f67238e1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1147 zcmb_bcTm#@7!43s*yAV!DO76_ET{!bI2l(9DhEvirP|{4K%t<5IfjiCDkBIYR%C-> zQC6tq5E4LH4N0I%3_A?TA|Zj05HfyQB!)Ep*Sr4Pci;Ekz5Cu@@9w*efSR(30Wd0kr7$Kh?un})c#os7wMJB&>k ze@7vg%NN1S zWP(3DE2OSTh>Ic$OUxfkOUHimP`k_$=feycglOYo8sN( zQ9hNUKy|WeUT!e>s={Y%!jd$jg^}7oO>SJh*+osNTfW%1T7se8$5GK3YFZ<8A_F{N zX!G*5IQVxUvzuIf!!1Lbdcg#CD@m; z^J$qVST$~n;+&c~t;NUu7}(G_BkiCUR!g0t0Tzp<7c=jz1g(1RbAQy-hfH~jhlLtx zPo-+^9B?&zSl0%vYy0>==+gbsJP6&F=Xl5TlLCe8R8s%n zc_xGhzbrkqSk!QNfS*$jBH0~ln{GHOW5!aB5lvP|CHvevHtHVCp3W`SmxY9&Sy|&`JFrJ8 z+AH0=EA#Dauesf?F2<99l}hAhCn7jD8*`^S^GAD9>}smK(mBy0XtrGiv>NYs~1-ZaX-5W9lOL8qQKJ>u$ zz}+Rvi0)jKB`G$a60{KDvprz_=2P>pW0|P<4bxbw!^ouR!Vq@BFcU#mkYql+6QEFKgWzf|$n;Nvcy7Z9kXepzpxQe3GqU)$t zia~1-H|q#daZ@RuV23k1q9p-3d+@S%Y@UKx>{)5=V#r$<)O7Foi)b|#xAdD*ibTeC9$_s+^k(dG^$ zkx1s(g!nhBakZ;`AGl&jMqb#Cs-l(mv-u*aKrDqIsC|mlIn8;|!7AxppQiC;GFkTw z_xCpDI#&`?y;9o8CX8)~B(jQujnoFlGFwzh+Gv~PKvEf#BhG4GV{C~BLEbD~P&d5U zOW}eH!MpkGDJnmyj?NW86U#dZZ&w5J!P_sXnJw!AXzwHc)BW<57hM}Gn=--#FS>?a zJ;W&`ZVoO8Curg>$_~hclIqt&ion~&%V}@Gpn}C)#mlL+U`ZR8QVZgW!TBDedS#pU zqOj;|fJWN7q9{Yzrh@ZX{%;&8Miq*2ov8!bK*25ms8oBqqkLmwyan}WDhBa%&FzB2 ztFi8o5V)-^PEmR$dqgC}mq+fMwn@BoZ*{+ym>^3e_kTEKEXm2$kIb$e3~y;KIoQno zYA|Tcs6IAdaSZwoJ1P*z89c8FKJ5MUjFp9j1rmw0w7i}?c@BNQu>L#TUB?^ksx0l@ zZ>PsLsVv#KY2&TVPxSC&Rg9D=B zI<7Gb(|Gl!3*82Gu z@oN6*4s6Zdhot79zhd5HJUqy-kJ%UBjR`-USZEj~%Tyh_Vk8eA8#0p{dN%C4Qi9F3 zTB9T3T{?z%K_1Q|&w1b>yNuy7NoV;*lSv#@gDg<^#S26PchRp>|-@#kmi= zJro#G@R-=-G*RlTSC$d6{Wx}{J*ft~>gIp>Pn^`aGd6WI?60TgV9Y5zB5*;MpOGHa zO4f_xHWwJ^ksnwxe+z4k&Ne~O@*(q-?Zt5?9bKl&bcU0H=Sai{?Ha)mRJwVFr*p{b zejhz5=7}rnz5Pi_SwFS~=Q4^tl+>u=_r5sPR2T{_%B#>RO%sVodA@JYWy)6?Pk?w2 z8_+9|pOmJ{VC_8MjC>^r{n_G@k>pqv2H$qA-*o5J2k2^ap zS~^76#Rn~0O@?A9)M0HnhNv;~fTd<@7p3W97k#29mtj3y`Fu$A!t|tmc*&yX!@Zy5 j(l4qToBfazOjZG+3QuiyoY(VLjs)=bK)Vy%f>Qngf%UtX diff --git a/route/static/img/ico/ico-mpg.png b/route/static/img/ico/ico-mpg.png deleted file mode 100755 index 4cc52b6fe7b11259e0a05f5340197394903c254b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1168 zcmb_bdobGv9RCGD5s%%iqsGwg`cRJ>(+qc=l$Lg!SKDkP3}wnKn_3e!i-vmjC_T?c zjm>k^Du1WBb*5Y&Ik86zi=By#ZtKngj7nUa)2SB&Pg5)Zq^Np=D1R| zT0O+vEu#oUy9#_Wk4zVL|G6Vqs2j(&^19a9VtLc}7UhLRsZt9iP)YB``VKU~+IuMg zJDzV3FYL0`!Dq{_85}9GL$C`fXW9EX?R-oHE3uXXD*rC(6QwusZnp?#xe#q(pCwe1 zN4A?MCA@7lv2$Z$MfOUn?VjCxNZU%TW+m3J^V?YQm8%Ig?7UW19fd_|Wu?@x9DH&8 z`ZiNDBg3Kq<9v~>x=`PyPV!FvZ@ke>>vS^~j5pk|$^!r}Acq77L=#{MwnMoU#dhO2 zF-|l~qX1iX)3(XtdLOT-+B(@=T9)l10mEO`czp6xLTWOX>|ixN=W*8e9K{2RNzbiDNw zPI6~4dgMHIh)aRFx~ZJ)d#~CE4#VW*4>z zPeIyPe5F;EgP+wB8v@I;;N?`oRClGnlpFA&48DaWQH0h;0@%X!t@ar=c04{yr2QdM_VxE#D@+E=FA2T&1y@~1blz!_1TX?ibjdcKo*!8 zoynjs=Dt-_UCDv`s?6Iw95ln2JC`i2jSM{N?Vr9Vp-220AtjQ|CNUaAlN*DZh?HO% z;!Kc-hV;NNt~YJp5|yX}TuQu6`%H>Yha4h7)Q<0W!IMeh^2^{wT$Q1`J#E~&sIdQ} zDgRly`g8U%&(>Q=Z+Nh+{b-Yn5$IZS{V0-}dpv5QNZ6LN;Iz7$+tsJdMwKmyI$S5d z8OeXoDD^ZV?uc(-4xE`5b%bP=2`9Q!@x;6(i_p0x#9{2pQ5+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|occ-g>oU?d$bBUd&nkeA=Q9XDtD=Rd_SY|VNv6k$4_s@w1Cy$!i(^Oy7{H?tcC7uXu;` zpXqPfG=dLIpZO>M^39lq%aZ~w^G=?Vlz7ohrmCyk_2|O}HG!WBKqPikK?ux%a6x3` zg5@?}xi9cH8SpID=z2VZNi9=N^khQZ6j7o4dH#%-Z)M5^NYAsJ&s666{=u#8>p}+> zO2~RXda-q_f~?*gH??^-TrZZKzItn73Wp*O*AC5Xoyy|8LOZNCnDIyO9Y_#p&^PE` zy)yUqwzuE@9gldOIPuH;hg5Wk44!^7a$@)b-~NnakXU(rygHc8Xd z;O(gkY8>~BW}h}zU>EDsoYJM^-S{nu)nV$3(+@knm2bz*N#(oc>Np@H}IjYju7 z8e?}kRm&-`E%U#a_hHUt=E%%*@AU+jojr|iw#sx|xR-r2cy~(S0Rs-!fVM=AX`QWBZ2$JZDz53U^T(F9_rh|AoGUoI4R&r|`ox!bHzw11_RE#4v)bHSr+4%&KFRae z!cUwxit3QVpHp00i_>zopr05>L$+W-In diff --git a/route/static/img/ico/ico-php.png b/route/static/img/ico/ico-php.png deleted file mode 100755 index 7145c652fdac3f15c2ac09c7e4a15d55d79531fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1318 zcmcgr`&W_&0R5CjnB{1#tu$P(9m~>jaxLe!(oC(RIUg;z)J0{srqv{2MEqN$0GEJ#8PCFS9RhajlH$M@kY8U5B@uzSwE_uO;tZ};5Xpg_NMYwXtm z0I=@Z(IX)i4|-1&!lF<4)};Vol^7Io!pCej4}AcgUT;YXg#w1*#l=ODNMwog^Yft5 zD3i%R5S*Kv)9EZNy}@8G8jUl0xKA|8c%i!0JaMCOJmaDC^8L~4^<%f0lg(W9z?e=4 z!VmC%*Hv1}R?DGbS#;vB}*Nv%W zjOtm4!jkn%=SrU`QfZP|1FX~<8(yl4TjkV$W$ZWF#B$MV@f>GlwyIMZNfMm9Bl^2Z zu7j4Q<`#7e^rk$xLU3lOs7vg>7R`z3(iqSvugyn+rqPy(JY#P+APSV2`*3s z*h9k)EiDHc^_fD zH5Nd4R?RZa4_V#?5kD?irRh7!^vClhJ>`SFD*^7`r))@b+V1FsJABh`U(b%sU8Ii( zUgqHB-dMx7O$%sZD$3V6V6vBprG&)9)k9JanizoU1u0I1GXif46-^9(t#bCd;s#Xy z5y_YQhS!&MN?x&A9bA`AAGQ@3_D~ZxUOO0$r@zXI(Kr-e_m6jQY~b_#R;?ILh9Uej zSIV}9NAFrDxr1axH>p;h|GfUAf|d|9(VwdhtFlJlnT_zMxm5nl?%<~#NOEyb z%(5`pjc4|7Wpdvx+2bohSA^757#w7{^%vR%2|KS0AMGT#=H+2GHPK_j(AXp;il~mB z_~!CuqGo59b5un#dMI-FS8)B=eD>-%E3xV`W(#2?MZT3=bf0=W0q&nc5uMjIR{Ej# zcqul!-jGAqZBo9YFD8Sx3crH99bV#xVb^<|io`V>KosY*z2mG{dpxfeB~@=7u5q!g z8{v`dFc}C5z@f#z=pMF}25(|EV3RK8Me^Bs$b5Eg0M1{%8bd4Zuwi9wf%~HMj}dq7 zx?2|sJqY?;oHmVP5LJ-Gtq6^D!esP7OkW=RL^K+SN8^1RxXPGIq_J&KuN(01<6w&Jv- z-Luph%qQ>&<1jAsKezKCz5oCK diff --git a/route/static/img/ico/ico-png.png b/route/static/img/ico/ico-png.png deleted file mode 100755 index f39804a9c0700773f848981e3ea7c646c9efcf20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1023 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPVEzz-Sua6XN>+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|$kD` z$fHY9+gf6_w|@Ql_1%|`C!SnA^zg#>@83%fP7m5#ci`dqoP86QUEA~F>!%gh_g;B< zZ{DSCMF*x<9iIK(ufy&u%Qb zvh&l|FSlMldiCjD#MY*L56+eynh~<4zF_~98?PTceE0m?tNVHTCik6Lwczshj6Ho@ z?i`zSar2)0XSUrrzUB5Y|BW?qJK9tC_U0d$^6}@_E%#2Gdv&|>^zz0N3m0GCU4CTN z-bd%IzkRs+*1`K9UmSgQ<;B-`6VI=kb$Ltf{z-WUCU+iPlDTg}$I&HoPj2iuvN(VL zWba1F&A@O>FA4GsW?*Z1@_$-8FdYBC0ufXOfBzrmBVgAYkdR{iU>+7`M_|IiIvmGnXiJY>6^(`F~oo$zwIk=Maa+ zPp1?@nj*Cpc|_V7_^VE_$d?eTKf$Urb)gSe&{Ho{w}(+H^BDdFEcOh#88(}-r)0(L z*UZf)CTQfy$fRc49IU=O1;bsg`R}Qytt2%IZ-V(mu%2#>T z`)0W=FP6O_8}9Nu=f1&?McpPXk5sMpA2RSTuQ_93!D3u7omn}`(Z*w;#+JsF%lCZu z$z(D8zCG5;F;Sqc$>5H#)#Mp>w}$QgFX**K*L7PG6VuuAb_dw|mo%{IBrvXLT>Yd^ zL1;I_lW7bp*$iD$2Gf>YR(Iq-5bS*E?EhRGweEsQ}NbR;%QkqrqS>5dq*&KmEjr z8y&C(yneqwF#%vHK;JaGxtFWfOGt(y*`{G+F-Z_P#p{Ohm3fVf^s0+7?Z?m#lXe74a5i=v=juUAGX zHJd!oXQQ>|Y+gVPKn~j5fEAD44H05MXw!%xDp<=B?;&YHCo1(?#?x!g#j{R-g^x(;^cv3lZlW!y$N|CZKxj z{<6RGVr9UM6d)!AXny+&r!9OrH+K^49KhlLBM`SJz>s8jpFl3F@V;-^*@x(? z2E>5;7W3QF0mSUnU%WE^vX1e_j@OF1wLd2YFt{Hlzoyp)LR3sI!?p4B0h+u=d9xY| zh|j=hO&=ikeOP){b;iZRSbPRPYx)3r7UKNPkAa6VDsJ|r4~XCj0!Le~)%qMbFov%_ zrW+8^-3+H9d^>Bk;JO#R7=y6_$nn<9+)%CZ4OK=fU!#U?=yAqii~uedlPgm5Zs{X< zt+E=nabNm?=|=$LBY*-ZfO*!~Vj?q8maqU$a0E~Q1yGK{G6Mxr00mG01yBG5Pyhu` z00mG01yBHgKWZELT@nd++_ux4WB~=Re)T@|Po6Ewp!q9h_nsKkgK%x7xbNR7`~N&? zy9M8mdjIz?sB-r9X#?VUe%*fl0_yl;vs{((k0M1{sct%hQh%Dj7=)830Ai_PNfX!QuZID4DGVnltzuO;olI#Gyy{L`{Xuz5K73P0O t+7K4V&mg~p!eH1Q1wVS0{%8P>VgMu<$j`AZGUosQ002ovPDHLkV1n8p5WoNc diff --git a/route/static/img/ico/ico-psd.png b/route/static/img/ico/ico-psd.png deleted file mode 100755 index f25968f4815573643150468b7919a415d5db4c4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1119 zcmcJN`A?b$7{^~;j~LO_#kF(VL}%N~*~Va(IoDCGtGarQShUW?#a6Ly#>S(pRjW~~ zSG890+>D)CN46SlJ&Gt=MHcWrUgS_vKtKgVt`|@@W%RfG1N$V;=gBvDe){IA#>9T( z?C9>f_z~2A>fx|>&!z>mH=Rat5I%|^4dOmIjGn^c~3YftxlMzC)%TG>=-i>S*b zBP&X+p?->6N9QhXsYZFSj_Gx?*(_FR38RqFWY!x^2BQf-w5WxRlY;Gvadz>@3WY2E zd1z6kgUe`8?!dy+NzRkEtBnkvK%uF6yUO3$m#TGa`QB?tGA-Kum9@qY?hLMO4X`Cs z!tG_Ls&`pLWbihWnm3%S1&Ok7c)1S})A+LX>Gkq4Hg{XyIV;@O=)32HZOpaizxd;8 z@|Q~@wH`K^&3S_h4C+-?z59^AXe z+=kO=5|Qq(i>pqgZ069VS4mL%0Q+*!YT5w1jK;P)m;}Y9qLtR(AKZ_Ny$RSqmRVXV zt-V{6@AQ9j%(Bcl%^&aKNDKe~FGqxiU>_o6^X2b@{vX(4WWw%1#YD95_AhBW&o(cL zW0;v&+V3iI&z`}>e=_Hero^}UsdR1l=)|&yQ}pS|6xnq&@ng3`m)b`T;X$_NJhvJg zEBDGn{}>8jCT4czc}t3#kYC{~O&ETDo&t-d27ceiZC6j|jt;5Xd&8Q}?4z+$Ig*Ct zf*Z9yRoy6b?IQd*OF&uR^DW+YXrt@LCw7m@3w8DBQBfTyAL{6?2t;#f_&>sdB=Qeu zz%B@ad#RTkrlOI>Grmsi8LYhbb{=&5>~?#Y^T&dSCneHeABO@rTNlU1hK$-;n)b^O zREVcI5+Z{20jP7xeH2pG#!9UN&(pnm#%tBMvGWL$Yr5ygX#kOcqqqJPAiVV~boM&X zcvzvvkAIXIS$cr|J*;BkrKA+|W}_$IbF>QvKXgB{!U0_LW7ORyWW#(<->T$<3ec4N z>I0&?((S9C@-HTFxDP> z%?>+xQ4v(eBgTMZ`FnNP(a8|Np5Rl!3C6SbJ%uk`b9OUtojvgS^kEgTNiRyVABc=0 zLpKR-rD%d(+I(MeGtapvBh{ugNq*&?S1zS~hL~Pl`lEXdhzq}9u`Roh=EEzT*`WL0 zwHZ`iUG`|>WOaIG(XkVy-?@5BI}(#DJ9>E X$%_BBwq+BtJ}?jw78~04bqe8MDe$uV diff --git a/route/static/img/ico/ico-rm.png b/route/static/img/ico/ico-rm.png deleted file mode 100755 index e4cb6ca3eaeb1cb9d4c06c6ce8b85bd3836c8cdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1129 zcmb_ado-H|9R1P=j#ky_svhH@?bg$Wu58%Y9LC11=GE4U$aG{nJ4PL1rk)Mf!|3r2 z#*XK-s31~FJv%KS)aTLUOF|w*XkK4lw0ukd+dunn_uSvT=brQX>z-R29Q38BiM0s; z0Hy){zM*;#etX7fy;7Nn{r~`!NATC-eh7jr{R?u1LZ3t;kw&B0+uLKa+4`u{>0p&g zAP~SXER)IPa{ZS=sZ>g2>XF5r2D+%EkDu4YOKFBy1d6hLzGz2P^H@k|8ns&8&yWnxO70H`YKMihkW8!9 zQkmPOWWI1)mEOW3(L~+Pwv;Mu)5u2tgEf{&**v;Io{^|ETD}DSt#^HG3myY^Uhw3d zPsPLY+u)jfeCZW~Ez9oYZOhfuENNB;H@Y0WS;LmAUr$4DVk4*Mk#JMCd!Mqw5o=o} zB-vf-lzAzME*@J}bWiW3wemo|I=%|LS)XclXp9e{)3q7c*iT~sK^-J>gp;4K`d4ZKuM>8PU&IXxxsH!(Ti5ZxEG-+`bU8u zkWRta<_Mq82)Ximr+X2il5D8%OhjkjwWQIWke)|3%=y_pi5S<&#Pnki+3{V)5p*vs z6(Xa~U(`6*^jXLaB^(ffIJheOI9k|K2{#MJz1 z+Pz%iemjo>$s+8wunODxXW`wb^a`^K zTMx5E7A!B_m4B7Mo9wReltJF_Dli*ZLP5DI-3EW`^TD5_g&N%8->5HadaJG&@(1GF z>k1R;jt)g`eH_1j*majUY0rh<5DqgN*%Y%4UNeyV#^vK*`DC3sDaD;=VsI{Ha3SYF zRT-ECdsM!5%i8jODNEZNC+1ob$A5OCZH``s4~~N;lMOJ#kk{Egp_k4GS^Flpc*wG# z_j>L-&K9LKOm*mc=-W?BVx0X?4BbHcHe42)A{XM$HQ{@kEuLkaGP0W4b!Q^ojJvpw z36wGsLybxrcdW6tf4m1QRDQOC`tkT^ea8|ikZ0F)CckU!PEJSj@1x0e4nH(j8Ank* fHSbT_-oksJ8QIn$bfa+}{pf)JzaZakA6(u)g`BQs diff --git a/route/static/img/ico/ico-rmvb.png b/route/static/img/ico/ico-rmvb.png deleted file mode 100755 index 5b9805a017cb4021fc1b92efa72b87bc1dd03f98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1209 zcmb_bc}&v>7;T}TpmnHJqXNnT%;3C08P|jo*klmwDw%U^rWDJDLQ)XR;W%tDR0wVm zDY8mcpfF(611nT21%U~r+B#Zvv;}(6lU~0b$l-7Gza{&3FW>iGUh@6*UcT$7v@hY_ zyS*V02t4^@BHE>>54+Lb#Vo$t9S8&(gGxZ zMycAWG1_f5n@DE4(m7Y!F~@$b2kg$VnWe&332jJavO3Iw!)~`f=INfi)HN^_&4bF9 z5|h*EWbySk`s7Nzt*Bi}WvdtyTC2@@m$OLvV_{Zhy*IqrH>I;XoN^sd^FX$s1xAF1 zzvj(dE?VM=O%+}9db6E7Yb^VH?uU9|)-AEwzRHyVxil&Hp;B#n*U-Nx z)i~QnbwatFK%cMZk@X9V)MuKJ84H7J$fgLhnr5pyg_xUfuuZe14q4lBoYan?9x>y9$+kXE+9Gf3E%Pj4C>ACG$b z8S~ipw-fyE=V~sXYD)hz-=WCf_>xdTk>%!nQA@yt zC2`WxHH?1j#*XmwGn1-K`<0i{D7^Nq-2;y5jH9p=+&A6F2SnWnN=m|-9#J=3N_x42 z$fV{9w{`Q*Li4&MKhY{yL)#;F$13Y9hWzf_etw@YHVVxT8sLY6@RD*UXNugo*DIRm zfs9})BEnEU4TA@N(1rZ1;B?ig$u z;W+Z`FO7w)AbiOP6-IZos^!{ zt)AIt$ZUeqY5?E%=Rb#&eFKw`DU2fzyp6@-Zhm_U;sY|Y(ZrtyTCWA0rl}!;=pMdD z7n7Y3a4_>=RMm3FpnK3g0O>z+V^?QP%GHbTZZU$ux=XB$qbmmQw**^H^#1lLYSj1C zW&$O2zo$R({E1#v=|O%*W@D;n@NMJ(bTS@CRAzWizL`Bbf!1pSj8*vNDt-*MRUb@||A(ShKgTBZ)WLeB9abt+fLnnrM&+MdLEG+T6$eOCS Q?0O#ct475^2>l%;N}u6C-{WzW)m19ZU<`dz>K=+HU0$D{B4)GbpICU>Dj;8s=~ ztU@a3a+3Q|RpBWEwKT!`DlU63@o6JhOe-Ru8YOM!bTQm}T1FRf17)WR%w}BgwsfH< zJ?GC9s64j(KB(vdVJ)U!Fdbj~QgpMcbf(Xa$TAIh&Tl3t0iV2l;skjU!rzExzJ?U_ z3jT(06%S07s)zHmhfC^DeRbII_N>+1G+woLPsA6LTDU$+W9+ewx6F|59$WdMant16 zZkwjVRF=yOU)6UbO0A`8&7$wl$jLMJ*;Nm@H#J@D45McXUv=gi9-^;zn>!u6(lb(C zrT%AO@a~asfe!iou|O;`c(tz$DEwvl*OTuamGBX)I{(-URRCoCkfCB4L#6G%ay;n6 z&KQy2TPp{J009~4olBSlfR(XeKcG0py-y%2yFju}oRK5V7b|cK1p-t=B0_*2Vo43l zle6D@*tUNh_@k4Vc8tscggPh_OEXp3)c!05CC!5QRYYmIT9%X6u6v6YG5HY z43iiPV{)&249-Qps1HVk-kA{*{I{I`gntuC)3W|QWvE}BjT0!TNEafpQA|E6{|V%x yqv#|+h2SGfe2A+{*T&Ll9z#g@LJXG>JPAaIAOS%LfD<52G3>-JC(0w*K*e7$k6>5; diff --git a/route/static/img/ico/ico-swf.png b/route/static/img/ico/ico-swf.png deleted file mode 100755 index d043a2e2a54635757d88f8f175471dfb516fe074..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 964 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 WvAE>z<+}c%fD@j69!>618UFz9-PDZ$ diff --git a/route/static/img/ico/ico-webp.png b/route/static/img/ico/ico-webp.png deleted file mode 100755 index 6b8cc4dc7f5e3816d540c88e14d50056c5ee1651..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1060 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPVEzz-Sua6XN>+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|$kD` z$fHY9+gf6_w|@Ql_1%|`C!SnA^zg#>@83%fP7m5#ci`dqoP86QUEA~F>!%gh_g;B< zZ{DSCMF*x<9iIK(ufy&u%Qb zvh&l|FSlMldiCjD#MY*L56+eynh~<4zF_~98?PTceE0m?tNVHTCik6Lwczshj6Ho@ z?i`zSar2)0XSUrrzUB5Y|BW?qJK9tC_U0d$a{uFt>u(=!xp(5@&#$X*9h`W6-HWg9 z%8$%C`s~W$>$_)N-qLw``MFoO_dYt;cw%Ah{z-WUCU+iPlDTg}$I&HoPj2iuvN(VL zn{{BDy|0K_uzs)y`Z!j=0nR~i8hEy=V zxx~CnC6K4>VfMe=CAqh^?fn0g=k*t3owSPA(Fdw0#O%GhEj{oSYBtis9NQlYrw6mx#!Wi=TKG}=`L!&+JQp%7X_B|uZoPt` zt>e%HO(&6E9rCuK3QxFd86Rm%@YoqPI@LU46%6HXOa2%UA?OmK`(k0gs)(aYXp^Ux zqN``BfYw2ar4yG~$j?~9V>HM7OQ-!4Cc(}?9j28zdh!bSioqrfb%85=fJS$X^L^Ix7uJbY~?hn9YckeA3*&onxFmLX(CqyDVdjN;ytqI9o? z80;~d$eFv%HDTjC0o}*Ghaa?*T@tut{l9?!u!8J|)ME$DW*l5B^WrksBNwip3we9y zJl$LDB4{#$bJhaRLoEL}e(a0c+|NpB<;KRp{K;F}* zPrrTp_UqTL+qZ86xj%mV`26L|D)~w50_C7jy^x2ig*LQcGUViS??Zy)ebN5fmJ21KP z=#tER6FQDAnR{|$$C1VP`zLD&8b1UEQ%p&aUoZn(%ai}p+JV9J{}qU!2Kf8``2UkU zXZ|WEPpe>HU=s9naSW+od~+r=N!gLd_2SGUIWr&5oH_IGoWJ=eb69utEG@Xdp6TVE z-@E_XTWxzY|Ek26o3iKjZr;)3y1n_^`A3Fgx+NShQUbYB0(r0@;V)71R2hH$`RAY@ zz{BRea017PMRq419sA7LD8i>>IUmSA#l8A~hsd#`N=J@9c9}Ax`eA_+tJ*@XQ^hMw z+Qc3m3T}#=P1O4L9u^@AA`11>P91pm#=~OEK@Zjy9}+^P*IaEZVpb6Q zdLWJO=(SI07Kz}E}rS26v6-Uu`H=Djo}QkrvV{yT?yg2X5ey81x#=gCbH8E*CPD^JS3j3^P6>TR diff --git a/route/static/img/ico/ico-wmv.png b/route/static/img/ico/ico-wmv.png deleted file mode 100755 index ba03d507fc8e9c15483f69892a654a01bbd8c480..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1211 zcmb_bZBWt&9R8!^kkr`qa=TK~ET_$^>84A`sp-t=kP+A0rlKX}DqUWw>7^4iOV16R zWr-OzwYt#MeCb6*5>ZIf3|}S)h++!J`&*>`N8fh0uY2zKJ-B22TBEUjX>~fCTy12$RoaK zr(0AR4F<#btTz9?D6dJ>^G2aJnpqRe7w!q`du3{!NuxIzjmC~)c^6M!)j4It+SFPJ(I5- z6Kft0EWQ|3@Fkj;Ty?LSy7kt1dqfC06oJ-V}6?sx-zAXX|IGx)+!e+7_@hxZTwE6W}AGQY+q^65jUeGU_3s}8x)+AFFR>MWF!>a>J zj#I$_ZsU#;Kc(|6wN;v00)#L{&px^G5di`T6<#`|sIv{I^$Mpw3=@^d%u} z|A`L!IY;X*Ddqxw|G|B?cDzM-Qx5u5&X1kieGUD-WUsK!^O#RNwxzEb8i31?(zwYu>v>ri72_w+>EnY|}@7~lO8=H@uc?l@TBCr=* zBFZ*8NJ7dh4z>Px3NjlM-v!QGNM)bC|+ zi=nNAkEVFT+OxCZ-A-=#8ZyC?jAZ;OuGfyb`?dLotA2hyxjxS1;po`z&A_&Jnl+u5 zn;3&($R5mcf8zP-OZ3N~Tnf_VcIo-$OS$Zh!bW~V<&vjpt#)-`o!cWlozb3M?inGhG7t=-v8^o*`8tr*o-)~s^1SA#ivfM)vA9D*rER}&F zLzOQlx?E;+(?`i2sUZp3Y9 zg`d#Gk!K^EX|*_ZY$oz-^XXn_x{1oF-ih`RJ>TN~OZN0n=nB@;5m%TfhGzOaya>9Q zL?SjGsESEI69W>+w90MCBqwpwV-u7!=7+Ap|8<7EzzzX1R8^P{6IQwc>{e1B{*nV8 z=K`&eOV@LD!VjRe1&>MM@oW+kQC)fUBae5-HPRMnN?aVPSwe7nxW>p(>c%MO5% VT$lPd=^tkV#73i{S|V}8e*n0&%*+4) diff --git a/route/static/img/ico/ico-xls.png b/route/static/img/ico/ico-xls.png deleted file mode 100755 index e141b11b2baf509468d769c37df87c353820f42d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1121 zcmcJNeK6Yx9L9fAiMB)OLg@^vxoM}HyYwtKFB8&3X)aQ`s`W@w?P|KTYFx9GWNnF; zo|lL&lH43Q&0JC6hc;s25(Kz`pDM8RgtE;Ozoz7q|%+Ai5aA#*n zt7G`A2m}cTDaTlmjZ75`&i^JHKpQGFE zOO-M_E1N#X(CT$^4cI!zz1;rbom{%9lHcem$Qi8B=(L*(nMR|j6>}uY4V6}NySwCW zU)j@%_Qa0-b#P0k*OPfwTCJ8g+VlgvFmLF00*6{L-nIp9|J3`qX{K*l_72plho#eU zl|rxA-((lRT^KAFc@3(;XCjtbr^yynU+;Vr*_uHWHuNt2!Iw<7%nk^pe_rPl{Mb#c zo9O{}we*SB!qEot_EPHte?TfC@vGAM>FZiKWvHfWSt#6`T?E&ki#rHCB{#dNx!md` zPEiiGx?->;hx_tY53MvQt=!aX@*N!J0sy-&G19V3%^Ek~+yA0HBhM(X9NrTK(jfo< zxg3f{MW&m@Ij}MdDURLe{O~M$bc91pHX~l%Io(z{Fa4W$wC>hay=XfFh-G=MT`NJA z%`f@-`flZ66thhKD{@_7B^8MzH#0zU>MmLH+jvvuAr>J&)p{V z-bqO8R~@^jS(}s2^4sAx7qX5bE0jdN#q)1Su>+}%m|c&rMFD3FGGBnOrPbL zH2p*?DU5+|T2!{Z^`ZOFh80VGDVMekA1`Gv@Z>S1^@E8GixfPi4u_=1No<*-g+7A8 zF8i|uzb~OLLUOP6X2OpZSw74sJw`#RJOo3p3J=A{ORfUMJNYpY2cc%^#g4GF>@7}) z{9FI*EEaB`$8PUSrKVD$Ul2$xQ2+C5w*3igC>-9wd4Y{YqQ5Un>G5d6x)O#u})4l>uNUmp=J0 g(2Oq-#yKE?cT~@u+y!BTQ{7uCt7$n;Raj(b}l>K7i7B z;W4d>T4X)uRkZU!P!tsvsmDC9-ck_-ew<2c8U40@U@v*^9%v4=g#rLz z9vp;-&^hu0P4sozI|FG10KGtDXq3NJt6lgHq#&qEySuw8l}e-0@cDdQJUTj(D-_$? z+j6;FB9Ta?x)vyt$wszjBVJ@hcjwIu*H%PZ;hhQU1#Y4>Zs7MQS~iK>HoVCj-&yG5k8%aeag5R=CW*Vx2alCy zGi|iB@nY@z zbx#jt)0@|WPW=s$jGaRP;b>d)9Qs0taCDyC4pU4yv;cwe*vdss+cgjfTI5kh2A+Y0L-b~6( z6VkGZm>H3$PGCEM(dsqJ(2)tjJmMRD>{VCxJA-^8Q&SVB-1icxd3G*vk2-PdvEk8E zk6V67dyw3oRqHB6U1A9@X0<?`ILM+~2 z2n316H2iYb7CiqreT`A|7VByUDSDLb} ze}$Vv89Nu9><Jv0?t$7}=r#>4JaCK=k?D1Q-barMUE0YM3l__nl5bPh0XuFKg`xggxwX*;K diff --git a/route/static/img/ico/ico-xml.png b/route/static/img/ico/ico-xml.png deleted file mode 100755 index 8368fb67bdb625e962a6fded1124f92112303a74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1095 zcmcJN{WBW|9L7^6bB+}2(3W=E%9JaSY;LEFc&A3{ZC-ZQsI+A#M|It7^z;(-azfRp zI%k_{wXCvuS6n5FM!kh3C5c2L@+vR#{z9i&`rH12J$K*F_ny1wr{|sqL~4SI(@7@) z0N|387@KbUh!5v@)W(;qcE14tM-mVznQ>ODb^SlmXtg#c7K=?L)4{<3hr_X1i^ZZh z7=%KhUawcH)f$a0rPb+l27|$9G-{3J@Bwx_e$!;OjB!=4K4#;R1pP`tlWEOn^QJ;y zP87(r#xI^RuZ(buX12LXeanjU%jc}fKiN;$<(+G?wpHm2e=nK9c`a05o#gRV2J((N zZJ3)f#Cf&7S2`;Q>t}V*WO|di;3dCfRVLUo^fMK5ohf6KOOo z!M^)i7&ExZlIsbZ%4&)T&sN@;-u{U!teg`zElcJ_8pf_ptTCdd_`frDf2Hr{jq^hO zpr;IRDG~!;yH8h`rUW{L>0QH;>@G!|GtTSTP@$#;?KJrXqrzBDL5AY zmHoLhI}bs$H+4u+3`FPaweO0na*Pl7e7)Pf=MJ^f4BpQ@B?H$!OL~II*<}G?xj>1|K^XX`>LLYZ$--xzNRQdFAd0K@b8&?Pc684Q0W?Kn zDheS6x8_imPeFJmbhM_^Aw^n<^FU!44VTf)ez)7_%&24-D+syP=6|@D96*`+8k#fM zCZ?A{+-ZZfpjJ|MU~dEnT95#%m@D?%nUxM4*UD?oEo@(}6=o6ZCL5HB6C=*IO7;@M zC#fH!sf*!dGYn~&ap&@o&jc=pEYY<0uK0HO+|0mNT#CK#mITa&WCu4Qnu1Xsk%`Rs zCH)0D%&J-*gWn5rtt~KFk_VJTkDPKE|Bf{bGwq$q1OJ?s%LDS%_Rl6BRB4o&`eVGm zFrU0YIs{r`h_`<*!y)g0hyeHED|sgsEg2hR_K#S{ak!lwZr$_d>BmE%ED!{h59pR) z5P28QuRRA}6tR$m(Bo?lqe}feQ{vA;{pn#|WpFq-3X1g_UI6|AF7`*3UUL6-E&qZ2 zq%)@EDA=p$O=C|ewi_KxYOQwjCY8*5lnPtz>ZshFQb}-w#^uj M6qg!{k49qt1^(@nrvLx| diff --git a/route/static/img/ico/index.html b/route/static/img/ico/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/img/ico/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/img/ico/menu_icon_control.png b/route/static/img/ico/menu_icon_control.png deleted file mode 100755 index c2ecbc0803d956683aefa960283080a8cdcedb5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 327 zcmV-N0l5B&P)$PMh=3FHc5=@l%kWITfy5G`VpN{duh1`*Uq zDmAP>{{4>c0RR912$UOVI#d7zB&&HP;mk;*s-lYERS*pAkuWjNFJ;;%LlY#=e&%th zIufD?8bOPs0X>lfAeuMSqHipsLITh@*LpBx=v4zM2_R40m06$t%C~@^647Y^A8cC# zFi}Z}dOFIKIo~D9SAF{kL@XJV4J=r;t1VwN`qqss67k2ezvY|vYJSw%bY;&EC+>+D z=PO74+j5EvJ1+BczRtfjButfg{~Wq-TLzx;L3vjOnARuZqkWyui-AC?f}rYI1poj5 Zz#H#)Uk_jFrXK(R002ovPDHLkV1hQ5e=-07 diff --git a/route/static/img/ico/menu_icon_control_active.gif b/route/static/img/ico/menu_icon_control_active.gif deleted file mode 100755 index ba56df8e92a5e434de5e8605095f2b0180bb1006..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 805 zcmZ?wbhEHb6krfw_{PBCxzIDQC^4`tFtjz)agL*3r(a@TqQzG?C0ADwgk#tmPx2j zaGmCAwbClKIM#Z(wds1(#F9j-rB+Q%O-D`~Id<&W(PKw};K-39hk*b{96G%3z`iF> zo@8cbHZ(M3WM&i>7au!ytgx^!BO@a}KR=@=BRxI6s;a88vNAO@H7zZzt*!0kg_A8U zEvc!g_wV0tZf?%X$|@}_Jq`rtjx!7pQ2fvB=Nc01>=@u`q-Vg)2y}+xPZm}VAg#l| zz{tSQz{kMAUf#f9A7*84ZrNekW)&91VCl{f=<7Mv(>IX8)tbSIfg!{z*wi%GD`fsW z^Z5?mOO|*$tYlcdz|k)tz|V2T+%5CnTtl6mLtWiAZ=HYo%s$Jr7Z~PPTs&*T;D7Z@ zID^Zr(`F2|_vb&Jz~JQJb76K)Bh)Rd3_45h zB!LcPX5a!!u>d9c8cYlr^bHvpjPw|cb&-^?ASq!GQe#k3R#8=EP-H+-!pgt_H-&+f zje&!khn)*(Es_#826mtlR-i=!4Gg>tf&zRD{KD)E-3&rVia3CJV6I_c;AT+Q(ALpp s(1Mx+3@ip6PM}plPXNskZD43(5S5UT6Jul$QIL{lkQSGhY-F$o089zWT>t<8 diff --git a/route/static/img/ico/menu_icon_control_active.png b/route/static/img/ico/menu_icon_control_active.png deleted file mode 100755 index d698ab70e87897cd8a7be5d352ac69effe792c65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmV-C0mJ@@P)P zy?gKZe4+*b000CYJSrtB0HkV7<7NYowq+6(64XdlMjQ|*{VH_K=@}w?xi&yaAQcL= zT&6%%u0}&j0BGpy$zQ;KR8^Yd+oibmPpDD^pr#2zDt@r-W*ZKJ-NdiJhu;7C(PrK?R~+M&|WpH(R;sYE4A5SRe~0DyluUssyEPV7(s O0000g%;bXCz8XcLGNobJbA1B?eJ?CK z?}91{mG9bdfLY&EQ5b1?Y}1;|m0XeD*z&}6qbSr`5fP@eb#$~%M?`3;qp)j7FK3;K zf1Gp;J9eW`Yns=$ZD`<=4V%`@YpSF0&ZF3qr}J!1=2+~pMH;M002ovPDHLkV1mX-pIra| diff --git a/route/static/img/ico/menu_icon_data_active.gif b/route/static/img/ico/menu_icon_data_active.gif deleted file mode 100755 index 04b6e0d42f301b4ac9ff1967a108c7b5084b6f72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 936 zcmZ?wbhEHb6krfw_~y(IQIhcY?_Y+SW=z-3xXxQ~owX7=YA15oLF}M|*nS78y-sr5 zU1WATDQtC7*y^UV+0AI7pT=qro#kGwt*v@XeVUt_P3HTV&Gl<+Y_ymiU@6*RNl_ zdiC<<%SVqMJ$(4^!Gj0)@87?B_wJoLcW&RleeK${%a<=-ym)bEXJ>nR``o#6XV0Gf z|NsA)GiR2Sl}(>My{4w7qN1X^yPIKX0>%H_ey$?d@*bkiiK)*o!zy|aKe*;5Pb4zPm zdq-zicTZ0v$e%zpN(?$6PxCRb1J(F6C@CTNQwiChN??B~0sRT_CaN#{U|#G4dUr(= b(4#08Faj+AIl_Z%3s6H5Y&a`TU2hElO~lOm diff --git a/route/static/img/ico/menu_icon_data_active.png b/route/static/img/ico/menu_icon_data_active.png deleted file mode 100755 index fe30c4b6370f5787b9cebd4fe5ae0666316d97c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 330 zcmV-Q0k!^#P)MYSpGA57v!D7NjaUAuS^#Em^5k(UR{%U_c%I<#y0s+L@bpxTrZ+s5^YiRm?J(W!ZLKS#BiQ}inE;YHDs c_&@*r3F$fHmO8#1^#A|>07*qoM6N<$f)#O@S^xk5 diff --git a/route/static/img/ico/menu_icon_day.png b/route/static/img/ico/menu_icon_day.png deleted file mode 100755 index 9fbccf0befcfb857cbdaedb61c263a61dc5f78f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 291 zcmV+;0o?wHP)I%a<=-x^(H{#fuj%TsV97?5R_y_V@Sq_V#vn zcXxJnwzs#pwzf7mH`mqGm6es1l$2b)e7UHoh=B$`@jthpYe=xOV}PrXo&hr>&Y*h}E45kQ5dCt7}agji%+67P7B|A7o``sTTy^PXh>}Ylr zYVlaG*nn4Q0vq=!6{q$78r+AYHyup**B~J+RK+F@45ut7X$}?E_HH*R_I{~JZjya0 z(`R_}vCf?#Tg5ChPeqL!Jj diff --git a/route/static/img/ico/menu_icon_day_active.png b/route/static/img/ico/menu_icon_day_active.png deleted file mode 100755 index e128e163f46126e4f2ae5df934d81d21d375e354..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271 zcmV+q0r38bP)9S+KD}HWn6BScszJBT^QM|M!S96O-XSb-Hz* z-g7%gq5flEf*6hmoxof0P+`WM8cmBDccxVEiZEfofEy*M7G z%t3@RCUqLLKWI?LWG%vx78*r#40MYk8Z9;=Y{;N^9%t29im*d-ok5nGY50}_}|<7$pa*v VKWs+gm%{)6002ovPDHLkV1ngkYi|Gm diff --git a/route/static/img/ico/menu_icon_exit.png b/route/static/img/ico/menu_icon_exit.png deleted file mode 100755 index 75feec4b12b9882f1e432642c32d8b275a874e09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmV~)AKNKjkr zp|#S>W@?bxTtA+(RvhQ8d|IRY8l$B4I)zlk#pNYUpFaKJ!-xI-{oUQ&ot>SJA3wf% z^XA#JXHTC#ef;?G!-o$aJa};b{{4IR?%ln6_tvdj*REZ=a^=e9%a<=+ym;Zlg>&c5 zZES2TD=RB0DY<&}Dg%i?@jthpYe=xOV}PrXo&hr>P`lz!7FG@*t;58?$iU6O$H2hm z;4pc)vrd`aTJOxIS!Utu&$O|wh+bvY zipg>Pk5Yn^6q}kARP-DXhO#rT1C3<@8p?8@Ns$8~!GVx)IHUlRU}n(a1WGW0b+8?9 zaA4qIg6d&n(BVetIlutd19UnMLXX4oMF-(}z*4L*;~cz}7;(ak14{8RumPo*fo@}W u;A9Z0^k_mjkB5O1C<6>1HdY5$CP5akj1?%Dco?{VxxVPH@M*;CYiUc)o5q)8kg;Q%?H(N8eZ~+owA;>=V-X+^5S!6U+%86dVb%Z zqtWyut8m{Oi_>~CCssZn@(0fr6E*Z5diW2`*{gT0&dxYJlz zd~SX|X6o2&=v-67*0N{CzyC^ydndhZvq`-@k+=5blYs2n71r7|Zbtnxc5(e`amlH7 geG@S$tZ^y->6x-$wf>tK0-es_>FVdQ&MBb@0I_XbjsO4v diff --git a/route/static/img/ico/menu_icon_firewall.png b/route/static/img/ico/menu_icon_firewall.png deleted file mode 100755 index f05938dd84e324fc7f21212cd7b960c4681ca1bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmV-K0lWT*P)t>*4K)T$jZU=)Lek=xEz%1VDRK+xckUEY;TR1LMq^aa z3lvgO)<7>>g1SGmzg2po@-86BSnTH7u;~i9tk<-i*qK4 zC?tq7%L7MpXp+(ZhXs!1@Igj>ND^b0Q#mwAs|Q&u(`-lZ4mr*wi2#emR|l77Ne*$I@FWTF)j@&= zp4!-_sy@{4_0Np(PEjG0c`?K4!sC%TaYnH*em&apT7I>(?(| zzI^G@rHdCYoz?P*Bj@+xz$L-`~G~ zfB*jd_3PI!U%q_){Q1+TPai*i{P5w!`}gnPy?giO&70S+U%z_w>cxu}PoF-0^5n_4 zZ{Gr44F`(Gc*ia%LcnSrzpND)8Kn+$9v4h;ey8uvJ8N2i&v zbSP|$^t*gBAjeqpD9eq99%mJ`M5-C0<`&FYWSxKItk=6Ujr{tb@80H{aVD{}MmX18 ztcJl#ghA4x-BgZ2%|N|OO@9i5P;r5|kgsl6qM%-)x}jMNgQ|(KMuC`~q`00$yQY>- zg0CakAuJ4x3_8pVYz#~cKsT|mA7Ef&C88NQVPS7o&mzOc&5DPM}>-^A4~bfSU)D;bLHg%dj{=Wh_AM<7Qxi N+r-RN-~qJB8UXF_xFP@m diff --git a/route/static/img/ico/menu_icon_firewall_active.png b/route/static/img/ico/menu_icon_firewall_active.png deleted file mode 100755 index 5c2f49f795f494c62305ceca38caa6fbbcd49f5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 310 zcmV-60m=S}P)?6D)m{vdGg5TlRH`sT6cmQdnWbdfj%34*|%lFurW)%I`Gqi&J?bE(XA;Bw0Z5f zPbpj)&ND3foWdVtdBTu(^R`pCG?GV*qt}dD3V*%VtUYx`F`igfOX1jvmu?LuiFcSz z63aGI*!3Z?iUDshm;Y>9OkvValGspGQ|FtvDRekeNfPU-snh91C3mc-L2wKKLCK>z>%07*qo IM6N<$g1ice>Hq)$ diff --git a/route/static/img/ico/menu_icon_folder.png b/route/static/img/ico/menu_icon_folder.png deleted file mode 100755 index eb3461c42ac1450228901ce0b20009a0d65b52ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmV+t0qy>YP)S}6gE?&HN_UzfE zOP8))yLR#7#oM=UU$}7L`Sa&bo;-Q{`0=Ahj~+aDaPQu|J9qBfx^?U3&6_uF+_--I z`jsnJE?>TU{`~oK=gyryd-lwkGbc};+}he&RaI3{QBhD(uzL0C=g*(ty?giS)vI5> zf}B7JQ2fvB=Nc01>=@u`q-Vg)2sBIaCkrbdkk(;fU;=uQhk=1D!QsGx1s*I6Czc3J z&`{vxb4zxNwc+q&EIa9%B`$KT%~>tqsE?yK?%sQ$JLP9EK z%B2h%0(J6o9$J!h;_M#kvg|Wv&Qucdly~7;xM-nfC4(c#QcgYwMg|>Lpv_!B1_K+L zLqmZF4->K!BW@`stWr!M13^+uKm)lBG$eSaF!h~C5rBFUF3k*-76MCKN%XcBeN^CJ zxUj;618O%kEch8f%D6zz(vj+ingVn+H-io%&;mZNwCI6G$B7&|eI6-`8Y~#*U0BgD zVU1EV!V?IU(jb)#68$G!7z+ywq`F@`a^&Dx)wrfingix(0g%azK$BU37Rxzw2ry|# z^)ojs2})SCb}~JBkYFNq<7hL3V8Q$c3Q#YBwJ-p+00WXu5vt|zk{}BM4WY)T27wMT UGCjwVc2@`}G_5z0WMQxd06wL~jQ{`u diff --git a/route/static/img/ico/menu_icon_folder_active.png b/route/static/img/ico/menu_icon_folder_active.png deleted file mode 100755 index 871688f67de175aada565e0e6256cbc3543e3758..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmVVVJjDO>2T+5YZxnAHji^M}}o^ z*Yq5J7G&44V%{JwH3T_y?m=mqp`jsjzEh`mL z$5xza$OESZpSHc4TCc2002ovPDHLkV1kQ^Wk>)3 diff --git a/route/static/img/ico/menu_icon_ftp.png b/route/static/img/ico/menu_icon_ftp.png deleted file mode 100755 index d57b21cd9bf4576b793e1ec501913f241c33ab9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 384 zcmV-`0e}99P)rer#$kv?5A0KT0Vsav)C1B9T8}v|`WevnYm=Y4c-6S(I{c z@fRp^aMj8ThpF#2DF>eS?RmZGd3qj^{}&@%@=k(t+`nL;hy%7b<%3T`>=Ibd|ADoD zn3>iJ!mP2%KE*5(_T_vFW`PgdiQ;6IDO~K)O^S)^>_VQ=Wl3t?WZ#Ca!`Ns_qLOAr*wO|6ycfP-fXKba;| eo#~eS`QtBMbHCt1iBA;(0000^Z$UzRj zDS(Q__3^zu>cWcF*P%H_WEL%3u#$Dgx8MlRQsLS5v|XAT7w2tny$j-kttaW2^SGz| z^Yb3JCP8g)Z*OgFZEkLEY;3Hrudl7G#bU8&G`hOF8i_=fmzP7KP%sz_1Ok4)-{|8SZf>rxudlAIE-x=HE-uc`&(F@zDwRsPTrL)ir>CcdLLr~e=W@A|lap*V zdwhI+baa%-WYXz$DwR4sJWM8&2L}g9rjC87K_#yl@mUSfBdn1OB&<=G1_tUz zb)hia3k4d5m_{M!HbGqoh~9lrOu!@wHYJ$5)J~}DfKmOW!G_^%;+#!@Y=bVx(l3DT z&7_~3_-#J`DWJe(`w8h5ksN=3`0vZ3EJQfSr^Wa!5ph~PmhdKewIo?v^!l00iKo}5YF<5`#E+*z;io2Ky#cIjVAmHK1Vhm}l)70>&SS-ZxRs?xi szm1(W#&Q97n~VmKNTm{H2@v7(ZG>P|^aA!X`0NI7oNfkalKElmAHErNu!=vBQLB_WyBk&J@(r*pL}%FKTl0~ zZA6vq^~+M19B|Sxhg~#^{Zb{FaKua-t+vW$BhEN4DI4rIZo5PF`rwPN-rM7lZN`mT zmnlO-L(~2M_-)$I&`)==*ucQRa$~-OZ`N9FU|?WTB}tMbNs|5p$y!S|4bG-h00000 LNkvXXu0mjfGA^oL diff --git a/route/static/img/ico/menu_icon_home.png b/route/static/img/ico/menu_icon_home.png deleted file mode 100755 index e198671c0db26bfec80d539ea822d638c50f4cab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271 zcmV+q0r38bP)DIesvPcGhQY4X-0h<)DnJ=U69x~m);`HloOoSQ zJWu}3mEQbDpPE}WV-6jeO*j3Tb`#qkt=Msw?+m)PnGWl&jjE?$NXu%zSn*^gpOmyM zq>FhirMy%2ppqX{wUm=MSFvOyT?|`Pb&(I6CexuO9mX|N&@hpxYP-=kofy|hLBm91 z!>Iwswi06+DX1GwEIUZ-Sxbzlr{LPWDJS+5d(KRmbCZIa7tdZTB^JGS_UbZ!@dkfQ VYC+90XtMwS002ovPDHLkV1oTVaTovq diff --git a/route/static/img/ico/menu_icon_home_active.gif b/route/static/img/ico/menu_icon_home_active.gif deleted file mode 100755 index 96c3bcbf9a14fb2aaa3ff93d3fa9388537cb896b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 976 zcmZ?wbhEHb6krfw_-e**`t)g;oh~{nz2vsL_%uiD-@kwF-o3kb@7}(B`_`>nH*Vax ze*OBWqJ)(zS1w<^eCg7qix)3mxNzazxpQaFo;`Es%<0poPn|k-_3G6xU%u?`@9*sF zY;SLGZf z9yOaCU@<$uXuhAt%mA0}FqhsihlwE}6>)ZxgEUrq2pzQ(I&8;v-iiSmQ2fvB=Nc01 z>=@u`q-Vg)2vno^lZ8``!HhwNfq@a|Nj?S#wpNGP1sSguERHlr~FT=os!yiHXT7?c*WCK&QRpSqAybM@IQ6IZsvG(}DgE?Hr2$uv0z zK4*S+E|*}zG9hhQCteXL#WF=H5fvw;YBLsQCPo%l))q4*b~!TxT{C?{Glgasc6A9g zEi)}O33YZSN2tpf8FUzdF5?HfjDaoxKtsU|$AUJG6phB9618q0qYsQROafP$Sp*bq z8IFHQ3{kLL>2B1ti09(OOovUcSacY=V>;HGXhu)E|X&?S2axWt*q1sojuU?=D+%mxOE zs-BL%t{&Vu`bf@Um18hMc20xCOjPFtePfJC!S0-uKAT>h!08+(9v4vT7=hhkB+E7p z7)M41x<>kjMhXidjxs`Wlo7I{0)S~+hmC=gft7(7m}2-G7#b8+cvu*l)|jon*u-EB E0QyoN$N&HU diff --git a/route/static/img/ico/menu_icon_home_active.png b/route/static/img/ico/menu_icon_home_active.png deleted file mode 100755 index 636e26ebef517827b166a4aea66594f095ee93f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmV-40n7f0P)7Ofjp08kj<_`z!m4q7npHOHH(@J; zdf`&95Yn2Zg^+hGq`v8lqMR_`$%$u!Le@!Av*8DrbD_(*1)*Cp0Wyws2;}1Cn)fJ;r1zWIKU=m z@^YozE@zNc(9W2Ds#(u=e)6vri}Xon mw!C}!bwxqc_Y~emk0crXX~i#zk_rI1iow&>&t;ucLK6V}1Cn)g!5&!YNfhF?7 zQrmy#%AWH7>?M6q{(q^l=jVUZf8Q5t&FDY(JOA^3$?Z*F6S}khl`q!(v7Vt-_5bVt z>VN!y?3ayoWMW!0vpcQnP(AA#E{<6ZA|X#S4{+3}u)h6YX|TldpTAo{%D?je>)o60 e{x|LDN@rmI(J7RtxaA(u84RATelF{r5}E*!lT0T7 diff --git a/route/static/img/ico/menu_icon_set.png b/route/static/img/ico/menu_icon_set.png deleted file mode 100755 index 74b247163b33a59a10cbd13d15d6b00a3154a846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319 zcmV-F0l@x=P)xuU^rUt`fpA5UGaMTNM+5&)$&Vn~F zPL+EWAvL+>c4~*1j6_A$yWq2fwt5{yDf7}!vv_g2)IgbEuG$Cg+6Rzw?tytX{ie*X zm^rTn526boHMX5?M@wDMP{2Xu9b3BxEo_&kB1(`5Lis7R5{=Y z*l#~Tsm22M=m-_&4R1X2-EnZo@LVlWW!@=p&W#vx9-Oe3zfzMQ{zNzb@eAvDddYw% RSF8X4002ovPDHLkV1ks9j^6+P diff --git a/route/static/img/ico/menu_icon_set_active.gif b/route/static/img/ico/menu_icon_set_active.gif deleted file mode 100755 index 82fa4f2641057cec61638e9f36ae551e1555433b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1923 zcmeH{-EW#z9LEnY1)lOwAKprNEu$1zOK4Y1cSH)5QU>_Qup@+!+UU}l#*n5l4I!jF z4;`bdAwsdFnXmA-D;;--gsfM^ZNtN=llJA z&Y7L_PTF4*00H9n&Pag`#eRLWbNUt3%A6{3NSSmL$C01*piW8ri> zwz?2sS!lQ0x_z%_Yu-@__aC~jO#JxxSaj@|UrQJpzDlLy+L|9|dGvc;+jgi>C`3{T z*_Te+cA!`+Mqf|xzJ-FeqyKYPsZ3pVCE}QE9$vc=YAJsHH zuhvttL#IC` z)g{KsB&>v2(yRXZV!Dt4|NYVa!4AztuDYkO8QX}JMj>z1{!T~u00IjD6u<{O;AGxi zhd7~}8qyNPGZHITFenlYbH$^mnc&%Ae-B**FIx(5Yyisp)EA&?tYFqN*;cI%;V_W5 zpdHmf8Xp(ac7%4^$c9`laimv&VuT8*K?*fr;A$2GNAFyOOckyhVf4y|d%43R?x<9E zsKBXJD5FB{$URx)h{0!3sm6y|;)kjnxqjl54rXT-J^;I4{cL(c^Ozd|3x(w7kpq~G zb%5(;G&ql=#EZ({5YvDK{l;SPibg4v0EwcgQ(s`Z_;~*;f_RkrA%g>S(|~8^7%F!k zI3v+AZv>@)_8}0{z$Fxy7y$J?kWoP}05lwMR)-F8D!ecZ1XM~dcwXvbKNsZr-ye^n z4}1*OCMu@1X`45>P7CtO!w2*pHtUw4%&OAx9vn+zdZXZ+zq@rlIe@uK@)JOp%jbet z$xXrx%p4R^sI6Gedrdg124hS)$C_iwH4#1@zo@!J44}4030`F9q?|K;lpzO!D>!YJ z41l{aEgQwDpLGLBS#tn&3m+%AdQF=fk3Qm`4|E9O7&k$GL39o5Tp_uPZiXtgtmvd*~;@rY{K{(DOpXgrhn&KFL)a=L}cpwCOdlr lQTsa`>)=UMU`j>;e^Eb`=2ZKq()?>_ddTPbzx`=0{|O~Nv*7>$ diff --git a/route/static/img/ico/menu_icon_set_active.png b/route/static/img/ico/menu_icon_set_active.png deleted file mode 100755 index 1f61f25396b6ed8a68e708361a9f408e60f6ea97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmV-40n7f0P)5QCgO~@;F3826E1^w z=ZIOc2==@0fy3Z}H$?1p%{QkU^1-~Jca9j^v}_+O{9zq8kDLWZJ@w2ySoDIJlUBhACjjg+2fkP! zV%C~vqHkegnQ)kHoG6uZyo6V}hr0j(002ovPDHLkV1nJVF&I%JrcRv` zW^IWPFd%i!S}~^0Ip>@ibH#6RDR)0e0<-DEq{d~TC{bXm?I2X9^9pLEb z=-}XBYHDhKe_yd=424397shZnER1O!4hQKn?d|Q=t&`Dc^z`&Jl}gRb%#>`a#2aMg z2kq+WYS|}MGMOaZBMS=)i9}*&XGiNESzKI9r_;yB$MviF&LyKPU~oE}gAViA+1cUY zVJsFi-5)br&BKqzl#4@#r>4otNy%GM8rHj9t|7~4JRaZP-gdj)2B&FvcXxAh(_*n$ zt=6jd+5v~z@Ar3ljpm6v{7seJZojy=2m}INuXlcaes*@2AJ?3moXA23qh(YW*GQv! z<-*XQ-3}@Dgt1>v%yuISIWzi!?iL| zod~%M1;x1xMnN^S(l{lYDhzs*;IBmi(fUEH0v#j54kDTeN8}Sn+BbS5>W?~GN}zP>`av0YOU%gc2aJ&-{A9zT#T~ikK0uh7KbJ zJ}^s6And&vOKbLGRW2P@i@LUPL`zt>8eR%KBLkRcRG z5ilu~TQ(VuA}C`Kjd>Ii-jH8K0ptsbmI4MNw-_y=6TTb)fj0YyCSGAlh>sC?MMxZB zhPdl^&=)SmsQC5px~9B>jh9M<*cT*+sKwD_wbcGs9gF(M%&0Tjvc|Ha5G~B9RfuK| zI8S;#YEf1Pr6JToc%o7qT{p{j8~PKB9H T1q~=As9y>ak`gB9v_tnVD?RZQ diff --git a/route/static/img/ico/menu_icon_soft_active.png b/route/static/img/ico/menu_icon_soft_active.png deleted file mode 100755 index f0c8e4a989dbc47422063e6eb2e2fcef24a722dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 311 zcmV-70m%M|P)F+{8MMPo|biLvuofcl? zmW!I*_c6?Ta|Zq{mwYiXc5K%ixM0h%v5Bv)0A6}))tVI_?SoC9tyr^a(+1$3+v%(W zC*Y??sopRG?6{X2Cyv0Or>WkxKW*4ajYG%ayT_^Cnl=Q_Em^i`#~2*ivuN3pXFdR~ z+c6jn-dF(hHd5VnlmBp7wr*rF*meyp*fJQ5yfP0s$n3la`(Dg4eF1)CQU|d6^QyVK ze{-25mm$0F1hAc9e+|}Wzj>OX&e=0DcI3bUxaylDV-q76zX7^5_vCd@Bn$um002ov JPDHLkV1m%}hdclP diff --git a/route/static/img/ico/menu_icon_web.png b/route/static/img/ico/menu_icon_web.png deleted file mode 100755 index cad87fbe344cc63ec8d9a995a36aec114f55d662..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363 zcmV-x0hIoUP)HMMoy`*p^QTgTOymu7%9goHB7S*IhM`jFt0;QWd5Ju4k@hcZ~=-k zHd3>(lk3piEX0cE(LXT4jlJ{99b@{93bt9|!sbYF$^ttC^v%^$aA6{Ztb-;RT)%7K zl`0ic$}z(%lV=={G*i5==0-T8R6gRCl&pdb&Fr#CGe?w5mLx78*90W%U`Vnax+F`| zDiPpPKAvsDqIeuygabT_*a;gJwMm}j=hsU~Rm)8hy)Ev&SmumSTo8=E=CAe#(|G#XTQ6k;8T&RWW9>18k-Oxd9FnL!Lf;`taey z`}gnPy?giS)vFgTUOapD?CH~|Po6w^{P^+x`}gnOy?f`*o!hr>U%!6++O=y}uU@@! z<;vyDmoHtqbm78<^XJc>J9qBv*|TTPoH>2^^vRPax3{;qwzgJPRaI0}eEj&atgP() z`}eP3zkd1h<@4vy_wL=xFqDDfe{Mh5kYH!W09PYD17=2`Ulf0`u<`+E9R>zQ27U%0 zlRdYAp{dD8!PZLNu({h>LrYmlS4CUhy4%9e+@#yoT-u^p)u_AMNVQ2{cgYf6eFnv8 ztDCKr7-S4KH5$Qti#GL~advOK_Gxm929xZXMjhy9yO9GYj$nqy|a;F_AIvaa4_ z^<%5`l`U diff --git a/route/static/img/ico/menu_icon_web_active.png b/route/static/img/ico/menu_icon_web_active.png deleted file mode 100755 index 262302881a9081072cda50ccd334b1ee73b6598f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 343 zcmV-d0jU0oP)e;(b7!GN0r2|$;7CUM9bMk#b!!= zxM@ORMcr$Kx(&}vnD#v--!xp(Rv1-i8?)l6aSdB3`JkE@(MXKBn3!`uQMH+prrAWF zLd~)THS2m3vzjUCwrbKN9k(od;f{{`u2|KbeNNgjr0$dpPV0BsIoposfMZ^H?43Ec py>-{3YX0Vk8DH($^VzlhyFXJt-4#5w#&iGx002ovPDHLkV1j~~pFRKp diff --git a/route/static/img/ico/rocket_min.png b/route/static/img/ico/rocket_min.png deleted file mode 100755 index 2198d40861f6e6ab3cb08a90d6bdbd096570750c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 435 zcmV;k0ZjghP)@62tHOeRxcO&<0u zP8~+FmzR9yRbfinvy2@qWfHxZ$oqnxVjU+KP3dU8Gxzf=smM{bhv(`vjWUy1lUqS) z+HiuE6lFX|ILHi&hU^OZ=Ty>)?R?;Tn6=;p6)Dedx=@sz)Qn4dax2_dP$$eAbB5+L z<_HZL!6HhgOPXa&mr;e9mcS;j`T z^OnZ(DT_qAonrxqXcFdwc*00}vz1~w7FnpnIR=LJjZ6v8J(xlhPSGMRNq_8_iPXt1 z(kQ&o|+JYo%VsS`?yGMZT-Ox{I@SU|0K dG5V-^`U?^@q7_KI)_DK`002ovPDHLkV1i2h$MygK diff --git a/route/static/img/ico_line.png b/route/static/img/ico_line.png deleted file mode 100755 index 330c8a93b64ccc3a2b6ab5c1a07762029c0114df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^j3CSbBp9sfW`}|}#X;^)4C~IxyaaMM3p^r=85p>Q zL70(Y)*K0-phB`okS_y6l_~>6Lo)-z&;LLgsJPUCf#FpG(3}|z4C48d;*Yuk)pB{d gIEGZ*O5VZEz;K`8Udu-=YoI8Dr>mdKI;Vst0RCPfH2?qr diff --git a/route/static/img/icon-item.png b/route/static/img/icon-item.png deleted file mode 100755 index 4f7afd85f97954d7ed25eecdf1fa9d73e2ca0614..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUt<(@8%Ar*{or#bQ-au8vi*ubJv zqv>&gGi<^Jz6}k`1rD-X_IvW5jk{MlY37r=>1NeG#1)j3l=|=OE?<6X{qcV(KWm$H z{=bZ!8Xi~qbZYp&Yt`Sneto)sIX3V}{^!|!ix>U<`AWB##nsN8;Yl%z176CN;fXU) pi3^n4@ML+%#p;Vses*-6U}MraDr%r7?+J7=gQu&X%Q~loCIGpNR(=2g diff --git a/route/static/img/icon3.png b/route/static/img/icon3.png deleted file mode 100755 index d2b25e28db94efeb1a312102d301fc9104721d86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 330 zcmV-Q0k!^#P)>%FvD zO%kOtNvviOYfc;|iBg#)j-6P`Px@{>+e;FqGD+-tcB`LnHcizAd8bt7oq^iaWV3#Do+|C-7I3=fy!Lv cz1{DXvO>lN_>g?zoS0|lB5ghDb zLu_ag@9uRlbqK*ePs$I}l8~(nw8q9(lIMm_G?H~$srJm+N|Jo^)Lh@X*2pV8|0F%% z9oQ^+p}@X9GryB;#|4KrxukNW^3q*X(meK0F>4S4_Bxebw(t*paC1=U#BuOOM z|Ai1*ElIX8wb?J7FgI~eX_mrRNMT{&v$@dNc3n^Q(--emCT?rYllw}6FtKl>Rr~6j9a-SW627;I=gy?X8!u&v5RJ!KY-F+{63K-9smFU07*qoM6N<$ Ef>R!%a{vGU diff --git a/route/static/img/index.html b/route/static/img/index.html deleted file mode 100755 index e69de29bb..000000000 diff --git a/route/static/img/ing.gif b/route/static/img/ing.gif deleted file mode 100755 index 6b87c92713cb4d4d92df950fcdb15041da326087..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131 zcmZ?wbhEHbC%u7!-f9u<|i5GUx!= YK&4DTf~h4CAV8ek2 z3miT)?4KWyaI|*gMxcToPZ!6K3dXl*wkEPTintt%t2%A-bGepm^Zoy8Rx)@+ezSIc z8F2Miz;s@@+~w2v9ZjoP&uiy2mv&NOAwLRfVC!ZgC+(96De{j36=&CFaf1RvE&LQ zfQDN@%d&*FHVD=WYPl2}F;HdQ>UgWR9VOUWD`n^|b=7rt+~aI#HnaQv`+f60&pW^O zeK$$d1Sx7Zzy_9xfY<9C9v;4P=Z??k8yg!l8jZ=x$sh=ZgoG%S%C4@iSFc_HekbA; zF+-wMY!qcmk`fZpAaWJ)0>WSbA`t)j+3RnG&?D^?=|Jl7C(Pz63KQG97P={fhS+p% zz@FbG?}Mv1Yi2VH&X$#<3#FZ8)XEe=bX<64|F4OkHic)iy=hfn>t$~k@ z!k2A*v&Djm!cwbuoM;4P>#FlXfHtn)eozj;Y<}|>Oz=RhZoHkd`_FWMkhI1kcmulq z&5fIf&DV|<_U2rkppL7k;3wlhQ&4fA(ALYl#vgrs&KlFD%keJ#G}y}#=_3E9bOsOv z08k}TdK`g2zsoUTJ!7L4ZJXAWOuyaR)*o56Mb6Hp--+%cK4S{A$9noa5)){T) z`ooUaw)Rc?50L+x!0R}|fWY51G>QEAi8kpDPdX|qnA^#|%%xieu5Q|?r*P0@j|}VF zBEI{ga>VsVa1F1d2_!~*7_b)gaLsm=kz>6iS`Cx-Q{R){%#HNo-ceqfQ)GD(q0)Ag30qI>@yF}2Z*zKj;I?& zhIEZ+VD1JM-7K>)OHIo~(Ro~@?OW6JSY8q3wk%~t+DFXA8sSVJJ>4@KT176>@N{V_ zIzRkJ$DB1}w7MpuYw;x?XEebZ4w@Qpb;L%T#gC4Z4^RLGLNCtbo<2Sy;C=!B*cADW zEX&}_!9T*=^KMhP`DEVF>4j&9K*dmhA|ul<>1j)3*0b$P?m;>A?D3}q3;VyMTWaLD zTdp<6GS#qQ0#o?8RxmR5G!U$0bmjq&KICvaxcThbCZO3gdFyX5Az;?_YuvLq- zSE`j9KdEH^h(r;NNF6}J@b)pxq%+~6zG5i4xr5YC3(7B>0%LHBXGtBL@zLatIN@cz z(x{O4=clf>x|L*$?4)~l$3K5bMPtpA!GY1e#g{f4`&P{Clz~RU*6Alx>Q%0&RMr6; zb+10yM8)onYP2*p)Kc+>87({(j4RI|;B;I3d|;$M8Aw0?RECVoJVDT|etDJc)3eXF zl-RMc1e*13u9kQ8R^Kga60ZI`v8SB}G>E@axk;)?t53T*o|J+0ENK+GbA;Lz+l1L zw$`(Bk)vQrM`KAW;Ob1U=x7Y)6vz%kI2&!n^QCMO z*PN5YYmWCye;E&11^@W1*HaO~vB!umJiot?t>f>7!Kw<19@$V~4RusV!>Xs_v4?6a z9eh6-09f|>sAw`>8|TLa^La1tsW|$=lHSKDv=}>EMr1C=X{0SH+@v!8rNzkapQ8+N z&#~2v1nc9K!mz%cYm$z00!jv6BafRPxlNAAD`sa9`ia)LuWA)gdU@bpDU@BxBw%*F z!2~eU5I;5uz({Aj9+dpV9))w_xl`|$nJv;bfR=8PqvU*6IY+50e^B$;qPvK(Ry_A? zkZd$L7rRCR2+`LUtFmYWqeRtow1D?r+2dTcVU&$}L17iPYfclJcA23{Vub3#A7Hc} z5CBj}_+bbT*^;A5S55EEr2Cs@)2Hg2sb>n@D8jBy!Fg?wjS&f+i!yv@0=NVPOj1jY ziufS&Pf3o%oRdcev(sD^qc?sHlyGT4lu^HMdWfDSzc(b@g8G|vOxOpYKnV+N*vWzn VreKue;JXHW8d%Lm+sVMXe*?P3dK&-$ diff --git a/route/static/img/morebtn.png b/route/static/img/morebtn.png deleted file mode 100755 index caf1d593f0c13d0957aa1c1d5d62bed1c1311d39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 530 zcmV+t0`2{YP)j>PD^`bGWEhS(RgJA_L4)Qy~o@vlX4K~}}wAx}&FotlXk-TL;_FEMjW zgOio{Tv;L$YC=B!&YbOthoTPBV%4zdelL0xw7NNcUT`DUy|y{uh`yBbqvu~?k9fP> z(D}$UC}OYa@^ZuUiKR(evW{-ik#Tp%=c0#{wHmQz+qo9!d%Zs+45MQ89swFjh;#kM zA1RAxNjaxRjDI4kTy*v^X8e`1Q}f59@n1|A#lAD;5uXZTW2SuKyNdm2$}8m#`eMp0 z0xa6FbEXoanl-0y6Ti^4Y-h2{1s`#~qr)6n*p0vcf+~o9#y%U=naYcobhuC71H@bc U)tbC_6951J07*qoM6N<$g3;Xpa{vGU diff --git a/route/static/img/ns-loading.gif b/route/static/img/ns-loading.gif deleted file mode 100755 index 095a1b5afb326e1df20e228b0ec574081450ca01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1513 zcmZ?wbhEHb)M8*`Sj5I~`t<3SFJFH6@ZtOS@3(H<`VR&S3=E3@x&2&2f}I@$T#fV$ zm>D5L5xxNmE{P?HK-$K>q98FjJGDe1DK$Ma&sORE?)^#%nJKnP;ikR@z6H*y8JQkc zMXAA6ej&+K*~ykEO7?aNHWgMCxdpkYC5Z|ZxjA{oRu#5Ni7EL>sa8NXNLXJ<0j#7X z+g8aDB%uJZ(>cE=Rl!uxOgGuk*h0bFQqR!T(!$6@N5ROz&`jUJQs2--*TB%qz|zXV zPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj%k|2Q_413-^$jg8E%gnI^o@*kfhu&1 zEAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5FD$Tv3bRMVDIQ9=jZBI zBo^o!>KTB%1XJkii(hGOE?jkSNl+@ny;uz{4yi0i)elN7&Mz%WP6aAg23b{L2=9ZF3nBND}m`v zLFjeHsTY(SatnYqyQCInmZhe+73JqDfW2*%iQ6rfIL(9VO~LIJN1S@~fsWA!MJ`fg z!-Rk-2*iXZT_6XZ_*3(MDZU7pv>E^Z`}^njub)4@fBX97^QVs=-oJbM=Jl(WFP=Yp z`sDGWhY#-GyL;#Mt(!NlU%Ptc@}-Lx&YwGb=JctPCypOGdgSn-g9rBS+q-A?uAMu! zZ`-%OrteG>WPn$Yr@}!9q`ulo&y1P0% z+S^)NnwuIM>g#H2s;eq1%F9Yiii-*h^7C?Yva>QX($i8?l9Lh>;^SgtqN5@s!oxyC zf`bAB{QZ1=yuCa<+}&JVoShsU?CorAtgS39%*{+qjExKp^!0Rgw6!!f)YVi~l$8_} zk-V+d2kVw5teS9n zYmc4ehZN~DA*|+c?7H@OgQVkX9t-b7Nao2y%#+H|zKCMpDMIFX?7H@0!%WB3A{M8S z-6yXIcAr*GHmdv3&ErJ(uf(ovUp7cOtd_CxK8WPLc!>L~a#rIv4jKGuFi~PRDnSJ9CXArToKrb39b>oT$Px`VI8q-UnT@5n+eDAwmZQbg@Ly4Eu<2 z!59H@i3*1J!U!Q|@N`&UjU#;iOFP;lHn?H|p&JAIz)K01Fu^-kkeDN{nq?eN&)6)` z#4B2;A-MID^K4aNLk&1%Y8B=ItDh5k7U3RFw<(s}r#P#^3gIn6T?DA1g%>m-GQxEn zP*z1Ab4aZ4feA_};Rk$Upx=VP0#|JCl(E^z5o-uNz{3n7M)<@K732~D#<(DcZ{C9< vmgu5@4r1&LJ49&1y}w8<`e^+3dtmwn^%@Xgpii-A00000NkvXXu0mjfpOlSA diff --git a/route/static/img/return-icon.png b/route/static/img/return-icon.png deleted file mode 100755 index 538fcdc6774bd05074281f2d16317da614cf2fbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)i!3-pq%)8qIr2GPWLR^7#z#UvhCg+EpO+a jY@$CeeCu^F{>ykRh3(z5CGrn}u4nLc^>bP0l+XkKkxY_B diff --git a/route/static/img/safety_ico.png b/route/static/img/safety_ico.png deleted file mode 100755 index c3fd93edfb760abdd36d2aaa31ac6fc8c910449f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyegQrqt{}n1779zP6qZ zU~qpzz=9770vi$<3NGZ!O%6^4s(R?@;uuoFxOM{Tro#pT&gM(xoev1?`tN98?mjt> z{db$rYssg*Cb`$YGaG!Vy@cG6^B3C-|Q%wFelU8%Dnqg?9T5;E4{3b?cVU{LB*@asZd9^Y0#kc~cm!|I=DpxmdKI;Vst0Q;7#oB#j- diff --git a/route/static/img/ser-icon.png b/route/static/img/ser-icon.png deleted file mode 100755 index 6ac0130be1f388fdd9584c2dc7b6924324013cc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 275 zcmV+u0qp*XP)HYyuqjuH$)IF08Ewj7p{}#Yro<#!7!I2< zxZCd0*U_D??s?*mNhdy39Lq(T4tr|&R>PO=XrOXtIl9w6KrOXfUd( z94YQpWh3DbbCF^R^O10?Za7lpun`G2>P8~PIMyPeh{Z^;j+sd4^X@VJ(XHY|S2S3` zsZ9LRtAy2Pa)38m@@DP$lz1={O_sg-^Q54JXM@qol!65l5fL)3ac?Zz(c=P_(Vna$ Z(?2!FxoTp$Od9|I002ovPDHLkV1o5XdMW?_ diff --git a/route/static/img/soft_ico/ico-app.png b/route/static/img/soft_ico/ico-app.png deleted file mode 100755 index 1f00d67120e07540f9a1f6e22fec28fd77dd6b1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 581 zcmeAS@N?(olHy`uVBq!ia0vp^MnJ5=!3-q3*F@F8dl`t- z`9MewNOE6Q<+}u8^ITTpyQIboLaJaAB+CP2Ush+huK-j4)Cg3-b6FiI%XbAx0%cXI zU*}B%S|M8!2m(}Ip&WqB&wZ*J(Khyf? zOi1IsZ_{S-I(>VobN66LMrE4mlfG}UAI=|mnlGw-c6{qX#IrIlrSl*~?*D;jm~*U;Ru*~iYQ=}8Y)$aEi$m9 z^u#>J32_2@Hho+);d`6MpTA$vggLL&@vW=34>}jxd&p~}+L@CqOc~FcO_LiHj>jxm zAsu&Pt>d)Bt%t&^PlSIko5)xA+jbm8m7lK6(@ zTyIOvV;I_Azn)#5pP$U{QW~aa@>#N*Rl@M8>NMM9d*;fC*)w+93;kZ2Q00001 zbW%=J06^y0W&i*I3`s;mRCwCel+ljFAP7a(E22X6|NnNQJ2p*5XKc1Fdmj2gI10pK z9>O~KTloYa*nJ_O;rA~mOj(!3V zCV~fo%qtB*dJhB6tjNGb;AobVnNaFl&}W28N#WQjDPAV*?g*CP&6be*w5!6KJ;5Q^ zmft;3C&#j+j3A0Up7|p^fXu;ob5N9tZ#^{wJn^qy96r9_(A`=1RmK-NM?obM4}fofyb_z<`5wqXBos zjMk_X(;`>wh&pgc=Z;;(?G00OsPS1Oyv0*&LsAHBQ}{d2k2VAgacIo~0|sOI{%qG$389DbQ--(9P}xh+S= cWb1FHC;cLNHdPx# diff --git a/route/static/img/soft_ico/ico-cmd.png b/route/static/img/soft_ico/ico-cmd.png deleted file mode 100755 index 981e03f4c501848994f6c0476bddff585368188e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6YCK&WLn>}fofyk|K!K+vn~{5K zLu=H5Ly`UAH;ryZXZ^vg0V f{0(>R`M(UiS1yliy;G|WbO(c{tDnm{r-UW|uj57F diff --git a/route/static/img/soft_ico/ico-deployment.png b/route/static/img/soft_ico/ico-deployment.png deleted file mode 100755 index dd2d565aa40c2aa2b887e7a602d0b53c5410855a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^#z3sW!3-o*@{AmSltF+`i0l9V|BFch=N7!Q0qPYf z3GxeOP`bPO|IE-;E@^_sts10uO6K0^g~%zy9T0 zH|F@6tv&em+w|to7Xxe!_k{UqvZdEHyis9qxST1}@JLzoz;X9mIhSkGe)wNzak%sE z@R}F~qxUyod9JFP!*n9c_{lr|lzZX3QyH$mVsiK;d~ofphTC^sx?X&L#bQubp>%7K z-Swyd55eC>O~Hz{CK-wU^j7q)c%vM#MUnk^Mxdw4GG9)9jj0=QYECjt^x3jZ`D?%A zicQmdKI;Vst0G4dK2mk;8 diff --git a/route/static/img/soft_ico/ico-load_leveling.png b/route/static/img/soft_ico/ico-load_leveling.png deleted file mode 100755 index c20e1edc66ca4cbfe2ae9df0a794ae7de825c4dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 477 zcmV<30V4j1P)0;H~%ShozMkYlPpiW(=zUU;sI2SgAY$1OPCp>;xzyN}y*?0Q&3FCuE=O z67=i}0K}dEU=bh!M1Tko0U|&I2)db*odPvPsl9CGP62kKr!4_EsQ5>~Ohd?UUg1)Q)`~0YC!)b7E`_0Cxa5wtE7UL;&u@_@OGm&fj-A z;n?240x;)u`^5xa6Dv@|yw7Ye{T^_0c23TNE~766KzenG{bruT*&`qJ37PX3G$fC> TAVLd;00000NkvXXu0mjf$&AUM diff --git a/route/static/img/soft_ico/ico-log.png b/route/static/img/soft_ico/ico-log.png deleted file mode 100755 index efe1b77c1695620ddd86f55cd7b858d2eacb5396..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^DnP8s!VDzWZ-@!3Dhf4 z666=m;Bw|}=sV+8g1gg{I{wcrFS*dI2b5dv>Eakt!I-Qd#v!T9=sJPJ+hEg#mCOMe zTbUxgs}&~9xYM9>B#~2PN}#V98|T7RMu#-7JUy^+F*DBtr*!6>Ch1KL%NRmB9_Mn{ z9|?6cP;9G}-yq>1toV*s!pZMy&-4Gy2Y&JzEWdW|(M~BYrf}H@tCkg7b2T|KAJVDX zy^mcm?uL`7;5Rofg$0@og3i_*+Xan%6Z|a~WxV7z7S%g2DXf!)!C+T#G?Q{;Jzopr0H?EWD*ylh diff --git a/route/static/img/soft_ico/ico-logs.png b/route/static/img/soft_ico/ico-logs.png deleted file mode 100755 index a27db7bbac950b89990ad790ad48c6b58904b212..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 753 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 jGw z;f8Wr_KPq=_m386!3{()h*4jhaby`EO9;*RN{;)mrjr!UU$J??HiDfGfHv*#KO%8950Ds zJ?p<0>G>!UgYD@1atf&6 z!(sxE4Cy>S{?ugK0H41{ftF)uwWE9uf!>L_>T-Js3FE9rzlOk|$nQUQ`=CVx|JmcG!4sa3^dIfAw+yr_&^s6yYHz(Fz>+AN#Gvvxn_Bmd zP@8lD<5Ln>;_%&W^>F@VsDZOjCs3Y~!0n;WGj+7$J>S*dKrG4lPPzD@93%SxHW1oXHVXhL>OMA35@oC8S89jmD09rCXDhq2?l0WOHN z$y7g+&x`m=z=JJu8U*&o83TkROC5rUO6#Q2OSOit8I;wiS|2u@hQk=hq7|m$RC-5_ zA;}H^k;PN$M@`kN(_I2JM(R{us*y2#M)Rhp5l}r-k1u+bdl~P@JivV>0nQIgWtR^z wb=kxgmCg4*@})dBXPx^>fTs>2u;n*?0g?fl60QGr82|tP07*qoM6N<$f;uswk^lez diff --git a/route/static/img/soft_ico/ico-psync.png b/route/static/img/soft_ico/ico-psync.png deleted file mode 100755 index 31015316d02d093a3674fccf5bc8debc1d2c295b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^20*OA!3-q(BRidelwW{Pi0l9V{}mQnC@i%Cp(U0e za+$TlG8kjNgWM9Z)KV+?rPd1bO+gBlTSzT41~TMuKjv*Dd-d;2pJD|YB5_mJI zv(MuG|3&XY4=iqKdRcE8vNiFE(E^58mLAqQ?-ykXsx3)oOiPJki8wqhZ+Tqz(K9=g zxeAmnt1>9xUCZ);Z$n#9>BTJyyau9Q_sCy;!>*mcwdcvi_N_px7(8A5T-G@yGywq0 Ci)Ebv diff --git a/route/static/img/soft_ico/ico-rsync.png b/route/static/img/soft_ico/ico-rsync.png deleted file mode 100755 index 2843739f1974fbbb88aa11b08b0bbb6783557b9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 835 zcmV-J1HAl+P)9Bl)=<$+3;1{%*PD0)kS8qW@Ryl(Kk1+}#9@m%9Oi`PZ6(*95F zo&k!c;1X02(h3HS)Sv>ER3w-Sc+l{*fvF7F(w!Pe(8z`mHoPRlr03UDyw>=BWN)5) zJWwY$%xehk<5>ikH0L-%?)1u922^wdoFWYk6hUkZPAsj7u^d#=2Bwzjt^8Hd44`Ln z_IlCnqGU(0t!M_&D>7ew^6atnm zCWJx9?@EYh<37a*8mQh=!o-E&ryyfAGOQsyX*;IVCoqO#JwsXOKu4Ad1*|pkd_5Sr5-T=NnM)eKv|KZ|)XDKJP~>Xf_r0223ny zg7^h-QPu!r_4b0s^R)%#u!82C`H^0D@gYC|!sZuWJ(AwnbD*Lw;*e1(dX|F5%YH{O z2Sgx3LGyGlte`204e-55cmlU)1{OsEz`h!RE{T0ER4CYJHXefv3#az9_v zL>@rXQQjiBWio#qzVH!0wz**DEo{%KZU zW=KK{)Z1@#MBnt0g+n5b4hsscVQzR*qp4|np6CO2laB^GNRP~2%6|$aC?|VBK8yeW N002ovPDHLkV1j%gc|8CC diff --git a/route/static/img/soft_ico/ico-safelogin.png b/route/static/img/soft_ico/ico-safelogin.png deleted file mode 100755 index 452dc0fbfdc364ac69f1e1f4bfbcef5ad3ffd777..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 603 zcmeAS@N?(olHy`uVBq!ia0vp^MnJ5=!3-q3*F@FAc z$L@FkXO_FToC!6)yDHRJ&{)YuP)SKJO{qLoumi|?d@nM9fq}8g)5S5Qg7M51)@g?g zcw7S|m&h;odYbs`@B6~OSU%pvx`(-MUwK~BafZoZhv(XIljA#GL%whv&s$V{ddUix zGbYd1q$#afBYE=HuX@AB6(v5|>dO^3&0KKeZyV!fnQ10lczUO;kyW{LY0G9d3waf{ zVymC>CtZa+#pONsG)ca?Ki|UG|H;`qyC?dAOZ%lqHXCq^@u zp3;fkr`>5V`4rE`SkL5ndU`8t_L#o+iMhG(m&tUO6Ib(epYHrqxqaG)zLuW~nGLVw zikM&C^H`D2>B;{4QYriIMFy{gKR5=x6=LR})N|I}`r+EM*W@@4DMY^6rsH^R!%r>d z<;_o`US%D-%QdC#U4zBcln3+LI(5z40yO)4LtDyENE#o!`E(V87hf-}h6w{(gAcGs&Oxl%r6^ zlyfWeG-O}Aa@qc3SFqRjjL0jMZ5ZuTVHXY;B)Sc+L@GTH*LyU7gVAbkLCwrKVP{&b2$Mm1_V^F0#yjf*$8G@lq<|{8(*{)F%*0000_dRg&*|=G^s$5RqWx(V)sZ zDo4g_2u35E?Qs=OfGzk2!B;FnG$-O~Awig6Yn(s~d^Cwa@hZ*)xe+&rXnYh#e|$EH ze?m(XL41=ras`U>VLYtDLo7xVZBRn&!)yG2Son^&*oOq#9N{q?53s^mK)){{VjV;| zhPc|mSm=#$>bYwG-OvWj(H33M1ktrQqGG4kScM3FUyRoF+Knh;$1$xo+j4E684^Oh z_=YJyi&;=>>LbL6KSnl(;-PU-kKaKW+v5tYGkW|!GC8>9=K{tvyEr&J)}gV7@h~`V z8MipYEys7vaV)ORQ2QYmgU~*WqX+sSY=8kta0G%;`ggvJPljMMBB~#!HFgm63u3Yn zIOw7pR`Jki3Bejv(8BFZmSiKuMJ-I@+Jp`94WIEI^@9rAw!`th3XY(P`po{gg}*fZ zKhAS{96|E{gVJLk+9Q-*@l?2rA83b=$b<{{hL*Ojc3vmL3B1Kv9L7^LbLuz`jiV5y ztjT00Fv0~r5X04YC0x_u<4ocQAubwW8CTo5F+SoUN?;ZQ^AX*0$*slsRXhvc<1{j9 zcP|ql=#MbI+~)VRP%`XA=^*xj&8Xw+d$@Z@A6$2@kCEM>aJYc??O)Nup?G<`Hsbps zxQ$#khm+wX1UrlmZfM(n2uh#;3ZbwU3ZWp1poex#b{&a~e^dmQAj)$*!BZc3h93}I oL}8(;uae~02C>mLBsZEr0VljzCdA|g82|tP07*qoM6N<$f}2t*Pyhe` diff --git a/route/static/img/soft_ico/ico-tomcat.png b/route/static/img/soft_ico/ico-tomcat.png deleted file mode 100755 index 2e3e7dd5b08819a44ac138e695473f7bab5043c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2286 zcmY*bc|4Tu8Xj@7rFx|>X!4Q<;bbfsA&jy$mSl^pXYBiE7@U+kGmIL^(qPn=wXw`m z)+T(VEE!v+1x3kHK3UQTjrpGHoZtD*{rkQ5`#kS`Ki7R<*ZutQraR(~OGzLl5D0{n zjWy00ys_XxiwOgNJxM+Tfe=PG+Phc@1cHooT3T8f$5>ccY_5I;fFO!i zg07`Fca;%jq=dl{5OQ*I(#2S!(R#=2Jb$|$tg0ptl0n6baYqHq*>ZAn_T~p%Z1qqm zl#$lHAWxIySl!hR8D)7cpojM}e9$dOP6{#;EKt~VziTA>`&GUJT)#Ijk|stn1Dti{ zM$fiM~eOc8U#U?oHVz1ry1eqfxC-{s45q5iv34TDnW@Y31|5 zMoYtIL3=<75CVk&O(553cbGuH=xvS3BAG`-gaLe@_AecP6lHbwNZA8Zz0t!hfzCF{ zMEd=}m~5cT>|lfm=I`FF{-7B!5g@|)i^Te}0Lr;6a2DuljCHoLb0PXF7bor+y`xb| zMagax1)?Ry1_Qs`rnl6Nnr+nREmt;B7uVN7hI;7$;-Gq1Vk&6t^a&IrS<4c$9q8X# zWzbrnKu_3fg+=n7lfXDE^!84+nT&JL;do?Pi24B}WQMiGpRi21kOQXtimg+VuHs|2 zEsrEuKt>JM$@epNXND`c)W!nt(;U=jx$;ak++Qv?Tms|F48c9oKq;h#BU|#&v2HRi zN|0k!a=F(sM{Cp{6`|9kWvA!2t(jc!Um$%^4K(n%#o8<@dq*z$iQ$2dOZ77T|RrC&e z;=F6+Y|;?Y!* z*f=OwNi26~fY?!SgnaUJpIPa<6UO>$qV}{Qe`5N-*y7@nJ9a3j(DcGGk4a*Gr>f1L z>_n%nJUcRS=2)>$_F!P}y|-6iQgk$?Sh^)l{o(9KTPTdwXGBRpaN?mpWx+xl*1(VhW+-LucudE1`QK2$!ALVf9tL6(a|k;qG~k+>N)zHj_;|y zaaZR-)Frx%Zrft32=75Fxt!rkfB&AOGqS7B=u7H;4PyKE%>n+iH-;gD)4l9j@p+DW z6$M|RnWad>XR2)+B5q$JQ$k-iuNtgZXb0YWtkV>p+}{^7*0maa(Im=4$z#^9)i+@& z(J8-qYNhXSwZ=s5w?bAv;uY_UeSQXYSskAZJ!nj@HMdqJy(>1;$viSPU!l5Ij1t{g zn7e=K2%@+8;^%1zT1P>p>K60L^*Wxgkk{Zd%csXADB~rja*B&?*E>^E;%41p?3lV) zMt<4XVEcvc34i9Kkx7TV4U2B}cISp<@`pvNYv?rg&d1$D=3Ct|k3JBTlU$6aiXVO+ z+K_LmI_~;#X^DTP?IEp_ny{Z2?oj0{ZR;cqBamFE4WGfR-+~{6jlNlv)*emTu3eL|fm#LYQansfjSXi>^t-8>l(g|)@ zGUt|b^LL-SGRAYXrYHIVw!KV+sW+PadAb#GHd3ijQrY6?im^Rjn`|Q{ex?UsF%^Us z5-V^huZtm}kWj@zYW(CjawTv3#2z;pAML)@nc{ow`PLan_N?x{-^OcJ{xAus`kdyQ zLyz|Upjxpk#;`w)+48iIer|P9q*1|2$dJ~Rapn&-oH0YTlLk`++=G}GTH}%7`P)mo$jo#6HTU|u&YnAsiqY!^9mm2lDst?1 zm_+)G**WwJI!NE=WC^@a`YL&-F0#jyej^SnnBDsS7*7Pe~K zZHrN^eb->95%d*qq=(#yxp;4$&0H_);z@Y*x>A*lkFi+J<(q5GtL)mYfr+5(No+!K ji!Afs^HrRflJBBrxW=Z^_ia4z=Yg=X!sBW!d@uY7ra(sp diff --git a/route/static/img/soft_ico/ico-txcos.png b/route/static/img/soft_ico/ico-txcos.png deleted file mode 100755 index 9bc7d21d27736377b45c6d04391b906020c4665d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3136 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 nQ%WE_jai^oX3XnJ0x -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    - \ No newline at end of file diff --git a/route/static/img/success-pic.png b/route/static/img/success-pic.png deleted file mode 100755 index c46be849e58c8557cd8a448d2d3ade843c985d5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3726 zcmV;94sr2`P)EE5_+mGYch2znO;?u9|;m`B!;rI0O{QLX< z{`UR+@%#DR_VdN@>8tDDk>uBY;Lv#A&2-+)eBjWT=GnLH<2s+)%EXi-OF^{%$4QWeBaOG`16|Q z*}w1S<@xpR`}w=?=8EIfz3=9q=iABg>x1FZ=lS*d{{7kY@pj+MiR079@#<~e$!FWg zyY1!Q_w%{!<%HqUwe91JCN-(hvCw} z@8{$9^|R~ak>%HJ-O23v_to|AGb{k%k%AZ-OF~}&8g|%_Wk>p<=Kbf z)8Y8@+xPN%-_LH_$-VFAe&5f$@8_1~*`ep%tLfkV_ep>N01UcGL_t(|0qx!STiV+C zHt_q^tteEH;ix>zp&$k~)*;xaufe7ZtUXlPqS(Po73j!T;;)|K3;l-!w4j85;Kbe7<`lpZUJy9rgIf0>L2u2ZX{u z8lRYqL?T4QZkw7KnVy-wAHCCAVsr87dBzx-QhtmBS|ZQLVOB{`e_A9q10xkXOsDSUb9 z?MH@T`p)Qe7`vM>9Gux)Ti+X}*tbU(Tz zmuhYl`uynQAf&{`g0SHzn?iTY9`a(5>S^%;dgGn885u)2aQrlk-bnfwV@3(i z1r{4SC?zr!KyL)%^gZQ$4o<}q2G!v_b%Gu_eXr1G`WhS>9C=-)8r|ndk3=^z8FB9( zhTzEAJTFt7co;o0_(5kR7)dzc*k@t4taT%YK-gvdL*`DQ1?=JRI1U3L+Fw75uF#5lutSrqKgouU;%t6Jsj~C$^SE z3RNqh2a+Fjy3HGa6Ke`p`FqnW)K)2?a3Wl5Yw8esphPVwhcP%Z!AQ6W!FAzFPPg%U zCvaq7hpNWlx8 znGCgZ1&93A3+oGTS#(jMR+Qx+9GW($LOX%G?yt0N7Mol9aOkl?!aLWyDBd!K;tRm3 z#cGVh5L`8TtWZlY`{C3PBOwo0UGKC>(T%dwLN&%s6mI&+7`5hyW6=mzC{JDPXsL~H zIQNvQ?!!eBoLbXI(FK!CTrLjxL{n{3WcT6RSfof518`0F;cIG5Nkq{FCGjwf=lgI= z>=CCnv?FvwR`h|d&*7F&H;meOfo^y_#l*wS!6gGXMQW#nt|%>umNo&GJUM36#?ltL zBE4%+#W~z@x}#7VMk0!?c%q3rC4WV8v&5+#Z4BMvU!sbXODW1_a<6#{qq@80vDqDJ zi~j^&68LFG%rX1nf>UZwoerW)VkBIkjlt9aWX+LEZH?s6E!#$?vgg{)>d370vrwqL zQ@yFTs@{r*#tJK!dp2FOQ68S$52GL3FY-)e&3TctPr=&tM;{~I( zj8qWj@|$Ale~J!5dsLA~_#phE0GsA^s6A!xK04SIJvCoP2VoK}gSh2jo|B*) z`)#HmQwQke=X)xBW_Z!T5i?o7=8Hy|>g4UEP|DO^-p15hxroBtnt1(;U!X~PA(JUn zJ!3dzCk2_kd~qiZC+w7DC@7qYjd=$7gZe*nepr14kN}<0;V>dJQtC%)q4>IPMSwr_=%WJ;C=T z4C){of<568t1-mWP@bpJ+Kb=)j!dbVA3)wHQU`;uDQ7vxJrUzFjW*6zwM^#nX@AEM zuj8;QCPv`ZJj`XqEj8)Vu2q#fc)mj&7?V-hRb4i{xq`VARcL)^>pVjp@FI0G2-{v& z&(og6+zeGHCur}*@5!kW?0Z(UI{06Uf2k*7;zcGyj_jpjUrKa-BPTF-AY$-WFjL~Z zOb(nv!U`8ht->(ZpP`ENpm`31ku>8EkPz^x^cnXCVeVHNRqUqGAqPp*`XS*+V^)9G zW50U*R@;ZUWrZZEzlDr*CjS2nIvG-^;>gn35(&CD2pKb**3XPx8`k%@ODf5TBq8Op z+EMkPlL6Jn#>=`+_9+{X5*x`>i>n>w_L#`Lk4^$B2HAEPhLkyjDu&V7uwi4J9Npz) zmmhN8ZBoT6boOf1&U_)d%gCNB$ob=+6mfqC(Ak_?o#euTsF#r~pCBhzq>4Yog7AfS zPqe?lf`F~}%6JD!SDY#~V8yyP!o%aRBJj{4tN2J5k{%YVRm_C7>WRsfpvAQ%CyR_% zkkm)Q%|5IMBvh*SD=aD8FpDW3rXeX#73u`6Nr@Pn%dlo(q)3*`LQ;BPRa<)+MWcM=1!4hKp1&8H7ax$DBG) zOOTa}h#33~R-KT*k6_i0HAd!+1CX_0h==hg4a;_mRIv-o4$5Ro_dr&mdW%#Ktb44G z@YgP#OeOUq3R%m#txiXXCKy$CVci*-`1>_v#cJh<3E1(EO{$omgLSjN|NZZ&H6t5^ ztlI9yKgVE4Up2ony>BOnIC6E{8Dtf9NbpK8-_IXV9fFU*8SR17oLJW>nc?l-ZbR&A2Je#A?w_zHIF=O2`w7V z_Yu<5MG}ttAZ^i*E_0=~43R>5YP-QmP+ve^?pP&38?c*$S5L#p8LyS4UvI&QYpPQ1 zHuKOHqwgI?A?2)YnmN)BN0y5uDAQ5clK#)<97B+jtp6dH$8hMOTCU{?!IsdaLG;g0 zknxurMgo5dr(#;|rsyka*pk~biYt;|#4eJ+Q*iE}ekJ7nBW&s0dou?K1ILW2d{K14 zm8z1U??z$E&r>0pXYrC#%@Mj{G(%NfJP$X`-Eg^mUvnu&RjTg{; zs!^Zq!y)s+v7(Km`@##PQjMC23l=j}@A5vnYw z;F5QZ2MoD)4qfwN#GpF-qD*JQ1$4#iu3?m^M$ZnwMFT!Y_4J$N92`5{EgMu% zT@S!j;RR}PTnY!_#9*qds?@*(*LljEnk>ZjAt`XWOHFtpi5^I1Uo&d*AAdh26g)nI zQ3Ip=-yC`)zQd`JVI)eYQP>iGmC{xjHClatik=zsa%!S(mM=zUL$D||zL4OYQ?r>I zdMMM3`}i{c`dr=Y{P$If zR`t(RRi!qTZiepT%Q;K^{%7Ra(#x3wzO1ph#>pAq*D-u~;p+rBaQUMs?udhlkw_+! zA$vG!Kb?u;F8S&gqm%PXON=a1bp3cbKY%-E?%md5W^+@u5TX1b^>GY$R&36*aY389 snaFPGI@yoAE>I|ZGSl}*wD*H=0Z{shef?EM^#A|>07*qoM6N<$f=Pm_JOBUy diff --git a/route/static/img/tip_suu.png b/route/static/img/tip_suu.png deleted file mode 100755 index 3884ec96c2583756264898c05251821e0550d87b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2862 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 diff --git a/route/static/img/weixin_zz.jpg b/route/static/img/weixin_zz.jpg deleted file mode 100644 index 4d3c6105b8a4d6f5c7ceb7d8b791fa6f460a74a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60575 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

                    L%I{K!v&F%RI%^KB2RTfYvn9X)d-qcXUyC0<$Alz2VFRZb5%!Iesw!P zE|D0dy8f?;EwFK#vkB%^63?@nL#Q5-^mkaF?e_S(&LLpN_{xV@J&C=9+uAfd9h(884bHpheuohmF9EQ@hDvq7Z4 z(0iT}>#&2F7rnE;5D+aRgF&Bu0=kSWk2;qVmr{ld?)s6!<0ix{S`&C?(3^8sWV@D# z4crr#q$e5q-x{nd9W>?QZt{Z86#t3Y>Hjbr{a?9L?*GBvdq*|Zt$U-P2!cqYcd1gP z_YNvunp7!4=|!5f(3@20RS*bOIuYr;7Xj&l0#ZT;k%Sr$;$82%_de&`bH6+M#<=@? z_dfRz#tJKBF*Dbkb3OCi&qb@H0Je*b2_ z&Vx1smFEC0{)YcsQ~%~MaowtDe}UnuVHxrK#SrEX5ekxwO#A4$Z{%18_p8=_CMkC* z;anPoH?Jb(L zy_|{|E;Wx!%XSqNq-%`my#CCKjU;)L6`@?icUtiGbHuGdaJAWx1EX6=8{-5>2fz7> z4Ygdb9#4x1d?KRp+Chvm?oQRs;nh?~lbKyu07VI!QC-S5$K=65ryAp-v}etDh@9kg zp>lu)fLcHRc}I~o92>hc^tvEZj;~h!=)fTqg)p>g+S!Sq&A95IjMyaMRQ zn;&NsvjZqIx4H7gW{Sq1>GZ$jOP+?$mQvQAlc7~tiyuF{T zx7MGm=wR#@qUlgXgfbCq0@V(Pu#ex}SNSmGC9U=?cg|K&4iP9MXexED{Z`o?y9gF; z<@@7Fe)(!!bWic4dQXY|E*Qm(gTv+`-eOoE$2@z*1LatO9Zv7n^=gq;m}A6B+?)|F zbKK{tO&>Zrsx#ybu2IA6cMONyQWHp@rY4}i@wwxlH7t_D05ggt%bTRLPH z@QFOBai>J9LR&Yy2e0GdxSaoVu>Ch%7AoWSu{I2k9~E4CK&p!Zype{b4SaEpFU9T5 z%C1zps9+!%k+o15=S%N?fiKQw3HF@Ce;nHF)4!$?hyl($jo!VR(>+M)I@C-GN2 zjXrgP(bvTAvc7lwjZQ8z428yT4!$iiU0yrwf)qgsks%C!XQ1Gym+uK1ER2k6cM&I9dV6ov?FBbUQp5>D9M!hRW~AfIk&9w3P{SUwbg{QACt>ahHHn;$36hzme$##_f- z5VHE2pn{%*MFvqrHF`56e}ObY$IwhN@y#KD5)?b0|5QQU@5nBHF*WXS=p10gw|y#4 z0rgPE-SdlRzoD$^K+ZT??VAo5w0B{fsBQMJ&C48F#Q{&=M9UD_#@MZPK}0U=K(wxUk-(dx(~~!(_xEL`J|FUz7(b7*$sGs5iRXjl(H7d?QEKsrPd$!754KSQ zK8)ltl4WREZ|ZKVk?@94je8pquHNDg{{_0^$}SKui0J8YpfLy*^1b53DVdX#Lyup{ zc;oS~afH)icLy3bSfjNO!3pwf_iEJ|Xlkso5faN>#G5wWAY@R(%1&2Z?s0mc_XR7% zG~=6VA!VM00DdH__bz3##=eHD6o3uh?-InH#YqAKW-Gh|HbhW1y|Tg(nVmHfs8$y$TV6l7og33HNT% zM&P`sF6S9**G1X&EK`Ar<{xZr%5TQWOHLH9hN81C5(m2I+Vv2ixdo?Oxi4qYYZITN z<$}+C-h?o<|0^vYXm|>^V2+g6u3r6LbHA{K?;TR% z5<_Y=(l75OMFnXLcADDlHH&UsZht>0dR)yO;dLMU)!KRVs_t@rnkf=1ZR76A9_WIq zuL~sF=InSY*90Nxd@D*??$~+53hsCLO|e|;h*Pb_gDJEEbnK(xy6 zSVVV-<_mUt_b`LUT=7(=EOb$efMI{Y@AdpA#pc9{1JRT7*dQvX#z1Pv=Jk7xOlqN_ zquL>lI6D-ae6GumC;c0Hco2Li)h#FsG>LCs5Hty)>#%uk9P|ClsoO2~pm6q%(Z`=f zt|3$W^lP&Nqt{Bjv&-pVvx89J2ObIn#gBjxk&5?<{mjLDH{{4oCuQDk# zVs_GsAgfr=sXAvB{~dIc268Zqy}I(_W)EZjM~?S^+TivA&!=`qlt$lO;Wnq+g3Hno zxY3*DB_ThgjfyA#YCZB_$rr>*SR{ebbaNcR!#i066j9102Pwp>kGWY6Gnjcf27muoTh8lY?f+%6HA%=nGSwd054K$K1_tNt}_{e@CGwI zOgFQO1C)&?{|F8Z{9nKISIzd%q7>0h8FK^W-=ZM@FhEGd{w!81LxQmy8vI&L8tU{& zrD>|qk?J99Xrn+Nu;o&;5Q5eLvE!B5PgmQcXACUSNnHurB3P$Uu^18P!#@A&gUz3T z!ws=M-j0WBO0o=N!y+WE-SuQ4w(SNd4& ze$;^a2ScB%Slq(WwBk<&Z~SJ<*C!F|#otdEXa=B?~BPotjf?a>1w1jw<8)gX`Sbdf)_S@%`T;c(<&6HVR zRXhbG%cobtZ)i6Qa}d?H;)vS~weA%aI3>?!7u}oo_++I9l``OHX3u^n{g%``VKD!v zMTeEhV4<)<8R6Dc+j%jS+-c0#^u?FbVS=~48-DXO0rYRw5IhCD_Q29;6z=;O0CjOz zf@5lit|gl=aq1O1^~t$*KZN(x`{!k3i=ipQU-l{YWz3aV3H0{BR;HdtzFRO3Xw#X0 z%Y*qKvRrFO=c7^wM&%90A&TR<+ORpis(Fuvi0aoNaLYu8_fg^9hI}n#S{1lRTc3;tU2{Z2 z`K(A6TZf~HxNw6k-@(Td2?LZ+;;(GRbLF#0_Q_kggIOfu?*i_j_WKq-+-q#3Gd9a1 zifexn7`+k15+ZvUTj}8I6iB#aqWWQQojGlTw&8CqT{lLOD`xR{8FBVhCzc@r=E+X) z6lr?fwo6W&t(6Fin++3$PAiMqOUe8hRps=~&AHuid4kKGVW0Vy`yRJ+#QW3>N{8<0 z+tzc%RqW{wDpIq4nZ)mFV~R*S)e2j&h=ZM))&`fatvn7d2l&?QU}R z+Iy!D!Sa?L}h@RyPLd2D&CX~_?{PI0Yu za2t^P{S$OhnBhEJWU*x1668@#+uK zAzVoTbj>{gvT^X^OE2thTrjBjMwJbcxCN8V&Q<>>(EjFIjx_Xl910R$!h)Ekk- zv(#lzy{S!VykxZ@=9qO(s_Jebe^|BT1)wO6*2Tx%S$+8^W)bUfesa>A2Te&1PwK*x+d@u?Al%vWDZ2`WN7G&(bxX5-nCK9_BYCeJJ9~*`LG{wU-S*~sD7U&>Wdpi?15v^ zj`lC~pS)|}Xi0oEf0|Srd=X|k+ z^mZSTJb^v%=C1aP>+0wGOta1jmd)|j+5kPTLxE8WT?wd^L_za%bXirK`ps&ze4C;mkKvce&m+$?cV#*ew84_D?mFOfCdjPb4-dlIXO!q z)R=uK9^XFZ!aV_Q+Ma-o_kb+ZwJ;7gU5$=)8g(YT43r^rOH!S} zns?KS3lhC(aAz;CSZRW7Et#utGi@%_%H;sK&m@HJI^T<@dajc)&XjrhILBuAY(jga zGx)P;ys3GsxL-+srm(n=<0^at*nRIaE7UzXA zt8ven&J7a~XKx%9V2S#7y5mi|q=8$*MfdcR3loB zEhxw6Tdq@7a1;cbYD7C@taO%yc>LYW1z&P^UIgo_sp^dBkT8*O8JnNA{z)#E{hyTv zUZa}AP7Y~zHJ|$)sb|^SFz9ym&2<`dpqbadaM&O47PTn2v5^jjB$^51Sh(sc{0QIy z5~rpJm1`V;Xot=G3xwE}U(*Os1+<|re}PsX$zPcV&a0{Sc&uropRAAqj4`H7K(84c zjBd5~#ohIIjF>HmIKYK*^xXLfk!2DvG{!#u}96B14=L zgdoxf!35}8{eFQ~(0_J_UKfe{&fh=QxmXBe0Xt5_fB|?X_ylb66L1p%`suMe9){&V zS{m5#iGFH0>HubiXyXs-ly1(oYHif8=ExKpTm_r)kXh)W`Fj?E!$k_7+Z~Uf9ZF=) zyZWZvxH)xIvot$;V>v6Bc(%Y40XN#_WYpX%9L}8>I%e~%Dv8p0M=d*Hg&{&G&O*F& zY7Nu*k{ZS-Oxk+&SlP3GC>n}~3)8zzI1At0cqlFP;DCDc#yyqtAqr7)tc&9+^y1*d zfe&M@HBaofztl!DVddOdwD8$6qB1h!P5-pR;>#4jf&1(SH^H^TJtXF}=+3N7T|^DO z8X=eK1Wk3ovu8TkQx;fP>htaDozkiwElJ;)4j3Xl1~ggV*g{3{A05Ge>wh^5ByiV{9y^^-L@Vmu%9%0J6z53G-e!G zBw6kEpJkzHJPZL0!+z%Oi?l9c}P@uVz8!#mT)VvT~w-%kK%2>0tsQDkL zxM%<1#7fwwrSgc-1stJ#@8+T^ds-73Jr$1nm!HSmBrC*6ORR=Lt*%yziwS zM;pmyQJ`cPaoSzHCh9)0xe<7r<@V8e%o5jQol`tVM?u~r$CKq6%1N0*tPf~7WI(`c zXOggGiKlT9Xu>1NF7jVFlsQ#7Pa10iX9=A=ypZyVNfM6(Jb!ICN4^znk+i;=*OJ!(mHt1KlmoUc0)7 z^6%~Bn+^U@HTK?2VB~)HZS)=%?(t*vyi47c^sF!}b@X|n17)W;+F#lfx~Q70B|lw6 ze@O;x<%=mcIyuopYx0D9)kWVfW+v{+OmHdQ)RtgNzBH=h8pfaNBtXxXFo+M$r-0i$ zNp;a>_^0GpXPAhZ-A>l&wa+KzGQ)rWCbNbu%z#}a;YHh-=iH+O-mU2Et)|Q|i*qx( zi>=Q1?0b0}5t#jQy@X~N9sGplD-Wb*Mulg`j5^MDVU}=e$-$Re?r5Xhhax_AijnWF z-Ka^HXwj*9XsUJ2hIfs%@$3x_a%$%wvDDdwhBKG=XA8FJ&ZTXx_N+HeXJY|txxba* z#Lg&SoBU#?@j_fZS(@qe83AK8Fgy{5G%)Jo-YpL|7qK@NgW^`yCj&uYUR6rStyh^{ z3mP_en&ppXTt%=-UmU5ql8uQC50^w0ux9h|J!?oDYj%s3PJO!2Sm#{ad)Fds@kiO7 z)5lW=!$ZBak8WQW(mb)-mt3p}@9urhg}z{`*q{7<*zQ0|njuIvME7Pj2)o5Ukz{Wk zH&sw^r=75`u+ydSIiz%eb-wgf>Fs2XpUUXtZSieArOG{ET2$r^tv`N0<8n&H<=8T$ z_rdiAMd)oa1GmQ*-&gbqhMsqXAkAkSd|ET3g~H1%3n4j^ zKc@?aNQ~&GKM#|dtQzh~T1|Lmq|7K*t+}AJGg5<>gJA&YbHZhT04wHOu_>>@>6*pL zGaJ3>M~>do2DR?W>#hS(~nvR*=~nXp6ti&XEQ zw`~7*$^I8H zR7k~#@r;|;QWW7Y(C+JB+I>sZpe#prp=YY$rLs4xbN#z9lqi{9uj(&D?%cNv#ud8{ zl9WJ}0lcWYC5S`DNSlR?-LId)hd=kUCeufj@K&EW4z-5xDYDf$_mkFxeFYTq1Vv zlvHnuKE!F#AbnOSCwwv%jXz*PAY-~_9L5D&aw8J&~#G1Gs)UXXHvWX9yEBYE?XmwN1}+$I1SF4KsHnBGf^}(Whm%6NsTV^V^8Hnka3=s} zp#yf9eRLD}AKfeVC4TBxkdOxxo^tF%kRAzK_W9s(id!ZFj*?E7`q+Bg4RcO^$9y9hi-%OSc2ODS5O#I+6?m%6>HMc z?~|__)Utjt|1BLQ9)cl9_pS_hXG+BvEh;()4Uc~#p2JTZ>HM0H{r(G(@xdz_Q{JIX3d)AOkTcY(S? z+7UWtA3J}1ihJU>UXh9h9tc%`?s>62D>5^6pPF`zl@f-Fy0mW(`i0Y2&8)ko-Ton~ z?YKE;NkQvk`^673?a0AV?kMbNr0Fhrru&7kK#2lHmQ%vPcV;BQl+c`d0?v_II%{f(^ z9HFS<3hoq}35u^O4&|=V&u!=cMdFxIK6H+YCg1Cc@=Z3v<13OLIHO?cM~E6P@fF)e zd1aKAlAmjYS7v+MSF=XzI2*M+CEs+D53MO9d(v;l2}xk3emTTklVv3{<;L0E#k{lU ze%u{Z`yMW-a23$!*q_!jMk?bC&Nm8UxuXA|qWv>}ey28@|EMy;O#l>O$*8^M#04B zY@;`B;+?|#wY3V2u4uj9OS@MmZ%)2R-FpnOekN&q%HH&@y}rpS!M7;>ZN4p`_9Shc z#f<>NMwBpg4^_k3%pB``;~@o4ec-h0F#PFXa=tK}HkOBlmN>gRBHCttaO&qF>g!UCJ&Jl6$d!N~YUY~@1{tNCh>O8_y7 z6;Ro0@MNxtX8yR?ulKwbgv?v;U_LF@mS4JG^XvgNmDVQ`g1AxlqFx(m*0*FYEJ%&8a%=Hy9T22q?bSUG0n#aE>}KbhkRgLw|;uOH_V)2*J>R_q7eY;6DhtvF3OKl$=Q z`RaC8iC?-=V^i4pjmAbrdV2vt*F#vVa3f$Jb>5eaX|gLdbGW1CK~IEu6z#&)DBvb3 z%d>Oq?WhK4`~KIIn?oqo9+fQz=bCC)7BUm{RAvn47a@#gh@2l9Ypv^!ed9v>Ts3aS z`M%@6FunZcPsan>#Z@uM+t41KpFLt8hb4X z!7a}f?g}yFc-Vz;fj2-Z!HjRMWX#0}*N^eK1laEyj3%_1VIsSp(7Qa4RfLbhNfzIl z2VH*6bV%?qBmM#`qPLJ|9`36hLY4xL<%g5abA9c4+9Fzzx09b}XP6TQDo&ij_5QYP z?X*Gfuim;g)Nm!SH>{SaeDi~nIg_=b5#P+?Vtz(n2Y;*!B*TxmP3!#N7s#M7Wsb%` zmr_T+4kyLU7txtr#@^i25ckQ0W<3{caA!$kVwOySatE0F z4iYq?YnKgu=8j~KwO;mv^AR2Ic9NZ$86LnkA!PhQE%M8NB$(*THA7JL@dxgMxXLpr zn;(UX&PneXv=#M%R2Ls5FNL{^ydazdE5cy!<;NW#%StJ?t(YM&f#WaGH;XaDxy&`Z z1cU7B&|BkEEBgkFgFY1lkDPi~uTW3L^X|>U$qNEw{rM@r(mX5gw0>(RZJbppOPo4j zQh)1($i5+N?M34W$h!ucAdkS%ejG4bR93S}d8ujKP}kCtI@0)Hdolkqd#&G+Bf5Mn zvtUL&&Ea9%iIWAT!%Tl6(mCjYhQjt-eMa=HSl%R;CGk8&5-#1<(D*^ug>ft=@Tkix zuw%aoX_{)o;!W%{-;1y@3F>Wa=`o3O7+RRNdn+a4qT9^v7EI`D?^s5ro2zm@aa?tI zahbfC8nf4P%r;?wjQ9xCIC`_uzZp#2FlTgQ^<}`@(8ZUq>9)O}GOb$rEMJemlpTFe z0mfvTmfPR=75}?7QosAV-xVMDD+z!9y{*6!`aV&A#>Mk%=jNC;$)&(RkVkSEnsvZ& zyv;@AXULb+1-~GcE_I$h49^vSJvM%-+$Ch4EyP_VzIQijWpWa~5UnzhZSnf#%_*;x zf)*$lysUh}(n|B9>Ic1=jNtI-rXCeLFd|G zcd1h!AhRJ$HDBSuP!+e*(p^Y{e}%)Pn_jRxJzX>17b1+8k-=;|;6rae<47eDyD%-Y z>fTL{bya!1J=iHcdu3DESpeNomWX8v(!bjqBCq^Bnhr?SC62}cY~o#fle zSA|bxWqI1Kb2eu;XHJD*{+KJ6RB7ID=^2+NGWtYy83Zqf#+s@X`LyM|EWYE>@|NQK zJhWZVbxoeQr=(Zz1zyrQpNa_97r<^63pGF3MGdXgMtSUJWD1#@^ha9}41Nz$o0#hu zUAx;-wZBXnduSqEtk+-`N2w!~ec$lg8%R5*~D4@|Nn8&;?J zV1UH}YDI~?br<}G?DZ^3$Hj5b2ZYV^aacHHRR)Fh5kRWx;NE8NXQlCFMQ z)umw-T#{H{!ujOy?CaWVadR2{u#eGtHpDb*Lr5xdjT%IF$_=DI|19Ae^%O8$hz@ut zz4$n}hkT53aGWYcTO;v=4>CLh%_Dau+O`vDRyKl*3`vLFiZ@^>+C^UL>&ZZ7A!f4KV(so4tz^b zdxdoO^}8`#pV8%|S-mVb=#c!m=nb zN;RrNyJ8ercRmlK22)%zJdNt^(B?|LwhGkLu%G1Z6&=ZVEib^}uB(Qr?R=L7QTI+C zpR1|z8C}zmo^GNPhOq&FWh}hhk+M}pMRQtZ;_9VOjqSXCXI;!%Q&L5jE`~y(*79kB z+Md&7o=|qw4=dYqyZdfOFR#~I@hto6=u;tCtHUD>N9w^bpM2W|KFWkiWZr9rn<#05 zf!mQq#_=uQIZd&=^}|=1^^zZDyk9pL((-Js#--bWrHa(Y%wIP>dFjA2lG9SWx%4A; zQ|)fzswc}bLfZ)Eh~k@7?DmzY-I*-6K+s8+6&fhsSD8<7_J+`foRu=*2fNv4wt>P} zp7-7V%rNtsAKO0RUSyA55`AP&mi)t^T}Vm{JtwIEh5j>;0H~Zhp@e9j zHgJq`oAe`D#a);nE_g0PP8$6gMe6LTli4Tb8UEeyFp4{PPUi?W zs&~foSWR%~_D;*We2E>rPoCb~aa&vJMXz#F*mr^OJgpyZ;7#o$YjpDX_Q|^$BQkx* z>g$!_00L04uuP``J#e07b1yT{oy0QyBP8UOq)8F8pewEqRl>ohq8)FQRnQx#G#X3QLr+HP&u(9dt~ z_9r<0kqw6cfD>Y~G>ku0ttsk&N(ra=d&tSeT!`$u*tGzxbyExaovsBp=9sCmm(1UL z+bTd@RXxhat6&qafEA!TP2^9_nUib2UY}P>!tHMfo*1~{v#y9>oj6KKEX7g#B{-ZO zaeTjqJ5KM5o0uJIhybQ8w)e@8_h&>;|0v)0Hzxs#t$7&1)ve%421`UYP43ko)*DuF zvvholT@f7|5=zmVA>5%M_=?)Vz~d3qWL3}DfP8eoF#}&XT0Qv%I-e6Z(f=FA*UTtR zjgVvX=8kMo!$gYlW`cIdJr9I1jecTX*ay*~S2~$8xnB$iBT92s zbQ10d#QKO&2FKejE`IV7>TxOg)`|t`C~zPsF*$ff1RrJGRW=Xzs$f4;eBICO_F-x9 zk-ko>0I0hZa7O&e2K{H@>HqaQzr)u5O5Wdp5BL7@JC3;i=lW~P5lt1ntY=}&8MZY} zeZhCyc@QlN^D1qmkK4aLGl)K=srm(qy-PJ4V7lh(`HeRu=gwZ08y%n+<40z$`Meg9 zCw*5})zAApvJ}xN-}_OjZg*NHbzNv5F&Z zBr1Li^0-p{+|}6bTmdOx>hxDGy?%qyy{bHPrQF*sK)*cKvr6Pg%l=U(F2 zQH%VO!4SD`=v&2|u+>*>NzdW;e3gS+iKnhA7b{`N#tacxVuwqJfb+q+@-GmSb7J58 z28iN_-kxd;7`zBzEpNEwZ1Sfdm!B_6li$xqR4_Fdy8B zI^*Ri{qxQp-X%c3KWPtt8$@wsKG$vlZ;YuLpgF$N?n=$-02z`*bf)BES1v6*v~#`p zxz9P_9^dwqzIAr3z84+mDX-kRc3M&iXkZ?EL6ITG&Q}ig|^-sCIMhH_=TYG0jHSgBbARV9gdP#zlcKa5F6%jf``P6?F zNX9zO9DV-ib4S)Q&&QgNDjR^EHt8Z)gZ0)-qer=YRnvi2X-?G=r zmsP!Zov9ycmpB!{mtN$M&D-2Cy=u(LkfwuI0;r+xj1qYaTBbkb+Pm%BSi2_u+S}nf zeioy7c-f^fa#yy{!9W!}03*TW zvUIKyOH`)0_OOsP<*20k;h1Qc_Z%N(?|VS;HMp0eEO^5B7sw=oAJ<%5XuR^o>=#Ja zY?QZO{=*AYT3e2igRz77LTacp4Y&4vs4jZG@F`kDv$0Pj>y6*ZRb^>{NX!G}0)_89 zlYJW>Tu#jsOj=?WvIRxV30Kt2*SFK+C;`sGN?}H4WIE?e;(dzbO)VGKY(*ty979lfMP5+tA0_s>{@lvmA@Z$#Ib$+BAr8u{^4 zMK%O9-$e>yJE6E}SzpX(MFo`zivbLdJ7A#TO=mkD!atZA>bC-EjO%9~w*NRjrxzw{ zZUEixu5<_YEgFHs@2AvG{M-muKa&FSz)&1fo)Z)fLOTl|GQ!Ll)7C=qnZgguZy2_= zhK(#}v}F*GtDAp00TE?`j=S&!xRDcKa0{K8$J68M_6~0tDtAEG9b)Z?kI@sW2YOFw zMTDf<#QMFd4dqQvJKJMH06!x{u%X&t4S8C+*x(=0^jhLZ8%*)>!`BuzhwSaP^T}lDkB?0cJ`eoK8D$+-1(_^*u*aCQS z606tWF`{QVLcZ^dJH*Rg1S;EO|Ix%h({_Ks@wamLznr7?k4M}8>IfH^#gIWcj6T0L zZm+vOiSmfBCH%CfAiC?RgDdcmqMpkY`&EMI%?b$+R>%#_KfSwc_Jzy_O3c-439>9L z-|Xk;;3b@L*t4=W`2_~~IGFqb#mTSnCK%W7WgnFGv2T6cPEsieiX1c^EJ3poUE5=; z12=(I;hEx&(?&4m6%h(Bz=*MJpC%No9zDE2hGllTntiz~u`y@uHvT$+?Jd=yPl8#k?BOC?h%)o-luy*DFKrpi$=GGm5W<;?Ydv zCKRX5gVs$+9y94cJ;Cx)^|OUI(t?W=1J+~s3@?kGT&jccOUTVH*XprXqb(L3I?38d z^}a+~_!JyRi!Xq=WDHN(r6aI8pZEhn`2lmHCyM#1S9xZdJQfae0~_;#QPWO{#G8(G zDkw2nxg9pwWYf>$RZRM&Y{gghl6U3~M?}^A+B5)LjEmx;y4->45&~y`E5nzn!Z+D0 zxjzUv=8P=cH2+5pui4X4C>P&4vL-JxKjzg#HrYm?QqXNc@Ltd$I|-ihWZ~<>bJxFuO=`3BUJp|5rG^bFJ`~tNr1qiOK5I{c%SYcNeqsCHy z_)_1=w@Ym+Rlf$6^}9zdk=xDFPhvk{uvALTAjG;{&jx};P?=q{CQh%PRQlB*w<9Oc z@yb9j_)60T;s2KNg-yD3TD{Bbbmj!V`Nbf-8JGHCg56f?|038d)|oRX*ldVNEtk)4>LK>?OdK>z|!F_}{tS@A3=(BKE`o zIvn_4EqniF=YE%I|Cd0U|7LykyZQWI-~U~I{wtA-|Ju&~DX)Lmpa1H7;{TL>{6{-~ z*Ps6msg{4V^?$i zdtrvt;5+J(_Y&U`bn>k`2WL7hsfZ2HtWB^(B}Y14!zb!L)RuLw$s;&yhLzMIu|D1Y z3-rj@A7R+IFk>$IF44wKCVG8!w6<;4r+Y$n2WN%zUB!V;XE;T2)&8YQ)T(K&<(hMm zN4@X~dx)5Q4L*i4I8=Uz2fPKKA`!4v?MuSGi)_$g*?)P*sb5BN{jG!?JH*1mQDTOE ziTdlCi-6Q7oC_WN&-F%QdEDs4+of?Yym;yKO>-rZY-0o?x(6j0E>GbBQ!<(b=os^Q zr{Wfp>w}ImoS;~q?f@ecSw9Aha-&UemeK%~-N9*NBJEBhRvaF5SGaqvWaA+9DYBvT zUFmbC6e=40nSF=`N*F;IMHS`pT_jMEt&?{nEiQNev*}Sj*tM{oe{CZEO0WbCS!Qs3 z8oeyG&XoKn`Iw$qs+ezBk?C8+pdyq(^(i>K&Jsr{o5 z>Wr?aXG3uq+m1RdTmM@=9vSvYsXz2}ZY=P;K{;==*c5&ekIFwj#1rF(tjh-0K{Sf4 z?EE#7-$4gQG@i~z+z%nid)25qw91LIL(3x;XgIQ{DOG=j+jNuB{X~{$BbNv9YG^RN z#&O0hFx-O#S5fjT|b~MH@Q)4a4#C^9XtyOULgRyNjQPqa0t*zFV=j2f&QpZ zImC&xumhJsb^(@nV04o%W^Bf?`Kg|P^+kN%Fe2)ds#E89Xc#dF@H=h}c*<>ae3I8E}f!CQ|k!fh?gNk;2 zp5Qs|JzNkO6d58D+;f>3-L8$?L+^zy?6}4=MAFQ1=N#{+LWGek{c?QMG7s$v9@&aj zyVZ_j;X6h?qv<02$w$rH6sLrJ-(HG41ax)7AnNfE(+82`}a1B-)!$D$ftu;imqhP!GQ4nEHk)DGSaEdxI{Wj z`Dqg6ph|gV7cw*ekmSNjeu27P{{r=YzEc>812^qKp*#uMsJslxtTV+buqT^JUpdpt7b# z-{jttnz=mEe&TOVn{Yz=yX5l$1~n>(m<+ZA;7_NcKWKFNo$herXduL5ke7W5Pp`zL z-ohnSMtm0=>K5lIzM2F)x_cYh7-Rp!rtR37;#_6Y=8e@!4G)6#KDixA$^Dq23emm zRFo!MSpmb)JN<04(Lc(g!96KIPv}#4;y#4A2yHO{BRdtD3wScESjvN^I{b_IeW~^x}^R^DkDdUy&HP)AQv^0JE&>L{R$4AiMx-q&2nu*=nXR%Q6vq zv*dLkXy2?bfOfKLjfz&<>LB0cr;cK?oLtMGziqRs7c3e^v>_&y-kzP8lf#qSZ&h}I z><;9_F~ZZ;XhbOdAAUW?L)SqG0k;%YxZ6>(uGa(piAV zQVl`DPw6<{WuaQWlBb4-e!LqNBB$SgdY1|w5JsGRJCheO-0_HtPjinul}kTZLAJL8 zB4$nb!wf*->-6yi26mu=QQJ;o#v=#0Z0;IPHDR-}##vSSi^pEwSmTi=a?JEz2;jPJ znA$3!gez3QlmOT`)gVh>pG;6KsQJFLvwlqe1NUW6zTvx;N~MBby}i0hscx)=BDNF- zkk!(Bd;sVdC^_gH(*{2h{V_d2zPtIjV0N83QiVR@HVEqT>g2#;P>u)SORSqTi8W5f zne;F8u5>^zo}FX_%?>eKpFR6UpR8O`N{F%oE-x z$BC@};ON79oy&NbXY9*5N`u)C=04^JUkRd}5Kb!XQ?C5%vBLE^5j~e83X}L;#k-es zS*VopnFl%CaYGkr6xGQ9M0hH! zP`{&%m9J4GG0~umCoMhk+BC9zf#PdgrIpZ>^{1$+Ed*Oud)X23DUo|#J4u3s2v!zS zT(z+yW4}K$CFVyr2`5{I!FuZ9QJlY#ug&`6=t}fU=#%ApDGLH4M?T6rpDtGk5u#lY zmxocO%AW&nct1N1vfykj-_F7Ijr1*SYfcZM$**grK=NVg=M_j;;>r<^N$~t$@JsOkozb|3uP%QOP||-cZzV!%4DMY>G~QuDltT)T5?OkIZjMvekYJ8J~+fX zsYw0uHFp_X-WwbYjskTTX(xld95wQ2Pb#41h$dZNOtr^Q_Tx!eJK+Cp43Iv9L~7Y& zb5v8F<&pUgF-Y>p9%*Vk5|rH>cwOIU_~8QL)N2n^oELU@qEi4>hN4FaQ-%G$aVTUDzt+lS9GrGrzcH~ufL!`3eFq|4S zG{6JHb1G8&02o)26!G5+LhgVN9fO@FsO_taKn_gXQ{bbPA%Zm+W%?j_S5CX8cMfcD%s@7RxsqYkL06Vpl#W%H*PTA$Sq^Y> zUp=tEw3h;AG0qT4R9jx*0?${+ZA=?@A5w?V6g`c{{`ojA7miPnI4v?o_5j#1+(pP zCc9YBkAN2oE3eyyt=3(MwA~4ZTC%@B?Rxbg17ww?Eag*kxHgoqhDykc8P@nBkuLmk z_ZVx49sr+9`)Ij$^6u78eZkpOiDXbV&X6y!5m!XpIOIcXEbe^On(F^X)V;Y5x3F_PRTDe5)UXQUh;gYr zbUv~8Aci8mH<*a2Ry*6L#V89EHg5QEVnzaPgZ2gXj9TfxKxRONnqzppOkgGSY>*dk zRLg!Mn@wUP1dR=h9`Y2hY4eMZEfgU^rL;pmcUwgj27=T>A z3mz7m$21QCel)Qc{QYvwpD`T!tp?R4uODSh2R(Su7PH^IXkqrWf70Y3j+g3Z?O7t7 z8x=@-N~kbowv&Aq!_*MvRZMCs-064o3ng3))fIi%&Rfi2I5KN~;~9e*D<}zH+XEu8 zLN3SRxT;dL(<|ghagiu1cV>^XGg>)nN=>_Wq!cSc!?(f77z0mRZ3&LCAS>!r(2Ord zktEf=+kWB?&TasQ1#v<;tEA3j^O=U@UThYB)2wC=s;1jpq^(?dCB zdE(qM>^OPSE85Fftz2POyU)jClDCqws5ZtD-*{j}hf$%uwHwh7^?WI$l)w7~-3sf< z1^IVHc<$%2_RX0%uVUM?5dPFs_V_Z_u=%YcqCPXzg*Eox{qI^Jkaac^U?wS`sj2oj1SC_N(5 zf`arCibw|)5rTk**cddF&^tsSKnNxDUPQWx2n3KON+$?H=plr6oq4YH)?07A@mtUD z&3xaQzix8x$+;){-o4K`d;j+Sg-vh22OK)kD;s*Bl4YJBX@47r=By4}8Nn>vD$YIs zc5g{3M#*dNA*7RQ7d0v6myPIgl*0ox6!(XW*Psa=^W7`2|b^X0fjsOLTQ-(()lux5T$GT zLGzPB_mGNy2M!!KbWyPDlMTB_0A1cavJ7egPW@Gh?A1qXTr=#Ep~>Ojc-SN}S*#$PJ5hzAfC z4>_5-gLeT;q2J;0Tu1C7Ufinyz=c_upLFmry#P*qho145cvc<7 zd}A&ChUw4(F@e7E++W%?cM;^pRs8$ee?rl;-|qI``u$e`d;cp$9>0(Ak0NG&MkPw>|YH#CnZ^tVygWY%T$P+~Tms4?flJ+Vbyy{9oQ)INYwCOHEpVL>?owWxEt? z&-N@T*2TEV_dfMMZI*syO>?Jv1+(keguc4JGwP6wl)KOl$n+d`<52dUIg2Q^0(>11 z3^^vq{Mp>dWU;X~u;J8mud#JajTDhOo7(WQPpS+gQppH5nOir;PrFf0gA#f3#1yZz zeRfd^^hpsZaIcM!`ozLO-c`P*6Ny!19zL`U?U>0UIF$!fUTOPu-7nSkhykiC4WeV3p`{#fau=f1hptosNewdFv)iJD{9!ZptfT)7};G+#TGb zPHKmP!h38JlJOWeqKk7`ZYz60#ud>wgwzREZgj8N=p zT#n`aONQunIS{QBoot8$xESjfdJby(fCWs5@wme`-lb!lXa>ceJKKk+k z4bPwjEN^d40I7LFX1ai)lYYnT zhsyEDjqA?&0t2(sZk3j3>X66>&TX;Cjg%(;F|E;u05?6ex~HCUqjH2s5ED~A8qBfj z;Jy*Vh$HHa-Cx@Z;$!T=Ou}E#r%EfIGXZ-^nNV&R2mvG zlA_o2oPmU6%6pUixozb{DrI}DdN~;g`};)PtVwKR}9q# zY4%p1Rh_Z>B1tZ_EcCvWyQAZx%vMTDLTSRbBZ#@6rSmToa-0@n<0jnS2EY5;;N5(t&77*71D|Al zn6F4nm@sT|p?goD_+jsj=Ef4wt*3_h+l(VLWgC<;)EbPe*i$gm{f!B3NXa^5ATKGu zav^GzIs`Gc-G(}ZzF12TevhLT-H0iQHR(a5zXr7YT{>du!dB67+L>HqXPwXC?1vGl zlVH9|*vNJNUvU=J6G7@+hJVf^5fRsY0yV`%aj|}-f+6&mx~*pfqs)x1X1iqfeTFgN zCe14WjXM#=KfoswpX1%{1q6;`253;otnpA|g7b`3Z4g5?sO1`>;|QT+JMsnHInTCQ z-%;g@0i+de@u=Du0@MRpy@^&c*O7hQ;v1FUE7&?m$P=zMl-~*?&?3;Uv&){BFm>5M z9_@hmn$7!oZw^UZr+6z;pzm;v3fW#^QeB#>`?Aj$WWhcr*B#)PG*hBI9+2+ahccfX zNIPa4RWd)mJ&T7e3KbxCZ#1lrL&`> zeFMNx%O9g1_vY-TbZVl|aO;Q#8WbI1jOH|x8ZqaeVP2K3*EiEq&Rf$yl7D8wJ7kOy zIQ$6P`;hS~u@2)1qkPg|1(F3E}uGH5CR_u?ebqHmueA`?=dIzyHkAekE zf4*|G=1L9*nqo%3T$U(dJ4-6GLTPL>R;d05G^s?AIEhgZgwl11GC706x8zne3g;4tD{&05Oqx;qn8YtOAAs`G;U<4M-UB_MGR2 z3Oe1R*d*velT_K}eoEAqK>5MyaJ=X#}aXiB*oNRrw^g6I6_0kER$6?mR<(HdJuFm)tuQt{2UEH{P-q= zQ#7F~*jB{kadzic|LfA+>J%Z)AsB zlD9gwxfmy^V=$!EUu{~Y2$Iuztai#gkmeK~!RQGtV|0$xChQPOZL1~U5km#NRXBWl z%v@8`b|WFZ5xNOGNDExYTBcS41qfT^}(c3i;>PNk;`MPEtt)63on=CxMY5{#yI|^VuV26QpxmygGxX6py@F^Aj6xHZ^$r_BIs05d}ZwWW6&-( zJD|=++{|lMVj9_GDBUm>NHBNCT#&#k{JGg0@PwRx>2>jm-|2f168*1 zTVJZ%Kr`d1P`5X=BlmYe%HO8YA-apo5a57R#{d4K9gx{^da%k%$?5DH5!IRVu}(Ri zKo5Mj73InfKq~rfnYun8igT=Q*SfNsjAM0h`)SG-H0KrZmaJ3hOKc3iM)WoGTfe32 z3Oxr3LUWkMtu@v4XaG-G8qhAI1UIoME4%CIAuUT~( zD#WBH3zm~=CEE_KsErAn@e0GG;uZMQ+g%U?9@VT7KAdzwv0)bMv9 zuh1c9HZq1bY&}^em8r^RT zF~rPh^}qp1eAw2O72qIzY0`Z#Tc91=5m_V*4q7(3$XvA{c-HP|Fh+OSE$vyIUW0gH;>5USn?j{DnKp0 z*djNUwQ-Zle}H#;fN>)|v60(FNCSL_;|0lvna?O|%QdvWQXVR=YZ;C>y4)acCoLq2 z(tr}W4!!7^t_H<4a|^gyT~VHoe#yqv9-5NLRhICuTPU=7L{&_BV11g_w{qrv0~skS zad?y*Pez_djmbQ{<`%gF5+7x3%Jl#0+&rzJPf-O|06Y=kPWm#uhG5B`dq9^04q}gP z&5sh{g(nTUwe}Bo$l%N5&Fq%b%?i0angI;k&CWDZq~lwi%NlH@)eVRpZhGZw8F zf{)fGuL2Y$@HN_5_r2{G^Lp3=%k-9V?jt@4pKl(}QS_1@tD1Y427bG&@z&*Z3*X3ZoTMOv1>NqMB}Uj`xMWUgBJ4>uCsm?136fz zkXFO~p529x@AIBTpjtg*TMmoBKYq=XH^#go>@O}@an{!dhgi7*DD`ZVXc30 z5;cejINjWiPz-^)QiI5^u{Yi*eKZnuBNYW+t`RTL@K=otA1v>ie7>F+dj;aqeOt2g zqg^qI9oAfacOd-&@5#O|qX&yC{hrGVf=n-~mG5R2pg7kD?qNX7ROZox;Hqo z_|7?ccDS-zX3h4yz&$pj4QR0*4otEZc^cm4eJWj;8_XFC)X{2E{6`h}|Jt5or|Zu) z1Tzn0JGMAzj_9~7$M^QW=G7a`SJgh(0RR(|n-sCpi{%o`wFTNlkdF$Em_>}%OiZa?ZPqdJU{-JB^)kl8KHQ_7rS!L0Nu1?}MF?!Ls&Nt#ebi$GR7}z&(?)XuZFN z`1RdgO?jTx)Gfn^UVPaZ@w-|oHvnbhdy`*P>DJntewTH|jLZPMDq)f6O6UbEP4;Qk z5|TlMl)i@Jad~-J$8Px)2j{SgORT%IO?5sOJ8i zvPk~N5or&GEIR=De1UY*&UM`{1fuVD({KyX5Kxw~@V6Yltx~f*Z{6r|kT7|2sFWrY z-V=2*UgjPLo5IkCxb?@Z5Ju%hI!4RJKFwwB3Bud2i5`2l`D?VuzQi%9P1drlY|FSZ zJ~9e#{kAW{0|!OKEnZrFliw_D_Q~|6-U*ikJN`TTZLjKAPa^4aoiCE-C(YBg+FSEN zYS4`GH!&en zS;RF6buVD!l|u}%7s>YO;l_6wgc4@fN`ys?^ryVZp=S8;`>vLP5UkR0=CnnBYV_I@(b*2dq{yq<~?+xJHpYNKB=vJPn%580rQ#t>mulGQb7ZnUzpul~rYrzPU9B`RPc8>^sEp zRo5vkp6o-xSa?8O(Gj}{H)4wHB}`_!cUAnGAkVuMeRGbSnTTg9Qz|x@;MSg?vmnk_ z8GSs}nx#1994o#D;RUu5l2(t2hp&416`~U1Wu^W+kHQx~9oK?vYB6W|z8v$2U`HoA zT3L1aio`40udU?;m;{I!gq$C8d{7h>8Pl2ry|1_BH`I1(4}W8_*;T5@D52=8SYN{GZyIY}uW%8fy`=2GZDn*a?A9f( zVOi|h(a7M6va@b^-F?hmrw}niypg~f;6cBT$&o4Kqul6cA)#=9`zhRVL+P?}+Z#BI z+axhTTfiySNib5b2sQTv*V->y77GWN^X&SnFJ36d)BDVjY=krmb}F8nG${ltSeDhM zcX{~VQLE^zad*xi#uwTQPqSt2SsyF9g=}xvx_tJjSj`0{+_e6P+l78-ySre$Yc+GJ z0j@HG#Hk@GmLw5q6I4jSuF}cfL}ZmPlUiN6u;7&o%+TL<5dUx32<9IT8T?#T|KrWx fU+I4T9DnOy=^y@#vHb1o`4K7p?dtg -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    - \ No newline at end of file diff --git a/route/static/js/Validform_v5.3.2_min.js b/route/static/js/Validform_v5.3.2_min.js deleted file mode 100755 index 4eb4f7d89..000000000 --- a/route/static/js/Validform_v5.3.2_min.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - Validform version 5.3.2 - By sean during April 7, 2010 - March 26, 2013 - For more information, please visit http://validform.rjboy.cn - Validform is available under the terms of the MIT license. -*/ - -(function(d,f,b){var g=null,j=null,i=true;var e={tit:"提示信息",w:{"*":"不能为空!","*6-16":"请填写6到16位任意字符!","n":"请填写数字!","n6-16":"请填写6到16位数字!","s":"不能输入特殊字符!","s6-18":"请填写6到18位字符!","p":"请填写邮政编码!","m":"请填写手机号码!","e":"邮箱地址格式不对!","url":"请填写网址!"},def:"请填写正确信息!",undef:"datatype未定义!",reck:"两次输入的内容不一致!",r:"",c:"正在检测信息…",s:"请{填写|选择}{0|信息}!",v:"所填信息没有经过验证,请稍后…",p:"正在提交数据…"};d.Tipmsg=e;var a=function(l,n,k){var n=d.extend({},a.defaults,n);n.datatype&&d.extend(a.util.dataType,n.datatype);var m=this;m.tipmsg={w:{}};m.forms=l;m.objects=[];if(k===true){return false}l.each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var p=this;p.settings=d.extend({},n);var o=d(p);p.validform_status="normal";o.data("tipmsg",m.tipmsg);o.delegate("[datatype]","blur",function(){var q=arguments[1];a.util.check.call(this,o,q)});o.delegate(":text","keypress",function(q){if(q.keyCode==13&&o.find(":submit").length==0){o.submit()}});a.util.enhance.call(o,p.settings.tiptype,p.settings.usePlugin,p.settings.tipSweep);p.settings.btnSubmit&&o.find(p.settings.btnSubmit).bind("click",function(){o.trigger("submit");return false});o.submit(function(){var q=a.util.submitForm.call(o,p.settings);q===b&&(q=true);return q});o.find("[type='reset']").add(o.find(p.settings.btnReset)).bind("click",function(){a.util.resetForm.call(o)})});if(n.tiptype==1||(n.tiptype==2||n.tiptype==3)&&n.ajaxPost){c()}};a.defaults={tiptype:1,tipSweep:false,showAllError:false,postonce:false,ajaxPost:false};a.util={dataType:{"*":/[\w\W]+/,"*6-16":/^[\w\W]{6,16}$/,n:/^\d+$/,"n6-16":/^\d{6,16}$/,s:/^[\u4E00-\u9FA5\uf900-\ufa2d\w\.\s]+$/,"s6-18":/^[\u4E00-\u9FA5\uf900-\ufa2d\w\.\s]{6,18}$/,p:/^[0-9]{6}$/,m:/^13[0-9]{9}$|14[0-9]{9}|15[0-9]{9}$|18[0-9]{9}$/,e:/^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,url:/^(\w+:\/\/)?\w+(\.\w+)+.*$/},toString:Object.prototype.toString,isEmpty:function(k){return k===""||k===d.trim(this.attr("tip"))},getValue:function(m){var l,k=this;if(m.is(":radio")){l=k.find(":radio[name='"+m.attr("name")+"']:checked").val();l=l===b?"":l}else{if(m.is(":checkbox")){l="";k.find(":checkbox[name='"+m.attr("name")+"']:checked").each(function(){l+=d(this).val()+","});l=l===b?"":l}else{l=m.val()}}l=d.trim(l);return a.util.isEmpty.call(m,l)?"":l},enhance:function(l,m,n,k){var o=this;o.find("[datatype]").each(function(){if(l==2){if(d(this).parent().next().find(".Validform_checktip").length==0){d(this).parent().next().append("");d(this).siblings(".Validform_checktip").remove()}}else{if(l==3||l==4){if(d(this).siblings(".Validform_checktip").length==0){d(this).parent().append("");d(this).parent().next().find(".Validform_checktip").remove()}}}});o.find("input[recheck]").each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var q=d(this);var p=o.find("input[name='"+d(this).attr("recheck")+"']");p.bind("keyup",function(){if(p.val()==q.val()&&p.val()!=""){if(p.attr("tip")){if(p.attr("tip")==p.val()){return false}}q.trigger("blur")}}).bind("blur",function(){if(p.val()!=q.val()&&q.val()!=""){if(q.attr("tip")){if(q.attr("tip")==q.val()){return false}}q.trigger("blur")}})});o.find("[tip]").each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var q=d(this).attr("tip");var p=d(this).attr("altercss");d(this).focus(function(){if(d(this).val()==q){d(this).val("");if(p){d(this).removeClass(p)}}}).blur(function(){if(d.trim(d(this).val())===""){d(this).val(q);if(p){d(this).addClass(p)}}})});o.find(":checkbox[datatype],:radio[datatype]").each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var q=d(this);var p=q.attr("name");o.find("[name='"+p+"']").filter(":checkbox,:radio").bind("click",function(){setTimeout(function(){q.trigger("blur")},0)})});o.find("select[datatype][multiple]").bind("click",function(){var p=d(this);setTimeout(function(){p.trigger("blur")},0)});a.util.usePlugin.call(o,m,l,n,k)},usePlugin:function(o,l,n,r){var s=this,o=o||{};if(s.find("input[plugin='swfupload']").length&&typeof(swfuploadhandler)!="undefined"){var k={custom_settings:{form:s,showmsg:function(v,t,u){a.util.showmsg.call(s,v,l,{obj:s.find("input[plugin='swfupload']"),type:t,sweep:n})}}};k=d.extend(true,{},o.swfupload,k);s.find("input[plugin='swfupload']").each(function(t){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";d(this).val("");swfuploadhandler.init(k,t)})}if(s.find("input[plugin='datepicker']").length&&d.fn.datePicker){o.datepicker=o.datepicker||{};if(o.datepicker.format){Date.format=o.datepicker.format;delete o.datepicker.format}if(o.datepicker.firstDayOfWeek){Date.firstDayOfWeek=o.datepicker.firstDayOfWeek;delete o.datepicker.firstDayOfWeek}s.find("input[plugin='datepicker']").each(function(t){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";o.datepicker.callback&&d(this).bind("dateSelected",function(){var u=new Date(d.event._dpCache[this._dpId].getSelected()[0]).asString(Date.format);o.datepicker.callback(u,this)});d(this).datePicker(o.datepicker)})}if(s.find("input[plugin*='passwordStrength']").length&&d.fn.passwordStrength){o.passwordstrength=o.passwordstrength||{};o.passwordstrength.showmsg=function(u,v,t){a.util.showmsg.call(s,v,l,{obj:u,type:t,sweep:n})};s.find("input[plugin='passwordStrength']").each(function(t){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";d(this).passwordStrength(o.passwordstrength)})}if(r!="addRule"&&o.jqtransform&&d.fn.jqTransSelect){if(s[0].jqTransSelected=="true"){return}s[0].jqTransSelected="true";var m=function(t){var u=d(".jqTransformSelectWrapper ul:visible");u.each(function(){var v=d(this).parents(".jqTransformSelectWrapper:first").find("select").get(0);if(!(t&&v.oLabel&&v.oLabel.get(0)==t.get(0))){d(this).hide()}})};var p=function(t){if(d(t.target).parents(".jqTransformSelectWrapper").length===0){m(d(t.target))}};var q=function(){d(document).mousedown(p)};if(o.jqtransform.selector){s.find(o.jqtransform.selector).filter('input:submit, input:reset, input[type="button"]').jqTransInputButton();s.find(o.jqtransform.selector).filter("input:text, input:password").jqTransInputText();s.find(o.jqtransform.selector).filter("input:checkbox").jqTransCheckBox();s.find(o.jqtransform.selector).filter("input:radio").jqTransRadio();s.find(o.jqtransform.selector).filter("textarea").jqTransTextarea();if(s.find(o.jqtransform.selector).filter("select").length>0){s.find(o.jqtransform.selector).filter("select").jqTransSelect();q()}}else{s.jqTransform()}s.find(".jqTransformSelectWrapper").find("li a").click(function(){d(this).parents(".jqTransformSelectWrapper").find("select").trigger("blur")})}},getNullmsg:function(o){var n=this;var m=/[\u4E00-\u9FA5\uf900-\ufa2da-zA-Z\s]+/g;var k;var l=o[0].settings.label||".Validform_label";l=n.siblings(l).eq(0).text()||n.siblings().find(l).eq(0).text()||n.parent().siblings(l).eq(0).text()||n.parent().siblings().find(l).eq(0).text();l=l.replace(/\s(?![a-zA-Z])/g,"").match(m);l=l?l.join(""):[""];m=/\{(.+)\|(.+)\}/;k=o.data("tipmsg").s||e.s;if(l!=""){k=k.replace(/\{0\|(.+)\}/,l);if(n.attr("recheck")){k=k.replace(/\{(.+)\}/,"");n.attr("nullmsg",k);return k}}else{k=n.is(":checkbox,:radio,select")?k.replace(/\{0\|(.+)\}/,""):k.replace(/\{0\|(.+)\}/,"$1")}k=n.is(":checkbox,:radio,select")?k.replace(m,"$2"):k.replace(m,"$1");n.attr("nullmsg",k);return k},getErrormsg:function(s,n,u){var o=/^(.+?)((\d+)-(\d+))?$/,m=/^(.+?)(\d+)-(\d+)$/,l=/(.*?)\d+(.+?)\d+(.*)/,q=n.match(o),t,r;if(u=="recheck"){r=s.data("tipmsg").reck||e.reck;return r}var p=d.extend({},e.w,s.data("tipmsg").w);if(q[0] in p){return s.data("tipmsg").w[q[0]]||e.w[q[0]]}for(var k in p){if(k.indexOf(q[1])!=-1&&m.test(k)){r=(s.data("tipmsg").w[k]||e.w[k]).replace(l,"$1"+q[3]+"$2"+q[4]+"$3");s.data("tipmsg").w[q[0]]=r;return r}}return s.data("tipmsg").def||e.def},_regcheck:function(t,n,u,A){var A=A,y=null,v=false,o=/\/.+\//g,k=/^(.+?)(\d+)-(\d+)$/,l=3;if(o.test(t)){var s=t.match(o)[0].slice(1,-1);var r=t.replace(o,"");var q=RegExp(s,r);v=q.test(n)}else{if(a.util.toString.call(a.util.dataType[t])=="[object Function]"){v=a.util.dataType[t](n,u,A,a.util.dataType);if(v===true||v===b){v=true}else{y=v;v=false}}else{if(!(t in a.util.dataType)){var m=t.match(k),z;if(!m){v=false;y=A.data("tipmsg").undef||e.undef}else{for(var B in a.util.dataType){z=B.match(k);if(!z){continue}if(m[1]===z[1]){var w=a.util.dataType[B].toString(),r=w.match(/\/[mgi]*/g)[1].replace("/",""),x=new RegExp("\\{"+z[2]+","+z[3]+"\\}","g");w=w.replace(/\/[mgi]*/g,"/").replace(x,"{"+m[2]+","+m[3]+"}").replace(/^\//,"").replace(/\/$/,"");a.util.dataType[t]=new RegExp(w,r);break}}}}if(a.util.toString.call(a.util.dataType[t])=="[object RegExp]"){v=a.util.dataType[t].test(n)}}}if(v){l=2;y=u.attr("sucmsg")||A.data("tipmsg").r||e.r;if(u.attr("recheck")){var p=A.find("input[name='"+u.attr("recheck")+"']:first");if(n!=p.val()){v=false;l=3;y=u.attr("errormsg")||a.util.getErrormsg.call(u,A,t,"recheck")}}}else{y=y||u.attr("errormsg")||a.util.getErrormsg.call(u,A,t);if(a.util.isEmpty.call(u,n)){y=u.attr("nullmsg")||a.util.getNullmsg.call(u,A)}}return{passed:v,type:l,info:y}},regcheck:function(n,s,m){var t=this,k=null,l=false,r=3;if(m.attr("ignore")==="ignore"&&a.util.isEmpty.call(m,s)){if(m.data("cked")){k=""}return{passed:true,type:4,info:k}}m.data("cked","cked");var u=a.util.parseDatatype(n);var q;for(var p=0;p=k.forms.length){return null}if(!(l in k.objects)){k.objects[l]=new a(d(k.forms[l]).get(),{},true)}return k.objects[l]},resetStatus:function(){var k=this;d(k.forms).each(function(){this.validform_status="normal"});return this},setStatus:function(k){var l=this;d(l.forms).each(function(){this.validform_status=k||"posting"});return this},getStatus:function(){var l=this;var k=d(l.forms)[0].validform_status;return k},ignore:function(k){var l=this;var k=k||"[datatype]";d(l.forms).find(k).each(function(){d(this).data("dataIgnore","dataIgnore").removeClass("Validform_error")});return this},unignore:function(k){var l=this;var k=k||"[datatype]";d(l.forms).find(k).each(function(){d(this).removeData("dataIgnore")});return this},addRule:function(n){var m=this;var n=n||[];for(var l=0;l0?k:0);n.css({left:l}).animate({top:k},{duration:m,queue:false})}function c(){if(d("#Validform_msg").length!==0){return false}j=d('
                    ').appendTo("body");j.find("a.Validform_close").click(function(){j.hide();i=true;if(g){g.focus().addClass("Validform_error")}return false}).focus(function(){this.blur()});d(window).bind("scroll resize",function(){!i&&h(j,400)})}d.Showmsg=function(k){c();a.util.showmsg.call(f,k,1,{})};d.Hidemsg=function(){j.hide();i=true}})(jQuery,window); diff --git a/route/static/js/bootstrap.min.js b/route/static/js/bootstrap.min.js deleted file mode 100755 index 133aeecb9..000000000 --- a/route/static/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
                    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/route/static/js/clipboard.min.js b/route/static/js/clipboard.min.js deleted file mode 100755 index 7a4fde688..000000000 --- a/route/static/js/clipboard.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * clipboard.js v2.0.1 - * https://zenorocha.github.io/clipboard.js - * - * Licensed MIT © Zeno Rocha - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); \ No newline at end of file diff --git a/route/static/js/echarts.min.js b/route/static/js/echarts.min.js deleted file mode 100644 index 85daf7e8d..000000000 --- a/route/static/js/echarts.min.js +++ /dev/null @@ -1,45 +0,0 @@ - -/* -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you under the Apache License, Version 2.0 (the -* "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ - -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).echarts={})}(this,(function(t){"use strict"; -/*! ***************************************************************************** - Copyright (c) Microsoft Corporation. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},e(t,n)};function n(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}var i=function(){this.firefox=!1,this.ie=!1,this.edge=!1,this.newEdge=!1,this.weChat=!1},r=new function(){this.browser=new i,this.node=!1,this.wxa=!1,this.worker=!1,this.svgSupported=!1,this.touchEventsSupported=!1,this.pointerEventsSupported=!1,this.domSupported=!1,this.transformSupported=!1,this.transform3dSupported=!1,this.hasGlobalWindow="undefined"!=typeof window};"object"==typeof wx&&"function"==typeof wx.getSystemInfoSync?(r.wxa=!0,r.touchEventsSupported=!0):"undefined"==typeof document&&"undefined"!=typeof self?r.worker=!0:"undefined"==typeof navigator?(r.node=!0,r.svgSupported=!0):function(t,e){var n=e.browser,i=t.match(/Firefox\/([\d.]+)/),r=t.match(/MSIE\s([\d.]+)/)||t.match(/Trident\/.+?rv:(([\d.]+))/),o=t.match(/Edge?\/([\d.]+)/),a=/micromessenger/i.test(t);i&&(n.firefox=!0,n.version=i[1]);r&&(n.ie=!0,n.version=r[1]);o&&(n.edge=!0,n.version=o[1],n.newEdge=+o[1].split(".")[0]>18);a&&(n.weChat=!0);e.svgSupported="undefined"!=typeof SVGRect,e.touchEventsSupported="ontouchstart"in window&&!n.ie&&!n.edge,e.pointerEventsSupported="onpointerdown"in window&&(n.edge||n.ie&&+n.version>=11),e.domSupported="undefined"!=typeof document;var s=document.documentElement.style;e.transform3dSupported=(n.ie&&"transition"in s||n.edge||"WebKitCSSMatrix"in window&&"m11"in new WebKitCSSMatrix||"MozPerspective"in s)&&!("OTransition"in s),e.transformSupported=e.transform3dSupported||n.ie&&+n.version>=9}(navigator.userAgent,r);var o="sans-serif",a="12px sans-serif";var s,l,u=function(t){var e={};if("undefined"==typeof JSON)return e;for(var n=0;n=0)o=r*t.length;else for(var c=0;c>1)%2;a.style.cssText=["position: absolute","visibility: hidden","padding: 0","margin: 0","border-width: 0","user-select: none","width:0","height:0",i[s]+":0",r[l]+":0",i[1-s]+":auto",r[1-l]+":auto",""].join("!important;"),t.appendChild(a),n.push(a)}return n}(e,a),l=function(t,e,n){for(var i=n?"invTrans":"trans",r=e[i],o=e.srcCoords,a=[],s=[],l=!0,u=0;u<4;u++){var h=t[u].getBoundingClientRect(),c=2*u,p=h.left,d=h.top;a.push(p,d),l=l&&o&&p===o[c]&&d===o[c+1],s.push(t[u].offsetLeft,t[u].offsetTop)}return l&&r?r:(e.srcCoords=a,e[i]=n?$t(s,a):$t(a,s))}(s,a,o);if(l)return l(t,n,i),!0}return!1}function te(t){return"CANVAS"===t.nodeName.toUpperCase()}var ee=/([&<>"'])/g,ne={"&":"&","<":"<",">":">",'"':""","'":"'"};function ie(t){return null==t?"":(t+"").replace(ee,(function(t,e){return ne[e]}))}var re=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,oe=[],ae=r.browser.firefox&&+r.browser.version.split(".")[0]<39;function se(t,e,n,i){return n=n||{},i?le(t,e,n):ae&&null!=e.layerX&&e.layerX!==e.offsetX?(n.zrX=e.layerX,n.zrY=e.layerY):null!=e.offsetX?(n.zrX=e.offsetX,n.zrY=e.offsetY):le(t,e,n),n}function le(t,e,n){if(r.domSupported&&t.getBoundingClientRect){var i=e.clientX,o=e.clientY;if(te(t)){var a=t.getBoundingClientRect();return n.zrX=i-a.left,void(n.zrY=o-a.top)}if(Qt(oe,t,i,o))return n.zrX=oe[0],void(n.zrY=oe[1])}n.zrX=n.zrY=0}function ue(t){return t||window.event}function he(t,e,n){if(null!=(e=ue(e)).zrX)return e;var i=e.type;if(i&&i.indexOf("touch")>=0){var r="touchend"!==i?e.targetTouches[0]:e.changedTouches[0];r&&se(t,r,e,n)}else{se(t,e,e,n);var o=function(t){var e=t.wheelDelta;if(e)return e;var n=t.deltaX,i=t.deltaY;if(null==n||null==i)return e;return 3*(0!==i?Math.abs(i):Math.abs(n))*(i>0?-1:i<0?1:n>0?-1:1)}(e);e.zrDelta=o?o/120:-(e.detail||0)/3}var a=e.button;return null==e.which&&void 0!==a&&re.test(e.type)&&(e.which=1&a?1:2&a?3:4&a?2:0),e}function ce(t,e,n,i){t.addEventListener(e,n,i)}var pe=function(t){t.preventDefault(),t.stopPropagation(),t.cancelBubble=!0};function de(t){return 2===t.which||3===t.which}var fe=function(){function t(){this._track=[]}return t.prototype.recognize=function(t,e,n){return this._doTrack(t,e,n),this._recognize(t)},t.prototype.clear=function(){return this._track.length=0,this},t.prototype._doTrack=function(t,e,n){var i=t.touches;if(i){for(var r={points:[],touches:[],target:e,event:t},o=0,a=i.length;o1&&r&&r.length>1){var a=ge(r)/ge(o);!isFinite(a)&&(a=1),e.pinchScale=a;var s=[((i=r)[0][0]+i[1][0])/2,(i[0][1]+i[1][1])/2];return e.pinchX=s[0],e.pinchY=s[1],{type:"pinch",target:t[0].target,event:e}}}}};function ve(){return[1,0,0,1,0,0]}function me(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t}function xe(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t}function _e(t,e,n){var i=e[0]*n[0]+e[2]*n[1],r=e[1]*n[0]+e[3]*n[1],o=e[0]*n[2]+e[2]*n[3],a=e[1]*n[2]+e[3]*n[3],s=e[0]*n[4]+e[2]*n[5]+e[4],l=e[1]*n[4]+e[3]*n[5]+e[5];return t[0]=i,t[1]=r,t[2]=o,t[3]=a,t[4]=s,t[5]=l,t}function be(t,e,n){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4]+n[0],t[5]=e[5]+n[1],t}function we(t,e,n){var i=e[0],r=e[2],o=e[4],a=e[1],s=e[3],l=e[5],u=Math.sin(n),h=Math.cos(n);return t[0]=i*h+a*u,t[1]=-i*u+a*h,t[2]=r*h+s*u,t[3]=-r*u+h*s,t[4]=h*o+u*l,t[5]=h*l-u*o,t}function Se(t,e,n){var i=n[0],r=n[1];return t[0]=e[0]*i,t[1]=e[1]*r,t[2]=e[2]*i,t[3]=e[3]*r,t[4]=e[4]*i,t[5]=e[5]*r,t}function Me(t,e){var n=e[0],i=e[2],r=e[4],o=e[1],a=e[3],s=e[5],l=n*a-o*i;return l?(l=1/l,t[0]=a*l,t[1]=-o*l,t[2]=-i*l,t[3]=n*l,t[4]=(i*s-a*r)*l,t[5]=(o*r-n*s)*l,t):null}function Ie(t){var e=[1,0,0,1,0,0];return xe(e,t),e}var Te=Object.freeze({__proto__:null,create:ve,identity:me,copy:xe,mul:_e,translate:be,rotate:we,scale:Se,invert:Me,clone:Ie}),Ce=function(){function t(t,e){this.x=t||0,this.y=e||0}return t.prototype.copy=function(t){return this.x=t.x,this.y=t.y,this},t.prototype.clone=function(){return new t(this.x,this.y)},t.prototype.set=function(t,e){return this.x=t,this.y=e,this},t.prototype.equal=function(t){return t.x===this.x&&t.y===this.y},t.prototype.add=function(t){return this.x+=t.x,this.y+=t.y,this},t.prototype.scale=function(t){this.x*=t,this.y*=t},t.prototype.scaleAndAdd=function(t,e){this.x+=t.x*e,this.y+=t.y*e},t.prototype.sub=function(t){return this.x-=t.x,this.y-=t.y,this},t.prototype.dot=function(t){return this.x*t.x+this.y*t.y},t.prototype.len=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},t.prototype.lenSquare=function(){return this.x*this.x+this.y*this.y},t.prototype.normalize=function(){var t=this.len();return this.x/=t,this.y/=t,this},t.prototype.distance=function(t){var e=this.x-t.x,n=this.y-t.y;return Math.sqrt(e*e+n*n)},t.prototype.distanceSquare=function(t){var e=this.x-t.x,n=this.y-t.y;return e*e+n*n},t.prototype.negate=function(){return this.x=-this.x,this.y=-this.y,this},t.prototype.transform=function(t){if(t){var e=this.x,n=this.y;return this.x=t[0]*e+t[2]*n+t[4],this.y=t[1]*e+t[3]*n+t[5],this}},t.prototype.toArray=function(t){return t[0]=this.x,t[1]=this.y,t},t.prototype.fromArray=function(t){this.x=t[0],this.y=t[1]},t.set=function(t,e,n){t.x=e,t.y=n},t.copy=function(t,e){t.x=e.x,t.y=e.y},t.len=function(t){return Math.sqrt(t.x*t.x+t.y*t.y)},t.lenSquare=function(t){return t.x*t.x+t.y*t.y},t.dot=function(t,e){return t.x*e.x+t.y*e.y},t.add=function(t,e,n){t.x=e.x+n.x,t.y=e.y+n.y},t.sub=function(t,e,n){t.x=e.x-n.x,t.y=e.y-n.y},t.scale=function(t,e,n){t.x=e.x*n,t.y=e.y*n},t.scaleAndAdd=function(t,e,n,i){t.x=e.x+n.x*i,t.y=e.y+n.y*i},t.lerp=function(t,e,n,i){var r=1-i;t.x=r*e.x+i*n.x,t.y=r*e.y+i*n.y},t}(),De=Math.min,Ae=Math.max,ke=new Ce,Le=new Ce,Pe=new Ce,Oe=new Ce,Re=new Ce,Ne=new Ce,Ee=function(){function t(t,e,n,i){n<0&&(t+=n,n=-n),i<0&&(e+=i,i=-i),this.x=t,this.y=e,this.width=n,this.height=i}return t.prototype.union=function(t){var e=De(t.x,this.x),n=De(t.y,this.y);isFinite(this.x)&&isFinite(this.width)?this.width=Ae(t.x+t.width,this.x+this.width)-e:this.width=t.width,isFinite(this.y)&&isFinite(this.height)?this.height=Ae(t.y+t.height,this.y+this.height)-n:this.height=t.height,this.x=e,this.y=n},t.prototype.applyTransform=function(e){t.applyTransform(this,this,e)},t.prototype.calculateTransform=function(t){var e=this,n=t.width/e.width,i=t.height/e.height,r=[1,0,0,1,0,0];return be(r,r,[-e.x,-e.y]),Se(r,r,[n,i]),be(r,r,[t.x,t.y]),r},t.prototype.intersect=function(e,n){if(!e)return!1;e instanceof t||(e=t.create(e));var i=this,r=i.x,o=i.x+i.width,a=i.y,s=i.y+i.height,l=e.x,u=e.x+e.width,h=e.y,c=e.y+e.height,p=!(of&&(f=x,gf&&(f=_,v=n.x&&t<=n.x+n.width&&e>=n.y&&e<=n.y+n.height},t.prototype.clone=function(){return new t(this.x,this.y,this.width,this.height)},t.prototype.copy=function(e){t.copy(this,e)},t.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},t.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},t.prototype.isZero=function(){return 0===this.width||0===this.height},t.create=function(e){return new t(e.x,e.y,e.width,e.height)},t.copy=function(t,e){t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height},t.applyTransform=function(e,n,i){if(i){if(i[1]<1e-5&&i[1]>-1e-5&&i[2]<1e-5&&i[2]>-1e-5){var r=i[0],o=i[3],a=i[4],s=i[5];return e.x=n.x*r+a,e.y=n.y*o+s,e.width=n.width*r,e.height=n.height*o,e.width<0&&(e.x+=e.width,e.width=-e.width),void(e.height<0&&(e.y+=e.height,e.height=-e.height))}ke.x=Pe.x=n.x,ke.y=Oe.y=n.y,Le.x=Oe.x=n.x+n.width,Le.y=Pe.y=n.y+n.height,ke.transform(i),Oe.transform(i),Le.transform(i),Pe.transform(i),e.x=De(ke.x,Le.x,Pe.x,Oe.x),e.y=De(ke.y,Le.y,Pe.y,Oe.y);var l=Ae(ke.x,Le.x,Pe.x,Oe.x),u=Ae(ke.y,Le.y,Pe.y,Oe.y);e.width=l-e.x,e.height=u-e.y}else e!==n&&t.copy(e,n)},t}(),ze="silent";function Ve(){pe(this.event)}var Be=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.handler=null,e}return n(e,t),e.prototype.dispose=function(){},e.prototype.setCursor=function(){},e}(jt),Fe=function(t,e){this.x=t,this.y=e},Ge=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],We=new Ee(0,0,0,0),He=function(t){function e(e,n,i,r,o){var a=t.call(this)||this;return a._hovered=new Fe(0,0),a.storage=e,a.painter=n,a.painterRoot=r,a._pointerSize=o,i=i||new Be,a.proxy=null,a.setHandlerProxy(i),a._draggingMgr=new Zt(a),a}return n(e,t),e.prototype.setHandlerProxy=function(t){this.proxy&&this.proxy.dispose(),t&&(E(Ge,(function(e){t.on&&t.on(e,this[e],this)}),this),t.handler=this),this.proxy=t},e.prototype.mousemove=function(t){var e=t.zrX,n=t.zrY,i=Xe(this,e,n),r=this._hovered,o=r.target;o&&!o.__zr&&(o=(r=this.findHover(r.x,r.y)).target);var a=this._hovered=i?new Fe(e,n):this.findHover(e,n),s=a.target,l=this.proxy;l.setCursor&&l.setCursor(s?s.cursor:"default"),o&&s!==o&&this.dispatchToElement(r,"mouseout",t),this.dispatchToElement(a,"mousemove",t),s&&s!==o&&this.dispatchToElement(a,"mouseover",t)},e.prototype.mouseout=function(t){var e=t.zrEventControl;"only_globalout"!==e&&this.dispatchToElement(this._hovered,"mouseout",t),"no_globalout"!==e&&this.trigger("globalout",{type:"globalout",event:t})},e.prototype.resize=function(){this._hovered=new Fe(0,0)},e.prototype.dispatch=function(t,e){var n=this[t];n&&n.call(this,e)},e.prototype.dispose=function(){this.proxy.dispose(),this.storage=null,this.proxy=null,this.painter=null},e.prototype.setCursorStyle=function(t){var e=this.proxy;e.setCursor&&e.setCursor(t)},e.prototype.dispatchToElement=function(t,e,n){var i=(t=t||{}).target;if(!i||!i.silent){for(var r="on"+e,o=function(t,e,n){return{type:t,event:n,target:e.target,topTarget:e.topTarget,cancelBubble:!1,offsetX:n.zrX,offsetY:n.zrY,gestureEvent:n.gestureEvent,pinchX:n.pinchX,pinchY:n.pinchY,pinchScale:n.pinchScale,wheelDelta:n.zrDelta,zrByTouch:n.zrByTouch,which:n.which,stop:Ve}}(e,t,n);i&&(i[r]&&(o.cancelBubble=!!i[r].call(i,o)),i.trigger(e,o),i=i.__hostTarget?i.__hostTarget:i.parent,!o.cancelBubble););o.cancelBubble||(this.trigger(e,o),this.painter&&this.painter.eachOtherLayer&&this.painter.eachOtherLayer((function(t){"function"==typeof t[r]&&t[r].call(t,o),t.trigger&&t.trigger(e,o)})))}},e.prototype.findHover=function(t,e,n){var i=this.storage.getDisplayList(),r=new Fe(t,e);if(Ue(i,r,t,e,n),this._pointerSize&&!r.target){for(var o=[],a=this._pointerSize,s=a/2,l=new Ee(t-s,e-s,a,a),u=i.length-1;u>=0;u--){var h=i[u];h===n||h.ignore||h.ignoreCoarsePointer||h.parent&&h.parent.ignoreCoarsePointer||(We.copy(h.getBoundingRect()),h.transform&&We.applyTransform(h.transform),We.intersect(l)&&o.push(h))}if(o.length)for(var c=Math.PI/12,p=2*Math.PI,d=0;d=0;o--){var a=t[o],s=void 0;if(a!==r&&!a.ignore&&(s=Ye(a,n,i))&&(!e.topTarget&&(e.topTarget=a),s!==ze)){e.target=a;break}}}function Xe(t,e,n){var i=t.painter;return e<0||e>i.getWidth()||n<0||n>i.getHeight()}E(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],(function(t){He.prototype[t]=function(e){var n,i,r=e.zrX,o=e.zrY,a=Xe(this,r,o);if("mouseup"===t&&a||(i=(n=this.findHover(r,o)).target),"mousedown"===t)this._downEl=i,this._downPoint=[e.zrX,e.zrY],this._upEl=i;else if("mouseup"===t)this._upEl=i;else if("click"===t){if(this._downEl!==this._upEl||!this._downPoint||Vt(this._downPoint,[e.zrX,e.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,t,e)}}));function Ze(t,e,n,i){var r=e+1;if(r===n)return 1;if(i(t[r++],t[e])<0){for(;r=0;)r++;return r-e}function je(t,e,n,i,r){for(i===e&&i++;i>>1])<0?l=o:s=o+1;var u=i-s;switch(u){case 3:t[s+3]=t[s+2];case 2:t[s+2]=t[s+1];case 1:t[s+1]=t[s];break;default:for(;u>0;)t[s+u]=t[s+u-1],u--}t[s]=a}}function qe(t,e,n,i,r,o){var a=0,s=0,l=1;if(o(t,e[n+r])>0){for(s=i-r;l0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}else{for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}for(a++;a>>1);o(t,e[n+h])>0?a=h+1:l=h}return l}function Ke(t,e,n,i,r,o){var a=0,s=0,l=1;if(o(t,e[n+r])<0){for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}else{for(s=i-r;l=0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}for(a++;a>>1);o(t,e[n+h])<0?l=h:a=h+1}return l}function $e(t,e){var n,i,r=7,o=0;t.length;var a=[];function s(s){var l=n[s],u=i[s],h=n[s+1],c=i[s+1];i[s]=u+c,s===o-3&&(n[s+1]=n[s+2],i[s+1]=i[s+2]),o--;var p=Ke(t[h],t,l,u,0,e);l+=p,0!==(u-=p)&&0!==(c=qe(t[l+u-1],t,h,c,c-1,e))&&(u<=c?function(n,i,o,s){var l=0;for(l=0;l=7||d>=7);if(f)break;g<0&&(g=0),g+=2}if((r=g)<1&&(r=1),1===i){for(l=0;l=0;l--)t[d+l]=t[p+l];return void(t[c]=a[h])}var f=r;for(;;){var g=0,y=0,v=!1;do{if(e(a[h],t[u])<0){if(t[c--]=t[u--],g++,y=0,0==--i){v=!0;break}}else if(t[c--]=a[h--],y++,g=0,1==--s){v=!0;break}}while((g|y)=0;l--)t[d+l]=t[p+l];if(0===i){v=!0;break}}if(t[c--]=a[h--],1==--s){v=!0;break}if(0!==(y=s-qe(t[u],a,0,s,s-1,e))){for(s-=y,d=(c-=y)+1,p=(h-=y)+1,l=0;l=7||y>=7);if(v)break;f<0&&(f=0),f+=2}(r=f)<1&&(r=1);if(1===s){for(d=(c-=i)+1,p=(u-=i)+1,l=i-1;l>=0;l--)t[d+l]=t[p+l];t[c]=a[h]}else{if(0===s)throw new Error;for(p=c-(s-1),l=0;l1;){var t=o-2;if(t>=1&&i[t-1]<=i[t]+i[t+1]||t>=2&&i[t-2]<=i[t]+i[t-1])i[t-1]i[t+1])break;s(t)}},forceMergeRuns:function(){for(;o>1;){var t=o-2;t>0&&i[t-1]=32;)e|=1&t,t>>=1;return t+e}(r);do{if((o=Ze(t,n,i,e))s&&(l=s),je(t,n,n+l,n+o,e),o=l}a.pushRun(n,o),a.mergeRuns(),r-=o,n+=o}while(0!==r);a.forceMergeRuns()}}}var Qe=!1;function tn(){Qe||(Qe=!0,console.warn("z / z2 / zlevel of displayable is invalid, which may cause unexpected errors"))}function en(t,e){return t.zlevel===e.zlevel?t.z===e.z?t.z2-e.z2:t.z-e.z:t.zlevel-e.zlevel}var nn=function(){function t(){this._roots=[],this._displayList=[],this._displayListLen=0,this.displayableSortFunc=en}return t.prototype.traverse=function(t,e){for(var n=0;n0&&(u.__clipPaths=[]),isNaN(u.z)&&(tn(),u.z=0),isNaN(u.z2)&&(tn(),u.z2=0),isNaN(u.zlevel)&&(tn(),u.zlevel=0),this._displayList[this._displayListLen++]=u}var h=t.getDecalElement&&t.getDecalElement();h&&this._updateAndAddDisplayable(h,e,n);var c=t.getTextGuideLine();c&&this._updateAndAddDisplayable(c,e,n);var p=t.getTextContent();p&&this._updateAndAddDisplayable(p,e,n)}},t.prototype.addRoot=function(t){t.__zr&&t.__zr.storage===this||this._roots.push(t)},t.prototype.delRoot=function(t){if(t instanceof Array)for(var e=0,n=t.length;e=0&&this._roots.splice(i,1)}},t.prototype.delAllRoots=function(){this._roots=[],this._displayList=[],this._displayListLen=0},t.prototype.getRoots=function(){return this._roots},t.prototype.dispose=function(){this._displayList=null,this._roots=null},t}(),rn=r.hasGlobalWindow&&(window.requestAnimationFrame&&window.requestAnimationFrame.bind(window)||window.msRequestAnimationFrame&&window.msRequestAnimationFrame.bind(window)||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame)||function(t){return setTimeout(t,16)},on={linear:function(t){return t},quadraticIn:function(t){return t*t},quadraticOut:function(t){return t*(2-t)},quadraticInOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)},cubicIn:function(t){return t*t*t},cubicOut:function(t){return--t*t*t+1},cubicInOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},quarticIn:function(t){return t*t*t*t},quarticOut:function(t){return 1- --t*t*t*t},quarticInOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},quinticIn:function(t){return t*t*t*t*t},quinticOut:function(t){return--t*t*t*t*t+1},quinticInOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},sinusoidalIn:function(t){return 1-Math.cos(t*Math.PI/2)},sinusoidalOut:function(t){return Math.sin(t*Math.PI/2)},sinusoidalInOut:function(t){return.5*(1-Math.cos(Math.PI*t))},exponentialIn:function(t){return 0===t?0:Math.pow(1024,t-1)},exponentialOut:function(t){return 1===t?1:1-Math.pow(2,-10*t)},exponentialInOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(2-Math.pow(2,-10*(t-1)))},circularIn:function(t){return 1-Math.sqrt(1-t*t)},circularOut:function(t){return Math.sqrt(1- --t*t)},circularInOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},elasticIn:function(t){var e,n=.1;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=.4*Math.asin(1/n)/(2*Math.PI),-n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4))},elasticOut:function(t){var e,n=.1;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=.4*Math.asin(1/n)/(2*Math.PI),n*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/.4)+1)},elasticInOut:function(t){var e,n=.1,i=.4;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=i*Math.asin(1/n)/(2*Math.PI),(t*=2)<1?n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*-.5:n*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*.5+1)},backIn:function(t){var e=1.70158;return t*t*((e+1)*t-e)},backOut:function(t){var e=1.70158;return--t*t*((e+1)*t+e)+1},backInOut:function(t){var e=2.5949095;return(t*=2)<1?t*t*((e+1)*t-e)*.5:.5*((t-=2)*t*((e+1)*t+e)+2)},bounceIn:function(t){return 1-on.bounceOut(1-t)},bounceOut:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},bounceInOut:function(t){return t<.5?.5*on.bounceIn(2*t):.5*on.bounceOut(2*t-1)+.5}},an=Math.pow,sn=Math.sqrt,ln=1e-8,un=1e-4,hn=sn(3),cn=1/3,pn=Mt(),dn=Mt(),fn=Mt();function gn(t){return t>-1e-8&&tln||t<-1e-8}function vn(t,e,n,i,r){var o=1-r;return o*o*(o*t+3*r*e)+r*r*(r*i+3*o*n)}function mn(t,e,n,i,r){var o=1-r;return 3*(((e-t)*o+2*(n-e)*r)*o+(i-n)*r*r)}function xn(t,e,n,i,r,o){var a=i+3*(e-n)-t,s=3*(n-2*e+t),l=3*(e-t),u=t-r,h=s*s-3*a*l,c=s*l-9*a*u,p=l*l-3*s*u,d=0;if(gn(h)&&gn(c)){if(gn(s))o[0]=0;else(M=-l/s)>=0&&M<=1&&(o[d++]=M)}else{var f=c*c-4*h*p;if(gn(f)){var g=c/h,y=-g/2;(M=-s/a+g)>=0&&M<=1&&(o[d++]=M),y>=0&&y<=1&&(o[d++]=y)}else if(f>0){var v=sn(f),m=h*s+1.5*a*(-c+v),x=h*s+1.5*a*(-c-v);(M=(-s-((m=m<0?-an(-m,cn):an(m,cn))+(x=x<0?-an(-x,cn):an(x,cn))))/(3*a))>=0&&M<=1&&(o[d++]=M)}else{var _=(2*h*s-3*a*c)/(2*sn(h*h*h)),b=Math.acos(_)/3,w=sn(h),S=Math.cos(b),M=(-s-2*w*S)/(3*a),I=(y=(-s+w*(S+hn*Math.sin(b)))/(3*a),(-s+w*(S-hn*Math.sin(b)))/(3*a));M>=0&&M<=1&&(o[d++]=M),y>=0&&y<=1&&(o[d++]=y),I>=0&&I<=1&&(o[d++]=I)}}return d}function _n(t,e,n,i,r){var o=6*n-12*e+6*t,a=9*e+3*i-3*t-9*n,s=3*e-3*t,l=0;if(gn(a)){if(yn(o))(h=-s/o)>=0&&h<=1&&(r[l++]=h)}else{var u=o*o-4*a*s;if(gn(u))r[0]=-o/(2*a);else if(u>0){var h,c=sn(u),p=(-o-c)/(2*a);(h=(-o+c)/(2*a))>=0&&h<=1&&(r[l++]=h),p>=0&&p<=1&&(r[l++]=p)}}return l}function bn(t,e,n,i,r,o){var a=(e-t)*r+t,s=(n-e)*r+e,l=(i-n)*r+n,u=(s-a)*r+a,h=(l-s)*r+s,c=(h-u)*r+u;o[0]=t,o[1]=a,o[2]=u,o[3]=c,o[4]=c,o[5]=h,o[6]=l,o[7]=i}function wn(t,e,n,i,r,o,a,s,l,u,h){var c,p,d,f,g,y=.005,v=1/0;pn[0]=l,pn[1]=u;for(var m=0;m<1;m+=.05)dn[0]=vn(t,n,r,a,m),dn[1]=vn(e,i,o,s,m),(f=Ft(pn,dn))=0&&f=0&&y=1?1:xn(0,i,o,1,t,s)&&vn(0,r,a,1,s[0])}}}var Pn=function(){function t(t){this._inited=!1,this._startTime=0,this._pausedTime=0,this._paused=!1,this._life=t.life||1e3,this._delay=t.delay||0,this.loop=t.loop||!1,this.onframe=t.onframe||bt,this.ondestroy=t.ondestroy||bt,this.onrestart=t.onrestart||bt,t.easing&&this.setEasing(t.easing)}return t.prototype.step=function(t,e){if(this._inited||(this._startTime=t+this._delay,this._inited=!0),!this._paused){var n=this._life,i=t-this._startTime-this._pausedTime,r=i/n;r<0&&(r=0),r=Math.min(r,1);var o=this.easingFunc,a=o?o(r):r;if(this.onframe(a),1===r){if(!this.loop)return!0;var s=i%n;this._startTime=t-s,this._pausedTime=0,this.onrestart()}return!1}this._pausedTime+=e},t.prototype.pause=function(){this._paused=!0},t.prototype.resume=function(){this._paused=!1},t.prototype.setEasing=function(t){this.easing=t,this.easingFunc=U(t)?t:on[t]||Ln(t)},t}(),On=function(t){this.value=t},Rn=function(){function t(){this._len=0}return t.prototype.insert=function(t){var e=new On(t);return this.insertEntry(e),e},t.prototype.insertEntry=function(t){this.head?(this.tail.next=t,t.prev=this.tail,t.next=null,this.tail=t):this.head=this.tail=t,this._len++},t.prototype.remove=function(t){var e=t.prev,n=t.next;e?e.next=n:this.head=n,n?n.prev=e:this.tail=e,t.next=t.prev=null,this._len--},t.prototype.len=function(){return this._len},t.prototype.clear=function(){this.head=this.tail=null,this._len=0},t}(),Nn=function(){function t(t){this._list=new Rn,this._maxSize=10,this._map={},this._maxSize=t}return t.prototype.put=function(t,e){var n=this._list,i=this._map,r=null;if(null==i[t]){var o=n.len(),a=this._lastRemovedEntry;if(o>=this._maxSize&&o>0){var s=n.head;n.remove(s),delete i[s.key],r=s.value,this._lastRemovedEntry=s}a?a.value=e:a=new On(e),a.key=t,n.insertEntry(a),i[t]=a}return r},t.prototype.get=function(t){var e=this._map[t],n=this._list;if(null!=e)return e!==n.tail&&(n.remove(e),n.insertEntry(e)),e.value},t.prototype.clear=function(){this._list.clear(),this._map={}},t.prototype.len=function(){return this._list.len()},t}(),En={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};function zn(t){return(t=Math.round(t))<0?0:t>255?255:t}function Vn(t){return t<0?0:t>1?1:t}function Bn(t){var e=t;return e.length&&"%"===e.charAt(e.length-1)?zn(parseFloat(e)/100*255):zn(parseInt(e,10))}function Fn(t){var e=t;return e.length&&"%"===e.charAt(e.length-1)?Vn(parseFloat(e)/100):Vn(parseFloat(e))}function Gn(t,e,n){return n<0?n+=1:n>1&&(n-=1),6*n<1?t+(e-t)*n*6:2*n<1?e:3*n<2?t+(e-t)*(2/3-n)*6:t}function Wn(t,e,n){return t+(e-t)*n}function Hn(t,e,n,i,r){return t[0]=e,t[1]=n,t[2]=i,t[3]=r,t}function Yn(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}var Un=new Nn(20),Xn=null;function Zn(t,e){Xn&&Yn(Xn,e),Xn=Un.put(t,Xn||e.slice())}function jn(t,e){if(t){e=e||[];var n=Un.get(t);if(n)return Yn(e,n);var i=(t+="").replace(/ /g,"").toLowerCase();if(i in En)return Yn(e,En[i]),Zn(t,e),e;var r,o=i.length;if("#"===i.charAt(0))return 4===o||5===o?(r=parseInt(i.slice(1,4),16))>=0&&r<=4095?(Hn(e,(3840&r)>>4|(3840&r)>>8,240&r|(240&r)>>4,15&r|(15&r)<<4,5===o?parseInt(i.slice(4),16)/15:1),Zn(t,e),e):void Hn(e,0,0,0,1):7===o||9===o?(r=parseInt(i.slice(1,7),16))>=0&&r<=16777215?(Hn(e,(16711680&r)>>16,(65280&r)>>8,255&r,9===o?parseInt(i.slice(7),16)/255:1),Zn(t,e),e):void Hn(e,0,0,0,1):void 0;var a=i.indexOf("("),s=i.indexOf(")");if(-1!==a&&s+1===o){var l=i.substr(0,a),u=i.substr(a+1,s-(a+1)).split(","),h=1;switch(l){case"rgba":if(4!==u.length)return 3===u.length?Hn(e,+u[0],+u[1],+u[2],1):Hn(e,0,0,0,1);h=Fn(u.pop());case"rgb":return u.length>=3?(Hn(e,Bn(u[0]),Bn(u[1]),Bn(u[2]),3===u.length?h:Fn(u[3])),Zn(t,e),e):void Hn(e,0,0,0,1);case"hsla":return 4!==u.length?void Hn(e,0,0,0,1):(u[3]=Fn(u[3]),qn(u,e),Zn(t,e),e);case"hsl":return 3!==u.length?void Hn(e,0,0,0,1):(qn(u,e),Zn(t,e),e);default:return}}Hn(e,0,0,0,1)}}function qn(t,e){var n=(parseFloat(t[0])%360+360)%360/360,i=Fn(t[1]),r=Fn(t[2]),o=r<=.5?r*(i+1):r+i-r*i,a=2*r-o;return Hn(e=e||[],zn(255*Gn(a,o,n+1/3)),zn(255*Gn(a,o,n)),zn(255*Gn(a,o,n-1/3)),1),4===t.length&&(e[3]=t[3]),e}function Kn(t,e){var n=jn(t);if(n){for(var i=0;i<3;i++)n[i]=e<0?n[i]*(1-e)|0:(255-n[i])*e+n[i]|0,n[i]>255?n[i]=255:n[i]<0&&(n[i]=0);return ii(n,4===n.length?"rgba":"rgb")}}function $n(t,e,n){if(e&&e.length&&t>=0&&t<=1){n=n||[];var i=t*(e.length-1),r=Math.floor(i),o=Math.ceil(i),a=e[r],s=e[o],l=i-r;return n[0]=zn(Wn(a[0],s[0],l)),n[1]=zn(Wn(a[1],s[1],l)),n[2]=zn(Wn(a[2],s[2],l)),n[3]=Vn(Wn(a[3],s[3],l)),n}}var Jn=$n;function Qn(t,e,n){if(e&&e.length&&t>=0&&t<=1){var i=t*(e.length-1),r=Math.floor(i),o=Math.ceil(i),a=jn(e[r]),s=jn(e[o]),l=i-r,u=ii([zn(Wn(a[0],s[0],l)),zn(Wn(a[1],s[1],l)),zn(Wn(a[2],s[2],l)),Vn(Wn(a[3],s[3],l))],"rgba");return n?{color:u,leftIndex:r,rightIndex:o,value:i}:u}}var ti=Qn;function ei(t,e,n,i){var r=jn(t);if(t)return r=function(t){if(t){var e,n,i=t[0]/255,r=t[1]/255,o=t[2]/255,a=Math.min(i,r,o),s=Math.max(i,r,o),l=s-a,u=(s+a)/2;if(0===l)e=0,n=0;else{n=u<.5?l/(s+a):l/(2-s-a);var h=((s-i)/6+l/2)/l,c=((s-r)/6+l/2)/l,p=((s-o)/6+l/2)/l;i===s?e=p-c:r===s?e=1/3+h-p:o===s&&(e=2/3+c-h),e<0&&(e+=1),e>1&&(e-=1)}var d=[360*e,n,u];return null!=t[3]&&d.push(t[3]),d}}(r),null!=e&&(r[0]=function(t){return(t=Math.round(t))<0?0:t>360?360:t}(e)),null!=n&&(r[1]=Fn(n)),null!=i&&(r[2]=Fn(i)),ii(qn(r),"rgba")}function ni(t,e){var n=jn(t);if(n&&null!=e)return n[3]=Vn(e),ii(n,"rgba")}function ii(t,e){if(t&&t.length){var n=t[0]+","+t[1]+","+t[2];return"rgba"!==e&&"hsva"!==e&&"hsla"!==e||(n+=","+t[3]),e+"("+n+")"}}function ri(t,e){var n=jn(t);return n?(.299*n[0]+.587*n[1]+.114*n[2])*n[3]/255+(1-n[3])*e:0}var oi=Object.freeze({__proto__:null,parse:jn,lift:Kn,toHex:function(t){var e=jn(t);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1)},fastLerp:$n,fastMapToColor:Jn,lerp:Qn,mapToColor:ti,modifyHSL:ei,modifyAlpha:ni,stringify:ii,lum:ri,random:function(){return ii([Math.round(255*Math.random()),Math.round(255*Math.random()),Math.round(255*Math.random())],"rgb")}}),ai=Math.round;function si(t){var e;if(t&&"transparent"!==t){if("string"==typeof t&&t.indexOf("rgba")>-1){var n=jn(t);n&&(t="rgb("+n[0]+","+n[1]+","+n[2]+")",e=n[3])}}else t="none";return{color:t,opacity:null==e?1:e}}var li=1e-4;function ui(t){return t-1e-4}function hi(t){return ai(1e3*t)/1e3}function ci(t){return ai(1e4*t)/1e4}var pi={left:"start",right:"end",center:"middle",middle:"middle"};function di(t){return t&&!!t.image}function fi(t){return di(t)||function(t){return t&&!!t.svgElement}(t)}function gi(t){return"linear"===t.type}function yi(t){return"radial"===t.type}function vi(t){return t&&("linear"===t.type||"radial"===t.type)}function mi(t){return"url(#"+t+")"}function xi(t){var e=t.getGlobalScale(),n=Math.max(e[0],e[1]);return Math.max(Math.ceil(Math.log(n)/Math.log(10)),1)}function _i(t){var e=t.x||0,n=t.y||0,i=(t.rotation||0)*wt,r=rt(t.scaleX,1),o=rt(t.scaleY,1),a=t.skewX||0,s=t.skewY||0,l=[];return(e||n)&&l.push("translate("+e+"px,"+n+"px)"),i&&l.push("rotate("+i+")"),1===r&&1===o||l.push("scale("+r+","+o+")"),(a||s)&&l.push("skew("+ai(a*wt)+"deg, "+ai(s*wt)+"deg)"),l.join(" ")}var bi=r.hasGlobalWindow&&U(window.btoa)?function(t){return window.btoa(unescape(encodeURIComponent(t)))}:"undefined"!=typeof Buffer?function(t){return Buffer.from(t).toString("base64")}:function(t){return null},wi=Array.prototype.slice;function Si(t,e,n){return(e-t)*n+t}function Mi(t,e,n,i){for(var r=e.length,o=0;oi?e:t,o=Math.min(n,i),a=r[o-1]||{color:[0,0,0,0],offset:0},s=o;sa)i.length=a;else for(var s=o;s=1},t.prototype.getAdditiveTrack=function(){return this._additiveTrack},t.prototype.addKeyframe=function(t,e,n){this._needsSort=!0;var i=this.keyframes,r=i.length,o=!1,a=6,s=e;if(N(e)){var l=function(t){return N(t&&t[0])?2:1}(e);a=l,(1===l&&!j(e[0])||2===l&&!j(e[0][0]))&&(o=!0)}else if(j(e)&&!nt(e))a=0;else if(X(e))if(isNaN(+e)){var u=jn(e);u&&(s=u,a=3)}else a=0;else if(Q(e)){var h=A({},s);h.colorStops=z(e.colorStops,(function(t){return{offset:t.offset,color:jn(t.color)}})),gi(e)?a=4:yi(e)&&(a=5),s=h}0===r?this.valType=a:a===this.valType&&6!==a||(o=!0),this.discrete=this.discrete||o;var c={time:t,value:s,rawValue:e,percent:0};return n&&(c.easing=n,c.easingFunc=U(n)?n:on[n]||Ln(n)),i.push(c),c},t.prototype.prepare=function(t,e){var n=this.keyframes;this._needsSort&&n.sort((function(t,e){return t.time-e.time}));for(var i=this.valType,r=n.length,o=n[r-1],a=this.discrete,s=Pi(i),l=Li(i),u=0;u=0&&!(l[n].percent<=e);n--);n=d(n,u-2)}else{for(n=p;ne);n++);n=d(n-1,u-2)}r=l[n+1],i=l[n]}if(i&&r){this._lastFr=n,this._lastFrP=e;var f=r.percent-i.percent,g=0===f?1:d((e-i.percent)/f,1);r.easingFunc&&(g=r.easingFunc(g));var y=o?this._additiveValue:c?Oi:t[h];if(!Pi(s)&&!c||y||(y=this._additiveValue=[]),this.discrete)t[h]=g<1?i.rawValue:r.rawValue;else if(Pi(s))1===s?Mi(y,i[a],r[a],g):function(t,e,n,i){for(var r=e.length,o=r&&e[0].length,a=0;a0&&s.addKeyframe(0,Ai(l),i),this._trackKeys.push(a)}s.addKeyframe(t,Ai(e[a]),i)}return this._maxTime=Math.max(this._maxTime,t),this},t.prototype.pause=function(){this._clip.pause(),this._paused=!0},t.prototype.resume=function(){this._clip.resume(),this._paused=!1},t.prototype.isPaused=function(){return!!this._paused},t.prototype.duration=function(t){return this._maxTime=t,this._force=!0,this},t.prototype._doneCallback=function(){this._setTracksFinished(),this._clip=null;var t=this._doneCbs;if(t)for(var e=t.length,n=0;n0)){this._started=1;for(var e=this,n=[],i=this._maxTime||0,r=0;r1){var a=o.pop();r.addKeyframe(a.time,t[i]),r.prepare(this._maxTime,r.getAdditiveTrack())}}}},t}();function Ei(){return(new Date).getTime()}var zi,Vi,Bi=function(t){function e(e){var n=t.call(this)||this;return n._running=!1,n._time=0,n._pausedTime=0,n._pauseStart=0,n._paused=!1,e=e||{},n.stage=e.stage||{},n}return n(e,t),e.prototype.addClip=function(t){t.animation&&this.removeClip(t),this._head?(this._tail.next=t,t.prev=this._tail,t.next=null,this._tail=t):this._head=this._tail=t,t.animation=this},e.prototype.addAnimator=function(t){t.animation=this;var e=t.getClip();e&&this.addClip(e)},e.prototype.removeClip=function(t){if(t.animation){var e=t.prev,n=t.next;e?e.next=n:this._head=n,n?n.prev=e:this._tail=e,t.next=t.prev=t.animation=null}},e.prototype.removeAnimator=function(t){var e=t.getClip();e&&this.removeClip(e),t.animation=null},e.prototype.update=function(t){for(var e=Ei()-this._pausedTime,n=e-this._time,i=this._head;i;){var r=i.next;i.step(e,n)?(i.ondestroy(),this.removeClip(i),i=r):i=r}this._time=e,t||(this.trigger("frame",n),this.stage.update&&this.stage.update())},e.prototype._startLoop=function(){var t=this;this._running=!0,rn((function e(){t._running&&(rn(e),!t._paused&&t.update())}))},e.prototype.start=function(){this._running||(this._time=Ei(),this._pausedTime=0,this._startLoop())},e.prototype.stop=function(){this._running=!1},e.prototype.pause=function(){this._paused||(this._pauseStart=Ei(),this._paused=!0)},e.prototype.resume=function(){this._paused&&(this._pausedTime+=Ei()-this._pauseStart,this._paused=!1)},e.prototype.clear=function(){for(var t=this._head;t;){var e=t.next;t.prev=t.next=t.animation=null,t=e}this._head=this._tail=null},e.prototype.isFinished=function(){return null==this._head},e.prototype.animate=function(t,e){e=e||{},this.start();var n=new Ni(t,e.loop);return this.addAnimator(n),n},e}(jt),Fi=r.domSupported,Gi=(Vi={pointerdown:1,pointerup:1,pointermove:1,pointerout:1},{mouse:zi=["click","dblclick","mousewheel","wheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],touch:["touchstart","touchend","touchmove"],pointer:z(zi,(function(t){var e=t.replace("mouse","pointer");return Vi.hasOwnProperty(e)?e:t}))}),Wi=["mousemove","mouseup"],Hi=["pointermove","pointerup"],Yi=!1;function Ui(t){var e=t.pointerType;return"pen"===e||"touch"===e}function Xi(t){t&&(t.zrByTouch=!0)}function Zi(t,e){for(var n=e,i=!1;n&&9!==n.nodeType&&!(i=n.domBelongToZr||n!==e&&n===t.painterRoot);)n=n.parentNode;return i}var ji=function(t,e){this.stopPropagation=bt,this.stopImmediatePropagation=bt,this.preventDefault=bt,this.type=e.type,this.target=this.currentTarget=t.dom,this.pointerType=e.pointerType,this.clientX=e.clientX,this.clientY=e.clientY},qi={mousedown:function(t){t=he(this.dom,t),this.__mayPointerCapture=[t.zrX,t.zrY],this.trigger("mousedown",t)},mousemove:function(t){t=he(this.dom,t);var e=this.__mayPointerCapture;!e||t.zrX===e[0]&&t.zrY===e[1]||this.__togglePointerCapture(!0),this.trigger("mousemove",t)},mouseup:function(t){t=he(this.dom,t),this.__togglePointerCapture(!1),this.trigger("mouseup",t)},mouseout:function(t){Zi(this,(t=he(this.dom,t)).toElement||t.relatedTarget)||(this.__pointerCapturing&&(t.zrEventControl="no_globalout"),this.trigger("mouseout",t))},wheel:function(t){Yi=!0,t=he(this.dom,t),this.trigger("mousewheel",t)},mousewheel:function(t){Yi||(t=he(this.dom,t),this.trigger("mousewheel",t))},touchstart:function(t){Xi(t=he(this.dom,t)),this.__lastTouchMoment=new Date,this.handler.processGesture(t,"start"),qi.mousemove.call(this,t),qi.mousedown.call(this,t)},touchmove:function(t){Xi(t=he(this.dom,t)),this.handler.processGesture(t,"change"),qi.mousemove.call(this,t)},touchend:function(t){Xi(t=he(this.dom,t)),this.handler.processGesture(t,"end"),qi.mouseup.call(this,t),+new Date-+this.__lastTouchMoment<300&&qi.click.call(this,t)},pointerdown:function(t){qi.mousedown.call(this,t)},pointermove:function(t){Ui(t)||qi.mousemove.call(this,t)},pointerup:function(t){qi.mouseup.call(this,t)},pointerout:function(t){Ui(t)||qi.mouseout.call(this,t)}};E(["click","dblclick","contextmenu"],(function(t){qi[t]=function(e){e=he(this.dom,e),this.trigger(t,e)}}));var Ki={pointermove:function(t){Ui(t)||Ki.mousemove.call(this,t)},pointerup:function(t){Ki.mouseup.call(this,t)},mousemove:function(t){this.trigger("mousemove",t)},mouseup:function(t){var e=this.__pointerCapturing;this.__togglePointerCapture(!1),this.trigger("mouseup",t),e&&(t.zrEventControl="only_globalout",this.trigger("mouseout",t))}};function $i(t,e){var n=e.domHandlers;r.pointerEventsSupported?E(Gi.pointer,(function(i){Qi(e,i,(function(e){n[i].call(t,e)}))})):(r.touchEventsSupported&&E(Gi.touch,(function(i){Qi(e,i,(function(r){n[i].call(t,r),function(t){t.touching=!0,null!=t.touchTimer&&(clearTimeout(t.touchTimer),t.touchTimer=null),t.touchTimer=setTimeout((function(){t.touching=!1,t.touchTimer=null}),700)}(e)}))})),E(Gi.mouse,(function(i){Qi(e,i,(function(r){r=ue(r),e.touching||n[i].call(t,r)}))})))}function Ji(t,e){function n(n){Qi(e,n,(function(i){i=ue(i),Zi(t,i.target)||(i=function(t,e){return he(t.dom,new ji(t,e),!0)}(t,i),e.domHandlers[n].call(t,i))}),{capture:!0})}r.pointerEventsSupported?E(Hi,n):r.touchEventsSupported||E(Wi,n)}function Qi(t,e,n,i){t.mounted[e]=n,t.listenerOpts[e]=i,ce(t.domTarget,e,n,i)}function tr(t){var e,n,i,r,o=t.mounted;for(var a in o)o.hasOwnProperty(a)&&(e=t.domTarget,n=a,i=o[a],r=t.listenerOpts[a],e.removeEventListener(n,i,r));t.mounted={}}var er=function(t,e){this.mounted={},this.listenerOpts={},this.touching=!1,this.domTarget=t,this.domHandlers=e},nr=function(t){function e(e,n){var i=t.call(this)||this;return i.__pointerCapturing=!1,i.dom=e,i.painterRoot=n,i._localHandlerScope=new er(e,qi),Fi&&(i._globalHandlerScope=new er(document,Ki)),$i(i,i._localHandlerScope),i}return n(e,t),e.prototype.dispose=function(){tr(this._localHandlerScope),Fi&&tr(this._globalHandlerScope)},e.prototype.setCursor=function(t){this.dom.style&&(this.dom.style.cursor=t||"default")},e.prototype.__togglePointerCapture=function(t){if(this.__mayPointerCapture=null,Fi&&+this.__pointerCapturing^+t){this.__pointerCapturing=t;var e=this._globalHandlerScope;t?Ji(this,e):tr(e)}},e}(jt),ir=1;r.hasGlobalWindow&&(ir=Math.max(window.devicePixelRatio||window.screen&&window.screen.deviceXDPI/window.screen.logicalXDPI||1,1));var rr=ir,or="#333",ar="#ccc",sr=me,lr=5e-5;function ur(t){return t>lr||t<-5e-5}var hr=[],cr=[],pr=[1,0,0,1,0,0],dr=Math.abs,fr=function(){function t(){}return t.prototype.getLocalTransform=function(e){return t.getLocalTransform(this,e)},t.prototype.setPosition=function(t){this.x=t[0],this.y=t[1]},t.prototype.setScale=function(t){this.scaleX=t[0],this.scaleY=t[1]},t.prototype.setSkew=function(t){this.skewX=t[0],this.skewY=t[1]},t.prototype.setOrigin=function(t){this.originX=t[0],this.originY=t[1]},t.prototype.needLocalTransform=function(){return ur(this.rotation)||ur(this.x)||ur(this.y)||ur(this.scaleX-1)||ur(this.scaleY-1)||ur(this.skewX)||ur(this.skewY)},t.prototype.updateTransform=function(){var t=this.parent&&this.parent.transform,e=this.needLocalTransform(),n=this.transform;e||t?(n=n||[1,0,0,1,0,0],e?this.getLocalTransform(n):sr(n),t&&(e?_e(n,t,n):xe(n,t)),this.transform=n,this._resolveGlobalScaleRatio(n)):n&&sr(n)},t.prototype._resolveGlobalScaleRatio=function(t){var e=this.globalScaleRatio;if(null!=e&&1!==e){this.getGlobalScale(hr);var n=hr[0]<0?-1:1,i=hr[1]<0?-1:1,r=((hr[0]-n)*e+n)/hr[0]||0,o=((hr[1]-i)*e+i)/hr[1]||0;t[0]*=r,t[1]*=r,t[2]*=o,t[3]*=o}this.invTransform=this.invTransform||[1,0,0,1,0,0],Me(this.invTransform,t)},t.prototype.getComputedTransform=function(){for(var t=this,e=[];t;)e.push(t),t=t.parent;for(;t=e.pop();)t.updateTransform();return this.transform},t.prototype.setLocalTransform=function(t){if(t){var e=t[0]*t[0]+t[1]*t[1],n=t[2]*t[2]+t[3]*t[3],i=Math.atan2(t[1],t[0]),r=Math.PI/2+i-Math.atan2(t[3],t[2]);n=Math.sqrt(n)*Math.cos(r),e=Math.sqrt(e),this.skewX=r,this.skewY=0,this.rotation=-i,this.x=+t[4],this.y=+t[5],this.scaleX=e,this.scaleY=n,this.originX=0,this.originY=0}},t.prototype.decomposeTransform=function(){if(this.transform){var t=this.parent,e=this.transform;t&&t.transform&&(_e(cr,t.invTransform,e),e=cr);var n=this.originX,i=this.originY;(n||i)&&(pr[4]=n,pr[5]=i,_e(cr,e,pr),cr[4]-=n,cr[5]-=i,e=cr),this.setLocalTransform(e)}},t.prototype.getGlobalScale=function(t){var e=this.transform;return t=t||[],e?(t[0]=Math.sqrt(e[0]*e[0]+e[1]*e[1]),t[1]=Math.sqrt(e[2]*e[2]+e[3]*e[3]),e[0]<0&&(t[0]=-t[0]),e[3]<0&&(t[1]=-t[1]),t):(t[0]=1,t[1]=1,t)},t.prototype.transformCoordToLocal=function(t,e){var n=[t,e],i=this.invTransform;return i&&Wt(n,n,i),n},t.prototype.transformCoordToGlobal=function(t,e){var n=[t,e],i=this.transform;return i&&Wt(n,n,i),n},t.prototype.getLineScale=function(){var t=this.transform;return t&&dr(t[0]-1)>1e-10&&dr(t[3]-1)>1e-10?Math.sqrt(dr(t[0]*t[3]-t[2]*t[1])):1},t.prototype.copyTransform=function(t){yr(this,t)},t.getLocalTransform=function(t,e){e=e||[];var n=t.originX||0,i=t.originY||0,r=t.scaleX,o=t.scaleY,a=t.anchorX,s=t.anchorY,l=t.rotation||0,u=t.x,h=t.y,c=t.skewX?Math.tan(t.skewX):0,p=t.skewY?Math.tan(-t.skewY):0;if(n||i||a||s){var d=n+a,f=i+s;e[4]=-d*r-c*f*o,e[5]=-f*o-p*d*r}else e[4]=e[5]=0;return e[0]=r,e[3]=o,e[1]=p*r,e[2]=c*o,l&&we(e,e,l),e[4]+=n+u,e[5]+=i+h,e},t.initDefaultProps=function(){var e=t.prototype;e.scaleX=e.scaleY=e.globalScaleRatio=1,e.x=e.y=e.originX=e.originY=e.skewX=e.skewY=e.rotation=e.anchorX=e.anchorY=0}(),t}(),gr=["x","y","originX","originY","anchorX","anchorY","rotation","scaleX","scaleY","skewX","skewY"];function yr(t,e){for(var n=0;n=0?parseFloat(t)/100*e:parseFloat(t):t}function Ir(t,e,n){var i=e.position||"inside",r=null!=e.distance?e.distance:5,o=n.height,a=n.width,s=o/2,l=n.x,u=n.y,h="left",c="top";if(i instanceof Array)l+=Mr(i[0],n.width),u+=Mr(i[1],n.height),h=null,c=null;else switch(i){case"left":l-=r,u+=s,h="right",c="middle";break;case"right":l+=r+a,u+=s,c="middle";break;case"top":l+=a/2,u-=r,h="center",c="bottom";break;case"bottom":l+=a/2,u+=o+r,h="center";break;case"inside":l+=a/2,u+=s,h="center",c="middle";break;case"insideLeft":l+=r,u+=s,c="middle";break;case"insideRight":l+=a-r,u+=s,h="right",c="middle";break;case"insideTop":l+=a/2,u+=r,h="center";break;case"insideBottom":l+=a/2,u+=o-r,h="center",c="bottom";break;case"insideTopLeft":l+=r,u+=r;break;case"insideTopRight":l+=a-r,u+=r,h="right";break;case"insideBottomLeft":l+=r,u+=o-r,c="bottom";break;case"insideBottomRight":l+=a-r,u+=o-r,h="right",c="bottom"}return(t=t||{}).x=l,t.y=u,t.align=h,t.verticalAlign=c,t}var Tr="__zr_normal__",Cr=gr.concat(["ignore"]),Dr=V(gr,(function(t,e){return t[e]=!0,t}),{ignore:!1}),Ar={},kr=new Ee(0,0,0,0),Lr=function(){function t(t){this.id=M(),this.animators=[],this.currentStates=[],this.states={},this._init(t)}return t.prototype._init=function(t){this.attr(t)},t.prototype.drift=function(t,e,n){switch(this.draggable){case"horizontal":e=0;break;case"vertical":t=0}var i=this.transform;i||(i=this.transform=[1,0,0,1,0,0]),i[4]+=t,i[5]+=e,this.decomposeTransform(),this.markRedraw()},t.prototype.beforeUpdate=function(){},t.prototype.afterUpdate=function(){},t.prototype.update=function(){this.updateTransform(),this.__dirty&&this.updateInnerText()},t.prototype.updateInnerText=function(t){var e=this._textContent;if(e&&(!e.ignore||t)){this.textConfig||(this.textConfig={});var n=this.textConfig,i=n.local,r=e.innerTransformable,o=void 0,a=void 0,s=!1;r.parent=i?this:null;var l=!1;if(r.copyTransform(e),null!=n.position){var u=kr;n.layoutRect?u.copy(n.layoutRect):u.copy(this.getBoundingRect()),i||u.applyTransform(this.transform),this.calculateTextPosition?this.calculateTextPosition(Ar,n,u):Ir(Ar,n,u),r.x=Ar.x,r.y=Ar.y,o=Ar.align,a=Ar.verticalAlign;var h=n.origin;if(h&&null!=n.rotation){var c=void 0,p=void 0;"center"===h?(c=.5*u.width,p=.5*u.height):(c=Mr(h[0],u.width),p=Mr(h[1],u.height)),l=!0,r.originX=-r.x+c+(i?0:u.x),r.originY=-r.y+p+(i?0:u.y)}}null!=n.rotation&&(r.rotation=n.rotation);var d=n.offset;d&&(r.x+=d[0],r.y+=d[1],l||(r.originX=-d[0],r.originY=-d[1]));var f=null==n.inside?"string"==typeof n.position&&n.position.indexOf("inside")>=0:n.inside,g=this._innerTextDefaultStyle||(this._innerTextDefaultStyle={}),y=void 0,v=void 0,m=void 0;f&&this.canBeInsideText()?(y=n.insideFill,v=n.insideStroke,null!=y&&"auto"!==y||(y=this.getInsideTextFill()),null!=v&&"auto"!==v||(v=this.getInsideTextStroke(y),m=!0)):(y=n.outsideFill,v=n.outsideStroke,null!=y&&"auto"!==y||(y=this.getOutsideFill()),null!=v&&"auto"!==v||(v=this.getOutsideStroke(y),m=!0)),(y=y||"#000")===g.fill&&v===g.stroke&&m===g.autoStroke&&o===g.align&&a===g.verticalAlign||(s=!0,g.fill=y,g.stroke=v,g.autoStroke=m,g.align=o,g.verticalAlign=a,e.setDefaultTextStyle(g)),e.__dirty|=1,s&&e.dirtyStyle(!0)}},t.prototype.canBeInsideText=function(){return!0},t.prototype.getInsideTextFill=function(){return"#fff"},t.prototype.getInsideTextStroke=function(t){return"#000"},t.prototype.getOutsideFill=function(){return this.__zr&&this.__zr.isDarkMode()?ar:or},t.prototype.getOutsideStroke=function(t){var e=this.__zr&&this.__zr.getBackgroundColor(),n="string"==typeof e&&jn(e);n||(n=[255,255,255,1]);for(var i=n[3],r=this.__zr.isDarkMode(),o=0;o<3;o++)n[o]=n[o]*i+(r?0:255)*(1-i);return n[3]=1,ii(n,"rgba")},t.prototype.traverse=function(t,e){},t.prototype.attrKV=function(t,e){"textConfig"===t?this.setTextConfig(e):"textContent"===t?this.setTextContent(e):"clipPath"===t?this.setClipPath(e):"extra"===t?(this.extra=this.extra||{},A(this.extra,e)):this[t]=e},t.prototype.hide=function(){this.ignore=!0,this.markRedraw()},t.prototype.show=function(){this.ignore=!1,this.markRedraw()},t.prototype.attr=function(t,e){if("string"==typeof t)this.attrKV(t,e);else if(q(t))for(var n=G(t),i=0;i0},t.prototype.getState=function(t){return this.states[t]},t.prototype.ensureState=function(t){var e=this.states;return e[t]||(e[t]={}),e[t]},t.prototype.clearStates=function(t){this.useState(Tr,!1,t)},t.prototype.useState=function(t,e,n,i){var r=t===Tr;if(this.hasState()||!r){var o=this.currentStates,a=this.stateTransition;if(!(P(o,t)>=0)||!e&&1!==o.length){var s;if(this.stateProxy&&!r&&(s=this.stateProxy(t)),s||(s=this.states&&this.states[t]),s||r){r||this.saveCurrentToNormalState(s);var l=!!(s&&s.hoverLayer||i);l&&this._toggleHoverLayerFlag(!0),this._applyStateObj(t,s,this._normalState,e,!n&&!this.__inHover&&a&&a.duration>0,a);var u=this._textContent,h=this._textGuide;return u&&u.useState(t,e,n,l),h&&h.useState(t,e,n,l),r?(this.currentStates=[],this._normalState={}):e?this.currentStates.push(t):this.currentStates=[t],this._updateAnimationTargets(),this.markRedraw(),!l&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2),s}I("State "+t+" not exists.")}}},t.prototype.useStates=function(t,e,n){if(t.length){var i=[],r=this.currentStates,o=t.length,a=o===r.length;if(a)for(var s=0;s0,d);var f=this._textContent,g=this._textGuide;f&&f.useStates(t,e,c),g&&g.useStates(t,e,c),this._updateAnimationTargets(),this.currentStates=t.slice(),this.markRedraw(),!c&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2)}else this.clearStates()},t.prototype._updateAnimationTargets=function(){for(var t=0;t=0){var n=this.currentStates.slice();n.splice(e,1),this.useStates(n)}},t.prototype.replaceState=function(t,e,n){var i=this.currentStates.slice(),r=P(i,t),o=P(i,e)>=0;r>=0?o?i.splice(r,1):i[r]=e:n&&!o&&i.push(e),this.useStates(i)},t.prototype.toggleState=function(t,e){e?this.useState(t,!0):this.removeState(t)},t.prototype._mergeStates=function(t){for(var e,n={},i=0;i=0&&e.splice(n,1)})),this.animators.push(t),n&&n.animation.addAnimator(t),n&&n.wakeUp()},t.prototype.updateDuringAnimation=function(t){this.markRedraw()},t.prototype.stopAnimation=function(t,e){for(var n=this.animators,i=n.length,r=[],o=0;o0&&n.during&&o[0].during((function(t,e){n.during(e)}));for(var p=0;p0||r.force&&!a.length){var w,S=void 0,M=void 0,I=void 0;if(s){M={},p&&(S={});for(_=0;_=0&&(n.splice(i,0,t),this._doAdd(t))}return this},e.prototype.replace=function(t,e){var n=P(this._children,t);return n>=0&&this.replaceAt(e,n),this},e.prototype.replaceAt=function(t,e){var n=this._children,i=n[e];if(t&&t!==this&&t.parent!==this&&t!==i){n[e]=t,i.parent=null;var r=this.__zr;r&&i.removeSelfFromZr(r),this._doAdd(t)}return this},e.prototype._doAdd=function(t){t.parent&&t.parent.remove(t),t.parent=this;var e=this.__zr;e&&e!==t.__zr&&t.addSelfToZr(e),e&&e.refresh()},e.prototype.remove=function(t){var e=this.__zr,n=this._children,i=P(n,t);return i<0||(n.splice(i,1),t.parent=null,e&&t.removeSelfFromZr(e),e&&e.refresh()),this},e.prototype.removeAll=function(){for(var t=this._children,e=this.__zr,n=0;n0&&(this._stillFrameAccum++,this._stillFrameAccum>this._sleepAfterStill&&this.animation.stop())},t.prototype.setSleepAfterStill=function(t){this._sleepAfterStill=t},t.prototype.wakeUp=function(){this.animation.start(),this._stillFrameAccum=0},t.prototype.refreshHover=function(){this._needsRefreshHover=!0},t.prototype.refreshHoverImmediately=function(){this._needsRefreshHover=!1,this.painter.refreshHover&&"canvas"===this.painter.getType()&&this.painter.refreshHover()},t.prototype.resize=function(t){t=t||{},this.painter.resize(t.width,t.height),this.handler.resize()},t.prototype.clearAnimation=function(){this.animation.clear()},t.prototype.getWidth=function(){return this.painter.getWidth()},t.prototype.getHeight=function(){return this.painter.getHeight()},t.prototype.setCursorStyle=function(t){this.handler.setCursorStyle(t)},t.prototype.findHover=function(t,e){return this.handler.findHover(t,e)},t.prototype.on=function(t,e,n){return this.handler.on(t,e,n),this},t.prototype.off=function(t,e){this.handler.off(t,e)},t.prototype.trigger=function(t,e){this.handler.trigger(t,e)},t.prototype.clear=function(){for(var t=this.storage.getRoots(),e=0;e0){if(t<=r)return a;if(t>=o)return s}else{if(t>=r)return a;if(t<=o)return s}else{if(t===r)return a;if(t===o)return s}return(t-r)/l*u+a}function Ur(t,e){switch(t){case"center":case"middle":t="50%";break;case"left":case"top":t="0%";break;case"right":case"bottom":t="100%"}return X(t)?(n=t,n.replace(/^\s+|\s+$/g,"")).match(/%$/)?parseFloat(t)/100*e:parseFloat(t):null==t?NaN:+t;var n}function Xr(t,e,n){return null==e&&(e=10),e=Math.min(Math.max(0,e),20),t=(+t).toFixed(e),n?t:+t}function Zr(t){return t.sort((function(t,e){return t-e})),t}function jr(t){if(t=+t,isNaN(t))return 0;if(t>1e-14)for(var e=1,n=0;n<15;n++,e*=10)if(Math.round(t*e)/e===t)return n;return qr(t)}function qr(t){var e=t.toString().toLowerCase(),n=e.indexOf("e"),i=n>0?+e.slice(n+1):0,r=n>0?n:e.length,o=e.indexOf("."),a=o<0?0:r-1-o;return Math.max(0,a-i)}function Kr(t,e){var n=Math.log,i=Math.LN10,r=Math.floor(n(t[1]-t[0])/i),o=Math.round(n(Math.abs(e[1]-e[0]))/i),a=Math.min(Math.max(-r+o,0),20);return isFinite(a)?a:20}function $r(t,e){var n=V(t,(function(t,e){return t+(isNaN(e)?0:e)}),0);if(0===n)return[];for(var i=Math.pow(10,e),r=z(t,(function(t){return(isNaN(t)?0:t)/n*i*100})),o=100*i,a=z(r,(function(t){return Math.floor(t)})),s=V(a,(function(t,e){return t+e}),0),l=z(r,(function(t,e){return t-a[e]}));su&&(u=l[c],h=c);++a[h],l[h]=0,++s}return z(a,(function(t){return t/i}))}function Jr(t,e){var n=Math.max(jr(t),jr(e)),i=t+e;return n>20?i:Xr(i,n)}var Qr=9007199254740991;function to(t){var e=2*Math.PI;return(t%e+e)%e}function eo(t){return t>-1e-4&&t=10&&e++,e}function ao(t,e){var n=oo(t),i=Math.pow(10,n),r=t/i;return t=(e?r<1.5?1:r<2.5?2:r<4?3:r<7?5:10:r<1?1:r<2?2:r<3?3:r<5?5:10)*i,n>=-20?+t.toFixed(n<0?-n:0):t}function so(t,e){var n=(t.length-1)*e+1,i=Math.floor(n),r=+t[i-1],o=n-i;return o?r+o*(t[i]-r):r}function lo(t){t.sort((function(t,e){return s(t,e,0)?-1:1}));for(var e=-1/0,n=1,i=0;i=0||r&&P(r,s)<0)){var l=n.getShallow(s,e);null!=l&&(o[t[a][0]]=l)}}return o}}var Jo=$o([["fill","color"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["opacity"],["shadowColor"]]),Qo=function(){function t(){}return t.prototype.getAreaStyle=function(t,e){return Jo(this,t,e)},t}(),ta=new Nn(50);function ea(t){if("string"==typeof t){var e=ta.get(t);return e&&e.image}return t}function na(t,e,n,i,r){if(t){if("string"==typeof t){if(e&&e.__zrImageSrc===t||!n)return e;var o=ta.get(t),a={hostEl:n,cb:i,cbPayload:r};return o?!ra(e=o.image)&&o.pending.push(a):((e=h.loadImage(t,ia,ia)).__zrImageSrc=t,ta.put(t,e.__cachedImgObj={image:e,pending:[a]})),e}return t}return e}function ia(){var t=this.__cachedImgObj;this.onload=this.onerror=this.__cachedImgObj=null;for(var e=0;e=a;l++)s-=a;var u=mr(n,e);return u>s&&(n="",u=0),s=t-u,r.ellipsis=n,r.ellipsisWidth=u,r.contentWidth=s,r.containerWidth=t,r}function la(t,e){var n=e.containerWidth,i=e.font,r=e.contentWidth;if(!n)return"";var o=mr(t,i);if(o<=n)return t;for(var a=0;;a++){if(o<=r||a>=e.maxIterations){t+=e.ellipsis;break}var s=0===a?ua(t,r,e.ascCharWidth,e.cnCharWidth):o>0?Math.floor(t.length*r/o):0;o=mr(t=t.substr(0,s),i)}return""===t&&(t=e.placeholder),t}function ua(t,e,n,i){for(var r=0,o=0,a=t.length;o0&&f+i.accumWidth>i.width&&(o=e.split("\n"),c=!0),i.accumWidth=f}else{var g=ya(e,h,i.width,i.breakAll,i.accumWidth);i.accumWidth=g.accumWidth+d,a=g.linesWidths,o=g.lines}}else o=e.split("\n");for(var y=0;y=33&&e<=383}(t)||!!fa[t]}function ya(t,e,n,i,r){for(var o=[],a=[],s="",l="",u=0,h=0,c=0;cn:r+h+d>n)?h?(s||l)&&(f?(s||(s=l,l="",h=u=0),o.push(s),a.push(h-u),l+=p,s="",h=u+=d):(l&&(s+=l,l="",u=0),o.push(s),a.push(h),s=p,h=d)):f?(o.push(l),a.push(u),l=p,u=d):(o.push(p),a.push(d)):(h+=d,f?(l+=p,u+=d):(l&&(s+=l,l="",u=0),s+=p))}else l&&(s+=l,h+=u),o.push(s),a.push(h),s="",l="",u=0,h=0}return o.length||s||(s=t,l="",u=0),l&&(s+=l),s&&(o.push(s),a.push(h)),1===o.length&&(h+=r),{accumWidth:h,lines:o,linesWidths:a}}var va="__zr_style_"+Math.round(10*Math.random()),ma={shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,shadowColor:"#000",opacity:1,blend:"source-over"},xa={style:{shadowBlur:!0,shadowOffsetX:!0,shadowOffsetY:!0,shadowColor:!0,opacity:!0}};ma[va]=!0;var _a=["z","z2","invisible"],ba=["invisible"],wa=function(t){function e(e){return t.call(this,e)||this}var i;return n(e,t),e.prototype._init=function(e){for(var n=G(e),i=0;i1e-4)return s[0]=t-n,s[1]=e-i,l[0]=t+n,void(l[1]=e+i);if(ka[0]=Da(r)*n+t,ka[1]=Ca(r)*i+e,La[0]=Da(o)*n+t,La[1]=Ca(o)*i+e,u(s,ka,La),h(l,ka,La),(r%=Aa)<0&&(r+=Aa),(o%=Aa)<0&&(o+=Aa),r>o&&!a?o+=Aa:rr&&(Pa[0]=Da(d)*n+t,Pa[1]=Ca(d)*i+e,u(s,Pa,s),h(l,Pa,l))}var Fa={M:1,L:2,C:3,Q:4,A:5,Z:6,R:7},Ga=[],Wa=[],Ha=[],Ya=[],Ua=[],Xa=[],Za=Math.min,ja=Math.max,qa=Math.cos,Ka=Math.sin,$a=Math.abs,Ja=Math.PI,Qa=2*Ja,ts="undefined"!=typeof Float32Array,es=[];function ns(t){return Math.round(t/Ja*1e8)/1e8%2*Ja}function is(t,e){var n=ns(t[0]);n<0&&(n+=Qa);var i=n-t[0],r=t[1];r+=i,!e&&r-n>=Qa?r=n+Qa:e&&n-r>=Qa?r=n-Qa:!e&&n>r?r=n+(Qa-ns(n-r)):e&&n0&&(this._ux=$a(n/rr/t)||0,this._uy=$a(n/rr/e)||0)},t.prototype.setDPR=function(t){this.dpr=t},t.prototype.setContext=function(t){this._ctx=t},t.prototype.getContext=function(){return this._ctx},t.prototype.beginPath=function(){return this._ctx&&this._ctx.beginPath(),this.reset(),this},t.prototype.reset=function(){this._saveData&&(this._len=0),this._pathSegLen&&(this._pathSegLen=null,this._pathLen=0),this._version++},t.prototype.moveTo=function(t,e){return this._drawPendingPt(),this.addData(Fa.M,t,e),this._ctx&&this._ctx.moveTo(t,e),this._x0=t,this._y0=e,this._xi=t,this._yi=e,this},t.prototype.lineTo=function(t,e){var n=$a(t-this._xi),i=$a(e-this._yi),r=n>this._ux||i>this._uy;if(this.addData(Fa.L,t,e),this._ctx&&r&&this._ctx.lineTo(t,e),r)this._xi=t,this._yi=e,this._pendingPtDist=0;else{var o=n*n+i*i;o>this._pendingPtDist&&(this._pendingPtX=t,this._pendingPtY=e,this._pendingPtDist=o)}return this},t.prototype.bezierCurveTo=function(t,e,n,i,r,o){return this._drawPendingPt(),this.addData(Fa.C,t,e,n,i,r,o),this._ctx&&this._ctx.bezierCurveTo(t,e,n,i,r,o),this._xi=r,this._yi=o,this},t.prototype.quadraticCurveTo=function(t,e,n,i){return this._drawPendingPt(),this.addData(Fa.Q,t,e,n,i),this._ctx&&this._ctx.quadraticCurveTo(t,e,n,i),this._xi=n,this._yi=i,this},t.prototype.arc=function(t,e,n,i,r,o){this._drawPendingPt(),es[0]=i,es[1]=r,is(es,o),i=es[0];var a=(r=es[1])-i;return this.addData(Fa.A,t,e,n,n,i,a,0,o?0:1),this._ctx&&this._ctx.arc(t,e,n,i,r,o),this._xi=qa(r)*n+t,this._yi=Ka(r)*n+e,this},t.prototype.arcTo=function(t,e,n,i,r){return this._drawPendingPt(),this._ctx&&this._ctx.arcTo(t,e,n,i,r),this},t.prototype.rect=function(t,e,n,i){return this._drawPendingPt(),this._ctx&&this._ctx.rect(t,e,n,i),this.addData(Fa.R,t,e,n,i),this},t.prototype.closePath=function(){this._drawPendingPt(),this.addData(Fa.Z);var t=this._ctx,e=this._x0,n=this._y0;return t&&t.closePath(),this._xi=e,this._yi=n,this},t.prototype.fill=function(t){t&&t.fill(),this.toStatic()},t.prototype.stroke=function(t){t&&t.stroke(),this.toStatic()},t.prototype.len=function(){return this._len},t.prototype.setData=function(t){var e=t.length;this.data&&this.data.length===e||!ts||(this.data=new Float32Array(e));for(var n=0;nu.length&&(this._expandData(),u=this.data);for(var h=0;h0&&(this._ctx&&this._ctx.lineTo(this._pendingPtX,this._pendingPtY),this._pendingPtDist=0)},t.prototype._expandData=function(){if(!(this.data instanceof Array)){for(var t=[],e=0;e11&&(this.data=new Float32Array(t)))}},t.prototype.getBoundingRect=function(){Ha[0]=Ha[1]=Ua[0]=Ua[1]=Number.MAX_VALUE,Ya[0]=Ya[1]=Xa[0]=Xa[1]=-Number.MAX_VALUE;var t,e=this.data,n=0,i=0,r=0,o=0;for(t=0;tn||$a(y)>i||c===e-1)&&(f=Math.sqrt(A*A+y*y),r=g,o=x);break;case Fa.C:var v=t[c++],m=t[c++],x=(g=t[c++],t[c++]),_=t[c++],b=t[c++];f=Sn(r,o,v,m,g,x,_,b,10),r=_,o=b;break;case Fa.Q:f=An(r,o,v=t[c++],m=t[c++],g=t[c++],x=t[c++],10),r=g,o=x;break;case Fa.A:var w=t[c++],S=t[c++],M=t[c++],I=t[c++],T=t[c++],C=t[c++],D=C+T;c+=1;t[c++];d&&(a=qa(T)*M+w,s=Ka(T)*I+S),f=ja(M,I)*Za(Qa,Math.abs(C)),r=qa(D)*M+w,o=Ka(D)*I+S;break;case Fa.R:a=r=t[c++],s=o=t[c++],f=2*t[c++]+2*t[c++];break;case Fa.Z:var A=a-r;y=s-o;f=Math.sqrt(A*A+y*y),r=a,o=s}f>=0&&(l[h++]=f,u+=f)}return this._pathLen=u,u},t.prototype.rebuildPath=function(t,e){var n,i,r,o,a,s,l,u,h,c,p=this.data,d=this._ux,f=this._uy,g=this._len,y=e<1,v=0,m=0,x=0;if(!y||(this._pathSegLen||this._calculateLength(),l=this._pathSegLen,u=e*this._pathLen))t:for(var _=0;_0&&(t.lineTo(h,c),x=0),b){case Fa.M:n=r=p[_++],i=o=p[_++],t.moveTo(r,o);break;case Fa.L:a=p[_++],s=p[_++];var S=$a(a-r),M=$a(s-o);if(S>d||M>f){if(y){if(v+(j=l[m++])>u){var I=(u-v)/j;t.lineTo(r*(1-I)+a*I,o*(1-I)+s*I);break t}v+=j}t.lineTo(a,s),r=a,o=s,x=0}else{var T=S*S+M*M;T>x&&(h=a,c=s,x=T)}break;case Fa.C:var C=p[_++],D=p[_++],A=p[_++],k=p[_++],L=p[_++],P=p[_++];if(y){if(v+(j=l[m++])>u){bn(r,C,A,L,I=(u-v)/j,Ga),bn(o,D,k,P,I,Wa),t.bezierCurveTo(Ga[1],Wa[1],Ga[2],Wa[2],Ga[3],Wa[3]);break t}v+=j}t.bezierCurveTo(C,D,A,k,L,P),r=L,o=P;break;case Fa.Q:C=p[_++],D=p[_++],A=p[_++],k=p[_++];if(y){if(v+(j=l[m++])>u){Cn(r,C,A,I=(u-v)/j,Ga),Cn(o,D,k,I,Wa),t.quadraticCurveTo(Ga[1],Wa[1],Ga[2],Wa[2]);break t}v+=j}t.quadraticCurveTo(C,D,A,k),r=A,o=k;break;case Fa.A:var O=p[_++],R=p[_++],N=p[_++],E=p[_++],z=p[_++],V=p[_++],B=p[_++],F=!p[_++],G=N>E?N:E,W=$a(N-E)>.001,H=z+V,Y=!1;if(y)v+(j=l[m++])>u&&(H=z+V*(u-v)/j,Y=!0),v+=j;if(W&&t.ellipse?t.ellipse(O,R,N,E,B,z,H,F):t.arc(O,R,G,z,H,F),Y)break t;w&&(n=qa(z)*N+O,i=Ka(z)*E+R),r=qa(H)*N+O,o=Ka(H)*E+R;break;case Fa.R:n=r=p[_],i=o=p[_+1],a=p[_++],s=p[_++];var U=p[_++],X=p[_++];if(y){if(v+(j=l[m++])>u){var Z=u-v;t.moveTo(a,s),t.lineTo(a+Za(Z,U),s),(Z-=U)>0&&t.lineTo(a+U,s+Za(Z,X)),(Z-=X)>0&&t.lineTo(a+ja(U-Z,0),s+X),(Z-=U)>0&&t.lineTo(a,s+ja(X-Z,0));break t}v+=j}t.rect(a,s,U,X);break;case Fa.Z:if(y){var j;if(v+(j=l[m++])>u){I=(u-v)/j;t.lineTo(r*(1-I)+n*I,o*(1-I)+i*I);break t}v+=j}t.closePath(),r=n,o=i}}},t.prototype.clone=function(){var e=new t,n=this.data;return e.data=n.slice?n.slice():Array.prototype.slice.call(n),e._len=this._len,e},t.CMD=Fa,t.initDefaultProps=function(){var e=t.prototype;e._saveData=!0,e._ux=0,e._uy=0,e._pendingPtDist=0,e._version=0}(),t}();function os(t,e,n,i,r,o,a){if(0===r)return!1;var s=r,l=0;if(a>e+s&&a>i+s||at+s&&o>n+s||oe+c&&h>i+c&&h>o+c&&h>s+c||ht+c&&u>n+c&&u>r+c&&u>a+c||ue+u&&l>i+u&&l>o+u||lt+u&&s>n+u&&s>r+u||sn||h+ur&&(r+=hs);var p=Math.atan2(l,s);return p<0&&(p+=hs),p>=i&&p<=r||p+hs>=i&&p+hs<=r}function ps(t,e,n,i,r,o){if(o>e&&o>i||or?s:0}var ds=rs.CMD,fs=2*Math.PI;var gs=[-1,-1,-1],ys=[-1,-1];function vs(t,e,n,i,r,o,a,s,l,u){if(u>e&&u>i&&u>o&&u>s||u1&&(h=void 0,h=ys[0],ys[0]=ys[1],ys[1]=h),f=vn(e,i,o,s,ys[0]),d>1&&(g=vn(e,i,o,s,ys[1]))),2===d?ve&&s>i&&s>o||s=0&&h<=1&&(r[l++]=h);else{var u=a*a-4*o*s;if(gn(u))(h=-a/(2*o))>=0&&h<=1&&(r[l++]=h);else if(u>0){var h,c=sn(u),p=(-a-c)/(2*o);(h=(-a+c)/(2*o))>=0&&h<=1&&(r[l++]=h),p>=0&&p<=1&&(r[l++]=p)}}return l}(e,i,o,s,gs);if(0===l)return 0;var u=Tn(e,i,o);if(u>=0&&u<=1){for(var h=0,c=Mn(e,i,o,u),p=0;pn||s<-n)return 0;var l=Math.sqrt(n*n-s*s);gs[0]=-l,gs[1]=l;var u=Math.abs(i-r);if(u<1e-4)return 0;if(u>=fs-1e-4){i=0,r=fs;var h=o?1:-1;return a>=gs[0]+t&&a<=gs[1]+t?h:0}if(i>r){var c=i;i=r,r=c}i<0&&(i+=fs,r+=fs);for(var p=0,d=0;d<2;d++){var f=gs[d];if(f+t>a){var g=Math.atan2(s,f);h=o?1:-1;g<0&&(g=fs+g),(g>=i&&g<=r||g+fs>=i&&g+fs<=r)&&(g>Math.PI/2&&g<1.5*Math.PI&&(h=-h),p+=h)}}return p}function _s(t,e,n,i,r){for(var o,a,s,l,u=t.data,h=t.len(),c=0,p=0,d=0,f=0,g=0,y=0;y1&&(n||(c+=ps(p,d,f,g,i,r))),m&&(f=p=u[y],g=d=u[y+1]),v){case ds.M:p=f=u[y++],d=g=u[y++];break;case ds.L:if(n){if(os(p,d,u[y],u[y+1],e,i,r))return!0}else c+=ps(p,d,u[y],u[y+1],i,r)||0;p=u[y++],d=u[y++];break;case ds.C:if(n){if(as(p,d,u[y++],u[y++],u[y++],u[y++],u[y],u[y+1],e,i,r))return!0}else c+=vs(p,d,u[y++],u[y++],u[y++],u[y++],u[y],u[y+1],i,r)||0;p=u[y++],d=u[y++];break;case ds.Q:if(n){if(ss(p,d,u[y++],u[y++],u[y],u[y+1],e,i,r))return!0}else c+=ms(p,d,u[y++],u[y++],u[y],u[y+1],i,r)||0;p=u[y++],d=u[y++];break;case ds.A:var x=u[y++],_=u[y++],b=u[y++],w=u[y++],S=u[y++],M=u[y++];y+=1;var I=!!(1-u[y++]);o=Math.cos(S)*b+x,a=Math.sin(S)*w+_,m?(f=o,g=a):c+=ps(p,d,o,a,i,r);var T=(i-x)*w/b+x;if(n){if(cs(x,_,w,S,S+M,I,e,T,r))return!0}else c+=xs(x,_,w,S,S+M,I,T,r);p=Math.cos(S+M)*b+x,d=Math.sin(S+M)*w+_;break;case ds.R:if(f=p=u[y++],g=d=u[y++],o=f+u[y++],a=g+u[y++],n){if(os(f,g,o,g,e,i,r)||os(o,g,o,a,e,i,r)||os(o,a,f,a,e,i,r)||os(f,a,f,g,e,i,r))return!0}else c+=ps(o,g,o,a,i,r),c+=ps(f,a,f,g,i,r);break;case ds.Z:if(n){if(os(p,d,f,g,e,i,r))return!0}else c+=ps(p,d,f,g,i,r);p=f,d=g}}return n||(s=d,l=g,Math.abs(s-l)<1e-4)||(c+=ps(p,d,f,g,i,r)||0),0!==c}var bs=k({fill:"#000",stroke:null,strokePercent:1,fillOpacity:1,strokeOpacity:1,lineDashOffset:0,lineWidth:1,lineCap:"butt",miterLimit:10,strokeNoScale:!1,strokeFirst:!1},ma),ws={style:k({fill:!0,stroke:!0,strokePercent:!0,fillOpacity:!0,strokeOpacity:!0,lineDashOffset:!0,lineWidth:!0,miterLimit:!0},xa.style)},Ss=gr.concat(["invisible","culling","z","z2","zlevel","parent"]),Ms=function(t){function e(e){return t.call(this,e)||this}var i;return n(e,t),e.prototype.update=function(){var n=this;t.prototype.update.call(this);var i=this.style;if(i.decal){var r=this._decalEl=this._decalEl||new e;r.buildPath===e.prototype.buildPath&&(r.buildPath=function(t){n.buildPath(t,n.shape)}),r.silent=!0;var o=r.style;for(var a in i)o[a]!==i[a]&&(o[a]=i[a]);o.fill=i.fill?i.decal:null,o.decal=null,o.shadowColor=null,i.strokeFirst&&(o.stroke=null);for(var s=0;s.5?or:e>.2?"#eee":ar}if(t)return ar}return or},e.prototype.getInsideTextStroke=function(t){var e=this.style.fill;if(X(e)){var n=this.__zr;if(!(!n||!n.isDarkMode())===ri(t,0)<.4)return e}},e.prototype.buildPath=function(t,e,n){},e.prototype.pathUpdated=function(){this.__dirty&=-5},e.prototype.getUpdatedPathProxy=function(t){return!this.path&&this.createPathProxy(),this.path.beginPath(),this.buildPath(this.path,this.shape,t),this.path},e.prototype.createPathProxy=function(){this.path=new rs(!1)},e.prototype.hasStroke=function(){var t=this.style,e=t.stroke;return!(null==e||"none"===e||!(t.lineWidth>0))},e.prototype.hasFill=function(){var t=this.style.fill;return null!=t&&"none"!==t},e.prototype.getBoundingRect=function(){var t=this._rect,e=this.style,n=!t;if(n){var i=!1;this.path||(i=!0,this.createPathProxy());var r=this.path;(i||4&this.__dirty)&&(r.beginPath(),this.buildPath(r,this.shape,!1),this.pathUpdated()),t=r.getBoundingRect()}if(this._rect=t,this.hasStroke()&&this.path&&this.path.len()>0){var o=this._rectStroke||(this._rectStroke=t.clone());if(this.__dirty||n){o.copy(t);var a=e.strokeNoScale?this.getLineScale():1,s=e.lineWidth;if(!this.hasFill()){var l=this.strokeContainThreshold;s=Math.max(s,null==l?4:l)}a>1e-10&&(o.width+=s/a,o.height+=s/a,o.x-=s/a/2,o.y-=s/a/2)}return o}return t},e.prototype.contain=function(t,e){var n=this.transformCoordToLocal(t,e),i=this.getBoundingRect(),r=this.style;if(t=n[0],e=n[1],i.contain(t,e)){var o=this.path;if(this.hasStroke()){var a=r.lineWidth,s=r.strokeNoScale?this.getLineScale():1;if(s>1e-10&&(this.hasFill()||(a=Math.max(a,this.strokeContainThreshold)),function(t,e,n,i){return _s(t,e,!0,n,i)}(o,a/s,t,e)))return!0}if(this.hasFill())return function(t,e,n){return _s(t,0,!1,e,n)}(o,t,e)}return!1},e.prototype.dirtyShape=function(){this.__dirty|=4,this._rect&&(this._rect=null),this._decalEl&&this._decalEl.dirtyShape(),this.markRedraw()},e.prototype.dirty=function(){this.dirtyStyle(),this.dirtyShape()},e.prototype.animateShape=function(t){return this.animate("shape",t)},e.prototype.updateDuringAnimation=function(t){"style"===t?this.dirtyStyle():"shape"===t?this.dirtyShape():this.markRedraw()},e.prototype.attrKV=function(e,n){"shape"===e?this.setShape(n):t.prototype.attrKV.call(this,e,n)},e.prototype.setShape=function(t,e){var n=this.shape;return n||(n=this.shape={}),"string"==typeof t?n[t]=e:A(n,t),this.dirtyShape(),this},e.prototype.shapeChanged=function(){return!!(4&this.__dirty)},e.prototype.createStyle=function(t){return mt(bs,t)},e.prototype._innerSaveToNormal=function(e){t.prototype._innerSaveToNormal.call(this,e);var n=this._normalState;e.shape&&!n.shape&&(n.shape=A({},this.shape))},e.prototype._applyStateObj=function(e,n,i,r,o,a){t.prototype._applyStateObj.call(this,e,n,i,r,o,a);var s,l=!(n&&r);if(n&&n.shape?o?r?s=n.shape:(s=A({},i.shape),A(s,n.shape)):(s=A({},r?this.shape:i.shape),A(s,n.shape)):l&&(s=i.shape),s)if(o){this.shape=A({},this.shape);for(var u={},h=G(s),c=0;c0},e.prototype.hasFill=function(){var t=this.style.fill;return null!=t&&"none"!==t},e.prototype.createStyle=function(t){return mt(Is,t)},e.prototype.setBoundingRect=function(t){this._rect=t},e.prototype.getBoundingRect=function(){var t=this.style;if(!this._rect){var e=t.text;null!=e?e+="":e="";var n=_r(e,t.font,t.textAlign,t.textBaseline);if(n.x+=t.x||0,n.y+=t.y||0,this.hasStroke()){var i=t.lineWidth;n.x-=i/2,n.y-=i/2,n.width+=i,n.height+=i}this._rect=n}return this._rect},e.initDefaultProps=void(e.prototype.dirtyRectTolerance=10),e}(wa);Ts.prototype.type="tspan";var Cs=k({x:0,y:0},ma),Ds={style:k({x:!0,y:!0,width:!0,height:!0,sx:!0,sy:!0,sWidth:!0,sHeight:!0},xa.style)};var As=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.createStyle=function(t){return mt(Cs,t)},e.prototype._getSize=function(t){var e=this.style,n=e[t];if(null!=n)return n;var i,r=(i=e.image)&&"string"!=typeof i&&i.width&&i.height?e.image:this.__image;if(!r)return 0;var o="width"===t?"height":"width",a=e[o];return null==a?r[t]:r[t]/r[o]*a},e.prototype.getWidth=function(){return this._getSize("width")},e.prototype.getHeight=function(){return this._getSize("height")},e.prototype.getAnimationStyleProps=function(){return Ds},e.prototype.getBoundingRect=function(){var t=this.style;return this._rect||(this._rect=new Ee(t.x||0,t.y||0,this.getWidth(),this.getHeight())),this._rect},e}(wa);As.prototype.type="image";var ks=Math.round;function Ls(t,e,n){if(e){var i=e.x1,r=e.x2,o=e.y1,a=e.y2;t.x1=i,t.x2=r,t.y1=o,t.y2=a;var s=n&&n.lineWidth;return s?(ks(2*i)===ks(2*r)&&(t.x1=t.x2=Os(i,s,!0)),ks(2*o)===ks(2*a)&&(t.y1=t.y2=Os(o,s,!0)),t):t}}function Ps(t,e,n){if(e){var i=e.x,r=e.y,o=e.width,a=e.height;t.x=i,t.y=r,t.width=o,t.height=a;var s=n&&n.lineWidth;return s?(t.x=Os(i,s,!0),t.y=Os(r,s,!0),t.width=Math.max(Os(i+o,s,!1)-t.x,0===o?0:1),t.height=Math.max(Os(r+a,s,!1)-t.y,0===a?0:1),t):t}}function Os(t,e,n){if(!e)return t;var i=ks(2*t);return(i+ks(e))%2==0?i/2:(i+(n?1:-1))/2}var Rs=function(){this.x=0,this.y=0,this.width=0,this.height=0},Ns={},Es=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new Rs},e.prototype.buildPath=function(t,e){var n,i,r,o;if(this.subPixelOptimize){var a=Ps(Ns,e,this.style);n=a.x,i=a.y,r=a.width,o=a.height,a.r=e.r,e=a}else n=e.x,i=e.y,r=e.width,o=e.height;e.r?function(t,e){var n,i,r,o,a,s=e.x,l=e.y,u=e.width,h=e.height,c=e.r;u<0&&(s+=u,u=-u),h<0&&(l+=h,h=-h),"number"==typeof c?n=i=r=o=c:c instanceof Array?1===c.length?n=i=r=o=c[0]:2===c.length?(n=r=c[0],i=o=c[1]):3===c.length?(n=c[0],i=o=c[1],r=c[2]):(n=c[0],i=c[1],r=c[2],o=c[3]):n=i=r=o=0,n+i>u&&(n*=u/(a=n+i),i*=u/a),r+o>u&&(r*=u/(a=r+o),o*=u/a),i+r>h&&(i*=h/(a=i+r),r*=h/a),n+o>h&&(n*=h/(a=n+o),o*=h/a),t.moveTo(s+n,l),t.lineTo(s+u-i,l),0!==i&&t.arc(s+u-i,l+i,i,-Math.PI/2,0),t.lineTo(s+u,l+h-r),0!==r&&t.arc(s+u-r,l+h-r,r,0,Math.PI/2),t.lineTo(s+o,l+h),0!==o&&t.arc(s+o,l+h-o,o,Math.PI/2,Math.PI),t.lineTo(s,l+n),0!==n&&t.arc(s+n,l+n,n,Math.PI,1.5*Math.PI)}(t,e):t.rect(n,i,r,o)},e.prototype.isZeroArea=function(){return!this.shape.width||!this.shape.height},e}(Ms);Es.prototype.type="rect";var zs={fill:"#000"},Vs={style:k({fill:!0,stroke:!0,fillOpacity:!0,strokeOpacity:!0,lineWidth:!0,fontSize:!0,lineHeight:!0,width:!0,height:!0,textShadowColor:!0,textShadowBlur:!0,textShadowOffsetX:!0,textShadowOffsetY:!0,backgroundColor:!0,padding:!0,borderColor:!0,borderWidth:!0,borderRadius:!0},xa.style)},Bs=function(t){function e(e){var n=t.call(this)||this;return n.type="text",n._children=[],n._defaultStyle=zs,n.attr(e),n}return n(e,t),e.prototype.childrenRef=function(){return this._children},e.prototype.update=function(){t.prototype.update.call(this),this.styleChanged()&&this._updateSubTexts();for(var e=0;ed&&h){var f=Math.floor(d/l);n=n.slice(0,f)}if(t&&a&&null!=c)for(var g=sa(c,o,e.ellipsis,{minChar:e.truncateMinChar,placeholder:e.placeholder}),y=0;y0,T=null!=t.width&&("truncate"===t.overflow||"break"===t.overflow||"breakAll"===t.overflow),C=i.calculatedLineHeight,D=0;Dl&&da(n,t.substring(l,u),e,s),da(n,i[2],e,s,i[1]),l=oa.lastIndex}lo){b>0?(m.tokens=m.tokens.slice(0,b),y(m,_,x),n.lines=n.lines.slice(0,v+1)):n.lines=n.lines.slice(0,v);break t}var C=w.width,D=null==C||"auto"===C;if("string"==typeof C&&"%"===C.charAt(C.length-1))P.percentWidth=C,h.push(P),P.contentWidth=mr(P.text,I);else{if(D){var A=w.backgroundColor,k=A&&A.image;k&&ra(k=ea(k))&&(P.width=Math.max(P.width,k.width*T/k.height))}var L=f&&null!=r?r-_:null;null!=L&&L=0&&"right"===(C=x[T]).align;)this._placeToken(C,t,b,f,I,"right",y),w-=C.width,I-=C.width,T--;for(M+=(n-(M-d)-(g-I)-w)/2;S<=T;)C=x[S],this._placeToken(C,t,b,f,M+C.width/2,"center",y),M+=C.width,S++;f+=b}},e.prototype._placeToken=function(t,e,n,i,r,o,s){var l=e.rich[t.styleName]||{};l.text=t.text;var u=t.verticalAlign,h=i+n/2;"top"===u?h=i+t.height/2:"bottom"===u&&(h=i+n-t.height/2),!t.isLineHolder&&$s(l)&&this._renderBackground(l,e,"right"===o?r-t.width:"center"===o?r-t.width/2:r,h-t.height/2,t.width,t.height);var c=!!l.backgroundColor,p=t.textPadding;p&&(r=qs(r,o,p),h-=t.height/2-p[0]-t.innerHeight/2);var d=this._getOrCreateChild(Ts),f=d.createStyle();d.useStyle(f);var g=this._defaultStyle,y=!1,v=0,m=js("fill"in l?l.fill:"fill"in e?e.fill:(y=!0,g.fill)),x=Zs("stroke"in l?l.stroke:"stroke"in e?e.stroke:c||s||g.autoStroke&&!y?null:(v=2,g.stroke)),_=l.textShadowBlur>0||e.textShadowBlur>0;f.text=t.text,f.x=r,f.y=h,_&&(f.shadowBlur=l.textShadowBlur||e.textShadowBlur||0,f.shadowColor=l.textShadowColor||e.textShadowColor||"transparent",f.shadowOffsetX=l.textShadowOffsetX||e.textShadowOffsetX||0,f.shadowOffsetY=l.textShadowOffsetY||e.textShadowOffsetY||0),f.textAlign=o,f.textBaseline="middle",f.font=t.font||a,f.opacity=ot(l.opacity,e.opacity,1),Ys(f,l),x&&(f.lineWidth=ot(l.lineWidth,e.lineWidth,v),f.lineDash=rt(l.lineDash,e.lineDash),f.lineDashOffset=e.lineDashOffset||0,f.stroke=x),m&&(f.fill=m);var b=t.contentWidth,w=t.contentHeight;d.setBoundingRect(new Ee(br(f.x,b,f.textAlign),wr(f.y,w,f.textBaseline),b,w))},e.prototype._renderBackground=function(t,e,n,i,r,o){var a,s,l,u=t.backgroundColor,h=t.borderWidth,c=t.borderColor,p=u&&u.image,d=u&&!p,f=t.borderRadius,g=this;if(d||t.lineHeight||h&&c){(a=this._getOrCreateChild(Es)).useStyle(a.createStyle()),a.style.fill=null;var y=a.shape;y.x=n,y.y=i,y.width=r,y.height=o,y.r=f,a.dirtyShape()}if(d)(l=a.style).fill=u||null,l.fillOpacity=rt(t.fillOpacity,1);else if(p){(s=this._getOrCreateChild(As)).onload=function(){g.dirtyStyle()};var v=s.style;v.image=u.image,v.x=n,v.y=i,v.width=r,v.height=o}h&&c&&((l=a.style).lineWidth=h,l.stroke=c,l.strokeOpacity=rt(t.strokeOpacity,1),l.lineDash=t.borderDash,l.lineDashOffset=t.borderDashOffset||0,a.strokeContainThreshold=0,a.hasFill()&&a.hasStroke()&&(l.strokeFirst=!0,l.lineWidth*=2));var m=(a||s).style;m.shadowBlur=t.shadowBlur||0,m.shadowColor=t.shadowColor||"transparent",m.shadowOffsetX=t.shadowOffsetX||0,m.shadowOffsetY=t.shadowOffsetY||0,m.opacity=ot(t.opacity,e.opacity,1)},e.makeFont=function(t){var e="";return Us(t)&&(e=[t.fontStyle,t.fontWeight,Hs(t.fontSize),t.fontFamily||"sans-serif"].join(" ")),e&&ut(e)||t.textFont||t.font},e}(wa),Fs={left:!0,right:1,center:1},Gs={top:1,bottom:1,middle:1},Ws=["fontStyle","fontWeight","fontSize","fontFamily"];function Hs(t){return"string"!=typeof t||-1===t.indexOf("px")&&-1===t.indexOf("rem")&&-1===t.indexOf("em")?isNaN(+t)?"12px":t+"px":t}function Ys(t,e){for(var n=0;n=0,o=!1;if(t instanceof Ms){var a=nl(t),s=r&&a.selectFill||a.normalFill,l=r&&a.selectStroke||a.normalStroke;if(pl(s)||pl(l)){var u=(i=i||{}).style||{};"inherit"===u.fill?(o=!0,i=A({},i),(u=A({},u)).fill=s):!pl(u.fill)&&pl(s)?(o=!0,i=A({},i),(u=A({},u)).fill=fl(s)):!pl(u.stroke)&&pl(l)&&(o||(i=A({},i),u=A({},u)),u.stroke=fl(l)),i.style=u}}if(i&&null==i.z2){o||(i=A({},i));var h=t.z2EmphasisLift;i.z2=t.z2+(null!=h?h:al)}return i}(this,0,e,n);if("blur"===t)return function(t,e,n){var i=P(t.currentStates,e)>=0,r=t.style.opacity,o=i?null:function(t,e,n,i){for(var r=t.style,o={},a=0;a0){var o={dataIndex:r,seriesIndex:t.seriesIndex};null!=i&&(o.dataType=i),e.push(o)}}))})),e}function Wl(t,e,n){jl(t,!0),Sl(t,Tl),Yl(t,e,n)}function Hl(t,e,n,i){i?function(t){jl(t,!1)}(t):Wl(t,e,n)}function Yl(t,e,n){var i=Js(t);null!=e?(i.focus=e,i.blurScope=n):i.focus&&(i.focus=null)}var Ul=["emphasis","blur","select"],Xl={itemStyle:"getItemStyle",lineStyle:"getLineStyle",areaStyle:"getAreaStyle"};function Zl(t,e,n,i){n=n||"itemStyle";for(var r=0;r1&&(a*=iu(f),s*=iu(f));var g=(r===o?-1:1)*iu((a*a*(s*s)-a*a*(d*d)-s*s*(p*p))/(a*a*(d*d)+s*s*(p*p)))||0,y=g*a*d/s,v=g*-s*p/a,m=(t+n)/2+ou(c)*y-ru(c)*v,x=(e+i)/2+ru(c)*y+ou(c)*v,_=uu([1,0],[(p-y)/a,(d-v)/s]),b=[(p-y)/a,(d-v)/s],w=[(-1*p-y)/a,(-1*d-v)/s],S=uu(b,w);if(lu(b,w)<=-1&&(S=au),lu(b,w)>=1&&(S=0),S<0){var M=Math.round(S/au*1e6)/1e6;S=2*au+M%2*au}h.addData(u,m,x,a,s,_,S,c,o)}var cu=/([mlvhzcqtsa])([^mlvhzcqtsa]*)/gi,pu=/-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g;var du=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.applyTransform=function(t){},e}(Ms);function fu(t){return null!=t.setData}function gu(t,e){var n=function(t){var e=new rs;if(!t)return e;var n,i=0,r=0,o=i,a=r,s=rs.CMD,l=t.match(cu);if(!l)return e;for(var u=0;uk*k+L*L&&(M=T,I=C),{cx:M,cy:I,x0:-h,y0:-c,x1:M*(r/b-1),y1:I*(r/b-1)}}function Ru(t,e){var n,i=ku(e.r,0),r=ku(e.r0||0,0),o=i>0;if(o||r>0){if(o||(i=r,r=0),r>i){var a=i;i=r,r=a}var s=e.startAngle,l=e.endAngle;if(!isNaN(s)&&!isNaN(l)){var u=e.cx,h=e.cy,c=!!e.clockwise,p=Du(l-s),d=p>Su&&p%Su;if(d>Pu&&(p=d),i>Pu)if(p>Su-Pu)t.moveTo(u+i*Iu(s),h+i*Mu(s)),t.arc(u,h,i,s,l,!c),r>Pu&&(t.moveTo(u+r*Iu(l),h+r*Mu(l)),t.arc(u,h,r,l,s,c));else{var f=void 0,g=void 0,y=void 0,v=void 0,m=void 0,x=void 0,_=void 0,b=void 0,w=void 0,S=void 0,M=void 0,I=void 0,T=void 0,C=void 0,D=void 0,A=void 0,k=i*Iu(s),L=i*Mu(s),P=r*Iu(l),O=r*Mu(l),R=p>Pu;if(R){var N=e.cornerRadius;N&&(n=function(t){var e;if(Y(t)){var n=t.length;if(!n)return t;e=1===n?[t[0],t[0],0,0]:2===n?[t[0],t[0],t[1],t[1]]:3===n?t.concat(t[2]):t}else e=[t,t,t,t];return e}(N),f=n[0],g=n[1],y=n[2],v=n[3]);var E=Du(i-r)/2;if(m=Lu(E,y),x=Lu(E,v),_=Lu(E,f),b=Lu(E,g),M=w=ku(m,x),I=S=ku(_,b),(w>Pu||S>Pu)&&(T=i*Iu(l),C=i*Mu(l),D=r*Iu(s),A=r*Mu(s),pPu){var U=Lu(y,M),X=Lu(v,M),Z=Ou(D,A,k,L,i,U,c),j=Ou(T,C,P,O,i,X,c);t.moveTo(u+Z.cx+Z.x0,h+Z.cy+Z.y0),M0&&t.arc(u+Z.cx,h+Z.cy,U,Cu(Z.y0,Z.x0),Cu(Z.y1,Z.x1),!c),t.arc(u,h,i,Cu(Z.cy+Z.y1,Z.cx+Z.x1),Cu(j.cy+j.y1,j.cx+j.x1),!c),X>0&&t.arc(u+j.cx,h+j.cy,X,Cu(j.y1,j.x1),Cu(j.y0,j.x0),!c))}else t.moveTo(u+k,h+L),t.arc(u,h,i,s,l,!c);else t.moveTo(u+k,h+L);if(r>Pu&&R)if(I>Pu){U=Lu(f,I),Z=Ou(P,O,T,C,r,-(X=Lu(g,I)),c),j=Ou(k,L,D,A,r,-U,c);t.lineTo(u+Z.cx+Z.x0,h+Z.cy+Z.y0),I0&&t.arc(u+Z.cx,h+Z.cy,X,Cu(Z.y0,Z.x0),Cu(Z.y1,Z.x1),!c),t.arc(u,h,r,Cu(Z.cy+Z.y1,Z.cx+Z.x1),Cu(j.cy+j.y1,j.cx+j.x1),c),U>0&&t.arc(u+j.cx,h+j.cy,U,Cu(j.y1,j.x1),Cu(j.y0,j.x0),!c))}else t.lineTo(u+P,h+O),t.arc(u,h,r,l,s,c);else t.lineTo(u+P,h+O)}else t.moveTo(u,h);t.closePath()}}}var Nu=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0,this.cornerRadius=0},Eu=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new Nu},e.prototype.buildPath=function(t,e){Ru(t,e)},e.prototype.isZeroArea=function(){return this.shape.startAngle===this.shape.endAngle||this.shape.r===this.shape.r0},e}(Ms);Eu.prototype.type="sector";var zu=function(){this.cx=0,this.cy=0,this.r=0,this.r0=0},Vu=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new zu},e.prototype.buildPath=function(t,e){var n=e.cx,i=e.cy,r=2*Math.PI;t.moveTo(n+e.r,i),t.arc(n,i,e.r,0,r,!1),t.moveTo(n+e.r0,i),t.arc(n,i,e.r0,0,r,!0)},e}(Ms);function Bu(t,e,n){var i=e.smooth,r=e.points;if(r&&r.length>=2){if(i){var o=function(t,e,n,i){var r,o,a,s,l=[],u=[],h=[],c=[];if(i){a=[1/0,1/0],s=[-1/0,-1/0];for(var p=0,d=t.length;prh[1]){if(a=!1,r)return a;var u=Math.abs(rh[0]-ih[1]),h=Math.abs(ih[0]-rh[1]);Math.min(u,h)>i.len()&&(u0){var c={duration:h.duration,delay:h.delay||0,easing:h.easing,done:o,force:!!o||!!a,setToFinal:!u,scope:t,during:a};l?e.animateFrom(n,c):e.animateTo(n,c)}else e.stopAnimation(),!l&&e.attr(n),a&&a(1),o&&o()}function dh(t,e,n,i,r,o){ph("update",t,e,n,i,r,o)}function fh(t,e,n,i,r,o){ph("enter",t,e,n,i,r,o)}function gh(t){if(!t.__zr)return!0;for(var e=0;eMath.abs(o[1])?o[0]>0?"right":"left":o[1]>0?"bottom":"top"}function Vh(t){return!t.isGroup}function Bh(t,e,n){if(t&&e){var i,r=(i={},t.traverse((function(t){Vh(t)&&t.anid&&(i[t.anid]=t)})),i);e.traverse((function(t){if(Vh(t)&&t.anid){var e=r[t.anid];if(e){var i=o(t);t.attr(o(e)),dh(t,i,n,Js(t).dataIndex)}}}))}function o(t){var e={x:t.x,y:t.y,rotation:t.rotation};return function(t){return null!=t.shape}(t)&&(e.shape=A({},t.shape)),e}}function Fh(t,e){return z(t,(function(t){var n=t[0];n=_h(n,e.x),n=bh(n,e.x+e.width);var i=t[1];return i=_h(i,e.y),[n,i=bh(i,e.y+e.height)]}))}function Gh(t,e){var n=_h(t.x,e.x),i=bh(t.x+t.width,e.x+e.width),r=_h(t.y,e.y),o=bh(t.y+t.height,e.y+e.height);if(i>=n&&o>=r)return{x:n,y:r,width:i-n,height:o-r}}function Wh(t,e,n){var i=A({rectHover:!0},e),r=i.style={strokeNoScale:!0};if(n=n||{x:-1,y:-1,width:2,height:2},t)return 0===t.indexOf("image://")?(r.image=t.slice(8),k(r,n),new As(i)):Dh(t.replace("path://",""),i,n,"center")}function Hh(t,e,n,i,r){for(var o=0,a=r[r.length-1];o=-1e-6)return!1;var f=t-r,g=e-o,y=Uh(f,g,u,h)/d;if(y<0||y>1)return!1;var v=Uh(f,g,c,p)/d;return!(v<0||v>1)}function Uh(t,e,n,i){return t*i-n*e}function Xh(t){var e=t.itemTooltipOption,n=t.componentModel,i=t.itemName,r=X(e)?{formatter:e}:e,o=n.mainType,a=n.componentIndex,s={componentType:o,name:i,$vars:["name"]};s[o+"Index"]=a;var l=t.formatterParamsExtra;l&&E(G(l),(function(t){_t(s,t)||(s[t]=l[t],s.$vars.push(t))}));var u=Js(t.el);u.componentMainType=o,u.componentIndex=a,u.tooltipConfig={name:i,option:k({content:i,formatterParams:s},r)}}function Zh(t,e){var n;t.isGroup&&(n=e(t)),n||t.traverse(e)}function jh(t,e){if(t)if(Y(t))for(var n=0;n-1?Cc:Ac;function Oc(t,e){t=t.toUpperCase(),Lc[t]=new Sc(e),kc[t]=e}function Rc(t){return Lc[t]}Oc(Dc,{time:{month:["January","February","March","April","May","June","July","August","September","October","November","December"],monthAbbr:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayOfWeekAbbr:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},legend:{selector:{all:"All",inverse:"Inv"}},toolbox:{brush:{title:{rect:"Box Select",polygon:"Lasso Select",lineX:"Horizontally Select",lineY:"Vertically Select",keep:"Keep Selections",clear:"Clear Selections"}},dataView:{title:"Data View",lang:["Data View","Close","Refresh"]},dataZoom:{title:{zoom:"Zoom",back:"Zoom Reset"}},magicType:{title:{line:"Switch to Line Chart",bar:"Switch to Bar Chart",stack:"Stack",tiled:"Tile"}},restore:{title:"Restore"},saveAsImage:{title:"Save as Image",lang:["Right Click to Save Image"]}},series:{typeNames:{pie:"Pie chart",bar:"Bar chart",line:"Line chart",scatter:"Scatter plot",effectScatter:"Ripple scatter plot",radar:"Radar chart",tree:"Tree",treemap:"Treemap",boxplot:"Boxplot",candlestick:"Candlestick",k:"K line chart",heatmap:"Heat map",map:"Map",parallel:"Parallel coordinate map",lines:"Line graph",graph:"Relationship graph",sankey:"Sankey diagram",funnel:"Funnel chart",gauge:"Gauge",pictorialBar:"Pictorial bar",themeRiver:"Theme River Map",sunburst:"Sunburst"}},aria:{general:{withTitle:'This is a chart about "{title}"',withoutTitle:"This is a chart"},series:{single:{prefix:"",withName:" with type {seriesType} named {seriesName}.",withoutName:" with type {seriesType}."},multiple:{prefix:". It consists of {seriesCount} series count.",withName:" The {seriesId} series is a {seriesType} representing {seriesName}.",withoutName:" The {seriesId} series is a {seriesType}.",separator:{middle:"",end:""}}},data:{allData:"The data is as follows: ",partialData:"The first {displayCnt} items are: ",withName:"the data for {name} is {value}",withoutName:"{value}",separator:{middle:", ",end:". "}}}}),Oc(Cc,{time:{month:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthAbbr:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayOfWeekAbbr:["日","一","二","三","四","五","六"]},legend:{selector:{all:"全选",inverse:"反选"}},toolbox:{brush:{title:{rect:"矩形选择",polygon:"圈选",lineX:"横向选择",lineY:"纵向选择",keep:"保持选择",clear:"清除选择"}},dataView:{title:"数据视图",lang:["数据视图","关闭","刷新"]},dataZoom:{title:{zoom:"区域缩放",back:"区域缩放还原"}},magicType:{title:{line:"切换为折线图",bar:"切换为柱状图",stack:"切换为堆叠",tiled:"切换为平铺"}},restore:{title:"还原"},saveAsImage:{title:"保存为图片",lang:["右键另存为图片"]}},series:{typeNames:{pie:"饼图",bar:"柱状图",line:"折线图",scatter:"散点图",effectScatter:"涟漪散点图",radar:"雷达图",tree:"树图",treemap:"矩形树图",boxplot:"箱型图",candlestick:"K线图",k:"K线图",heatmap:"热力图",map:"地图",parallel:"平行坐标图",lines:"线图",graph:"关系图",sankey:"桑基图",funnel:"漏斗图",gauge:"仪表盘图",pictorialBar:"象形柱图",themeRiver:"主题河流图",sunburst:"旭日图"}},aria:{general:{withTitle:"这是一个关于“{title}”的图表。",withoutTitle:"这是一个图表,"},series:{single:{prefix:"",withName:"图表类型是{seriesType},表示{seriesName}。",withoutName:"图表类型是{seriesType}。"},multiple:{prefix:"它由{seriesCount}个图表系列组成。",withName:"第{seriesId}个系列是一个表示{seriesName}的{seriesType},",withoutName:"第{seriesId}个系列是一个{seriesType},",separator:{middle:";",end:"。"}}},data:{allData:"其数据是——",partialData:"其中,前{displayCnt}项是——",withName:"{name}的数据是{value}",withoutName:"{value}",separator:{middle:",",end:""}}}});var Nc=1e3,Ec=6e4,zc=36e5,Vc=864e5,Bc=31536e6,Fc={year:"{yyyy}",month:"{MMM}",day:"{d}",hour:"{HH}:{mm}",minute:"{HH}:{mm}",second:"{HH}:{mm}:{ss}",millisecond:"{HH}:{mm}:{ss} {SSS}",none:"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}"},Gc="{yyyy}-{MM}-{dd}",Wc={year:"{yyyy}",month:"{yyyy}-{MM}",day:Gc,hour:"{yyyy}-{MM}-{dd} "+Fc.hour,minute:"{yyyy}-{MM}-{dd} "+Fc.minute,second:"{yyyy}-{MM}-{dd} "+Fc.second,millisecond:Fc.none},Hc=["year","month","day","hour","minute","second","millisecond"],Yc=["year","half-year","quarter","month","week","half-week","day","half-day","quarter-day","hour","minute","second","millisecond"];function Uc(t,e){return"0000".substr(0,e-(t+="").length)+t}function Xc(t){switch(t){case"half-year":case"quarter":return"month";case"week":case"half-week":return"day";case"half-day":case"quarter-day":return"hour";default:return t}}function Zc(t){return t===Xc(t)}function jc(t,e,n,i){var r=io(t),o=r[$c(n)](),a=r[Jc(n)]()+1,s=Math.floor((a-1)/3)+1,l=r[Qc(n)](),u=r["get"+(n?"UTC":"")+"Day"](),h=r[tp(n)](),c=(h-1)%12+1,p=r[ep(n)](),d=r[np(n)](),f=r[ip(n)](),g=(i instanceof Sc?i:Rc(i||Pc)||Lc.EN).getModel("time"),y=g.get("month"),v=g.get("monthAbbr"),m=g.get("dayOfWeek"),x=g.get("dayOfWeekAbbr");return(e||"").replace(/{yyyy}/g,o+"").replace(/{yy}/g,o%100+"").replace(/{Q}/g,s+"").replace(/{MMMM}/g,y[a-1]).replace(/{MMM}/g,v[a-1]).replace(/{MM}/g,Uc(a,2)).replace(/{M}/g,a+"").replace(/{dd}/g,Uc(l,2)).replace(/{d}/g,l+"").replace(/{eeee}/g,m[u]).replace(/{ee}/g,x[u]).replace(/{e}/g,u+"").replace(/{HH}/g,Uc(h,2)).replace(/{H}/g,h+"").replace(/{hh}/g,Uc(c+"",2)).replace(/{h}/g,c+"").replace(/{mm}/g,Uc(p,2)).replace(/{m}/g,p+"").replace(/{ss}/g,Uc(d,2)).replace(/{s}/g,d+"").replace(/{SSS}/g,Uc(f,3)).replace(/{S}/g,f+"")}function qc(t,e){var n=io(t),i=n[Jc(e)]()+1,r=n[Qc(e)](),o=n[tp(e)](),a=n[ep(e)](),s=n[np(e)](),l=0===n[ip(e)](),u=l&&0===s,h=u&&0===a,c=h&&0===o,p=c&&1===r;return p&&1===i?"year":p?"month":c?"day":h?"hour":u?"minute":l?"second":"millisecond"}function Kc(t,e,n){var i=j(t)?io(t):t;switch(e=e||qc(t,n)){case"year":return i[$c(n)]();case"half-year":return i[Jc(n)]()>=6?1:0;case"quarter":return Math.floor((i[Jc(n)]()+1)/4);case"month":return i[Jc(n)]();case"day":return i[Qc(n)]();case"half-day":return i[tp(n)]()/24;case"hour":return i[tp(n)]();case"minute":return i[ep(n)]();case"second":return i[np(n)]();case"millisecond":return i[ip(n)]()}}function $c(t){return t?"getUTCFullYear":"getFullYear"}function Jc(t){return t?"getUTCMonth":"getMonth"}function Qc(t){return t?"getUTCDate":"getDate"}function tp(t){return t?"getUTCHours":"getHours"}function ep(t){return t?"getUTCMinutes":"getMinutes"}function np(t){return t?"getUTCSeconds":"getSeconds"}function ip(t){return t?"getUTCMilliseconds":"getMilliseconds"}function rp(t){return t?"setUTCFullYear":"setFullYear"}function op(t){return t?"setUTCMonth":"setMonth"}function ap(t){return t?"setUTCDate":"setDate"}function sp(t){return t?"setUTCHours":"setHours"}function lp(t){return t?"setUTCMinutes":"setMinutes"}function up(t){return t?"setUTCSeconds":"setSeconds"}function hp(t){return t?"setUTCMilliseconds":"setMilliseconds"}function cp(t){if(!ho(t))return X(t)?t:"-";var e=(t+"").split(".");return e[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(e.length>1?"."+e[1]:"")}function pp(t,e){return t=(t||"").toLowerCase().replace(/-(.)/g,(function(t,e){return e.toUpperCase()})),e&&t&&(t=t.charAt(0).toUpperCase()+t.slice(1)),t}var dp=st;function fp(t,e,n){function i(t){return t&&ut(t)?t:"-"}function r(t){return!(null==t||isNaN(t)||!isFinite(t))}var o="time"===e,a=t instanceof Date;if(o||a){var s=o?io(t):t;if(!isNaN(+s))return jc(s,"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}",n);if(a)return"-"}if("ordinal"===e)return Z(t)?i(t):j(t)&&r(t)?t+"":"-";var l=uo(t);return r(l)?cp(l):Z(t)?i(t):"boolean"==typeof t?t+"":"-"}var gp=["a","b","c","d","e","f","g"],yp=function(t,e){return"{"+t+(null==e?"":e)+"}"};function vp(t,e,n){Y(e)||(e=[e]);var i=e.length;if(!i)return"";for(var r=e[0].$vars||[],o=0;o':'':{renderMode:o,content:"{"+(n.markerId||"markerX")+"|} ",style:"subItem"===r?{width:4,height:4,borderRadius:2,backgroundColor:i}:{width:10,height:10,borderRadius:5,backgroundColor:i}}:""}function xp(t,e){return e=e||"transparent",X(t)?t:q(t)&&t.colorStops&&(t.colorStops[0]||{}).color||e}function _p(t,e){if("_blank"===e||"blank"===e){var n=window.open();n.opener=null,n.location.href=t}else window.open(t,e)}var bp=E,wp=["left","right","top","bottom","width","height"],Sp=[["width","left","right"],["height","top","bottom"]];function Mp(t,e,n,i,r){var o=0,a=0;null==i&&(i=1/0),null==r&&(r=1/0);var s=0;e.eachChild((function(l,u){var h,c,p=l.getBoundingRect(),d=e.childAt(u+1),f=d&&d.getBoundingRect();if("horizontal"===t){var g=p.width+(f?-f.x+p.x:0);(h=o+g)>i||l.newline?(o=0,h=g,a+=s+n,s=p.height):s=Math.max(s,p.height)}else{var y=p.height+(f?-f.y+p.y:0);(c=a+y)>r||l.newline?(o+=s+n,a=0,c=y,s=p.width):s=Math.max(s,p.width)}l.newline||(l.x=o,l.y=a,l.markRedraw(),"horizontal"===t?o=h+n:a=c+n)}))}var Ip=Mp;H(Mp,"vertical"),H(Mp,"horizontal");function Tp(t,e,n){n=dp(n||0);var i=e.width,r=e.height,o=Ur(t.left,i),a=Ur(t.top,r),s=Ur(t.right,i),l=Ur(t.bottom,r),u=Ur(t.width,i),h=Ur(t.height,r),c=n[2]+n[0],p=n[1]+n[3],d=t.aspect;switch(isNaN(u)&&(u=i-s-p-o),isNaN(h)&&(h=r-l-c-a),null!=d&&(isNaN(u)&&isNaN(h)&&(d>i/r?u=.8*i:h=.8*r),isNaN(u)&&(u=d*h),isNaN(h)&&(h=u/d)),isNaN(o)&&(o=i-s-u-p),isNaN(a)&&(a=r-l-h-c),t.left||t.right){case"center":o=i/2-u/2-n[3];break;case"right":o=i-u-p}switch(t.top||t.bottom){case"middle":case"center":a=r/2-h/2-n[0];break;case"bottom":a=r-h-c}o=o||0,a=a||0,isNaN(u)&&(u=i-p-o-(s||0)),isNaN(h)&&(h=r-c-a-(l||0));var f=new Ee(o+n[3],a+n[0],u,h);return f.margin=n,f}function Cp(t,e,n,i,r,o){var a,s=!r||!r.hv||r.hv[0],l=!r||!r.hv||r.hv[1],u=r&&r.boundingMode||"all";if((o=o||t).x=t.x,o.y=t.y,!s&&!l)return!1;if("raw"===u)a="group"===t.type?new Ee(0,0,+e.width||0,+e.height||0):t.getBoundingRect();else if(a=t.getBoundingRect(),t.needLocalTransform()){var h=t.getLocalTransform();(a=a.clone()).applyTransform(h)}var c=Tp(k({width:a.width,height:a.height},e),n,i),p=s?c.x-a.x:0,d=l?c.y-a.y:0;return"raw"===u?(o.x=p,o.y=d):(o.x+=p,o.y+=d),o===t&&t.markRedraw(),!0}function Dp(t){var e=t.layoutMode||t.constructor.layoutMode;return q(e)?e:e?{type:e}:null}function Ap(t,e,n){var i=n&&n.ignoreSize;!Y(i)&&(i=[i,i]);var r=a(Sp[0],0),o=a(Sp[1],1);function a(n,r){var o={},a=0,u={},h=0;if(bp(n,(function(e){u[e]=t[e]})),bp(n,(function(t){s(e,t)&&(o[t]=u[t]=e[t]),l(o,t)&&a++,l(u,t)&&h++})),i[r])return l(e,n[1])?u[n[2]]=null:l(e,n[2])&&(u[n[1]]=null),u;if(2!==h&&a){if(a>=2)return o;for(var c=0;c=0;a--)o=C(o,n[a],!0);e.defaultOption=o}return e.defaultOption},e.prototype.getReferringComponents=function(t,e){var n=t+"Index",i=t+"Id";return Vo(this.ecModel,t,{index:this.get(n,!0),id:this.get(i,!0)},e)},e.prototype.getBoxLayoutParams=function(){var t=this;return{left:t.get("left"),top:t.get("top"),right:t.get("right"),bottom:t.get("bottom"),width:t.get("width"),height:t.get("height")}},e.prototype.getZLevelKey=function(){return""},e.prototype.setZLevel=function(t){this.option.zlevel=t},e.protoInitialize=function(){var t=e.prototype;t.type="component",t.id="",t.name="",t.mainType="",t.subType="",t.componentIndex=0}(),e}(Sc);Xo(Op,Sc),Ko(Op),function(t){var e={};t.registerSubTypeDefaulter=function(t,n){var i=Yo(t);e[i.main]=n},t.determineSubType=function(n,i){var r=i.type;if(!r){var o=Yo(n).main;t.hasSubTypes(n)&&e[o]&&(r=e[o](i))}return r}}(Op),function(t,e){function n(t,e){return t[e]||(t[e]={predecessor:[],successor:[]}),t[e]}t.topologicalTravel=function(t,i,r,o){if(t.length){var a=function(t){var i={},r=[];return E(t,(function(o){var a=n(i,o),s=function(t,e){var n=[];return E(t,(function(t){P(e,t)>=0&&n.push(t)})),n}(a.originalDeps=e(o),t);a.entryCount=s.length,0===a.entryCount&&r.push(o),E(s,(function(t){P(a.predecessor,t)<0&&a.predecessor.push(t);var e=n(i,t);P(e.successor,t)<0&&e.successor.push(o)}))})),{graph:i,noEntryList:r}}(i),s=a.graph,l=a.noEntryList,u={};for(E(t,(function(t){u[t]=!0}));l.length;){var h=l.pop(),c=s[h],p=!!u[h];p&&(r.call(o,h,c.originalDeps.slice()),delete u[h]),E(c.successor,p?f:d)}E(u,(function(){var t="";throw new Error(t)}))}function d(t){s[t].entryCount--,0===s[t].entryCount&&l.push(t)}function f(t){u[t]=!0,d(t)}}}(Op,(function(t){var e=[];E(Op.getClassesByMainType(t),(function(t){e=e.concat(t.dependencies||t.prototype.dependencies||[])})),e=z(e,(function(t){return Yo(t).main})),"dataset"!==t&&P(e,"dataset")<=0&&e.unshift("dataset");return e}));var Rp="";"undefined"!=typeof navigator&&(Rp=navigator.platform||"");var Np="rgba(0, 0, 0, 0.2)",Ep={darkMode:"auto",colorBy:"series",color:["#5470c6","#91cc75","#fac858","#ee6666","#73c0de","#3ba272","#fc8452","#9a60b4","#ea7ccc"],gradientColor:["#f6efa6","#d88273","#bf444c"],aria:{decal:{decals:[{color:Np,dashArrayX:[1,0],dashArrayY:[2,5],symbolSize:1,rotation:Math.PI/6},{color:Np,symbol:"circle",dashArrayX:[[8,8],[0,8,8,0]],dashArrayY:[6,0],symbolSize:.8},{color:Np,dashArrayX:[1,0],dashArrayY:[4,3],rotation:-Math.PI/4},{color:Np,dashArrayX:[[6,6],[0,6,6,0]],dashArrayY:[6,0]},{color:Np,dashArrayX:[[1,0],[1,6]],dashArrayY:[1,0,6,0],rotation:Math.PI/4},{color:Np,symbol:"triangle",dashArrayX:[[9,9],[0,9,9,0]],dashArrayY:[7,2],symbolSize:.75}]}},textStyle:{fontFamily:Rp.match(/^Win/)?"Microsoft YaHei":"sans-serif",fontSize:12,fontStyle:"normal",fontWeight:"normal"},blendMode:null,stateAnimation:{duration:300,easing:"cubicOut"},animation:"auto",animationDuration:1e3,animationDurationUpdate:500,animationEasing:"cubicInOut",animationEasingUpdate:"cubicInOut",animationThreshold:2e3,progressiveThreshold:3e3,progressive:400,hoverLayerThreshold:3e3,useUTC:!1},zp=yt(["tooltip","label","itemName","itemId","itemGroupId","seriesName"]),Vp="original",Bp="arrayRows",Fp="objectRows",Gp="keyedColumns",Wp="typedArray",Hp="unknown",Yp="column",Up="row",Xp=1,Zp=2,jp=3,qp=Po();function Kp(t,e,n){var i={},r=Jp(e);if(!r||!t)return i;var o,a,s=[],l=[],u=e.ecModel,h=qp(u).datasetMap,c=r.uid+"_"+n.seriesLayoutBy;E(t=t.slice(),(function(e,n){var r=q(e)?e:t[n]={name:e};"ordinal"===r.type&&null==o&&(o=n,a=f(r)),i[r.name]=[]}));var p=h.get(c)||h.set(c,{categoryWayDim:a,valueWayDim:0});function d(t,e,n){for(var i=0;ie)return t[i];return t[n-1]}(i,a):n;if((h=h||n)&&h.length){var c=h[l];return r&&(u[r]=c),s.paletteIdx=(l+1)%h.length,c}}var hd=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.init=function(t,e,n,i,r,o){i=i||{},this.option=null,this._theme=new Sc(i),this._locale=new Sc(r),this._optionManager=o},e.prototype.setOption=function(t,e,n){var i=dd(e);this._optionManager.setOption(t,n,i),this._resetOption(null,i)},e.prototype.resetOption=function(t,e){return this._resetOption(t,dd(e))},e.prototype._resetOption=function(t,e){var n=!1,i=this._optionManager;if(!t||"recreate"===t){var r=i.mountOption("recreate"===t);0,this.option&&"recreate"!==t?(this.restoreData(),this._mergeOption(r,e)):rd(this,r),n=!0}if("timeline"!==t&&"media"!==t||this.restoreData(),!t||"recreate"===t||"timeline"===t){var o=i.getTimelineOption(this);o&&(n=!0,this._mergeOption(o,e))}if(!t||"recreate"===t||"media"===t){var a=i.getMediaOption(this);a.length&&E(a,(function(t){n=!0,this._mergeOption(t,e)}),this)}return n},e.prototype.mergeOption=function(t){this._mergeOption(t,null)},e.prototype._mergeOption=function(t,e){var n=this.option,i=this._componentsMap,r=this._componentsCount,o=[],a=yt(),s=e&&e.replaceMergeMainTypeMap;qp(this).datasetMap=yt(),E(t,(function(t,e){null!=t&&(Op.hasClass(e)?e&&(o.push(e),a.set(e,!0)):n[e]=null==n[e]?T(t):C(n[e],t,!0))})),s&&s.each((function(t,e){Op.hasClass(e)&&!a.get(e)&&(o.push(e),a.set(e,!0))})),Op.topologicalTravel(o,Op.getAllClassMainTypes(),(function(e){var o=function(t,e,n){var i=ed.get(e);if(!i)return n;var r=i(t);return r?n.concat(r):n}(this,e,_o(t[e])),a=i.get(e),l=a?s&&s.get(e)?"replaceMerge":"normalMerge":"replaceAll",u=Io(a,o,l);(function(t,e,n){E(t,(function(t){var i=t.newOption;q(i)&&(t.keyInfo.mainType=e,t.keyInfo.subType=function(t,e,n,i){return e.type?e.type:n?n.subType:i.determineSubType(t,e)}(e,i,t.existing,n))}))})(u,e,Op),n[e]=null,i.set(e,null),r.set(e,0);var h,c=[],p=[],d=0;E(u,(function(t,n){var i=t.existing,r=t.newOption;if(r){var o="series"===e,a=Op.getClass(e,t.keyInfo.subType,!o);if(!a)return;if("tooltip"===e){if(h)return void 0;h=!0}if(i&&i.constructor===a)i.name=t.keyInfo.name,i.mergeOption(r,this),i.optionUpdated(r,!1);else{var s=A({componentIndex:n},t.keyInfo);A(i=new a(r,this,this,s),s),t.brandNew&&(i.__requireNewView=!0),i.init(r,this,this),i.optionUpdated(null,!0)}}else i&&(i.mergeOption({},this),i.optionUpdated({},!1));i?(c.push(i.option),p.push(i),d++):(c.push(void 0),p.push(void 0))}),this),n[e]=c,i.set(e,p),r.set(e,d),"series"===e&&nd(this)}),this),this._seriesIndices||nd(this)},e.prototype.getOption=function(){var t=T(this.option);return E(t,(function(e,n){if(Op.hasClass(n)){for(var i=_o(e),r=i.length,o=!1,a=r-1;a>=0;a--)i[a]&&!ko(i[a])?o=!0:(i[a]=null,!o&&r--);i.length=r,t[n]=i}})),delete t["\0_ec_inner"],t},e.prototype.getTheme=function(){return this._theme},e.prototype.getLocaleModel=function(){return this._locale},e.prototype.setUpdatePayload=function(t){this._payload=t},e.prototype.getUpdatePayload=function(){return this._payload},e.prototype.getComponent=function(t,e){var n=this._componentsMap.get(t);if(n){var i=n[e||0];if(i)return i;if(null==e)for(var r=0;r=e:"max"===n?t<=e:t===e})(i[a],t,o)||(r=!1)}})),r}var bd=E,wd=q,Sd=["areaStyle","lineStyle","nodeStyle","linkStyle","chordStyle","label","labelLine"];function Md(t){var e=t&&t.itemStyle;if(e)for(var n=0,i=Sd.length;n=0;g--){var y=t[g];if(s||(p=y.data.rawIndexOf(y.stackedByDimension,c)),p>=0){var v=y.data.getByRawIndex(y.stackResultDimension,p);if("all"===l||"positive"===l&&v>0||"negative"===l&&v<0||"samesign"===l&&d>=0&&v>0||"samesign"===l&&d<=0&&v<0){d=Jr(d,v),f=v;break}}}return i[0]=d,i[1]=f,i}))}))}var Wd,Hd,Yd,Ud,Xd,Zd=function(t){this.data=t.data||(t.sourceFormat===Gp?{}:[]),this.sourceFormat=t.sourceFormat||Hp,this.seriesLayoutBy=t.seriesLayoutBy||Yp,this.startIndex=t.startIndex||0,this.dimensionsDetectedCount=t.dimensionsDetectedCount,this.metaRawOption=t.metaRawOption;var e=this.dimensionsDefine=t.dimensionsDefine;if(e)for(var n=0;nu&&(u=d)}s[0]=l,s[1]=u}},i=function(){return this._data?this._data.length/this._dimSize:0};function r(t){for(var e=0;e=0&&(s=o.interpolatedValue[l])}return null!=s?s+"":""})):void 0},t.prototype.getRawValue=function(t,e){return df(this.getData(e),t)},t.prototype.formatTooltip=function(t,e,n){},t}();function yf(t){var e,n;return q(t)?t.type&&(n=t):e=t,{text:e,frag:n}}function vf(t){return new mf(t)}var mf=function(){function t(t){t=t||{},this._reset=t.reset,this._plan=t.plan,this._count=t.count,this._onDirty=t.onDirty,this._dirty=!0}return t.prototype.perform=function(t){var e,n=this._upstream,i=t&&t.skip;if(this._dirty&&n){var r=this.context;r.data=r.outputData=n.context.outputData}this.__pipeline&&(this.__pipeline.currentTask=this),this._plan&&!i&&(e=this._plan(this.context));var o,a=h(this._modBy),s=this._modDataCount||0,l=h(t&&t.modBy),u=t&&t.modDataCount||0;function h(t){return!(t>=1)&&(t=1),t}a===l&&s===u||(e="reset"),(this._dirty||"reset"===e)&&(this._dirty=!1,o=this._doReset(i)),this._modBy=l,this._modDataCount=u;var c=t&&t.step;if(this._dueEnd=n?n._outputDueEnd:this._count?this._count(this.context):1/0,this._progress){var p=this._dueIndex,d=Math.min(null!=c?this._dueIndex+c:1/0,this._dueEnd);if(!i&&(o||p1&&i>0?s:a}};return o;function a(){return e=t?null:oe},gte:function(t,e){return t>=e}},Mf=function(){function t(t,e){if(!j(e)){var n="";0,yo(n)}this._opFn=Sf[t],this._rvalFloat=uo(e)}return t.prototype.evaluate=function(t){return j(t)?this._opFn(t,this._rvalFloat):this._opFn(uo(t),this._rvalFloat)},t}(),If=function(){function t(t,e){var n="desc"===t;this._resultLT=n?1:-1,null==e&&(e=n?"min":"max"),this._incomparable="min"===e?-1/0:1/0}return t.prototype.evaluate=function(t,e){var n=j(t)?t:uo(t),i=j(e)?e:uo(e),r=isNaN(n),o=isNaN(i);if(r&&(n=this._incomparable),o&&(i=this._incomparable),r&&o){var a=X(t),s=X(e);a&&(n=s?t:0),s&&(i=a?e:0)}return ni?-this._resultLT:0},t}(),Tf=function(){function t(t,e){this._rval=e,this._isEQ=t,this._rvalTypeof=typeof e,this._rvalFloat=uo(e)}return t.prototype.evaluate=function(t){var e=t===this._rval;if(!e){var n=typeof t;n===this._rvalTypeof||"number"!==n&&"number"!==this._rvalTypeof||(e=uo(t)===this._rvalFloat)}return this._isEQ?e:!e},t}();function Cf(t,e){return"eq"===t||"ne"===t?new Tf("eq"===t,e):_t(Sf,t)?new Mf(t,e):null}var Df=function(){function t(){}return t.prototype.getRawData=function(){throw new Error("not supported")},t.prototype.getRawDataItem=function(t){throw new Error("not supported")},t.prototype.cloneRawData=function(){},t.prototype.getDimensionInfo=function(t){},t.prototype.cloneAllDimensionInfo=function(){},t.prototype.count=function(){},t.prototype.retrieveValue=function(t,e){},t.prototype.retrieveValueFromItem=function(t,e){},t.prototype.convertValue=function(t,e){return _f(t,e)},t}();function Af(t){var e=t.sourceFormat;if(!Nf(e)){var n="";0,yo(n)}return t.data}function kf(t){var e=t.sourceFormat,n=t.data;if(!Nf(e)){var i="";0,yo(i)}if(e===Bp){for(var r=[],o=0,a=n.length;o65535?Vf:Bf}function Yf(t,e,n,i,r){var o=Wf[n||"float"];if(r){var a=t[e],s=a&&a.length;if(s!==i){for(var l=new o(i),u=0;ug[1]&&(g[1]=f)}return this._rawCount=this._count=s,{start:a,end:s}},t.prototype._initDataFromProvider=function(t,e,n){for(var i=this._provider,r=this._chunks,o=this._dimensions,a=o.length,s=this._rawExtent,l=z(o,(function(t){return t.property})),u=0;uy[1]&&(y[1]=g)}}!i.persistent&&i.clean&&i.clean(),this._rawCount=this._count=e,this._extent=[]},t.prototype.count=function(){return this._count},t.prototype.get=function(t,e){if(!(e>=0&&e=0&&e=this._rawCount||t<0)return-1;if(!this._indices)return t;var e=this._indices,n=e[t];if(null!=n&&nt))return o;r=o-1}}return-1},t.prototype.indicesOfNearest=function(t,e,n){var i=this._chunks[t],r=[];if(!i)return r;null==n&&(n=1/0);for(var o=1/0,a=-1,s=0,l=0,u=this.count();l=0&&a<0)&&(o=c,a=h,s=0),h===a&&(r[s++]=l))}return r.length=s,r},t.prototype.getIndices=function(){var t,e=this._indices;if(e){var n=e.constructor,i=this._count;if(n===Array){t=new n(i);for(var r=0;r=u&&x<=h||isNaN(x))&&(a[s++]=d),d++}p=!0}else if(2===r){f=c[i[0]];var y=c[i[1]],v=t[i[1]][0],m=t[i[1]][1];for(g=0;g=u&&x<=h||isNaN(x))&&(_>=v&&_<=m||isNaN(_))&&(a[s++]=d),d++}p=!0}}if(!p)if(1===r)for(g=0;g=u&&x<=h||isNaN(x))&&(a[s++]=b)}else for(g=0;gt[M][1])&&(w=!1)}w&&(a[s++]=e.getRawIndex(g))}return sy[1]&&(y[1]=g)}}}},t.prototype.lttbDownSample=function(t,e){var n,i,r,o=this.clone([t],!0),a=o._chunks[t],s=this.count(),l=0,u=Math.floor(1/e),h=this.getRawIndex(0),c=new(Hf(this._rawCount))(Math.min(2*(Math.ceil(s/u)+2),s));c[l++]=h;for(var p=1;pn&&(n=i,r=I)}M>0&&M<_-x&&(c[l++]=Math.min(S,r),r=Math.max(S,r)),c[l++]=r,h=r}return c[l++]=this.getRawIndex(s-1),o._count=l,o._indices=c,o.getRawIndex=this._getRawIdx,o},t.prototype.downSample=function(t,e,n,i){for(var r=this.clone([t],!0),o=r._chunks,a=[],s=Math.floor(1/e),l=o[t],u=this.count(),h=r._rawExtent[t]=[1/0,-1/0],c=new(Hf(this._rawCount))(Math.ceil(u/s)),p=0,d=0;du-d&&(s=u-d,a.length=s);for(var f=0;fh[1]&&(h[1]=y),c[p++]=v}return r._count=p,r._indices=c,r._updateGetRawIdx(),r},t.prototype.each=function(t,e){if(this._count)for(var n=t.length,i=this._chunks,r=0,o=this.count();ra&&(a=l)}return i=[o,a],this._extent[t]=i,i},t.prototype.getRawDataItem=function(t){var e=this.getRawIndex(t);if(this._provider.persistent)return this._provider.getItem(e);for(var n=[],i=this._chunks,r=0;r=0?this._indices[t]:-1},t.prototype._updateGetRawIdx=function(){this.getRawIndex=this._indices?this._getRawIdx:this._getRawIdxIdentity},t.internalField=function(){function t(t,e,n,i){return _f(t[i],this._dimensions[i])}Ef={arrayRows:t,objectRows:function(t,e,n,i){return _f(t[e],this._dimensions[i])},keyedColumns:t,original:function(t,e,n,i){var r=t&&(null==t.value?t:t.value);return _f(r instanceof Array?r[i]:r,this._dimensions[i])},typedArray:function(t,e,n,i){return t[i]}}}(),t}(),Xf=function(){function t(t){this._sourceList=[],this._storeList=[],this._upstreamSignList=[],this._versionSignBase=0,this._dirty=!0,this._sourceHost=t}return t.prototype.dirty=function(){this._setLocalSource([],[]),this._storeList=[],this._dirty=!0},t.prototype._setLocalSource=function(t,e){this._sourceList=t,this._upstreamSignList=e,this._versionSignBase++,this._versionSignBase>9e10&&(this._versionSignBase=0)},t.prototype._getVersionSign=function(){return this._sourceHost.uid+"_"+this._versionSignBase},t.prototype.prepareSource=function(){this._isDirty()&&(this._createSource(),this._dirty=!1)},t.prototype._createSource=function(){this._setLocalSource([],[]);var t,e,n=this._sourceHost,i=this._getUpstreamSourceManagers(),r=!!i.length;if(jf(n)){var o=n,a=void 0,s=void 0,l=void 0;if(r){var u=i[0];u.prepareSource(),a=(l=u.getSource()).data,s=l.sourceFormat,e=[u._getVersionSign()]}else s=$(a=o.get("data",!0))?Wp:Vp,e=[];var h=this._getSourceMetaRawOption()||{},c=l&&l.metaRawOption||{},p=rt(h.seriesLayoutBy,c.seriesLayoutBy)||null,d=rt(h.sourceHeader,c.sourceHeader),f=rt(h.dimensions,c.dimensions);t=p!==c.seriesLayoutBy||!!d!=!!c.sourceHeader||f?[qd(a,{seriesLayoutBy:p,sourceHeader:d,dimensions:f},s)]:[]}else{var g=n;if(r){var y=this._applyTransform(i);t=y.sourceList,e=y.upstreamSignList}else{t=[qd(g.get("source",!0),this._getSourceMetaRawOption(),null)],e=[]}}this._setLocalSource(t,e)},t.prototype._applyTransform=function(t){var e,n=this._sourceHost,i=n.get("transform",!0),r=n.get("fromTransformResult",!0);if(null!=r){var o="";1!==t.length&&qf(o)}var a,s=[],l=[];return E(t,(function(t){t.prepareSource();var e=t.getSource(r||0),n="";null==r||e||qf(n),s.push(e),l.push(t._getVersionSign())})),i?e=function(t,e,n){var i=_o(t),r=i.length,o="";r||yo(o);for(var a=0,s=r;a1||n>0&&!t.noHeader;return E(t.blocks,(function(t){var n=ng(t);n>=e&&(e=n+ +(i&&(!n||tg(t)&&!t.noHeader)))})),e}return 0}function ig(t,e,n,i){var r,o=e.noHeader,a=(r=ng(e),{html:$f[r],richText:Jf[r]}),s=[],l=e.blocks||[];lt(!l||Y(l)),l=l||[];var u=t.orderMode;if(e.sortBlocks&&u){l=l.slice();var h={valueAsc:"asc",valueDesc:"desc"};if(_t(h,u)){var c=new If(h[u],null);l.sort((function(t,e){return c.evaluate(t.sortParam,e.sortParam)}))}else"seriesDesc"===u&&l.reverse()}E(l,(function(n,r){var o=e.valueFormatter,l=eg(n)(o?A(A({},t),{valueFormatter:o}):t,n,r>0?a.html:0,i);null!=l&&s.push(l)}));var p="richText"===t.renderMode?s.join(a.richText):ag(s.join(""),o?n:a.html);if(o)return p;var d=fp(e.header,"ordinal",t.useUTC),f=Kf(i,t.renderMode).nameStyle;return"richText"===t.renderMode?sg(t,d,f)+a.richText+p:ag('
                    '+ie(d)+"
                    "+p,n)}function rg(t,e,n,i){var r=t.renderMode,o=e.noName,a=e.noValue,s=!e.markerType,l=e.name,u=t.useUTC,h=e.valueFormatter||t.valueFormatter||function(t){return z(t=Y(t)?t:[t],(function(t,e){return fp(t,Y(d)?d[e]:d,u)}))};if(!o||!a){var c=s?"":t.markupStyleCreator.makeTooltipMarker(e.markerType,e.markerColor||"#333",r),p=o?"":fp(l,"ordinal",u),d=e.valueType,f=a?[]:h(e.value),g=!s||!o,y=!s&&o,v=Kf(i,r),m=v.nameStyle,x=v.valueStyle;return"richText"===r?(s?"":c)+(o?"":sg(t,p,m))+(a?"":function(t,e,n,i,r){var o=[r],a=i?10:20;return n&&o.push({padding:[0,0,0,a],align:"right"}),t.markupStyleCreator.wrapRichTextStyle(Y(e)?e.join(" "):e,o)}(t,f,g,y,x)):ag((s?"":c)+(o?"":function(t,e,n){return''+ie(t)+""}(p,!s,m))+(a?"":function(t,e,n,i){var r=n?"10px":"20px",o=e?"float:right;margin-left:"+r:"";return t=Y(t)?t:[t],''+z(t,(function(t){return ie(t)})).join("  ")+""}(f,g,y,x)),n)}}function og(t,e,n,i,r,o){if(t)return eg(t)({useUTC:r,renderMode:n,orderMode:i,markupStyleCreator:e,valueFormatter:t.valueFormatter},t,0,o)}function ag(t,e){return'
                    '+t+'
                    '}function sg(t,e,n){return t.markupStyleCreator.wrapRichTextStyle(e,n)}function lg(t,e){return xp(t.getData().getItemVisual(e,"style")[t.visualDrawType])}function ug(t,e){var n=t.get("padding");return null!=n?n:"richText"===e?[8,10]:10}var hg=function(){function t(){this.richTextStyles={},this._nextStyleNameId=co()}return t.prototype._generateStyleName=function(){return"__EC_aUTo_"+this._nextStyleNameId++},t.prototype.makeTooltipMarker=function(t,e,n){var i="richText"===n?this._generateStyleName():null,r=mp({color:e,type:t,renderMode:n,markerId:i});return X(r)?r:(this.richTextStyles[i]=r.style,r.content)},t.prototype.wrapRichTextStyle=function(t,e){var n={};Y(e)?E(e,(function(t){return A(n,t)})):A(n,e);var i=this._generateStyleName();return this.richTextStyles[i]=n,"{"+i+"|"+t+"}"},t}();function cg(t){var e,n,i,r,o=t.series,a=t.dataIndex,s=t.multipleSeries,l=o.getData(),u=l.mapDimensionsAll("defaultedTooltip"),h=u.length,c=o.getRawValue(a),p=Y(c),d=lg(o,a);if(h>1||p&&!h){var f=function(t,e,n,i,r){var o=e.getData(),a=V(t,(function(t,e,n){var i=o.getDimensionInfo(n);return t||i&&!1!==i.tooltip&&null!=i.displayName}),!1),s=[],l=[],u=[];function h(t,e){var n=o.getDimensionInfo(e);n&&!1!==n.otherDims.tooltip&&(a?u.push(Qf("nameValue",{markerType:"subItem",markerColor:r,name:n.displayName,value:t,valueType:n.type})):(s.push(t),l.push(n.type)))}return i.length?E(i,(function(t){h(df(o,n,t),t)})):E(t,h),{inlineValues:s,inlineValueTypes:l,blocks:u}}(c,o,a,u,d);e=f.inlineValues,n=f.inlineValueTypes,i=f.blocks,r=f.inlineValues[0]}else if(h){var g=l.getDimensionInfo(u[0]);r=e=df(l,a,u[0]),n=g.type}else r=e=p?c[0]:c;var y=Ao(o),v=y&&o.name||"",m=l.getName(a),x=s?v:m;return Qf("section",{header:v,noHeader:s||!y,sortParam:r,blocks:[Qf("nameValue",{markerType:"item",markerColor:d,name:x,noName:!ut(x),value:e,valueType:n})].concat(i||[])})}var pg=Po();function dg(t,e){return t.getName(e)||t.getId(e)}var fg=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e._selectedDataIndicesMap={},e}return n(e,t),e.prototype.init=function(t,e,n){this.seriesIndex=this.componentIndex,this.dataTask=vf({count:yg,reset:vg}),this.dataTask.context={model:this},this.mergeDefaultAndTheme(t,n),(pg(this).sourceManager=new Xf(this)).prepareSource();var i=this.getInitialData(t,n);xg(i,this),this.dataTask.context.data=i,pg(this).dataBeforeProcessed=i,gg(this),this._initSelectedMapFromData(i)},e.prototype.mergeDefaultAndTheme=function(t,e){var n=Dp(this),i=n?kp(t):{},r=this.subType;Op.hasClass(r)&&(r+="Series"),C(t,e.getTheme().get(this.subType)),C(t,this.getDefaultOption()),bo(t,"label",["show"]),this.fillDataTextStyle(t.data),n&&Ap(t,i,n)},e.prototype.mergeOption=function(t,e){t=C(this.option,t,!0),this.fillDataTextStyle(t.data);var n=Dp(this);n&&Ap(this.option,t,n);var i=pg(this).sourceManager;i.dirty(),i.prepareSource();var r=this.getInitialData(t,e);xg(r,this),this.dataTask.dirty(),this.dataTask.context.data=r,pg(this).dataBeforeProcessed=r,gg(this),this._initSelectedMapFromData(r)},e.prototype.fillDataTextStyle=function(t){if(t&&!$(t))for(var e=["show"],n=0;nthis.getShallow("animationThreshold")&&(e=!1),!!e},e.prototype.restoreData=function(){this.dataTask.dirty()},e.prototype.getColorFromPalette=function(t,e,n){var i=this.ecModel,r=sd.prototype.getColorFromPalette.call(this,t,e,n);return r||(r=i.getColorFromPalette(t,e,n)),r},e.prototype.coordDimToDataDim=function(t){return this.getRawData().mapDimensionsAll(t)},e.prototype.getProgressive=function(){return this.get("progressive")},e.prototype.getProgressiveThreshold=function(){return this.get("progressiveThreshold")},e.prototype.select=function(t,e){this._innerSelect(this.getData(e),t)},e.prototype.unselect=function(t,e){var n=this.option.selectedMap;if(n){var i=this.option.selectedMode,r=this.getData(e);if("series"===i||"all"===n)return this.option.selectedMap={},void(this._selectedDataIndicesMap={});for(var o=0;o=0&&n.push(r)}return n},e.prototype.isSelected=function(t,e){var n=this.option.selectedMap;if(!n)return!1;var i=this.getData(e);return("all"===n||n[dg(i,t)])&&!i.getItemModel(t).get(["select","disabled"])},e.prototype.isUniversalTransitionEnabled=function(){if(this.__universalTransitionEnabled)return!0;var t=this.option.universalTransition;return!!t&&(!0===t||t&&t.enabled)},e.prototype._innerSelect=function(t,e){var n,i,r=this.option,o=r.selectedMode,a=e.length;if(o&&a)if("series"===o)r.selectedMap="all";else if("multiple"===o){q(r.selectedMap)||(r.selectedMap={});for(var s=r.selectedMap,l=0;l0&&this._innerSelect(t,e)}},e.registerClass=function(t){return Op.registerClass(t)},e.protoInitialize=function(){var t=e.prototype;t.type="series.__base__",t.seriesIndex=0,t.ignoreStyleOnData=!1,t.hasSymbolVisual=!1,t.defaultSymbol="circle",t.visualStyleAccessPath="itemStyle",t.visualDrawType="fill"}(),e}(Op);function gg(t){var e=t.name;Ao(t)||(t.name=function(t){var e=t.getRawData(),n=e.mapDimensionsAll("seriesName"),i=[];return E(n,(function(t){var n=e.getDimensionInfo(t);n.displayName&&i.push(n.displayName)})),i.join(" ")}(t)||e)}function yg(t){return t.model.getRawData().count()}function vg(t){var e=t.model;return e.setData(e.getRawData().cloneShallow()),mg}function mg(t,e){e.outputData&&t.end>e.outputData.count()&&e.model.getRawData().cloneShallow(e.outputData)}function xg(t,e){E(vt(t.CHANGABLE_METHODS,t.DOWNSAMPLE_METHODS),(function(n){t.wrapMethod(n,H(_g,e))}))}function _g(t,e){var n=bg(t);return n&&n.setOutputEnd((e||this).count()),e}function bg(t){var e=(t.ecModel||{}).scheduler,n=e&&e.getPipeline(t.uid);if(n){var i=n.currentTask;if(i){var r=i.agentStubMap;r&&(i=r.get(t.uid))}return i}}R(fg,gf),R(fg,sd),Xo(fg,Op);var wg=function(){function t(){this.group=new Er,this.uid=Ic("viewComponent")}return t.prototype.init=function(t,e){},t.prototype.render=function(t,e,n,i){},t.prototype.dispose=function(t,e){},t.prototype.updateView=function(t,e,n,i){},t.prototype.updateLayout=function(t,e,n,i){},t.prototype.updateVisual=function(t,e,n,i){},t.prototype.toggleBlurSeries=function(t,e,n){},t.prototype.eachRendered=function(t){var e=this.group;e&&e.traverse(t)},t}();function Sg(){var t=Po();return function(e){var n=t(e),i=e.pipelineContext,r=!!n.large,o=!!n.progressiveRender,a=n.large=!(!i||!i.large),s=n.progressiveRender=!(!i||!i.progressiveRender);return!(r===a&&o===s)&&"reset"}}Uo(wg),Ko(wg);var Mg=Po(),Ig=Sg(),Tg=function(){function t(){this.group=new Er,this.uid=Ic("viewChart"),this.renderTask=vf({plan:Ag,reset:kg}),this.renderTask.context={view:this}}return t.prototype.init=function(t,e){},t.prototype.render=function(t,e,n,i){0},t.prototype.highlight=function(t,e,n,i){var r=t.getData(i&&i.dataType);r&&Dg(r,i,"emphasis")},t.prototype.downplay=function(t,e,n,i){var r=t.getData(i&&i.dataType);r&&Dg(r,i,"normal")},t.prototype.remove=function(t,e){this.group.removeAll()},t.prototype.dispose=function(t,e){},t.prototype.updateView=function(t,e,n,i){this.render(t,e,n,i)},t.prototype.updateLayout=function(t,e,n,i){this.render(t,e,n,i)},t.prototype.updateVisual=function(t,e,n,i){this.render(t,e,n,i)},t.prototype.eachRendered=function(t){jh(this.group,t)},t.markUpdateMethod=function(t,e){Mg(t).updateMethod=e},t.protoInitialize=void(t.prototype.type="chart"),t}();function Cg(t,e,n){t&&ql(t)&&("emphasis"===e?Al:kl)(t,n)}function Dg(t,e,n){var i=Lo(t,e),r=e&&null!=e.highlightKey?function(t){var e=el[t];return null==e&&tl<=32&&(e=el[t]=tl++),e}(e.highlightKey):null;null!=i?E(_o(i),(function(e){Cg(t.getItemGraphicEl(e),n,r)})):t.eachItemGraphicEl((function(t){Cg(t,n,r)}))}function Ag(t){return Ig(t.model)}function kg(t){var e=t.model,n=t.ecModel,i=t.api,r=t.payload,o=e.pipelineContext.progressiveRender,a=t.view,s=r&&Mg(r).updateMethod,l=o?"incrementalPrepareRender":s&&a[s]?s:"render";return"render"!==l&&a[l](e,n,i,r),Lg[l]}Uo(Tg),Ko(Tg);var Lg={incrementalPrepareRender:{progress:function(t,e){e.view.incrementalRender(t,e.model,e.ecModel,e.api,e.payload)}},render:{forceFirstProgress:!0,progress:function(t,e){e.view.render(e.model,e.ecModel,e.api,e.payload)}}},Pg="\0__throttleOriginMethod",Og="\0__throttleRate",Rg="\0__throttleType";function Ng(t,e,n){var i,r,o,a,s,l=0,u=0,h=null;function c(){u=(new Date).getTime(),h=null,t.apply(o,a||[])}e=e||0;var p=function(){for(var t=[],p=0;p=0?c():h=setTimeout(c,-r),l=i};return p.clear=function(){h&&(clearTimeout(h),h=null)},p.debounceNextCall=function(t){s=t},p}function Eg(t,e,n,i){var r=t[e];if(r){var o=r[Pg]||r,a=r[Rg];if(r[Og]!==n||a!==i){if(null==n||!i)return t[e]=o;(r=t[e]=Ng(o,n,"debounce"===i))[Pg]=o,r[Rg]=i,r[Og]=n}return r}}function zg(t,e){var n=t[e];n&&n[Pg]&&(n.clear&&n.clear(),t[e]=n[Pg])}var Vg=Po(),Bg={itemStyle:$o(_c,!0),lineStyle:$o(vc,!0)},Fg={lineStyle:"stroke",itemStyle:"fill"};function Gg(t,e){var n=t.visualStyleMapper||Bg[e];return n||(console.warn("Unknown style type '"+e+"'."),Bg.itemStyle)}function Wg(t,e){var n=t.visualDrawType||Fg[e];return n||(console.warn("Unknown style type '"+e+"'."),"fill")}var Hg={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){var n=t.getData(),i=t.visualStyleAccessPath||"itemStyle",r=t.getModel(i),o=Gg(t,i)(r),a=r.getShallow("decal");a&&(n.setVisual("decal",a),a.dirty=!0);var s=Wg(t,i),l=o[s],u=U(l)?l:null,h="auto"===o.fill||"auto"===o.stroke;if(!o[s]||u||h){var c=t.getColorFromPalette(t.name,null,e.getSeriesCount());o[s]||(o[s]=c,n.setVisual("colorFromPalette",!0)),o.fill="auto"===o.fill||U(o.fill)?c:o.fill,o.stroke="auto"===o.stroke||U(o.stroke)?c:o.stroke}if(n.setVisual("style",o),n.setVisual("drawType",s),!e.isSeriesFiltered(t)&&u)return n.setVisual("colorFromPalette",!1),{dataEach:function(e,n){var i=t.getDataParams(n),r=A({},o);r[s]=u(i),e.setItemVisual(n,"style",r)}}}},Yg=new Sc,Ug={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){if(!t.ignoreStyleOnData&&!e.isSeriesFiltered(t)){var n=t.getData(),i=t.visualStyleAccessPath||"itemStyle",r=Gg(t,i),o=n.getVisual("drawType");return{dataEach:n.hasItemOption?function(t,e){var n=t.getRawDataItem(e);if(n&&n[i]){Yg.option=n[i];var a=r(Yg);A(t.ensureUniqueItemVisual(e,"style"),a),Yg.option.decal&&(t.setItemVisual(e,"decal",Yg.option.decal),Yg.option.decal.dirty=!0),o in a&&t.setItemVisual(e,"colorFromPalette",!1)}}:null}}}},Xg={performRawSeries:!0,overallReset:function(t){var e=yt();t.eachSeries((function(t){var n=t.getColorBy();if(!t.isColorBySeries()){var i=t.type+"-"+n,r=e.get(i);r||(r={},e.set(i,r)),Vg(t).scope=r}})),t.eachSeries((function(e){if(!e.isColorBySeries()&&!t.isSeriesFiltered(e)){var n=e.getRawData(),i={},r=e.getData(),o=Vg(e).scope,a=e.visualStyleAccessPath||"itemStyle",s=Wg(e,a);r.each((function(t){var e=r.getRawIndex(t);i[e]=t})),n.each((function(t){var a=i[t];if(r.getItemVisual(a,"colorFromPalette")){var l=r.ensureUniqueItemVisual(a,"style"),u=n.getName(t)||t+"",h=n.count();l[s]=e.getColorFromPalette(u,o,h)}}))}}))}},Zg=Math.PI;var jg=function(){function t(t,e,n,i){this._stageTaskMap=yt(),this.ecInstance=t,this.api=e,n=this._dataProcessorHandlers=n.slice(),i=this._visualHandlers=i.slice(),this._allHandlers=n.concat(i)}return t.prototype.restoreData=function(t,e){t.restoreData(e),this._stageTaskMap.each((function(t){var e=t.overallTask;e&&e.dirty()}))},t.prototype.getPerformArgs=function(t,e){if(t.__pipeline){var n=this._pipelineMap.get(t.__pipeline.id),i=n.context,r=!e&&n.progressiveEnabled&&(!i||i.progressiveRender)&&t.__idxInPipeline>n.blockIndex?n.step:null,o=i&&i.modDataCount;return{step:r,modBy:null!=o?Math.ceil(o/r):null,modDataCount:o}}},t.prototype.getPipeline=function(t){return this._pipelineMap.get(t)},t.prototype.updateStreamModes=function(t,e){var n=this._pipelineMap.get(t.uid),i=t.getData().count(),r=n.progressiveEnabled&&e.incrementalPrepareRender&&i>=n.threshold,o=t.get("large")&&i>=t.get("largeThreshold"),a="mod"===t.get("progressiveChunkMode")?i:null;t.pipelineContext=n.context={progressiveRender:r,modDataCount:a,large:o}},t.prototype.restorePipelines=function(t){var e=this,n=e._pipelineMap=yt();t.eachSeries((function(t){var i=t.getProgressive(),r=t.uid;n.set(r,{id:r,head:null,tail:null,threshold:t.getProgressiveThreshold(),progressiveEnabled:i&&!(t.preventIncremental&&t.preventIncremental()),blockIndex:-1,step:Math.round(i||700),count:0}),e._pipe(t,t.dataTask)}))},t.prototype.prepareStageTasks=function(){var t=this._stageTaskMap,e=this.api.getModel(),n=this.api;E(this._allHandlers,(function(i){var r=t.get(i.uid)||t.set(i.uid,{}),o="";lt(!(i.reset&&i.overallReset),o),i.reset&&this._createSeriesStageTask(i,r,e,n),i.overallReset&&this._createOverallStageTask(i,r,e,n)}),this)},t.prototype.prepareView=function(t,e,n,i){var r=t.renderTask,o=r.context;o.model=e,o.ecModel=n,o.api=i,r.__block=!t.incrementalPrepareRender,this._pipe(e,r)},t.prototype.performDataProcessorTasks=function(t,e){this._performStageTasks(this._dataProcessorHandlers,t,e,{block:!0})},t.prototype.performVisualTasks=function(t,e,n){this._performStageTasks(this._visualHandlers,t,e,n)},t.prototype._performStageTasks=function(t,e,n,i){i=i||{};var r=!1,o=this;function a(t,e){return t.setDirty&&(!t.dirtyMap||t.dirtyMap.get(e.__pipeline.id))}E(t,(function(t,s){if(!i.visualType||i.visualType===t.visualType){var l=o._stageTaskMap.get(t.uid),u=l.seriesTaskMap,h=l.overallTask;if(h){var c,p=h.agentStubMap;p.each((function(t){a(i,t)&&(t.dirty(),c=!0)})),c&&h.dirty(),o.updatePayload(h,n);var d=o.getPerformArgs(h,i.block);p.each((function(t){t.perform(d)})),h.perform(d)&&(r=!0)}else u&&u.each((function(s,l){a(i,s)&&s.dirty();var u=o.getPerformArgs(s,i.block);u.skip=!t.performRawSeries&&e.isSeriesFiltered(s.context.model),o.updatePayload(s,n),s.perform(u)&&(r=!0)}))}})),this.unfinished=r||this.unfinished},t.prototype.performSeriesTasks=function(t){var e;t.eachSeries((function(t){e=t.dataTask.perform()||e})),this.unfinished=e||this.unfinished},t.prototype.plan=function(){this._pipelineMap.each((function(t){var e=t.tail;do{if(e.__block){t.blockIndex=e.__idxInPipeline;break}e=e.getUpstream()}while(e)}))},t.prototype.updatePayload=function(t,e){"remain"!==e&&(t.context.payload=e)},t.prototype._createSeriesStageTask=function(t,e,n,i){var r=this,o=e.seriesTaskMap,a=e.seriesTaskMap=yt(),s=t.seriesType,l=t.getTargetSeries;function u(e){var s=e.uid,l=a.set(s,o&&o.get(s)||vf({plan:Qg,reset:ty,count:iy}));l.context={model:e,ecModel:n,api:i,useClearVisual:t.isVisual&&!t.isLayout,plan:t.plan,reset:t.reset,scheduler:r},r._pipe(e,l)}t.createOnAllSeries?n.eachRawSeries(u):s?n.eachRawSeriesByType(s,u):l&&l(n,i).each(u)},t.prototype._createOverallStageTask=function(t,e,n,i){var r=this,o=e.overallTask=e.overallTask||vf({reset:qg});o.context={ecModel:n,api:i,overallReset:t.overallReset,scheduler:r};var a=o.agentStubMap,s=o.agentStubMap=yt(),l=t.seriesType,u=t.getTargetSeries,h=!0,c=!1,p="";function d(t){var e=t.uid,n=s.set(e,a&&a.get(e)||(c=!0,vf({reset:Kg,onDirty:Jg})));n.context={model:t,overallProgress:h},n.agent=o,n.__block=h,r._pipe(t,n)}lt(!t.createOnAllSeries,p),l?n.eachRawSeriesByType(l,d):u?u(n,i).each(d):(h=!1,E(n.getSeries(),d)),c&&o.dirty()},t.prototype._pipe=function(t,e){var n=t.uid,i=this._pipelineMap.get(n);!i.head&&(i.head=e),i.tail&&i.tail.pipe(e),i.tail=e,e.__idxInPipeline=i.count++,e.__pipeline=i},t.wrapStageHandler=function(t,e){return U(t)&&(t={overallReset:t,seriesType:ry(t)}),t.uid=Ic("stageHandler"),e&&(t.visualType=e),t},t}();function qg(t){t.overallReset(t.ecModel,t.api,t.payload)}function Kg(t){return t.overallProgress&&$g}function $g(){this.agent.dirty(),this.getDownstream().dirty()}function Jg(){this.agent&&this.agent.dirty()}function Qg(t){return t.plan?t.plan(t.model,t.ecModel,t.api,t.payload):null}function ty(t){t.useClearVisual&&t.data.clearAllVisual();var e=t.resetDefines=_o(t.reset(t.model,t.ecModel,t.api,t.payload));return e.length>1?z(e,(function(t,e){return ny(e)})):ey}var ey=ny(0);function ny(t){return function(e,n){var i=n.data,r=n.resetDefines[t];if(r&&r.dataEach)for(var o=e.start;o0&&h===r.length-u.length){var c=r.slice(0,h);"data"!==c&&(e.mainType=c,e[u.toLowerCase()]=t,s=!0)}}a.hasOwnProperty(r)&&(n[r]=t,s=!0),s||(i[r]=t)}))}return{cptQuery:e,dataQuery:n,otherQuery:i}},t.prototype.filter=function(t,e){var n=this.eventInfo;if(!n)return!0;var i=n.targetEl,r=n.packedEvent,o=n.model,a=n.view;if(!o||!a)return!0;var s=e.cptQuery,l=e.dataQuery;return u(s,o,"mainType")&&u(s,o,"subType")&&u(s,o,"index","componentIndex")&&u(s,o,"name")&&u(s,o,"id")&&u(l,r,"name")&&u(l,r,"dataIndex")&&u(l,r,"dataType")&&(!a.filterForExposedEvent||a.filterForExposedEvent(t,e.otherQuery,i,r));function u(t,e,n,i){return null==t[n]||e[i||n]===t[n]}},t.prototype.afterTrigger=function(){this.eventInfo=null},t}(),vy=["symbol","symbolSize","symbolRotate","symbolOffset"],my=vy.concat(["symbolKeepAspect"]),xy={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){var n=t.getData();if(t.legendIcon&&n.setVisual("legendIcon",t.legendIcon),t.hasSymbolVisual){for(var i={},r={},o=!1,a=0;a=0&&Gy(l)?l:.5,t.createRadialGradient(a,s,0,a,s,l)}(t,e,n):function(t,e,n){var i=null==e.x?0:e.x,r=null==e.x2?1:e.x2,o=null==e.y?0:e.y,a=null==e.y2?0:e.y2;return e.global||(i=i*n.width+n.x,r=r*n.width+n.x,o=o*n.height+n.y,a=a*n.height+n.y),i=Gy(i)?i:0,r=Gy(r)?r:1,o=Gy(o)?o:0,a=Gy(a)?a:0,t.createLinearGradient(i,o,r,a)}(t,e,n),r=e.colorStops,o=0;o0&&(e=i.lineDash,n=i.lineWidth,e&&"solid"!==e&&n>0?"dashed"===e?[4*n,2*n]:"dotted"===e?[n]:j(e)?[e]:Y(e)?e:null:null),o=i.lineDashOffset;if(r){var a=i.strokeNoScale&&t.getLineScale?t.getLineScale():1;a&&1!==a&&(r=z(r,(function(t){return t/a})),o/=a)}return[r,o]}var Xy=new rs(!0);function Zy(t){var e=t.stroke;return!(null==e||"none"===e||!(t.lineWidth>0))}function jy(t){return"string"==typeof t&&"none"!==t}function qy(t){var e=t.fill;return null!=e&&"none"!==e}function Ky(t,e){if(null!=e.fillOpacity&&1!==e.fillOpacity){var n=t.globalAlpha;t.globalAlpha=e.fillOpacity*e.opacity,t.fill(),t.globalAlpha=n}else t.fill()}function $y(t,e){if(null!=e.strokeOpacity&&1!==e.strokeOpacity){var n=t.globalAlpha;t.globalAlpha=e.strokeOpacity*e.opacity,t.stroke(),t.globalAlpha=n}else t.stroke()}function Jy(t,e,n){var i=na(e.image,e.__image,n);if(ra(i)){var r=t.createPattern(i,e.repeat||"repeat");if("function"==typeof DOMMatrix&&r&&r.setTransform){var o=new DOMMatrix;o.translateSelf(e.x||0,e.y||0),o.rotateSelf(0,0,(e.rotation||0)*wt),o.scaleSelf(e.scaleX||1,e.scaleY||1),r.setTransform(o)}return r}}var Qy=["shadowBlur","shadowOffsetX","shadowOffsetY"],tv=[["lineCap","butt"],["lineJoin","miter"],["miterLimit",10]];function ev(t,e,n,i,r){var o=!1;if(!i&&e===(n=n||{}))return!1;if(i||e.opacity!==n.opacity){rv(t,r),o=!0;var a=Math.max(Math.min(e.opacity,1),0);t.globalAlpha=isNaN(a)?ma.opacity:a}(i||e.blend!==n.blend)&&(o||(rv(t,r),o=!0),t.globalCompositeOperation=e.blend||ma.blend);for(var s=0;s0&&t.unfinished);t.unfinished||this._zr.flush()}}},e.prototype.getDom=function(){return this._dom},e.prototype.getId=function(){return this.id},e.prototype.getZr=function(){return this._zr},e.prototype.isSSR=function(){return this._ssr},e.prototype.setOption=function(t,e,n){if(!this.__flagInMainProcess)if(this._disposed)qv(this.id);else{var i,r,o;if(q(e)&&(n=e.lazyUpdate,i=e.silent,r=e.replaceMerge,o=e.transition,e=e.notMerge),this.__flagInMainProcess=!0,!this._model||e){var a=new xd(this._api),s=this._theme,l=this._model=new hd;l.scheduler=this._scheduler,l.ssr=this._ssr,l.init(null,null,null,s,this._locale,a)}this._model.setOption(t,{replaceMerge:r},Qv);var u={seriesTransition:o,optionChanged:!0};if(n)this.__pendingUpdate={silent:i,updateParams:u},this.__flagInMainProcess=!1,this.getZr().wakeUp();else{try{Tv(this),Av.update.call(this,null,u)}catch(t){throw this.__pendingUpdate=null,this.__flagInMainProcess=!1,t}this._ssr||this._zr.flush(),this.__pendingUpdate=null,this.__flagInMainProcess=!1,Ov.call(this,i),Rv.call(this,i)}}},e.prototype.setTheme=function(){go()},e.prototype.getModel=function(){return this._model},e.prototype.getOption=function(){return this._model&&this._model.getOption()},e.prototype.getWidth=function(){return this._zr.getWidth()},e.prototype.getHeight=function(){return this._zr.getHeight()},e.prototype.getDevicePixelRatio=function(){return this._zr.painter.dpr||r.hasGlobalWindow&&window.devicePixelRatio||1},e.prototype.getRenderedCanvas=function(t){return this.renderToCanvas(t)},e.prototype.renderToCanvas=function(t){t=t||{};var e=this._zr.painter;return e.getRenderedCanvas({backgroundColor:t.backgroundColor||this._model.get("backgroundColor"),pixelRatio:t.pixelRatio||this.getDevicePixelRatio()})},e.prototype.renderToSVGString=function(t){t=t||{};var e=this._zr.painter;return e.renderToString({useViewBox:t.useViewBox})},e.prototype.getSvgDataURL=function(){if(r.svgSupported){var t=this._zr;return E(t.storage.getDisplayList(),(function(t){t.stopAnimation(null,!0)})),t.painter.toDataURL()}},e.prototype.getDataURL=function(t){if(!this._disposed){var e=(t=t||{}).excludeComponents,n=this._model,i=[],r=this;E(e,(function(t){n.eachComponent({mainType:t},(function(t){var e=r._componentsMap[t.__viewId];e.group.ignore||(i.push(e),e.group.ignore=!0)}))}));var o="svg"===this._zr.painter.getType()?this.getSvgDataURL():this.renderToCanvas(t).toDataURL("image/"+(t&&t.type||"png"));return E(i,(function(t){t.group.ignore=!1})),o}qv(this.id)},e.prototype.getConnectedDataURL=function(t){if(!this._disposed){var e="svg"===t.type,n=this.group,i=Math.min,r=Math.max,o=1/0;if(rm[n]){var a=o,s=o,l=-1/0,u=-1/0,c=[],p=t&&t.pixelRatio||this.getDevicePixelRatio();E(im,(function(o,h){if(o.group===n){var p=e?o.getZr().painter.getSvgDom().innerHTML:o.renderToCanvas(T(t)),d=o.getDom().getBoundingClientRect();a=i(d.left,a),s=i(d.top,s),l=r(d.right,l),u=r(d.bottom,u),c.push({dom:p,left:d.left,top:d.top})}}));var d=(l*=p)-(a*=p),f=(u*=p)-(s*=p),g=h.createCanvas(),y=Fr(g,{renderer:e?"svg":"canvas"});if(y.resize({width:d,height:f}),e){var v="";return E(c,(function(t){var e=t.left-a,n=t.top-s;v+=''+t.dom+""})),y.painter.getSvgRoot().innerHTML=v,t.connectedBackgroundColor&&y.painter.setBackgroundColor(t.connectedBackgroundColor),y.refreshImmediately(),y.painter.toDataURL()}return t.connectedBackgroundColor&&y.add(new Es({shape:{x:0,y:0,width:d,height:f},style:{fill:t.connectedBackgroundColor}})),E(c,(function(t){var e=new As({style:{x:t.left*p-a,y:t.top*p-s,image:t.dom}});y.add(e)})),y.refreshImmediately(),g.toDataURL("image/"+(t&&t.type||"png"))}return this.getDataURL(t)}qv(this.id)},e.prototype.convertToPixel=function(t,e){return kv(this,"convertToPixel",t,e)},e.prototype.convertFromPixel=function(t,e){return kv(this,"convertFromPixel",t,e)},e.prototype.containPixel=function(t,e){var n;if(!this._disposed)return E(Ro(this._model,t),(function(t,i){i.indexOf("Models")>=0&&E(t,(function(t){var r=t.coordinateSystem;if(r&&r.containPoint)n=n||!!r.containPoint(e);else if("seriesModels"===i){var o=this._chartsMap[t.__viewId];o&&o.containPoint&&(n=n||o.containPoint(e,t))}else 0}),this)}),this),!!n;qv(this.id)},e.prototype.getVisual=function(t,e){var n=Ro(this._model,t,{defaultMainType:"series"}),i=n.seriesModel;var r=i.getData(),o=n.hasOwnProperty("dataIndexInside")?n.dataIndexInside:n.hasOwnProperty("dataIndex")?r.indexOfRawIndex(n.dataIndex):null;return null!=o?by(r,o,e):wy(r,e)},e.prototype.getViewOfComponentModel=function(t){return this._componentsMap[t.__viewId]},e.prototype.getViewOfSeriesModel=function(t){return this._chartsMap[t.__viewId]},e.prototype._initEvents=function(){var t,e,n,i=this;E(jv,(function(t){var e=function(e){var n,r=i.getModel(),o=e.target,a="globalout"===t;if(a?n={}:o&&Ty(o,(function(t){var e=Js(t);if(e&&null!=e.dataIndex){var i=e.dataModel||r.getSeriesByIndex(e.seriesIndex);return n=i&&i.getDataParams(e.dataIndex,e.dataType)||{},!0}if(e.eventData)return n=A({},e.eventData),!0}),!0),n){var s=n.componentType,l=n.componentIndex;"markLine"!==s&&"markPoint"!==s&&"markArea"!==s||(s="series",l=n.seriesIndex);var u=s&&null!=l&&r.getComponent(s,l),h=u&&i["series"===u.mainType?"_chartsMap":"_componentsMap"][u.__viewId];0,n.event=e,n.type=t,i._$eventProcessor.eventInfo={targetEl:o,packedEvent:n,model:u,view:h},i.trigger(t,n)}};e.zrEventfulCallAtLast=!0,i._zr.on(t,e,i)})),E($v,(function(t,e){i._messageCenter.on(e,(function(t){this.trigger(e,t)}),i)})),E(["selectchanged"],(function(t){i._messageCenter.on(t,(function(e){this.trigger(t,e)}),i)})),t=this._messageCenter,e=this,n=this._api,t.on("selectchanged",(function(t){var i=n.getModel();t.isFromClick?(Iy("map","selectchanged",e,i,t),Iy("pie","selectchanged",e,i,t)):"select"===t.fromAction?(Iy("map","selected",e,i,t),Iy("pie","selected",e,i,t)):"unselect"===t.fromAction&&(Iy("map","unselected",e,i,t),Iy("pie","unselected",e,i,t))}))},e.prototype.isDisposed=function(){return this._disposed},e.prototype.clear=function(){this._disposed?qv(this.id):this.setOption({series:[]},!0)},e.prototype.dispose=function(){if(this._disposed)qv(this.id);else{this._disposed=!0,this.getDom()&&Bo(this.getDom(),sm,"");var t=this,e=t._api,n=t._model;E(t._componentsViews,(function(t){t.dispose(n,e)})),E(t._chartsViews,(function(t){t.dispose(n,e)})),t._zr.dispose(),t._dom=t._model=t._chartsMap=t._componentsMap=t._chartsViews=t._componentsViews=t._scheduler=t._api=t._zr=t._throttledZrFlush=t._theme=t._coordSysMgr=t._messageCenter=null,delete im[t.id]}},e.prototype.resize=function(t){if(!this.__flagInMainProcess)if(this._disposed)qv(this.id);else{this._zr.resize(t);var e=this._model;if(this._loadingFX&&this._loadingFX.resize(),e){var n=e.resetOption("media"),i=t&&t.silent;this.__pendingUpdate&&(null==i&&(i=this.__pendingUpdate.silent),n=!0,this.__pendingUpdate=null),this.__flagInMainProcess=!0;try{n&&Tv(this),Av.update.call(this,{type:"resize",animation:A({duration:0},t&&t.animation)})}catch(t){throw this.__flagInMainProcess=!1,t}this.__flagInMainProcess=!1,Ov.call(this,i),Rv.call(this,i)}}},e.prototype.showLoading=function(t,e){if(this._disposed)qv(this.id);else if(q(t)&&(e=t,t=""),t=t||"default",this.hideLoading(),nm[t]){var n=nm[t](this._api,e),i=this._zr;this._loadingFX=n,i.add(n)}},e.prototype.hideLoading=function(){this._disposed?qv(this.id):(this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null)},e.prototype.makeActionFromEvent=function(t){var e=A({},t);return e.type=$v[t.type],e},e.prototype.dispatchAction=function(t,e){if(this._disposed)qv(this.id);else if(q(e)||(e={silent:!!e}),Kv[t.type]&&this._model)if(this.__flagInMainProcess)this._pendingActions.push(t);else{var n=e.silent;Pv.call(this,t,n);var i=e.flush;i?this._zr.flush():!1!==i&&r.browser.weChat&&this._throttledZrFlush(),Ov.call(this,n),Rv.call(this,n)}},e.prototype.updateLabelLayout=function(){gv.trigger("series:layoutlabels",this._model,this._api,{updatedSeries:[]})},e.prototype.appendData=function(t){if(this._disposed)qv(this.id);else{var e=t.seriesIndex,n=this.getModel().getSeriesByIndex(e);0,n.appendData(t),this._scheduler.unfinished=!0,this.getZr().wakeUp()}},e.internalField=function(){function t(t){t.clearColorPalette(),t.eachSeries((function(t){t.clearColorPalette()}))}function e(t){for(var e=[],n=t.currentStates,i=0;i0?{duration:o,delay:i.get("delay"),easing:i.get("easing")}:null;n.eachRendered((function(t){if(t.states&&t.states.emphasis){if(gh(t))return;if(t instanceof Ms&&function(t){var e=nl(t);e.normalFill=t.style.fill,e.normalStroke=t.style.stroke;var n=t.states.select||{};e.selectFill=n.style&&n.style.fill||null,e.selectStroke=n.style&&n.style.stroke||null}(t),t.__dirty){var n=t.prevStates;n&&t.useStates(n)}if(r){t.stateTransition=a;var i=t.getTextContent(),o=t.getTextGuideLine();i&&(i.stateTransition=a),o&&(o.stateTransition=a)}t.__dirty&&e(t)}}))}Tv=function(t){var e=t._scheduler;e.restorePipelines(t._model),e.prepareStageTasks(),Cv(t,!0),Cv(t,!1),e.plan()},Cv=function(t,e){for(var n=t._model,i=t._scheduler,r=e?t._componentsViews:t._chartsViews,o=e?t._componentsMap:t._chartsMap,a=t._zr,s=t._api,l=0;le.get("hoverLayerThreshold")&&!r.node&&!r.worker&&e.eachSeries((function(e){if(!e.preventUsingHoverLayer){var n=t._chartsMap[e.__viewId];n.__alive&&n.eachRendered((function(t){t.states.emphasis&&(t.states.emphasis.hoverLayer=!0)}))}}))}(t,e),gv.trigger("series:afterupdate",e,n,l)},Wv=function(t){t.__needsUpdateStatus=!0,t.getZr().wakeUp()},Hv=function(t){t.__needsUpdateStatus&&(t.getZr().storage.traverse((function(t){gh(t)||e(t)})),t.__needsUpdateStatus=!1)},Fv=function(t){return new(function(e){function i(){return null!==e&&e.apply(this,arguments)||this}return n(i,e),i.prototype.getCoordinateSystems=function(){return t._coordSysMgr.getCoordinateSystems()},i.prototype.getComponentByElement=function(e){for(;e;){var n=e.__ecComponentInfo;if(null!=n)return t._model.getComponent(n.mainType,n.index);e=e.parent}},i.prototype.enterEmphasis=function(e,n){Al(e,n),Wv(t)},i.prototype.leaveEmphasis=function(e,n){kl(e,n),Wv(t)},i.prototype.enterBlur=function(e){Ll(e),Wv(t)},i.prototype.leaveBlur=function(e){Pl(e),Wv(t)},i.prototype.enterSelect=function(e){Ol(e),Wv(t)},i.prototype.leaveSelect=function(e){Rl(e),Wv(t)},i.prototype.getModel=function(){return t.getModel()},i.prototype.getViewOfComponentModel=function(e){return t.getViewOfComponentModel(e)},i.prototype.getViewOfSeriesModel=function(e){return t.getViewOfSeriesModel(e)},i}(gd))(t)},Gv=function(t){function e(t,e){for(var n=0;n=0)){bm.push(n);var o=jg.wrapStageHandler(n,r);o.__prio=e,o.__raw=n,t.push(o)}}function Sm(t,e){nm[t]=e}function Mm(t,e,n){var i=vv("registerMap");i&&i(t,e,n)}var Im=function(t){var e=(t=T(t)).type,n="";e||yo(n);var i=e.split(":");2!==i.length&&yo(n);var r=!1;"echarts"===i[0]&&(e=i[1],r=!0),t.__isBuiltIn=r,Of.set(e,t)};_m(mv,Hg),_m(xv,Ug),_m(xv,Xg),_m(mv,xy),_m(xv,_y),_m(7e3,(function(t,e){t.eachRawSeries((function(n){if(!t.isSeriesFiltered(n)){var i=n.getData();i.hasItemVisual()&&i.each((function(t){var n=i.getItemVisual(t,"decal");n&&(i.ensureUniqueItemVisual(t,"style").decal=cv(n,e))}));var r=i.getVisual("decal");if(r)i.getVisual("style").decal=cv(r,e)}}))})),pm(Fd),dm(900,(function(t){var e=yt();t.eachSeries((function(t){var n=t.get("stack");if(n){var i=e.get(n)||e.set(n,[]),r=t.getData(),o={stackResultDimension:r.getCalculationInfo("stackResultDimension"),stackedOverDimension:r.getCalculationInfo("stackedOverDimension"),stackedDimension:r.getCalculationInfo("stackedDimension"),stackedByDimension:r.getCalculationInfo("stackedByDimension"),isStackedByIndex:r.getCalculationInfo("isStackedByIndex"),data:r,seriesModel:t};if(!o.stackedDimension||!o.isStackedByIndex&&!o.stackedByDimension)return;i.length&&r.setCalculationInfo("stackedOnSeries",i[i.length-1].seriesModel),i.push(o)}})),e.each(Gd)})),Sm("default",(function(t,e){k(e=e||{},{text:"loading",textColor:"#000",fontSize:12,fontWeight:"normal",fontStyle:"normal",fontFamily:"sans-serif",maskColor:"rgba(255, 255, 255, 0.8)",showSpinner:!0,color:"#5470c6",spinnerRadius:10,lineWidth:5,zlevel:0});var n=new Er,i=new Es({style:{fill:e.maskColor},zlevel:e.zlevel,z:1e4});n.add(i);var r,o=new Bs({style:{text:e.text,fill:e.textColor,fontSize:e.fontSize,fontWeight:e.fontWeight,fontStyle:e.fontStyle,fontFamily:e.fontFamily},zlevel:e.zlevel,z:10001}),a=new Es({style:{fill:"none"},textContent:o,textConfig:{position:"right",distance:10},zlevel:e.zlevel,z:10001});return n.add(a),e.showSpinner&&((r=new Ju({shape:{startAngle:-Zg/2,endAngle:-Zg/2+.1,r:e.spinnerRadius},style:{stroke:e.color,lineCap:"round",lineWidth:e.lineWidth},zlevel:e.zlevel,z:10001})).animateShape(!0).when(1e3,{endAngle:3*Zg/2}).start("circularInOut"),r.animateShape(!0).when(1e3,{startAngle:3*Zg/2}).delay(300).start("circularInOut"),n.add(r)),n.resize=function(){var n=o.getBoundingRect().width,s=e.showSpinner?e.spinnerRadius:0,l=(t.getWidth()-2*s-(e.showSpinner&&n?10:0)-n)/2-(e.showSpinner&&n?0:5+n/2)+(e.showSpinner?0:n/2)+(n?0:s),u=t.getHeight()/2;e.showSpinner&&r.setShape({cx:l,cy:u}),a.setShape({x:l-s,y:u-s,width:2*s,height:2*s}),i.setShape({x:0,y:0,width:t.getWidth(),height:t.getHeight()})},n.resize(),n})),vm({type:sl,event:sl,update:sl},bt),vm({type:ll,event:ll,update:ll},bt),vm({type:ul,event:ul,update:ul},bt),vm({type:hl,event:hl,update:hl},bt),vm({type:cl,event:cl,update:cl},bt),cm("light",hy),cm("dark",gy);var Tm=[],Cm={registerPreprocessor:pm,registerProcessor:dm,registerPostInit:fm,registerPostUpdate:gm,registerUpdateLifecycle:ym,registerAction:vm,registerCoordinateSystem:mm,registerLayout:xm,registerVisual:_m,registerTransform:Im,registerLoading:Sm,registerMap:Mm,registerImpl:function(t,e){yv[t]=e},PRIORITY:_v,ComponentModel:Op,ComponentView:wg,SeriesModel:fg,ChartView:Tg,registerComponentModel:function(t){Op.registerClass(t)},registerComponentView:function(t){wg.registerClass(t)},registerSeriesModel:function(t){fg.registerClass(t)},registerChartView:function(t){Tg.registerClass(t)},registerSubTypeDefaulter:function(t,e){Op.registerSubTypeDefaulter(t,e)},registerPainter:function(t,e){Gr(t,e)}};function Dm(t){Y(t)?E(t,(function(t){Dm(t)})):P(Tm,t)>=0||(Tm.push(t),U(t)&&(t={install:t}),t.install(Cm))}function Am(t){return null==t?0:t.length||1}function km(t){return t}var Lm=function(){function t(t,e,n,i,r,o){this._old=t,this._new=e,this._oldKeyGetter=n||km,this._newKeyGetter=i||km,this.context=r,this._diffModeMultiple="multiple"===o}return t.prototype.add=function(t){return this._add=t,this},t.prototype.update=function(t){return this._update=t,this},t.prototype.updateManyToOne=function(t){return this._updateManyToOne=t,this},t.prototype.updateOneToMany=function(t){return this._updateOneToMany=t,this},t.prototype.updateManyToMany=function(t){return this._updateManyToMany=t,this},t.prototype.remove=function(t){return this._remove=t,this},t.prototype.execute=function(){this[this._diffModeMultiple?"_executeMultiple":"_executeOneToOne"]()},t.prototype._executeOneToOne=function(){var t=this._old,e=this._new,n={},i=new Array(t.length),r=new Array(e.length);this._initIndexMap(t,null,i,"_oldKeyGetter"),this._initIndexMap(e,n,r,"_newKeyGetter");for(var o=0;o1){var u=s.shift();1===s.length&&(n[a]=s[0]),this._update&&this._update(u,o)}else 1===l?(n[a]=null,this._update&&this._update(s,o)):this._remove&&this._remove(o)}this._performRestAdd(r,n)},t.prototype._executeMultiple=function(){var t=this._old,e=this._new,n={},i={},r=[],o=[];this._initIndexMap(t,n,r,"_oldKeyGetter"),this._initIndexMap(e,i,o,"_newKeyGetter");for(var a=0;a1&&1===c)this._updateManyToOne&&this._updateManyToOne(u,l),i[s]=null;else if(1===h&&c>1)this._updateOneToMany&&this._updateOneToMany(u,l),i[s]=null;else if(1===h&&1===c)this._update&&this._update(u,l),i[s]=null;else if(h>1&&c>1)this._updateManyToMany&&this._updateManyToMany(u,l),i[s]=null;else if(h>1)for(var p=0;p1)for(var a=0;a30}var Hm,Ym,Um,Xm,Zm,jm,qm,Km=q,$m=z,Jm="undefined"==typeof Int32Array?Array:Int32Array,Qm=["hasItemOption","_nameList","_idList","_invertedIndicesMap","_dimSummary","userOutput","_rawData","_dimValueGetter","_nameDimIdx","_idDimIdx","_nameRepeatCount"],tx=["_approximateExtent"],ex=function(){function t(t,e){var n;this.type="list",this._dimOmitted=!1,this._nameList=[],this._idList=[],this._visual={},this._layout={},this._itemVisuals=[],this._itemLayouts=[],this._graphicEls=[],this._approximateExtent={},this._calculationInfo={},this.hasItemOption=!1,this.TRANSFERABLE_METHODS=["cloneShallow","downSample","lttbDownSample","map"],this.CHANGABLE_METHODS=["filterSelf","selectRange"],this.DOWNSAMPLE_METHODS=["downSample","lttbDownSample"];var i=!1;Bm(t)?(n=t.dimensions,this._dimOmitted=t.isDimensionOmitted(),this._schema=t):(i=!0,n=t),n=n||["x","y"];for(var r={},o=[],a={},s=!1,l={},u=0;u=e)){var n=this._store.getProvider();this._updateOrdinalMeta();var i=this._nameList,r=this._idList;if(n.getSource().sourceFormat===Vp&&!n.pure)for(var o=[],a=t;a0},t.prototype.ensureUniqueItemVisual=function(t,e){var n=this._itemVisuals,i=n[t];i||(i=n[t]={});var r=i[e];return null==r&&(Y(r=this.getVisual(e))?r=r.slice():Km(r)&&(r=A({},r)),i[e]=r),r},t.prototype.setItemVisual=function(t,e,n){var i=this._itemVisuals[t]||{};this._itemVisuals[t]=i,Km(e)?A(i,e):i[e]=n},t.prototype.clearAllVisual=function(){this._visual={},this._itemVisuals=[]},t.prototype.setLayout=function(t,e){Km(t)?A(this._layout,t):this._layout[t]=e},t.prototype.getLayout=function(t){return this._layout[t]},t.prototype.getItemLayout=function(t){return this._itemLayouts[t]},t.prototype.setItemLayout=function(t,e,n){this._itemLayouts[t]=n?A(this._itemLayouts[t]||{},e):e},t.prototype.clearItemLayouts=function(){this._itemLayouts.length=0},t.prototype.setItemGraphicEl=function(t,e){var n=this.hostModel&&this.hostModel.seriesIndex;Qs(n,this.dataType,t,e),this._graphicEls[t]=e},t.prototype.getItemGraphicEl=function(t){return this._graphicEls[t]},t.prototype.eachItemGraphicEl=function(t,e){E(this._graphicEls,(function(n,i){n&&t&&t.call(e,n,i)}))},t.prototype.cloneShallow=function(e){return e||(e=new t(this._schema?this._schema:$m(this.dimensions,this._getDimInfo,this),this.hostModel)),Zm(e,this),e._store=this._store,e},t.prototype.wrapMethod=function(t,e){var n=this[t];U(n)&&(this.__wrappedMethods=this.__wrappedMethods||[],this.__wrappedMethods.push(t),this[t]=function(){var t=n.apply(this,arguments);return e.apply(this,[t].concat(at(arguments)))})},t.internalField=(Hm=function(t){var e=t._invertedIndicesMap;E(e,(function(n,i){var r=t._dimInfos[i],o=r.ordinalMeta,a=t._store;if(o){n=e[i]=new Jm(o.categories.length);for(var s=0;s1&&(s+="__ec__"+u),i[e]=s}})),t}();function nx(t,e){jd(t)||(t=Kd(t));var n=(e=e||{}).coordDimensions||[],i=e.dimensionsDefine||t.dimensionsDefine||[],r=yt(),o=[],a=function(t,e,n,i){var r=Math.max(t.dimensionsDetectedCount||1,e.length,n.length,i||0);return E(e,(function(t){var e;q(t)&&(e=t.dimsDef)&&(r=Math.max(r,e.length))})),r}(t,n,i,e.dimensionsCount),s=e.canOmitUnusedDimensions&&Wm(a),l=i===t.dimensionsDefine,u=l?Gm(t):Fm(i),h=e.encodeDefine;!h&&e.encodeDefaulter&&(h=e.encodeDefaulter(t,a));for(var c=yt(h),p=new Ff(a),d=0;d0&&(i.name=r+(o-1)),o++,e.set(r,o)}}(o),new Vm({source:t,dimensions:o,fullDimensionCount:a,dimensionOmitted:s})}function ix(t,e,n){if(n||e.hasKey(t)){for(var i=0;e.hasKey(t+i);)i++;t+=i}return e.set(t,!0),t}var rx=function(t){this.coordSysDims=[],this.axisMap=yt(),this.categoryAxisMap=yt(),this.coordSysName=t};var ox={cartesian2d:function(t,e,n,i){var r=t.getReferringComponents("xAxis",Eo).models[0],o=t.getReferringComponents("yAxis",Eo).models[0];e.coordSysDims=["x","y"],n.set("x",r),n.set("y",o),ax(r)&&(i.set("x",r),e.firstCategoryDimIndex=0),ax(o)&&(i.set("y",o),null==e.firstCategoryDimIndex&&(e.firstCategoryDimIndex=1))},singleAxis:function(t,e,n,i){var r=t.getReferringComponents("singleAxis",Eo).models[0];e.coordSysDims=["single"],n.set("single",r),ax(r)&&(i.set("single",r),e.firstCategoryDimIndex=0)},polar:function(t,e,n,i){var r=t.getReferringComponents("polar",Eo).models[0],o=r.findAxisModel("radiusAxis"),a=r.findAxisModel("angleAxis");e.coordSysDims=["radius","angle"],n.set("radius",o),n.set("angle",a),ax(o)&&(i.set("radius",o),e.firstCategoryDimIndex=0),ax(a)&&(i.set("angle",a),null==e.firstCategoryDimIndex&&(e.firstCategoryDimIndex=1))},geo:function(t,e,n,i){e.coordSysDims=["lng","lat"]},parallel:function(t,e,n,i){var r=t.ecModel,o=r.getComponent("parallel",t.get("parallelIndex")),a=e.coordSysDims=o.dimensions.slice();E(o.parallelAxisIndex,(function(t,o){var s=r.getComponent("parallelAxis",t),l=a[o];n.set(l,s),ax(s)&&(i.set(l,s),null==e.firstCategoryDimIndex&&(e.firstCategoryDimIndex=o))}))}};function ax(t){return"category"===t.get("type")}function sx(t,e,n){var i,r,o,a=(n=n||{}).byIndex,s=n.stackedCoordDimension;!function(t){return!Bm(t.schema)}(e)?(r=e.schema,i=r.dimensions,o=e.store):i=e;var l,u,h,c,p=!(!t||!t.get("stack"));if(E(i,(function(t,e){X(t)&&(i[e]=t={name:t}),p&&!t.isExtraCoord&&(a||l||!t.ordinalMeta||(l=t),u||"ordinal"===t.type||"time"===t.type||s&&s!==t.coordDim||(u=t))})),!u||a||l||(a=!0),u){h="__\0ecstackresult_"+t.id,c="__\0ecstackedover_"+t.id,l&&(l.createInvertedIndices=!0);var d=u.coordDim,f=u.type,g=0;E(i,(function(t){t.coordDim===d&&g++}));var y={name:h,coordDim:d,coordDimIndex:g,type:f,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length},v={name:c,coordDim:c,coordDimIndex:g+1,type:f,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length+1};r?(o&&(y.storeDimIndex=o.ensureCalculationDimension(c,f),v.storeDimIndex=o.ensureCalculationDimension(h,f)),r.appendCalculationDimension(y),r.appendCalculationDimension(v)):(i.push(y),i.push(v))}return{stackedDimension:u&&u.name,stackedByDimension:l&&l.name,isStackedByIndex:a,stackedOverDimension:c,stackResultDimension:h}}function lx(t,e){return!!e&&e===t.getCalculationInfo("stackedDimension")}function ux(t,e){return lx(t,e)?t.getCalculationInfo("stackResultDimension"):e}function hx(t,e,n){n=n||{};var i,r=e.getSourceManager(),o=!1;t?(o=!0,i=Kd(t)):o=(i=r.getSource()).sourceFormat===Vp;var a=function(t){var e=t.get("coordinateSystem"),n=new rx(e),i=ox[e];if(i)return i(t,n,n.axisMap,n.categoryAxisMap),n}(e),s=function(t,e){var n,i=t.get("coordinateSystem"),r=vd.get(i);return e&&e.coordSysDims&&(n=z(e.coordSysDims,(function(t){var n={name:t},i=e.axisMap.get(t);if(i){var r=i.get("type");n.type=Rm(r)}return n}))),n||(n=r&&(r.getDimensionsInfo?r.getDimensionsInfo():r.dimensions.slice())||["x","y"]),n}(e,a),l=n.useEncodeDefaulter,u=U(l)?l:l?H(Kp,s,e):null,h=nx(i,{coordDimensions:s,generateCoord:n.generateCoord,encodeDefine:e.getEncode(),encodeDefaulter:u,canOmitUnusedDimensions:!o}),c=function(t,e,n){var i,r;return n&&E(t,(function(t,o){var a=t.coordDim,s=n.categoryAxisMap.get(a);s&&(null==i&&(i=o),t.ordinalMeta=s.getOrdinalMeta(),e&&(t.createInvertedIndices=!0)),null!=t.otherDims.itemName&&(r=!0)})),r||null==i||(t[i].otherDims.itemName=0),i}(h.dimensions,n.createInvertedIndices,a),p=o?null:r.getSharedDataStore(h),d=sx(e,{schema:h,store:p}),f=new ex(h,e);f.setCalculationInfo(d);var g=null!=c&&function(t){if(t.sourceFormat===Vp){var e=function(t){var e=0;for(;ee[1]&&(e[1]=t[1])},t.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},t.prototype.getExtent=function(){return this._extent.slice()},t.prototype.setExtent=function(t,e){var n=this._extent;isNaN(t)||(n[0]=t),isNaN(e)||(n[1]=e)},t.prototype.isInExtentRange=function(t){return this._extent[0]<=t&&this._extent[1]>=t},t.prototype.isBlank=function(){return this._isBlank},t.prototype.setBlank=function(t){this._isBlank=t},t}();Ko(cx);var px=0,dx=function(){function t(t){this.categories=t.categories||[],this._needCollect=t.needCollect,this._deduplication=t.deduplication,this.uid=++px}return t.createByAxisModel=function(e){var n=e.option,i=n.data,r=i&&z(i,fx);return new t({categories:r,needCollect:!r,deduplication:!1!==n.dedplication})},t.prototype.getOrdinal=function(t){return this._getOrCreateMap().get(t)},t.prototype.parseAndCollect=function(t){var e,n=this._needCollect;if(!X(t)&&!n)return t;if(n&&!this._deduplication)return e=this.categories.length,this.categories[e]=t,e;var i=this._getOrCreateMap();return null==(e=i.get(t))&&(n?(e=this.categories.length,this.categories[e]=t,i.set(t,e)):e=NaN),e},t.prototype._getOrCreateMap=function(){return this._map||(this._map=yt(this.categories))},t}();function fx(t){return q(t)&&null!=t.value?t.value:t+""}function gx(t){return"interval"===t.type||"log"===t.type}function yx(t,e,n,i){var r={},o=t[1]-t[0],a=r.interval=ao(o/e,!0);null!=n&&ai&&(a=r.interval=i);var s=r.intervalPrecision=mx(a);return function(t,e){!isFinite(t[0])&&(t[0]=e[0]),!isFinite(t[1])&&(t[1]=e[1]),xx(t,0,e),xx(t,1,e),t[0]>t[1]&&(t[0]=t[1])}(r.niceTickExtent=[Xr(Math.ceil(t[0]/a)*a,s),Xr(Math.floor(t[1]/a)*a,s)],t),r}function vx(t){var e=Math.pow(10,oo(t)),n=t/e;return n?2===n?n=3:3===n?n=5:n*=2:n=1,Xr(n*e)}function mx(t){return jr(t)+2}function xx(t,e,n){t[e]=Math.max(Math.min(t[e],n[1]),n[0])}function _x(t,e){return t>=e[0]&&t<=e[1]}function bx(t,e){return e[1]===e[0]?.5:(t-e[0])/(e[1]-e[0])}function Sx(t,e){return t*(e[1]-e[0])+e[0]}var Mx=function(t){function e(e){var n=t.call(this,e)||this;n.type="ordinal";var i=n.getSetting("ordinalMeta");return i||(i=new dx({})),Y(i)&&(i=new dx({categories:z(i,(function(t){return q(t)?t.value:t}))})),n._ordinalMeta=i,n._extent=n.getSetting("extent")||[0,i.categories.length-1],n}return n(e,t),e.prototype.parse=function(t){return null==t?NaN:X(t)?this._ordinalMeta.getOrdinal(t):Math.round(t)},e.prototype.contain=function(t){return _x(t=this.parse(t),this._extent)&&null!=this._ordinalMeta.categories[t]},e.prototype.normalize=function(t){return bx(t=this._getTickNumber(this.parse(t)),this._extent)},e.prototype.scale=function(t){return t=Math.round(Sx(t,this._extent)),this.getRawOrdinalNumber(t)},e.prototype.getTicks=function(){for(var t=[],e=this._extent,n=e[0];n<=e[1];)t.push({value:n}),n++;return t},e.prototype.getMinorTicks=function(t){},e.prototype.setSortInfo=function(t){if(null!=t){for(var e=t.ordinalNumbers,n=this._ordinalNumbersByTick=[],i=this._ticksByOrdinalNumber=[],r=0,o=this._ordinalMeta.categories.length,a=Math.min(o,e.length);r=0&&t=0&&t=t},e.prototype.getOrdinalMeta=function(){return this._ordinalMeta},e.prototype.calcNiceTicks=function(){},e.prototype.calcNiceExtent=function(){},e.type="ordinal",e}(cx);cx.registerClass(Mx);var Ix=Xr,Tx=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="interval",e._interval=0,e._intervalPrecision=2,e}return n(e,t),e.prototype.parse=function(t){return t},e.prototype.contain=function(t){return _x(t,this._extent)},e.prototype.normalize=function(t){return bx(t,this._extent)},e.prototype.scale=function(t){return Sx(t,this._extent)},e.prototype.setExtent=function(t,e){var n=this._extent;isNaN(t)||(n[0]=parseFloat(t)),isNaN(e)||(n[1]=parseFloat(e))},e.prototype.unionExtent=function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1]),this.setExtent(e[0],e[1])},e.prototype.getInterval=function(){return this._interval},e.prototype.setInterval=function(t){this._interval=t,this._niceExtent=this._extent.slice(),this._intervalPrecision=mx(t)},e.prototype.getTicks=function(t){var e=this._interval,n=this._extent,i=this._niceExtent,r=this._intervalPrecision,o=[];if(!e)return o;n[0]1e4)return[];var s=o.length?o[o.length-1].value:i[1];return n[1]>s&&(t?o.push({value:Ix(s+e,r)}):o.push({value:n[1]})),o},e.prototype.getMinorTicks=function(t){for(var e=this.getTicks(!0),n=[],i=this.getExtent(),r=1;ri[0]&&h0&&(o=null===o?s:Math.min(o,s))}n[i]=o}}return n}(t),n=[];return E(t,(function(t){var i,r=t.coordinateSystem.getBaseAxis(),o=r.getExtent();if("category"===r.type)i=r.getBandWidth();else if("value"===r.type||"time"===r.type){var a=r.dim+"_"+r.index,s=e[a],l=Math.abs(o[1]-o[0]),u=r.scale.getExtent(),h=Math.abs(u[1]-u[0]);i=s?l/h*s:l}else{var c=t.getData();i=Math.abs(o[1]-o[0])/c.count()}var p=Ur(t.get("barWidth"),i),d=Ur(t.get("barMaxWidth"),i),f=Ur(t.get("barMinWidth")||(Bx(t)?.5:1),i),g=t.get("barGap"),y=t.get("barCategoryGap");n.push({bandWidth:i,barWidth:p,barMaxWidth:d,barMinWidth:f,barGap:g,barCategoryGap:y,axisKey:Px(r),stackId:Lx(t)})})),Nx(n)}function Nx(t){var e={};E(t,(function(t,n){var i=t.axisKey,r=t.bandWidth,o=e[i]||{bandWidth:r,remainedWidth:r,autoWidthCount:0,categoryGap:null,gap:"20%",stacks:{}},a=o.stacks;e[i]=o;var s=t.stackId;a[s]||o.autoWidthCount++,a[s]=a[s]||{width:0,maxWidth:0};var l=t.barWidth;l&&!a[s].width&&(a[s].width=l,l=Math.min(o.remainedWidth,l),o.remainedWidth-=l);var u=t.barMaxWidth;u&&(a[s].maxWidth=u);var h=t.barMinWidth;h&&(a[s].minWidth=h);var c=t.barGap;null!=c&&(o.gap=c);var p=t.barCategoryGap;null!=p&&(o.categoryGap=p)}));var n={};return E(e,(function(t,e){n[e]={};var i=t.stacks,r=t.bandWidth,o=t.categoryGap;if(null==o){var a=G(i).length;o=Math.max(35-4*a,15)+"%"}var s=Ur(o,r),l=Ur(t.gap,1),u=t.remainedWidth,h=t.autoWidthCount,c=(u-s)/(h+(h-1)*l);c=Math.max(c,0),E(i,(function(t){var e=t.maxWidth,n=t.minWidth;if(t.width){i=t.width;e&&(i=Math.min(i,e)),n&&(i=Math.max(i,n)),t.width=i,u-=i+l*i,h--}else{var i=c;e&&ei&&(i=n),i!==c&&(t.width=i,u-=i+l*i,h--)}})),c=(u-s)/(h+(h-1)*l),c=Math.max(c,0);var p,d=0;E(i,(function(t,e){t.width||(t.width=c),p=t,d+=t.width*(1+l)})),p&&(d-=p.width*l);var f=-d/2;E(i,(function(t,i){n[e][i]=n[e][i]||{bandWidth:r,offset:f,width:t.width},f+=t.width*(1+l)}))})),n}function Ex(t,e){var n=Ox(t,e),i=Rx(n);E(n,(function(t){var e=t.getData(),n=t.coordinateSystem.getBaseAxis(),r=Lx(t),o=i[Px(n)][r],a=o.offset,s=o.width;e.setLayout({bandWidth:o.bandWidth,offset:a,size:s})}))}function zx(t){return{seriesType:t,plan:Sg(),reset:function(t){if(Vx(t)){var e=t.getData(),n=t.coordinateSystem,i=n.getBaseAxis(),r=n.getOtherAxis(i),o=e.getDimensionIndex(e.mapDimension(r.dim)),a=e.getDimensionIndex(e.mapDimension(i.dim)),s=t.get("showBackground",!0),l=e.mapDimension(r.dim),u=e.getCalculationInfo("stackResultDimension"),h=lx(e,l)&&!!e.getCalculationInfo("stackedOnSeries"),c=r.isHorizontal(),p=function(t,e){return e.toGlobalCoord(e.dataToCoord("log"===e.type?1:0))}(0,r),d=Bx(t),f=t.get("barMinHeight")||0,g=u&&e.getDimensionIndex(u),y=e.getLayout("size"),v=e.getLayout("offset");return{progress:function(t,e){for(var i,r=t.count,l=d&&Ax(3*r),u=d&&s&&Ax(3*r),m=d&&Ax(r),x=n.master.getRect(),_=c?x.width:x.height,b=e.getStore(),w=0;null!=(i=t.next());){var S=b.get(h?g:o,i),M=b.get(a,i),I=p,T=void 0;h&&(T=+S-b.get(o,i));var C=void 0,D=void 0,A=void 0,k=void 0;if(c){var L=n.dataToPoint([S,M]);if(h)I=n.dataToPoint([T,M])[0];C=I,D=L[1]+v,A=L[0]-I,k=y,Math.abs(A)0)for(var s=0;s=0;--s)if(l[u]){o=l[u];break}o=o||a.none}if(Y(o)){var h=null==t.level?0:t.level>=0?t.level:o.length+t.level;o=o[h=Math.min(h,o.length-1)]}}return jc(new Date(t.value),o,r,i)}(t,e,n,this.getSetting("locale"),i)},e.prototype.getTicks=function(){var t=this._interval,e=this._extent,n=[];if(!t)return n;n.push({value:e[0],level:0});var i=this.getSetting("useUTC"),r=function(t,e,n,i){var r=1e4,o=Yc,a=0;function s(t,e,n,r,o,a,s){for(var l=new Date(e),u=e,h=l[r]();u1&&0===u&&o.unshift({value:o[0].value-p})}}for(u=0;u=i[0]&&v<=i[1]&&c++)}var m=(i[1]-i[0])/e;if(c>1.5*m&&p>m/1.5)break;if(u.push(g),c>m||t===o[d])break}h=[]}}0;var x=B(z(u,(function(t){return B(t,(function(t){return t.value>=i[0]&&t.value<=i[1]&&!t.notAdd}))})),(function(t){return t.length>0})),_=[],b=x.length-1;for(d=0;dn&&(this._approxInterval=n);var o=Gx.length,a=Math.min(function(t,e,n,i){for(;n>>1;t[r][1]16?16:t>7.5?7:t>3.5?4:t>1.5?2:1}function Hx(t){return(t/=2592e6)>6?6:t>3?3:t>2?2:1}function Yx(t){return(t/=zc)>12?12:t>6?6:t>3.5?4:t>2?2:1}function Ux(t,e){return(t/=e?Ec:Nc)>30?30:t>20?20:t>15?15:t>10?10:t>5?5:t>2?2:1}function Xx(t){return ao(t,!0)}function Zx(t,e,n){var i=new Date(t);switch(Xc(e)){case"year":case"month":i[op(n)](0);case"day":i[ap(n)](1);case"hour":i[sp(n)](0);case"minute":i[lp(n)](0);case"second":i[up(n)](0),i[hp(n)](0)}return i.getTime()}cx.registerClass(Fx);var jx=cx.prototype,qx=Tx.prototype,Kx=Xr,$x=Math.floor,Jx=Math.ceil,Qx=Math.pow,t_=Math.log,e_=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="log",e.base=10,e._originalScale=new Tx,e._interval=0,e}return n(e,t),e.prototype.getTicks=function(t){var e=this._originalScale,n=this._extent,i=e.getExtent();return z(qx.getTicks.call(this,t),(function(t){var e=t.value,r=Xr(Qx(this.base,e));return r=e===n[0]&&this._fixMin?i_(r,i[0]):r,{value:r=e===n[1]&&this._fixMax?i_(r,i[1]):r}}),this)},e.prototype.setExtent=function(t,e){var n=t_(this.base);t=t_(Math.max(0,t))/n,e=t_(Math.max(0,e))/n,qx.setExtent.call(this,t,e)},e.prototype.getExtent=function(){var t=this.base,e=jx.getExtent.call(this);e[0]=Qx(t,e[0]),e[1]=Qx(t,e[1]);var n=this._originalScale.getExtent();return this._fixMin&&(e[0]=i_(e[0],n[0])),this._fixMax&&(e[1]=i_(e[1],n[1])),e},e.prototype.unionExtent=function(t){this._originalScale.unionExtent(t);var e=this.base;t[0]=t_(t[0])/t_(e),t[1]=t_(t[1])/t_(e),jx.unionExtent.call(this,t)},e.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},e.prototype.calcNiceTicks=function(t){t=t||10;var e=this._extent,n=e[1]-e[0];if(!(n===1/0||n<=0)){var i=ro(n);for(t/n*i<=.5&&(i*=10);!isNaN(i)&&Math.abs(i)<1&&Math.abs(i)>0;)i*=10;var r=[Xr(Jx(e[0]/i)*i),Xr($x(e[1]/i)*i)];this._interval=i,this._niceExtent=r}},e.prototype.calcNiceExtent=function(t){qx.calcNiceExtent.call(this,t),this._fixMin=t.fixMin,this._fixMax=t.fixMax},e.prototype.parse=function(t){return t},e.prototype.contain=function(t){return _x(t=t_(t)/t_(this.base),this._extent)},e.prototype.normalize=function(t){return bx(t=t_(t)/t_(this.base),this._extent)},e.prototype.scale=function(t){return t=Sx(t,this._extent),Qx(this.base,t)},e.type="log",e}(cx),n_=e_.prototype;function i_(t,e){return Kx(t,jr(e))}n_.getMinorTicks=qx.getMinorTicks,n_.getLabel=qx.getLabel,cx.registerClass(e_);var r_=function(){function t(t,e,n){this._prepareParams(t,e,n)}return t.prototype._prepareParams=function(t,e,n){n[1]0&&s>0&&!l&&(a=0),a<0&&s<0&&!u&&(s=0));var c=this._determinedMin,p=this._determinedMax;return null!=c&&(a=c,l=!0),null!=p&&(s=p,u=!0),{min:a,max:s,minFixed:l,maxFixed:u,isBlank:h}},t.prototype.modifyDataMinMax=function(t,e){this[a_[t]]=e},t.prototype.setDeterminedMinMax=function(t,e){var n=o_[t];this[n]=e},t.prototype.freeze=function(){this.frozen=!0},t}(),o_={min:"_determinedMin",max:"_determinedMax"},a_={min:"_dataMin",max:"_dataMax"};function s_(t,e,n){var i=t.rawExtentInfo;return i||(i=new r_(t,e,n),t.rawExtentInfo=i,i)}function l_(t,e){return null==e?null:nt(e)?NaN:t.parse(e)}function u_(t,e){var n=t.type,i=s_(t,e,t.getExtent()).calculate();t.setBlank(i.isBlank);var r=i.min,o=i.max,a=e.ecModel;if(a&&"time"===n){var s=Ox("bar",a),l=!1;if(E(s,(function(t){l=l||t.getBaseAxis()===e.axis})),l){var u=Rx(s),h=function(t,e,n,i){var r=n.axis.getExtent(),o=r[1]-r[0],a=function(t,e,n){if(t&&e){var i=t[Px(e)];return null!=i&&null!=n?i[Lx(n)]:i}}(i,n.axis);if(void 0===a)return{min:t,max:e};var s=1/0;E(a,(function(t){s=Math.min(t.offset,s)}));var l=-1/0;E(a,(function(t){l=Math.max(t.offset+t.width,l)})),s=Math.abs(s),l=Math.abs(l);var u=s+l,h=e-t,c=h/(1-(s+l)/o)-h;return{min:t-=c*(s/u),max:e+=c*(l/u)}}(r,o,e,u);r=h.min,o=h.max}}return{extent:[r,o],fixMin:i.minFixed,fixMax:i.maxFixed}}function h_(t,e){var n=e,i=u_(t,n),r=i.extent,o=n.get("splitNumber");t instanceof e_&&(t.base=n.get("logBase"));var a=t.type,s=n.get("interval"),l="interval"===a||"time"===a;t.setExtent(r[0],r[1]),t.calcNiceExtent({splitNumber:o,fixMin:i.fixMin,fixMax:i.fixMax,minInterval:l?n.get("minInterval"):null,maxInterval:l?n.get("maxInterval"):null}),null!=s&&t.setInterval&&t.setInterval(s)}function c_(t,e){if(e=e||t.get("type"))switch(e){case"category":return new Mx({ordinalMeta:t.getOrdinalMeta?t.getOrdinalMeta():t.getCategories(),extent:[1/0,-1/0]});case"time":return new Fx({locale:t.ecModel.getLocaleModel(),useUTC:t.ecModel.get("useUTC")});default:return new(cx.getClass(e)||Tx)}}function p_(t){var e,n,i=t.getLabelModel().get("formatter"),r="category"===t.type?t.scale.getExtent()[0]:null;return"time"===t.scale.type?(n=i,function(e,i){return t.scale.getFormattedLabel(e,i,n)}):X(i)?function(e){return function(n){var i=t.scale.getLabel(n);return e.replace("{value}",null!=i?i:"")}}(i):U(i)?(e=i,function(n,i){return null!=r&&(i=n.value-r),e(d_(t,n),i,null!=n.level?{level:n.level}:null)}):function(e){return t.scale.getLabel(e)}}function d_(t,e){return"category"===t.type?t.scale.getLabel(e):e.value}function f_(t,e){var n=e*Math.PI/180,i=t.width,r=t.height,o=i*Math.abs(Math.cos(n))+Math.abs(r*Math.sin(n)),a=i*Math.abs(Math.sin(n))+Math.abs(r*Math.cos(n));return new Ee(t.x,t.y,o,a)}function g_(t){var e=t.get("interval");return null==e?"auto":e}function y_(t){return"category"===t.type&&0===g_(t.getLabelModel())}function v_(t,e){var n={};return E(t.mapDimensionsAll(e),(function(e){n[ux(t,e)]=!0})),G(n)}var m_=function(){function t(){}return t.prototype.getNeedCrossZero=function(){return!this.option.scale},t.prototype.getCoordSysModel=function(){},t}();var x_={isDimensionStacked:lx,enableDataStack:sx,getStackedDimension:ux};var __=Object.freeze({__proto__:null,createList:function(t){return hx(null,t)},getLayoutRect:Tp,dataStack:x_,createScale:function(t,e){var n=e;e instanceof Sc||(n=new Sc(e));var i=c_(n);return i.setExtent(t[0],t[1]),h_(i,n),i},mixinAxisModelCommonMethods:function(t){R(t,m_)},getECData:Js,createTextStyle:function(t,e){return ec(t,null,null,"normal"!==(e=e||{}).state)},createDimensions:function(t,e){return nx(t,e).dimensions},createSymbol:Vy,enableHoverEmphasis:Wl});function b_(t,e){return Math.abs(t-e)<1e-8}function w_(t,e,n){var i=0,r=t[0];if(!r)return!1;for(var o=1;on&&(t=r,n=a)}if(t)return function(t){for(var e=0,n=0,i=0,r=t.length,o=t[r-1][0],a=t[r-1][1],s=0;s>1^-(1&s),l=l>>1^-(1&l),r=s+=r,o=l+=o,i.push([s/n,l/n])}return i}function O_(t,e){return z(B((t=function(t){if(!t.UTF8Encoding)return t;var e=t,n=e.UTF8Scale;return null==n&&(n=1024),E(e.features,(function(t){var e=t.geometry,i=e.encodeOffsets,r=e.coordinates;if(i)switch(e.type){case"LineString":e.coordinates=P_(r,i,n);break;case"Polygon":case"MultiLineString":L_(r,i,n);break;case"MultiPolygon":E(r,(function(t,e){return L_(t,i[e],n)}))}})),e.UTF8Encoding=!1,e}(t)).features,(function(t){return t.geometry&&t.properties&&t.geometry.coordinates.length>0})),(function(t){var n=t.properties,i=t.geometry,r=[];switch(i.type){case"Polygon":var o=i.coordinates;r.push(new C_(o[0],o.slice(1)));break;case"MultiPolygon":E(i.coordinates,(function(t){t[0]&&r.push(new C_(t[0],t.slice(1)))}));break;case"LineString":r.push(new D_([i.coordinates]));break;case"MultiLineString":r.push(new D_(i.coordinates))}var a=new A_(n[e||"name"],r,n.cp);return a.properties=n,a}))}var R_=Object.freeze({__proto__:null,linearMap:Yr,round:Xr,asc:Zr,getPrecision:jr,getPrecisionSafe:qr,getPixelPrecision:Kr,getPercentWithPrecision:function(t,e,n){return t[e]&&$r(t,n)[e]||0},MAX_SAFE_INTEGER:Qr,remRadian:to,isRadianAroundZero:eo,parseDate:io,quantity:ro,quantityExponent:oo,nice:ao,quantile:so,reformIntervals:lo,isNumeric:ho,numericToNumber:uo}),N_=Object.freeze({__proto__:null,parse:io,format:jc}),E_=Object.freeze({__proto__:null,extendShape:Sh,extendPath:Ih,makePath:Dh,makeImage:Ah,mergePath:Lh,resizePath:Ph,createIcon:Wh,updateProps:dh,initProps:fh,getTransform:Nh,clipPointsByRect:Fh,clipRectByRect:Gh,registerShape:Th,getShapeClass:Ch,Group:Er,Image:As,Text:Bs,Circle:xu,Ellipse:bu,Sector:Eu,Ring:Vu,Polygon:Gu,Polyline:Hu,Rect:Es,Line:Xu,BezierCurve:Ku,Arc:Ju,IncrementalDisplayable:uh,CompoundPath:Qu,LinearGradient:eh,RadialGradient:nh,BoundingRect:Ee}),z_=Object.freeze({__proto__:null,addCommas:cp,toCamelCase:pp,normalizeCssArray:dp,encodeHTML:ie,formatTpl:vp,getTooltipMarker:mp,formatTime:function(t,e,n){"week"!==t&&"month"!==t&&"quarter"!==t&&"half-year"!==t&&"year"!==t||(t="MM-dd\nyyyy");var i=io(e),r=n?"getUTC":"get",o=i[r+"FullYear"](),a=i[r+"Month"]()+1,s=i[r+"Date"](),l=i[r+"Hours"](),u=i[r+"Minutes"](),h=i[r+"Seconds"](),c=i[r+"Milliseconds"]();return t=t.replace("MM",Uc(a,2)).replace("M",a).replace("yyyy",o).replace("yy",Uc(o%100+"",2)).replace("dd",Uc(s,2)).replace("d",s).replace("hh",Uc(l,2)).replace("h",l).replace("mm",Uc(u,2)).replace("m",u).replace("ss",Uc(h,2)).replace("s",h).replace("SSS",Uc(c,3))},capitalFirst:function(t){return t?t.charAt(0).toUpperCase()+t.substr(1):t},truncateText:aa,getTextRect:function(t,e,n,i,r,o,a,s){return new Bs({style:{text:t,font:e,align:n,verticalAlign:i,padding:r,rich:o,overflow:a?"truncate":null,lineHeight:s}}).getBoundingRect()}}),V_=Object.freeze({__proto__:null,map:z,each:E,indexOf:P,inherits:O,reduce:V,filter:B,bind:W,curry:H,isArray:Y,isString:X,isObject:q,isFunction:U,extend:A,defaults:k,clone:T,merge:C}),B_=Po();function F_(t){return"category"===t.type?function(t){var e=t.getLabelModel(),n=W_(t,e);return!e.get("show")||t.scale.isBlank()?{labels:[],labelCategoryInterval:n.labelCategoryInterval}:n}(t):function(t){var e=t.scale.getTicks(),n=p_(t);return{labels:z(e,(function(e,i){return{level:e.level,formattedLabel:n(e,i),rawLabel:t.scale.getLabel(e),tickValue:e.value}}))}}(t)}function G_(t,e){return"category"===t.type?function(t,e){var n,i,r=H_(t,"ticks"),o=g_(e),a=Y_(r,o);if(a)return a;e.get("show")&&!t.scale.isBlank()||(n=[]);if(U(o))n=Z_(t,o,!0);else if("auto"===o){var s=W_(t,t.getLabelModel());i=s.labelCategoryInterval,n=z(s.labels,(function(t){return t.tickValue}))}else n=X_(t,i=o,!0);return U_(r,o,{ticks:n,tickCategoryInterval:i})}(t,e):{ticks:z(t.scale.getTicks(),(function(t){return t.value}))}}function W_(t,e){var n,i,r=H_(t,"labels"),o=g_(e),a=Y_(r,o);return a||(U(o)?n=Z_(t,o):(i="auto"===o?function(t){var e=B_(t).autoInterval;return null!=e?e:B_(t).autoInterval=t.calculateCategoryInterval()}(t):o,n=X_(t,i)),U_(r,o,{labels:n,labelCategoryInterval:i}))}function H_(t,e){return B_(t)[e]||(B_(t)[e]=[])}function Y_(t,e){for(var n=0;n1&&h/l>2&&(u=Math.round(Math.ceil(u/l)*l));var c=y_(t),p=a.get("showMinLabel")||c,d=a.get("showMaxLabel")||c;p&&u!==o[0]&&g(o[0]);for(var f=u;f<=o[1];f+=l)g(f);function g(t){var e={value:t};s.push(n?t:{formattedLabel:i(e),rawLabel:r.getLabel(e),tickValue:t})}return d&&f-l!==o[1]&&g(o[1]),s}function Z_(t,e,n){var i=t.scale,r=p_(t),o=[];return E(i.getTicks(),(function(t){var a=i.getLabel(t),s=t.value;e(t.value,a)&&o.push(n?s:{formattedLabel:r(t),rawLabel:a,tickValue:s})})),o}var j_=[0,1],q_=function(){function t(t,e,n){this.onBand=!1,this.inverse=!1,this.dim=t,this.scale=e,this._extent=n||[0,0]}return t.prototype.contain=function(t){var e=this._extent,n=Math.min(e[0],e[1]),i=Math.max(e[0],e[1]);return t>=n&&t<=i},t.prototype.containData=function(t){return this.scale.contain(t)},t.prototype.getExtent=function(){return this._extent.slice()},t.prototype.getPixelPrecision=function(t){return Kr(t||this.scale.getExtent(),this._extent)},t.prototype.setExtent=function(t,e){var n=this._extent;n[0]=t,n[1]=e},t.prototype.dataToCoord=function(t,e){var n=this._extent,i=this.scale;return t=i.normalize(t),this.onBand&&"ordinal"===i.type&&K_(n=n.slice(),i.count()),Yr(t,j_,n,e)},t.prototype.coordToData=function(t,e){var n=this._extent,i=this.scale;this.onBand&&"ordinal"===i.type&&K_(n=n.slice(),i.count());var r=Yr(t,n,j_,e);return this.scale.scale(r)},t.prototype.pointToData=function(t,e){},t.prototype.getTicksCoords=function(t){var e=(t=t||{}).tickModel||this.getTickModel(),n=z(G_(this,e).ticks,(function(t){return{coord:this.dataToCoord("ordinal"===this.scale.type?this.scale.getRawOrdinalNumber(t):t),tickValue:t}}),this);return function(t,e,n,i){var r=e.length;if(!t.onBand||n||!r)return;var o,a,s=t.getExtent();if(1===r)e[0].coord=s[0],o=e[1]={coord:s[0]};else{var l=e[r-1].tickValue-e[0].tickValue,u=(e[r-1].coord-e[0].coord)/l;E(e,(function(t){t.coord-=u/2})),a=1+t.scale.getExtent()[1]-e[r-1].tickValue,o={coord:e[r-1].coord+u*a},e.push(o)}var h=s[0]>s[1];c(e[0].coord,s[0])&&(i?e[0].coord=s[0]:e.shift());i&&c(s[0],e[0].coord)&&e.unshift({coord:s[0]});c(s[1],o.coord)&&(i?o.coord=s[1]:e.pop());i&&c(o.coord,s[1])&&e.push({coord:s[1]});function c(t,e){return t=Xr(t),e=Xr(e),h?t>e:t0&&t<100||(t=5),z(this.scale.getMinorTicks(t),(function(t){return z(t,(function(t){return{coord:this.dataToCoord(t),tickValue:t}}),this)}),this)},t.prototype.getViewLabels=function(){return F_(this).labels},t.prototype.getLabelModel=function(){return this.model.getModel("axisLabel")},t.prototype.getTickModel=function(){return this.model.getModel("axisTick")},t.prototype.getBandWidth=function(){var t=this._extent,e=this.scale.getExtent(),n=e[1]-e[0]+(this.onBand?1:0);0===n&&(n=1);var i=Math.abs(t[1]-t[0]);return Math.abs(i)/n},t.prototype.calculateCategoryInterval=function(){return function(t){var e=function(t){var e=t.getLabelModel();return{axisRotate:t.getRotate?t.getRotate():t.isHorizontal&&!t.isHorizontal()?90:0,labelRotate:e.get("rotate")||0,font:e.getFont()}}(t),n=p_(t),i=(e.axisRotate-e.labelRotate)/180*Math.PI,r=t.scale,o=r.getExtent(),a=r.count();if(o[1]-o[0]<1)return 0;var s=1;a>40&&(s=Math.max(1,Math.floor(a/40)));for(var l=o[0],u=t.dataToCoord(l+1)-t.dataToCoord(l),h=Math.abs(u*Math.cos(i)),c=Math.abs(u*Math.sin(i)),p=0,d=0;l<=o[1];l+=s){var f,g,y=_r(n({value:l}),e.font,"center","top");f=1.3*y.width,g=1.3*y.height,p=Math.max(p,f,7),d=Math.max(d,g,7)}var v=p/h,m=d/c;isNaN(v)&&(v=1/0),isNaN(m)&&(m=1/0);var x=Math.max(0,Math.floor(Math.min(v,m))),_=B_(t.model),b=t.getExtent(),w=_.lastAutoInterval,S=_.lastTickCount;return null!=w&&null!=S&&Math.abs(w-x)<=1&&Math.abs(S-a)<=1&&w>x&&_.axisExtent0===b[0]&&_.axisExtent1===b[1]?x=w:(_.lastTickCount=a,_.lastAutoInterval=x,_.axisExtent0=b[0],_.axisExtent1=b[1]),x}(this)},t}();function K_(t,e){var n=(t[1]-t[0])/e/2;t[0]+=n,t[1]-=n}var $_=2*Math.PI,J_=rs.CMD,Q_=["top","right","bottom","left"];function tb(t,e,n,i,r){var o=n.width,a=n.height;switch(t){case"top":i.set(n.x+o/2,n.y-e),r.set(0,-1);break;case"bottom":i.set(n.x+o/2,n.y+a+e),r.set(0,1);break;case"left":i.set(n.x-e,n.y+a/2),r.set(-1,0);break;case"right":i.set(n.x+o+e,n.y+a/2),r.set(1,0)}}function eb(t,e,n,i,r,o,a,s,l){a-=t,s-=e;var u=Math.sqrt(a*a+s*s),h=(a/=u)*n+t,c=(s/=u)*n+e;if(Math.abs(i-r)%$_<1e-4)return l[0]=h,l[1]=c,u-n;if(o){var p=i;i=us(r),r=us(p)}else i=us(i),r=us(r);i>r&&(r+=$_);var d=Math.atan2(s,a);if(d<0&&(d+=$_),d>=i&&d<=r||d+$_>=i&&d+$_<=r)return l[0]=h,l[1]=c,u-n;var f=n*Math.cos(i)+t,g=n*Math.sin(i)+e,y=n*Math.cos(r)+t,v=n*Math.sin(r)+e,m=(f-a)*(f-a)+(g-s)*(g-s),x=(y-a)*(y-a)+(v-s)*(v-s);return m0){e=e/180*Math.PI,sb.fromArray(t[0]),lb.fromArray(t[1]),ub.fromArray(t[2]),Ce.sub(hb,sb,lb),Ce.sub(cb,ub,lb);var n=hb.len(),i=cb.len();if(!(n<.001||i<.001)){hb.scale(1/n),cb.scale(1/i);var r=hb.dot(cb);if(Math.cos(e)1&&Ce.copy(fb,ub),fb.toArray(t[1])}}}}function yb(t,e,n){if(n<=180&&n>0){n=n/180*Math.PI,sb.fromArray(t[0]),lb.fromArray(t[1]),ub.fromArray(t[2]),Ce.sub(hb,lb,sb),Ce.sub(cb,ub,lb);var i=hb.len(),r=cb.len();if(!(i<.001||r<.001))if(hb.scale(1/i),cb.scale(1/r),hb.dot(e)=a)Ce.copy(fb,ub);else{fb.scaleAndAdd(cb,o/Math.tan(Math.PI/2-s));var l=ub.x!==lb.x?(fb.x-lb.x)/(ub.x-lb.x):(fb.y-lb.y)/(ub.y-lb.y);if(isNaN(l))return;l<0?Ce.copy(fb,lb):l>1&&Ce.copy(fb,ub)}fb.toArray(t[1])}}}function vb(t,e,n,i){var r="normal"===n,o=r?t:t.ensureState(n);o.ignore=e;var a=i.get("smooth");a&&!0===a&&(a=.3),o.shape=o.shape||{},a>0&&(o.shape.smooth=a);var s=i.getModel("lineStyle").getLineStyle();r?t.useStyle(s):o.style=s}function mb(t,e){var n=e.smooth,i=e.points;if(i)if(t.moveTo(i[0][0],i[0][1]),n>0&&i.length>=3){var r=Vt(i[0],i[1]),o=Vt(i[1],i[2]);if(!r||!o)return t.lineTo(i[1][0],i[1][1]),void t.lineTo(i[2][0],i[2][1]);var a=Math.min(r,o)*n,s=Gt([],i[1],i[0],a/r),l=Gt([],i[1],i[2],a/o),u=Gt([],s,l,.5);t.bezierCurveTo(s[0],s[1],s[0],s[1],u[0],u[1]),t.bezierCurveTo(l[0],l[1],l[0],l[1],i[2][0],i[2][1])}else for(var h=1;h0&&o&&_(-h/a,0,a);var f,g,y=t[0],v=t[a-1];return m(),f<0&&b(-f,.8),g<0&&b(g,.8),m(),x(f,g,1),x(g,f,-1),m(),f<0&&w(-f),g<0&&w(g),u}function m(){f=y.rect[e]-i,g=r-v.rect[e]-v.rect[n]}function x(t,e,n){if(t<0){var i=Math.min(e,-t);if(i>0){_(i*n,0,a);var r=i+t;r<0&&b(-r*n,1)}else b(-t*n,1)}}function _(n,i,r){0!==n&&(u=!0);for(var o=i;o0)for(l=0;l0;l--){_(-(o[l-1]*c),l,a)}}}function w(t){var e=t<0?-1:1;t=Math.abs(t);for(var n=Math.ceil(t/(a-1)),i=0;i0?_(n,0,i+1):_(-n,a-i-1,a),(t-=n)<=0)return}}function Sb(t,e,n,i){return wb(t,"y","height",e,n,i)}function Mb(t){var e=[];t.sort((function(t,e){return e.priority-t.priority}));var n=new Ee(0,0,0,0);function i(t){if(!t.ignore){var e=t.ensureState("emphasis");null==e.ignore&&(e.ignore=!1)}t.ignore=!0}for(var r=0;r=0&&n.attr(d.oldLayoutSelect),P(u,"emphasis")>=0&&n.attr(d.oldLayoutEmphasis)),dh(n,s,e,a)}else if(n.attr(s),!lc(n).valueAnimation){var h=rt(n.style.opacity,1);n.style.opacity=0,fh(n,{style:{opacity:h}},e,a)}if(d.oldLayout=s,n.states.select){var c=d.oldLayoutSelect={};Lb(c,s,Pb),Lb(c,n.states.select,Pb)}if(n.states.emphasis){var p=d.oldLayoutEmphasis={};Lb(p,s,Pb),Lb(p,n.states.emphasis,Pb)}hc(n,a,l,e,e)}if(i&&!i.ignore&&!i.invisible){r=(d=kb(i)).oldLayout;var d,f={points:i.shape.points};r?(i.attr({shape:r}),dh(i,{shape:f},e)):(i.setShape(f),i.style.strokePercent=0,fh(i,{style:{strokePercent:1}},e)),d.oldLayout=f}},t}(),Rb=Po();var Nb=Math.sin,Eb=Math.cos,zb=Math.PI,Vb=2*Math.PI,Bb=180/zb,Fb=function(){function t(){}return t.prototype.reset=function(t){this._start=!0,this._d=[],this._str="",this._p=Math.pow(10,t||4)},t.prototype.moveTo=function(t,e){this._add("M",t,e)},t.prototype.lineTo=function(t,e){this._add("L",t,e)},t.prototype.bezierCurveTo=function(t,e,n,i,r,o){this._add("C",t,e,n,i,r,o)},t.prototype.quadraticCurveTo=function(t,e,n,i){this._add("Q",t,e,n,i)},t.prototype.arc=function(t,e,n,i,r,o){this.ellipse(t,e,n,n,0,i,r,o)},t.prototype.ellipse=function(t,e,n,i,r,o,a,s){var l=a-o,u=!s,h=Math.abs(l),c=ui(h-Vb)||(u?l>=Vb:-l>=Vb),p=l>0?l%Vb:l%Vb+Vb,d=!1;d=!!c||!ui(h)&&p>=zb==!!u;var f=t+n*Eb(o),g=e+i*Nb(o);this._start&&this._add("M",f,g);var y=Math.round(r*Bb);if(c){var v=1/this._p,m=(u?1:-1)*(Vb-v);this._add("A",n,i,y,1,+u,t+n*Eb(o+m),e+i*Nb(o+m)),v>.01&&this._add("A",n,i,y,0,+u,f,g)}else{var x=t+n*Eb(a),_=e+i*Nb(a);this._add("A",n,i,y,+d,+u,x,_)}},t.prototype.rect=function(t,e,n,i){this._add("M",t,e),this._add("l",n,0),this._add("l",0,i),this._add("l",-n,0),this._add("Z")},t.prototype.closePath=function(){this._d.length>0&&this._add("Z")},t.prototype._add=function(t,e,n,i,r,o,a,s,l){for(var u=[],h=this._p,c=1;c"}(r,e.attrs)+ie(e.text)+(i?""+n+z(i,(function(e){return t(e)})).join(n)+n:"")+("")}(t)}function $b(t){return{zrId:t,shadowCache:{},patternCache:{},gradientCache:{},clipPathCache:{},defs:{},cssNodes:{},cssAnims:{},cssClassIdx:0,cssAnimIdx:0,shadowIdx:0,gradientIdx:0,patternIdx:0,clipPathIdx:0}}function Jb(t,e,n,i){return qb("svg","root",{width:t,height:e,xmlns:Xb,"xmlns:xlink":Zb,version:"1.1",baseProfile:"full",viewBox:!!i&&"0 0 "+t+" "+e},n)}var Qb={cubicIn:"0.32,0,0.67,0",cubicOut:"0.33,1,0.68,1",cubicInOut:"0.65,0,0.35,1",quadraticIn:"0.11,0,0.5,0",quadraticOut:"0.5,1,0.89,1",quadraticInOut:"0.45,0,0.55,1",quarticIn:"0.5,0,0.75,0",quarticOut:"0.25,1,0.5,1",quarticInOut:"0.76,0,0.24,1",quinticIn:"0.64,0,0.78,0",quinticOut:"0.22,1,0.36,1",quinticInOut:"0.83,0,0.17,1",sinusoidalIn:"0.12,0,0.39,0",sinusoidalOut:"0.61,1,0.88,1",sinusoidalInOut:"0.37,0,0.63,1",exponentialIn:"0.7,0,0.84,0",exponentialOut:"0.16,1,0.3,1",exponentialInOut:"0.87,0,0.13,1",circularIn:"0.55,0,1,0.45",circularOut:"0,0.55,0.45,1",circularInOut:"0.85,0,0.15,1"},tw="transform-origin";function ew(t,e,n){var i=A({},t.shape);A(i,e),t.buildPath(n,i);var r=new Fb;return r.reset(xi(t)),n.rebuildPath(r,1),r.generateStr(),r.getStr()}function nw(t,e){var n=e.originX,i=e.originY;(n||i)&&(t[tw]=n+"px "+i+"px")}var iw={fill:"fill",opacity:"opacity",lineWidth:"stroke-width",lineDashOffset:"stroke-dashoffset"};function rw(t,e){var n=e.zrId+"-ani-"+e.cssAnimIdx++;return e.cssAnims[n]=t,n}function ow(t){return X(t)?Qb[t]?"cubic-bezier("+Qb[t]+")":Ln(t)?t:"":""}function aw(t,e,n,i){var r=t.animators,o=r.length,a=[];if(t instanceof Qu){var s=function(t,e,n){var i,r,o=t.shape.paths,a={};if(E(o,(function(t){var e=$b(n.zrId);e.animation=!0,aw(t,{},e,!0);var o=e.cssAnims,s=e.cssNodes,l=G(o),u=l.length;if(u){var h=o[r=l[u-1]];for(var c in h){var p=h[c];a[c]=a[c]||{d:""},a[c].d+=p.d||""}for(var d in s){var f=s[d].animation;f.indexOf(r)>=0&&(i=f)}}})),i){e.d=!1;var s=rw(a,n);return i.replace(r,s)}}(t,e,n);if(s)a.push(s);else if(!o)return}else if(!o)return;for(var l={},u=0;u0})).length)return rw(h,n)+" "+r[0]+" both"}for(var y in l){(s=g(l[y]))&&a.push(s)}if(a.length){var v=n.zrId+"-cls-"+n.cssClassIdx++;n.cssNodes["."+v]={animation:a.join(",")},e.class=v}}var sw=Math.round;function lw(t){return t&&X(t.src)}function uw(t){return t&&U(t.toDataURL)}function hw(t,e,n,i){Ub((function(r,o){var a="fill"===r||"stroke"===r;a&&vi(o)?_w(e,t,r,i):a&&fi(o)?bw(n,t,r,i):t[r]=o}),e,n,!1),function(t,e,n){var i=t.style;if(function(t){return t&&(t.shadowBlur||t.shadowOffsetX||t.shadowOffsetY)}(i)){var r=function(t){var e=t.style,n=t.getGlobalScale();return[e.shadowColor,(e.shadowBlur||0).toFixed(2),(e.shadowOffsetX||0).toFixed(2),(e.shadowOffsetY||0).toFixed(2),n[0],n[1]].join(",")}(t),o=n.shadowCache,a=o[r];if(!a){var s=t.getGlobalScale(),l=s[0],u=s[1];if(!l||!u)return;var h=i.shadowOffsetX||0,c=i.shadowOffsetY||0,p=i.shadowBlur,d=si(i.shadowColor),f=d.opacity,g=d.color,y=p/2/l+" "+p/2/u;a=n.zrId+"-s"+n.shadowIdx++,n.defs[a]=qb("filter",a,{id:a,x:"-100%",y:"-100%",width:"300%",height:"300%"},[qb("feDropShadow","",{dx:h/l,dy:c/u,stdDeviation:y,"flood-color":g,"flood-opacity":f})]),o[r]=a}e.filter=mi(a)}}(n,t,i)}function cw(t){return ui(t[0]-1)&&ui(t[1])&&ui(t[2])&&ui(t[3]-1)}function pw(t,e,n){if(e&&(!function(t){return ui(t[4])&&ui(t[5])}(e)||!cw(e))){var i=n?10:1e4;t.transform=cw(e)?"translate("+sw(e[4]*i)/i+" "+sw(e[5]*i)/i+")":function(t){return"matrix("+hi(t[0])+","+hi(t[1])+","+hi(t[2])+","+hi(t[3])+","+ci(t[4])+","+ci(t[5])+")"}(e)}}function dw(t,e,n){for(var i=t.points,r=[],o=0;ol?Ew(t,null==n[c+1]?null:n[c+1].elm,n,s,c):zw(t,e,a,l))}(n,i,r):Pw(r)?(Pw(t.text)&&Aw(n,""),Ew(n,null,r,0,r.length-1)):Pw(i)?zw(n,i,0,i.length-1):Pw(t.text)&&Aw(n,""):t.text!==e.text&&(Pw(i)&&zw(n,i,0,i.length-1),Aw(n,e.text)))}var Fw=0,Gw=function(){function t(t,e,n){if(this.type="svg",this.refreshHover=Ww("refreshHover"),this.configLayer=Ww("configLayer"),this.storage=e,this._opts=n=A({},n),this.root=t,this._id="zr"+Fw++,this._oldVNode=Jb(n.width,n.height),t&&!n.ssr){var i=this._viewport=document.createElement("div");i.style.cssText="position:relative;overflow:hidden";var r=this._svgDom=this._oldVNode.elm=jb("svg");Vw(null,this._oldVNode),i.appendChild(r),t.appendChild(i)}this.resize(n.width,n.height)}return t.prototype.getType=function(){return this.type},t.prototype.getViewportRoot=function(){return this._viewport},t.prototype.getViewportRootOffset=function(){var t=this.getViewportRoot();if(t)return{offsetLeft:t.offsetLeft||0,offsetTop:t.offsetTop||0}},t.prototype.getSvgDom=function(){return this._svgDom},t.prototype.refresh=function(){if(this.root){var t=this.renderToVNode({willUpdate:!0});t.attrs.style="position:absolute;left:0;top:0;user-select:none",function(t,e){if(Rw(t,e))Bw(t,e);else{var n=t.elm,i=Cw(n);Nw(e),null!==i&&(Mw(i,e.elm,Dw(n)),zw(i,[t],0,0))}}(this._oldVNode,t),this._oldVNode=t}},t.prototype.renderOneToVNode=function(t){return xw(t,$b(this._id))},t.prototype.renderToVNode=function(t){t=t||{};var e=this.storage.getDisplayList(!0),n=this._width,i=this._height,r=$b(this._id);r.animation=t.animation,r.willUpdate=t.willUpdate,r.compress=t.compress;var o=[],a=this._bgVNode=function(t,e,n,i){var r;if(n&&"none"!==n)if(r=qb("rect","bg",{width:t,height:e,x:"0",y:"0",id:"0"}),vi(n))_w({fill:n},r.attrs,"fill",i);else if(fi(n))bw({style:{fill:n},dirty:bt,getBoundingRect:function(){return{width:t,height:e}}},r.attrs,"fill",i);else{var o=si(n),a=o.color,s=o.opacity;r.attrs.fill=a,s<1&&(r.attrs["fill-opacity"]=s)}return r}(n,i,this._backgroundColor,r);a&&o.push(a);var s=t.compress?null:this._mainVNode=qb("g","main",{},[]);this._paintList(e,r,s?s.children:o),s&&o.push(s);var l=z(G(r.defs),(function(t){return r.defs[t]}));if(l.length&&o.push(qb("defs","defs",{},l)),t.animation){var u=function(t,e,n){var i=(n=n||{}).newline?"\n":"",r=" {"+i,o=i+"}",a=z(G(t),(function(e){return e+r+z(G(t[e]),(function(n){return n+":"+t[e][n]+";"})).join(i)+o})).join(i),s=z(G(e),(function(t){return"@keyframes "+t+r+z(G(e[t]),(function(n){return n+r+z(G(e[t][n]),(function(i){var r=e[t][n][i];return"d"===i&&(r='path("'+r+'")'),i+":"+r+";"})).join(i)+o})).join(i)+o})).join(i);return a||s?[""].join(i):""}(r.cssNodes,r.cssAnims,{newline:!0});if(u){var h=qb("style","stl",{},[],u);o.push(h)}}return Jb(n,i,o,t.useViewBox)},t.prototype.renderToString=function(t){return t=t||{},Kb(this.renderToVNode({animation:rt(t.cssAnimation,!0),willUpdate:!1,compress:!0,useViewBox:rt(t.useViewBox,!0)}),{newline:!0})},t.prototype.setBackgroundColor=function(t){this._backgroundColor=t},t.prototype.getSvgRoot=function(){return this._mainVNode&&this._mainVNode.elm},t.prototype._paintList=function(t,e,n){for(var i,r,o=t.length,a=[],s=0,l=0,u=0;u=0&&(!c||!r||c[f]!==r[f]);f--);for(var g=d-1;g>f;g--)i=a[--s-1];for(var y=f+1;y=a)}}for(var h=this.__startIndex;h15)break}n.prevElClipPaths&&u.restore()};if(p)if(0===p.length)s=l.__endIndex;else for(var _=d.dpr,b=0;b0&&t>i[0]){for(s=0;st);s++);a=n[i[s]]}if(i.splice(s+1,0,t),n[t]=e,!e.virtual)if(a){var l=a.dom;l.nextSibling?o.insertBefore(e.dom,l.nextSibling):o.appendChild(e.dom)}else o.firstChild?o.insertBefore(e.dom,o.firstChild):o.appendChild(e.dom);e.__painter=this}},t.prototype.eachLayer=function(t,e){for(var n=this._zlevelList,i=0;i0?Zw:0),this._needsManuallyCompositing),u.__builtin__||I("ZLevel "+l+" has been used by unkown layer "+u.id),u!==o&&(u.__used=!0,u.__startIndex!==r&&(u.__dirty=!0),u.__startIndex=r,u.incremental?u.__drawIndex=-1:u.__drawIndex=r,e(r),o=u),1&s.__dirty&&!s.__inHover&&(u.__dirty=!0,u.incremental&&u.__drawIndex<0&&(u.__drawIndex=r))}e(r),this.eachBuiltinLayer((function(t,e){!t.__used&&t.getElementCount()>0&&(t.__dirty=!0,t.__startIndex=t.__endIndex=t.__drawIndex=0),t.__dirty&&t.__drawIndex<0&&(t.__drawIndex=t.__startIndex)}))},t.prototype.clear=function(){return this.eachBuiltinLayer(this._clearLayer),this},t.prototype._clearLayer=function(t){t.clear()},t.prototype.setBackgroundColor=function(t){this._backgroundColor=t,E(this._layers,(function(t){t.setUnpainted()}))},t.prototype.configLayer=function(t,e){if(e){var n=this._layerConfig;n[t]?C(n[t],e,!0):n[t]=e;for(var i=0;i-1&&(s.style.stroke=s.style.fill,s.style.fill="#fff",s.style.lineWidth=2),e},e.type="series.line",e.dependencies=["grid","polar"],e.defaultOption={z:3,coordinateSystem:"cartesian2d",legendHoverLink:!0,clip:!0,label:{position:"top"},endLabel:{show:!1,valueAnimation:!0,distance:8},lineStyle:{width:2,type:"solid"},emphasis:{scale:!0},step:!1,smooth:!1,smoothMonotone:null,symbol:"emptyCircle",symbolSize:4,symbolRotate:null,showSymbol:!0,showAllSymbol:"auto",connectNulls:!1,sampling:"none",animationEasing:"linear",progressive:0,hoverLayerThreshold:1/0,universalTransition:{divideShape:"clone"},triggerLineEvent:!1},e}(fg);function Kw(t,e){var n=t.mapDimensionsAll("defaultedLabel"),i=n.length;if(1===i){var r=df(t,e,n[0]);return null!=r?r+"":null}if(i){for(var o=[],a=0;a=0&&i.push(e[o])}return i.join(" ")}var Jw=function(t){function e(e,n,i,r){var o=t.call(this)||this;return o.updateData(e,n,i,r),o}return n(e,t),e.prototype._createSymbol=function(t,e,n,i,r){this.removeAll();var o=Vy(t,-1,-1,2,2,null,r);o.attr({z2:100,culling:!0,scaleX:i[0]/2,scaleY:i[1]/2}),o.drift=Qw,this._symbolType=t,this.add(o)},e.prototype.stopSymbolAnimation=function(t){this.childAt(0).stopAnimation(null,t)},e.prototype.getSymbolType=function(){return this._symbolType},e.prototype.getSymbolPath=function(){return this.childAt(0)},e.prototype.highlight=function(){Al(this.childAt(0))},e.prototype.downplay=function(){kl(this.childAt(0))},e.prototype.setZ=function(t,e){var n=this.childAt(0);n.zlevel=t,n.z=e},e.prototype.setDraggable=function(t,e){var n=this.childAt(0);n.draggable=t,n.cursor=!e&&t?"move":n.cursor},e.prototype.updateData=function(t,n,i,r){this.silent=!1;var o=t.getItemVisual(n,"symbol")||"circle",a=t.hostModel,s=e.getSymbolSize(t,n),l=o!==this._symbolType,u=r&&r.disableAnimation;if(l){var h=t.getItemVisual(n,"symbolKeepAspect");this._createSymbol(o,t,n,s,h)}else{(p=this.childAt(0)).silent=!1;var c={scaleX:s[0]/2,scaleY:s[1]/2};u?p.attr(c):dh(p,c,a,n),xh(p)}if(this._updateCommon(t,n,s,i,r),l){var p=this.childAt(0);if(!u){c={scaleX:this._sizeX,scaleY:this._sizeY,style:{opacity:p.style.opacity}};p.scaleX=p.scaleY=0,p.style.opacity=0,fh(p,c,a,n)}}u&&this.childAt(0).stopAnimation("leave")},e.prototype._updateCommon=function(t,e,n,i,r){var o,a,s,l,u,h,c,p,d,f=this.childAt(0),g=t.hostModel;if(i&&(o=i.emphasisItemStyle,a=i.blurItemStyle,s=i.selectItemStyle,l=i.focus,u=i.blurScope,c=i.labelStatesModels,p=i.hoverScale,d=i.cursorStyle,h=i.emphasisDisabled),!i||t.hasItemOption){var y=i&&i.itemModel?i.itemModel:t.getItemModel(e),v=y.getModel("emphasis");o=v.getModel("itemStyle").getItemStyle(),s=y.getModel(["select","itemStyle"]).getItemStyle(),a=y.getModel(["blur","itemStyle"]).getItemStyle(),l=v.get("focus"),u=v.get("blurScope"),h=v.get("disabled"),c=tc(y),p=v.getShallow("scale"),d=y.getShallow("cursor")}var m=t.getItemVisual(e,"symbolRotate");f.attr("rotation",(m||0)*Math.PI/180||0);var x=Fy(t.getItemVisual(e,"symbolOffset"),n);x&&(f.x=x[0],f.y=x[1]),d&&f.attr("cursor",d);var _=t.getItemVisual(e,"style"),b=_.fill;if(f instanceof As){var w=f.style;f.useStyle(A({image:w.image,x:w.x,y:w.y,width:w.width,height:w.height},_))}else f.__isEmptyBrush?f.useStyle(A({},_)):f.useStyle(_),f.style.decal=null,f.setColor(b,r&&r.symbolInnerColor),f.style.strokeNoScale=!0;var S=t.getItemVisual(e,"liftZ"),M=this._z2;null!=S?null==M&&(this._z2=f.z2,f.z2+=S):null!=M&&(f.z2=M,this._z2=null);var I=r&&r.useNameLabel;Qh(f,c,{labelFetcher:g,labelDataIndex:e,defaultText:function(e){return I?t.getName(e):Kw(t,e)},inheritColor:b,defaultOpacity:_.opacity}),this._sizeX=n[0]/2,this._sizeY=n[1]/2;var T=f.ensureState("emphasis");T.style=o,f.ensureState("select").style=s,f.ensureState("blur").style=a;var C=null==p||!0===p?Math.max(1.1,3/this._sizeY):isFinite(p)&&p>0?+p:1;T.scaleX=this._sizeX*C,T.scaleY=this._sizeY*C,this.setSymbolScale(1),Hl(this,l,u,h)},e.prototype.setSymbolScale=function(t){this.scaleX=this.scaleY=t},e.prototype.fadeOut=function(t,e,n){var i=this.childAt(0),r=Js(this).dataIndex,o=n&&n.animation;if(this.silent=i.silent=!0,n&&n.fadeLabel){var a=i.getTextContent();a&&yh(a,{style:{opacity:0}},e,{dataIndex:r,removeOpt:o,cb:function(){i.removeTextContent()}})}else i.removeTextContent();yh(i,{style:{opacity:0},scaleX:0,scaleY:0},e,{dataIndex:r,cb:t,removeOpt:o})},e.getSymbolSize=function(t,e){return By(t.getItemVisual(e,"symbolSize"))},e}(Er);function Qw(t,e){this.parent.drift(t,e)}function tS(t,e,n,i){return e&&!isNaN(e[0])&&!isNaN(e[1])&&!(i.isIgnore&&i.isIgnore(n))&&!(i.clipShape&&!i.clipShape.contain(e[0],e[1]))&&"none"!==t.getItemVisual(n,"symbol")}function eS(t){return null==t||q(t)||(t={isIgnore:t}),t||{}}function nS(t){var e=t.hostModel,n=e.getModel("emphasis");return{emphasisItemStyle:n.getModel("itemStyle").getItemStyle(),blurItemStyle:e.getModel(["blur","itemStyle"]).getItemStyle(),selectItemStyle:e.getModel(["select","itemStyle"]).getItemStyle(),focus:n.get("focus"),blurScope:n.get("blurScope"),emphasisDisabled:n.get("disabled"),hoverScale:n.get("scale"),labelStatesModels:tc(e),cursorStyle:e.get("cursor")}}var iS=function(){function t(t){this.group=new Er,this._SymbolCtor=t||Jw}return t.prototype.updateData=function(t,e){this._progressiveEls=null,e=eS(e);var n=this.group,i=t.hostModel,r=this._data,o=this._SymbolCtor,a=e.disableAnimation,s=nS(t),l={disableAnimation:a},u=e.getSymbolPoint||function(e){return t.getItemLayout(e)};r||n.removeAll(),t.diff(r).add((function(i){var r=u(i);if(tS(t,r,i,e)){var a=new o(t,i,s,l);a.setPosition(r),t.setItemGraphicEl(i,a),n.add(a)}})).update((function(h,c){var p=r.getItemGraphicEl(c),d=u(h);if(tS(t,d,h,e)){var f=t.getItemVisual(h,"symbol")||"circle",g=p&&p.getSymbolType&&p.getSymbolType();if(!p||g&&g!==f)n.remove(p),(p=new o(t,h,s,l)).setPosition(d);else{p.updateData(t,h,s,l);var y={x:d[0],y:d[1]};a?p.attr(y):dh(p,y,i)}n.add(p),t.setItemGraphicEl(h,p)}else n.remove(p)})).remove((function(t){var e=r.getItemGraphicEl(t);e&&e.fadeOut((function(){n.remove(e)}),i)})).execute(),this._getSymbolPoint=u,this._data=t},t.prototype.updateLayout=function(){var t=this,e=this._data;e&&e.eachItemGraphicEl((function(e,n){var i=t._getSymbolPoint(n);e.setPosition(i),e.markRedraw()}))},t.prototype.incrementalPrepareUpdate=function(t){this._seriesScope=nS(t),this._data=null,this.group.removeAll()},t.prototype.incrementalUpdate=function(t,e,n){function i(t){t.isGroup||(t.incremental=!0,t.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[],n=eS(n);for(var r=t.start;r0?n=i[0]:i[1]<0&&(n=i[1]);return n}(r,n),a=i.dim,s=r.dim,l=e.mapDimension(s),u=e.mapDimension(a),h="x"===s||"radius"===s?1:0,c=z(t.dimensions,(function(t){return e.mapDimension(t)})),p=!1,d=e.getCalculationInfo("stackResultDimension");return lx(e,c[0])&&(p=!0,c[0]=d),lx(e,c[1])&&(p=!0,c[1]=d),{dataDimsForPoint:c,valueStart:o,valueAxisDim:s,baseAxisDim:a,stacked:!!p,valueDim:l,baseDim:u,baseDataOffset:h,stackedOverDimension:e.getCalculationInfo("stackedOverDimension")}}function oS(t,e,n,i){var r=NaN;t.stacked&&(r=n.get(n.getCalculationInfo("stackedOverDimension"),i)),isNaN(r)&&(r=t.valueStart);var o=t.baseDataOffset,a=[];return a[o]=n.get(t.baseDim,i),a[1-o]=r,e.dataToPoint(a)}var aS=Math.min,sS=Math.max;function lS(t,e){return isNaN(t)||isNaN(e)}function uS(t,e,n,i,r,o,a,s,l){for(var u,h,c,p,d,f,g=n,y=0;y=r||g<0)break;if(lS(v,m)){if(l){g+=o;continue}break}if(g===n)t[o>0?"moveTo":"lineTo"](v,m),c=v,p=m;else{var x=v-u,_=m-h;if(x*x+_*_<.5){g+=o;continue}if(a>0){for(var b=g+o,w=e[2*b],S=e[2*b+1];w===v&&S===m&&y=i||lS(w,S))d=v,f=m;else{T=w-u,C=S-h;var k=v-u,L=w-v,P=m-h,O=S-m,R=void 0,N=void 0;if("x"===s){var E=T>0?1:-1;d=v-E*(R=Math.abs(k))*a,f=m,D=v+E*(N=Math.abs(L))*a,A=m}else if("y"===s){var z=C>0?1:-1;d=v,f=m-z*(R=Math.abs(P))*a,D=v,A=m+z*(N=Math.abs(O))*a}else R=Math.sqrt(k*k+P*P),d=v-T*a*(1-(I=(N=Math.sqrt(L*L+O*O))/(N+R))),f=m-C*a*(1-I),A=m+C*a*I,D=aS(D=v+T*a*I,sS(w,v)),A=aS(A,sS(S,m)),D=sS(D,aS(w,v)),f=m-(C=(A=sS(A,aS(S,m)))-m)*R/N,d=aS(d=v-(T=D-v)*R/N,sS(u,v)),f=aS(f,sS(h,m)),D=v+(T=v-(d=sS(d,aS(u,v))))*N/R,A=m+(C=m-(f=sS(f,aS(h,m))))*N/R}t.bezierCurveTo(c,p,d,f,v,m),c=D,p=A}else t.lineTo(v,m)}u=v,h=m,g+=o}return y}var hS=function(){this.smooth=0,this.smoothConstraint=!0},cS=function(t){function e(e){var n=t.call(this,e)||this;return n.type="ec-polyline",n}return n(e,t),e.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},e.prototype.getDefaultShape=function(){return new hS},e.prototype.buildPath=function(t,e){var n=e.points,i=0,r=n.length/2;if(e.connectNulls){for(;r>0&&lS(n[2*r-2],n[2*r-1]);r--);for(;i=0){var y=a?(h-i)*g+i:(u-n)*g+n;return a?[t,y]:[y,t]}n=u,i=h;break;case o.C:u=r[l++],h=r[l++],c=r[l++],p=r[l++],d=r[l++],f=r[l++];var v=a?xn(n,u,c,d,t,s):xn(i,h,p,f,t,s);if(v>0)for(var m=0;m=0){y=a?vn(i,h,p,f,x):vn(n,u,c,d,x);return a?[t,y]:[y,t]}}n=d,i=f}}},e}(Ms),pS=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e}(hS),dS=function(t){function e(e){var n=t.call(this,e)||this;return n.type="ec-polygon",n}return n(e,t),e.prototype.getDefaultShape=function(){return new pS},e.prototype.buildPath=function(t,e){var n=e.points,i=e.stackedOnPoints,r=0,o=n.length/2,a=e.smoothMonotone;if(e.connectNulls){for(;o>0&&lS(n[2*o-2],n[2*o-1]);o--);for(;r=0;a--){var s=t.getDimensionInfo(i[a].dimension);if("x"===(r=s&&s.coordDim)||"y"===r){o=i[a];break}}if(o){var l=e.getAxis(r),u=z(o.stops,(function(t){return{coord:l.toGlobalCoord(l.dataToCoord(t.value)),color:t.color}})),h=u.length,c=o.outerColors.slice();h&&u[0].coord>u[h-1].coord&&(u.reverse(),c.reverse());var p=function(t,e){var n,i,r=[],o=t.length;function a(t,e,n){var i=t.coord;return{coord:n,color:Qn((n-i)/(e.coord-i),[t.color,e.color])}}for(var s=0;se){i?r.push(a(i,l,e)):n&&r.push(a(n,l,0),a(n,l,e));break}n&&(r.push(a(n,l,0)),n=null),r.push(l),i=l}}return r}(u,"x"===r?n.getWidth():n.getHeight()),d=p.length;if(!d&&h)return u[0].coord<0?c[1]?c[1]:u[h-1].color:c[0]?c[0]:u[0].color;var f=p[0].coord-10,g=p[d-1].coord+10,y=g-f;if(y<.001)return"transparent";E(p,(function(t){t.offset=(t.coord-f)/y})),p.push({offset:d?p[d-1].offset:.5,color:c[1]||"transparent"}),p.unshift({offset:d?p[0].offset:.5,color:c[0]||"transparent"});var v=new eh(0,0,0,0,p,!0);return v[r]=f,v[r+"2"]=g,v}}}function MS(t,e,n){var i=t.get("showAllSymbol"),r="auto"===i;if(!i||r){var o=n.getAxesByScale("ordinal")[0];if(o&&(!r||!function(t,e){var n=t.getExtent(),i=Math.abs(n[1]-n[0])/t.scale.count();isNaN(i)&&(i=0);for(var r=e.count(),o=Math.max(1,Math.round(r/5)),a=0;ai)return!1;return!0}(o,e))){var a=e.mapDimension(o.dim),s={};return E(o.getViewLabels(),(function(t){var e=o.scale.getRawOrdinalNumber(t.tickValue);s[e]=1})),function(t){return!s.hasOwnProperty(e.get(a,t))}}}}function IS(t,e){return[t[2*e],t[2*e+1]]}function TS(t){if(t.get(["endLabel","show"]))return!0;for(var e=0;e0&&"bolder"===t.get(["emphasis","lineStyle","width"]))&&(d.getState("emphasis").style.lineWidth=+d.style.lineWidth+1);Js(d).seriesIndex=t.seriesIndex,Hl(d,L,P,O);var R=bS(t.get("smooth")),N=t.get("smoothMonotone");if(d.setShape({smooth:R,smoothMonotone:N,connectNulls:w}),f){var E=a.getCalculationInfo("stackedOnSeries"),z=0;f.useStyle(k(l.getAreaStyle(),{fill:C,opacity:.7,lineJoin:"bevel",decal:a.getVisual("style").decal})),E&&(z=bS(E.get("smooth"))),f.setShape({smooth:R,stackedOnSmooth:z,smoothMonotone:N,connectNulls:w}),Zl(f,t,"areaStyle"),Js(f).seriesIndex=t.seriesIndex,Hl(f,L,P,O)}var V=function(t){i._changePolyState(t)};a.eachItemGraphicEl((function(t){t&&(t.onHoverStateChange=V)})),this._polyline.onHoverStateChange=V,this._data=a,this._coordSys=r,this._stackedOnPoints=_,this._points=u,this._step=T,this._valueOrigin=m,t.get("triggerLineEvent")&&(this.packEventData(t,d),f&&this.packEventData(t,f))},e.prototype.packEventData=function(t,e){Js(e).eventData={componentType:"series",componentSubType:"line",componentIndex:t.componentIndex,seriesIndex:t.seriesIndex,seriesName:t.name,seriesType:"line"}},e.prototype.highlight=function(t,e,n,i){var r=t.getData(),o=Lo(r,i);if(this._changePolyState("emphasis"),!(o instanceof Array)&&null!=o&&o>=0){var a=r.getLayout("points"),s=r.getItemGraphicEl(o);if(!s){var l=a[2*o],u=a[2*o+1];if(isNaN(l)||isNaN(u))return;if(this._clipShapeForSymbol&&!this._clipShapeForSymbol.contain(l,u))return;var h=t.get("zlevel")||0,c=t.get("z")||0;(s=new Jw(r,o)).x=l,s.y=u,s.setZ(h,c);var p=s.getSymbolPath().getTextContent();p&&(p.zlevel=h,p.z=c,p.z2=this._polyline.z2+1),s.__temp=!0,r.setItemGraphicEl(o,s),s.stopSymbolAnimation(!0),this.group.add(s)}s.highlight()}else Tg.prototype.highlight.call(this,t,e,n,i)},e.prototype.downplay=function(t,e,n,i){var r=t.getData(),o=Lo(r,i);if(this._changePolyState("normal"),null!=o&&o>=0){var a=r.getItemGraphicEl(o);a&&(a.__temp?(r.setItemGraphicEl(o,null),this.group.remove(a)):a.downplay())}else Tg.prototype.downplay.call(this,t,e,n,i)},e.prototype._changePolyState=function(t){var e=this._polygon;Ml(this._polyline,t),e&&Ml(e,t)},e.prototype._newPolyline=function(t){var e=this._polyline;return e&&this._lineGroup.remove(e),e=new cS({shape:{points:t},segmentIgnoreThreshold:2,z2:10}),this._lineGroup.add(e),this._polyline=e,e},e.prototype._newPolygon=function(t,e){var n=this._polygon;return n&&this._lineGroup.remove(n),n=new dS({shape:{points:t,stackedOnPoints:e},segmentIgnoreThreshold:2}),this._lineGroup.add(n),this._polygon=n,n},e.prototype._initSymbolLabelAnimation=function(t,e,n){var i,r,o=e.getBaseAxis(),a=o.inverse;"cartesian2d"===e.type?(i=o.isHorizontal(),r=!1):"polar"===e.type&&(i="angle"===o.dim,r=!0);var s=t.hostModel,l=s.get("animationDuration");U(l)&&(l=l(null));var u=s.get("animationDelay")||0,h=U(u)?u(null):u;t.eachItemGraphicEl((function(t,o){var s=t;if(s){var c=[t.x,t.y],p=void 0,d=void 0,f=void 0;if(n)if(r){var g=n,y=e.pointToCoord(c);i?(p=g.startAngle,d=g.endAngle,f=-y[1]/180*Math.PI):(p=g.r0,d=g.r,f=y[0])}else{var v=n;i?(p=v.x,d=v.x+v.width,f=t.x):(p=v.y+v.height,d=v.y,f=t.y)}var m=d===p?0:(f-p)/(d-p);a&&(m=1-m);var x=U(u)?u(o):l*m+h,_=s.getSymbolPath(),b=_.getTextContent();s.attr({scaleX:0,scaleY:0}),s.animateTo({scaleX:1,scaleY:1},{duration:200,setToFinal:!0,delay:x}),b&&b.animateFrom({style:{opacity:0}},{duration:300,delay:x}),_.disableLabelAnimation=!0}}))},e.prototype._initOrUpdateEndLabel=function(t,e,n){var i=t.getModel("endLabel");if(TS(t)){var r=t.getData(),o=this._polyline,a=r.getLayout("points");if(!a)return o.removeTextContent(),void(this._endLabel=null);var s=this._endLabel;s||((s=this._endLabel=new Bs({z2:200})).ignoreClip=!0,o.setTextContent(this._endLabel),o.disableLabelAnimation=!0);var l=function(t){for(var e,n,i=t.length/2;i>0&&(e=t[2*i-2],n=t[2*i-1],isNaN(e)||isNaN(n));i--);return i-1}(a);l>=0&&(Qh(o,tc(t,"endLabel"),{inheritColor:n,labelFetcher:t,labelDataIndex:l,defaultText:function(t,e,n){return null!=n?$w(r,n):Kw(r,t)},enableTextSetter:!0},function(t,e){var n=e.getBaseAxis(),i=n.isHorizontal(),r=n.inverse,o=i?r?"right":"left":"center",a=i?"middle":r?"top":"bottom";return{normal:{align:t.get("align")||o,verticalAlign:t.get("verticalAlign")||a}}}(i,e)),o.textConfig.position=null)}else this._endLabel&&(this._polyline.removeTextContent(),this._endLabel=null)},e.prototype._endLabelOnDuring=function(t,e,n,i,r,o,a){var s=this._endLabel,l=this._polyline;if(s){t<1&&null==i.originalX&&(i.originalX=s.x,i.originalY=s.y);var u=n.getLayout("points"),h=n.hostModel,c=h.get("connectNulls"),p=o.get("precision"),d=o.get("distance")||0,f=a.getBaseAxis(),g=f.isHorizontal(),y=f.inverse,v=e.shape,m=y?g?v.x:v.y+v.height:g?v.x+v.width:v.y,x=(g?d:0)*(y?-1:1),_=(g?0:-d)*(y?-1:1),b=g?"x":"y",w=function(t,e,n){for(var i,r,o=t.length/2,a="x"===n?0:1,s=0,l=-1,u=0;u=e||i>=e&&r<=e){l=u;break}s=u,i=r}else i=r;return{range:[s,l],t:(e-i)/(r-i)}}(u,m,b),S=w.range,M=S[1]-S[0],I=void 0;if(M>=1){if(M>1&&!c){var T=IS(u,S[0]);s.attr({x:T[0]+x,y:T[1]+_}),r&&(I=h.getRawValue(S[0]))}else{(T=l.getPointOn(m,b))&&s.attr({x:T[0]+x,y:T[1]+_});var C=h.getRawValue(S[0]),D=h.getRawValue(S[1]);r&&(I=Go(n,p,C,D,w.t))}i.lastFrameIndex=S[0]}else{var A=1===t||i.lastFrameIndex>0?S[0]:0;T=IS(u,A);r&&(I=h.getRawValue(A)),s.attr({x:T[0]+x,y:T[1]+_})}r&&lc(s).setLabelText(I)}},e.prototype._doUpdateAnimation=function(t,e,n,i,r,o,a){var s=this._polyline,l=this._polygon,u=t.hostModel,h=function(t,e,n,i,r,o,a,s){for(var l=function(t,e){var n=[];return e.diff(t).add((function(t){n.push({cmd:"+",idx:t})})).update((function(t,e){n.push({cmd:"=",idx:e,idx1:t})})).remove((function(t){n.push({cmd:"-",idx:t})})).execute(),n}(t,e),u=[],h=[],c=[],p=[],d=[],f=[],g=[],y=rS(r,e,a),v=t.getLayout("points")||[],m=e.getLayout("points")||[],x=0;x3e3||l&&_S(p,f)>3e3)return s.stopAnimation(),s.setShape({points:d}),void(l&&(l.stopAnimation(),l.setShape({points:d,stackedOnPoints:f})));s.shape.__points=h.current,s.shape.points=c;var g={shape:{points:d}};h.current!==c&&(g.shape.__points=h.next),s.stopAnimation(),dh(s,g,u),l&&(l.setShape({points:c,stackedOnPoints:p}),l.stopAnimation(),dh(l,{shape:{stackedOnPoints:f}},u),s.shape.points!==l.shape.points&&(l.shape.points=s.shape.points));for(var y=[],v=h.status,m=0;me&&(e=t[n]);return isFinite(e)?e:NaN},min:function(t){for(var e=1/0,n=0;n10&&"cartesian2d"===o.type&&r){var s=o.getBaseAxis(),l=o.getOtherAxis(s),u=s.getExtent(),h=n.getDevicePixelRatio(),c=Math.abs(u[1]-u[0])*(h||1),p=Math.round(a/c);if(isFinite(p)&&p>1){"lttb"===r&&t.setData(i.lttbDownSample(i.mapDimension(l.dim),1/p));var d=void 0;X(r)?d=kS[r]:U(r)&&(d=r),d&&t.setData(i.downSample(i.mapDimension(l.dim),1/p,d,LS))}}}}}var OS=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.getInitialData=function(t,e){return hx(null,this,{useEncodeDefaulter:!0})},e.prototype.getMarkerPosition=function(t,e,n){var i=this.coordinateSystem;if(i&&i.clampData){var r=i.dataToPoint(i.clampData(t));if(n)E(i.getAxes(),(function(n,o){if("category"===n.type){var a=n.getTicksCoords(),s=i.clampData(t)[o];!e||"x1"!==e[o]&&"y1"!==e[o]||(s+=1),s>a.length-1&&(s=a.length-1),s<0&&(s=0),a[s]&&(r[o]=n.toGlobalCoord(a[s].coord))}}));else{var o=this.getData(),a=o.getLayout("offset"),s=o.getLayout("size"),l=i.getBaseAxis().isHorizontal()?0:1;r[l]+=a+s/2}return r}return[NaN,NaN]},e.type="series.__base_bar__",e.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,barMinHeight:0,barMinAngle:0,large:!1,largeThreshold:400,progressive:3e3,progressiveChunkMode:"mod"},e}(fg);fg.registerClass(OS);var RS=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.getInitialData=function(){return hx(null,this,{useEncodeDefaulter:!0,createInvertedIndices:!!this.get("realtimeSort",!0)||null})},e.prototype.getProgressive=function(){return!!this.get("large")&&this.get("progressive")},e.prototype.getProgressiveThreshold=function(){var t=this.get("progressiveThreshold"),e=this.get("largeThreshold");return e>t&&(t=e),t},e.prototype.brushSelector=function(t,e,n){return n.rect(e.getItemLayout(t))},e.type="series.bar",e.dependencies=["grid","polar"],e.defaultOption=Tc(OS.defaultOption,{clip:!0,roundCap:!1,showBackground:!1,backgroundStyle:{color:"rgba(180, 180, 180, 0.2)",borderColor:null,borderWidth:0,borderType:"solid",borderRadius:0,shadowBlur:0,shadowColor:null,shadowOffsetX:0,shadowOffsetY:0,opacity:1},select:{itemStyle:{borderColor:"#212121"}},realtimeSort:!1}),e}(OS),NS=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0},ES=function(t){function e(e){var n=t.call(this,e)||this;return n.type="sausage",n}return n(e,t),e.prototype.getDefaultShape=function(){return new NS},e.prototype.buildPath=function(t,e){var n=e.cx,i=e.cy,r=Math.max(e.r0||0,0),o=Math.max(e.r,0),a=.5*(o-r),s=r+a,l=e.startAngle,u=e.endAngle,h=e.clockwise,c=2*Math.PI,p=h?u-lo)return!0;o=u}return!1},e.prototype._isOrderDifferentInView=function(t,e){for(var n=e.scale,i=n.getExtent(),r=Math.max(0,i[0]),o=Math.min(i[1],n.getOrdinalMeta().categories.length-1);r<=o;++r)if(t.ordinalNumbers[r]!==n.getRawOrdinalNumber(r))return!0},e.prototype._updateSortWithinSameData=function(t,e,n,i){if(this._isOrderChangedWithinSameData(t,e,n)){var r=this._dataSort(t,n,e);this._isOrderDifferentInView(r,n)&&(this._removeOnRenderedListener(i),i.dispatchAction({type:"changeAxisOrder",componentType:n.dim+"Axis",axisId:n.index,sortInfo:r}))}},e.prototype._dispatchInitSort=function(t,e,n){var i=e.baseAxis,r=this._dataSort(t,i,(function(n){return t.get(t.mapDimension(e.otherAxis.dim),n)}));n.dispatchAction({type:"changeAxisOrder",componentType:i.dim+"Axis",isInitSort:!0,axisId:i.index,sortInfo:r})},e.prototype.remove=function(t,e){this._clear(this._model),this._removeOnRenderedListener(e)},e.prototype.dispose=function(t,e){this._removeOnRenderedListener(e)},e.prototype._removeOnRenderedListener=function(t){this._onRendered&&(t.getZr().off("rendered",this._onRendered),this._onRendered=null)},e.prototype._clear=function(t){var e=this.group,n=this._data;t&&t.isAnimationEnabled()&&n&&!this._isLargeDraw?(this._removeBackground(),this._backgroundEls=[],n.eachItemGraphicEl((function(e){mh(e,t,Js(e).dataIndex)}))):e.removeAll(),this._data=null,this._isFirstFrame=!0},e.prototype._removeBackground=function(){this.group.remove(this._backgroundGroup),this._backgroundGroup=null},e.type="bar",e}(Tg),WS={cartesian2d:function(t,e){var n=e.width<0?-1:1,i=e.height<0?-1:1;n<0&&(e.x+=e.width,e.width=-e.width),i<0&&(e.y+=e.height,e.height=-e.height);var r=t.x+t.width,o=t.y+t.height,a=BS(e.x,t.x),s=FS(e.x+e.width,r),l=BS(e.y,t.y),u=FS(e.y+e.height,o),h=sr?s:a,e.y=c&&l>o?u:l,e.width=h?0:s-a,e.height=c?0:u-l,n<0&&(e.x+=e.width,e.width=-e.width),i<0&&(e.y+=e.height,e.height=-e.height),h||c},polar:function(t,e){var n=e.r0<=e.r?1:-1;if(n<0){var i=e.r;e.r=e.r0,e.r0=i}var r=FS(e.r,t.r),o=BS(e.r0,t.r0);e.r=r,e.r0=o;var a=r-o<0;if(n<0){i=e.r;e.r=e.r0,e.r0=i}return a}},HS={cartesian2d:function(t,e,n,i,r,o,a,s,l){var u=new Es({shape:A({},i),z2:1});(u.__dataIndex=n,u.name="item",o)&&(u.shape[r?"height":"width"]=0);return u},polar:function(t,e,n,i,r,o,a,s,l){var u=!r&&l?ES:Eu,h=new u({shape:i,z2:1});h.name="item";var c,p,d=KS(r);if(h.calculateTextPosition=(c=d,p=({isRoundCap:u===ES}||{}).isRoundCap,function(t,e,n){var i=e.position;if(!i||i instanceof Array)return Ir(t,e,n);var r=c(i),o=null!=e.distance?e.distance:5,a=this.shape,s=a.cx,l=a.cy,u=a.r,h=a.r0,d=(u+h)/2,f=a.startAngle,g=a.endAngle,y=(f+g)/2,v=p?Math.abs(u-h)/2:0,m=Math.cos,x=Math.sin,_=s+u*m(f),b=l+u*x(f),w="left",S="top";switch(r){case"startArc":_=s+(h-o)*m(y),b=l+(h-o)*x(y),w="center",S="top";break;case"insideStartArc":_=s+(h+o)*m(y),b=l+(h+o)*x(y),w="center",S="bottom";break;case"startAngle":_=s+d*m(f)+zS(f,o+v,!1),b=l+d*x(f)+VS(f,o+v,!1),w="right",S="middle";break;case"insideStartAngle":_=s+d*m(f)+zS(f,-o+v,!1),b=l+d*x(f)+VS(f,-o+v,!1),w="left",S="middle";break;case"middle":_=s+d*m(y),b=l+d*x(y),w="center",S="middle";break;case"endArc":_=s+(u+o)*m(y),b=l+(u+o)*x(y),w="center",S="bottom";break;case"insideEndArc":_=s+(u-o)*m(y),b=l+(u-o)*x(y),w="center",S="top";break;case"endAngle":_=s+d*m(g)+zS(g,o+v,!0),b=l+d*x(g)+VS(g,o+v,!0),w="left",S="middle";break;case"insideEndAngle":_=s+d*m(g)+zS(g,-o+v,!0),b=l+d*x(g)+VS(g,-o+v,!0),w="right",S="middle";break;default:return Ir(t,e,n)}return(t=t||{}).x=_,t.y=b,t.align=w,t.verticalAlign=S,t}),o){var f=r?"r":"endAngle",g={};h.shape[f]=r?0:i.startAngle,g[f]=i[f],(s?dh:fh)(h,{shape:g},o)}return h}};function YS(t,e,n,i,r,o,a,s){var l,u;o?(u={x:i.x,width:i.width},l={y:i.y,height:i.height}):(u={y:i.y,height:i.height},l={x:i.x,width:i.width}),s||(a?dh:fh)(n,{shape:l},e,r,null),(a?dh:fh)(n,{shape:u},e?t.baseAxis.model:null,r)}function US(t,e){for(var n=0;n0?1:-1,a=i.height>0?1:-1;return{x:i.x+o*r/2,y:i.y+a*r/2,width:i.width-o*r,height:i.height-a*r}},polar:function(t,e,n){var i=t.getItemLayout(e);return{cx:i.cx,cy:i.cy,r0:i.r0,r:i.r,startAngle:i.startAngle,endAngle:i.endAngle,clockwise:i.clockwise}}};function KS(t){return function(t){var e=t?"Arc":"Angle";return function(t){switch(t){case"start":case"insideStart":case"end":case"insideEnd":return t+e;default:return t}}}(t)}function $S(t,e,n,i,r,o,a,s){var l=e.getItemVisual(n,"style");s||t.setShape("r",i.get(["itemStyle","borderRadius"])||0),t.useStyle(l);var u=i.getShallow("cursor");u&&t.attr("cursor",u);var h=s?a?r.r>=r.r0?"endArc":"startArc":r.endAngle>=r.startAngle?"endAngle":"startAngle":a?r.height>=0?"bottom":"top":r.width>=0?"right":"left",c=tc(i);Qh(t,c,{labelFetcher:o,labelDataIndex:n,defaultText:Kw(o.getData(),n),inheritColor:l.fill,defaultOpacity:l.opacity,defaultOutsidePosition:h});var p=t.getTextContent();if(s&&p){var d=i.get(["label","position"]);t.textConfig.inside="middle"===d||null,function(t,e,n,i){if(j(i))t.setTextConfig({rotation:i});else if(Y(e))t.setTextConfig({rotation:0});else{var r,o=t.shape,a=o.clockwise?o.startAngle:o.endAngle,s=o.clockwise?o.endAngle:o.startAngle,l=(a+s)/2,u=n(e);switch(u){case"startArc":case"insideStartArc":case"middle":case"insideEndArc":case"endArc":r=l;break;case"startAngle":case"insideStartAngle":r=a;break;case"endAngle":case"insideEndAngle":r=s;break;default:return void t.setTextConfig({rotation:0})}var h=1.5*Math.PI-r;"middle"===u&&h>Math.PI/2&&h<1.5*Math.PI&&(h-=Math.PI),t.setTextConfig({rotation:h})}}(t,"outside"===d?h:d,KS(a),i.get(["label","rotate"]))}uc(p,c,o.getRawValue(n),(function(t){return $w(e,t)}));var f=i.getModel(["emphasis"]);Hl(t,f.get("focus"),f.get("blurScope"),f.get("disabled")),Zl(t,i),function(t){return null!=t.startAngle&&null!=t.endAngle&&t.startAngle===t.endAngle}(r)&&(t.style.fill="none",t.style.stroke="none",E(t.states,(function(t){t.style&&(t.style.fill=t.style.stroke="none")})))}var JS=function(){},QS=function(t){function e(e){var n=t.call(this,e)||this;return n.type="largeBar",n}return n(e,t),e.prototype.getDefaultShape=function(){return new JS},e.prototype.buildPath=function(t,e){for(var n=e.points,i=this.baseDimIdx,r=1-this.baseDimIdx,o=[],a=[],s=this.barWidth,l=0;l=s[0]&&e<=s[0]+l[0]&&n>=s[1]&&n<=s[1]+l[1])return a[h]}return-1}(this,t.offsetX,t.offsetY);Js(this).dataIndex=e>=0?e:null}),30,!1);function nM(t,e,n){if(vS(n,"cartesian2d")){var i=e,r=n.getArea();return{x:t?i.x:r.x,y:t?r.y:i.y,width:t?i.width:r.width,height:t?r.height:i.height}}var o=e;return{cx:(r=n.getArea()).cx,cy:r.cy,r0:t?r.r0:o.r0,r:t?r.r:o.r,startAngle:t?o.startAngle:0,endAngle:t?o.endAngle:2*Math.PI}}var iM=2*Math.PI,rM=Math.PI/180;function oM(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}function aM(t,e){var n=oM(t,e),i=t.get("center"),r=t.get("radius");Y(r)||(r=[0,r]);var o,a,s=Ur(n.width,e.getWidth()),l=Ur(n.height,e.getHeight()),u=Math.min(s,l),h=Ur(r[0],u/2),c=Ur(r[1],u/2),p=t.coordinateSystem;if(p){var d=p.dataToPoint(i);o=d[0]||0,a=d[1]||0}else Y(i)||(i=[i,i]),o=Ur(i[0],s)+n.x,a=Ur(i[1],l)+n.y;return{cx:o,cy:a,r0:h,r:c}}function sM(t,e,n){e.eachSeriesByType(t,(function(t){var e=t.getData(),i=e.mapDimension("value"),r=oM(t,n),o=aM(t,n),a=o.cx,s=o.cy,l=o.r,u=o.r0,h=-t.get("startAngle")*rM,c=t.get("minAngle")*rM,p=0;e.each(i,(function(t){!isNaN(t)&&p++}));var d=e.getSum(i),f=Math.PI/(d||p)*2,g=t.get("clockwise"),y=t.get("roseType"),v=t.get("stillShowZeroSum"),m=e.getDataExtent(i);m[0]=0;var x=iM,_=0,b=h,w=g?1:-1;if(e.setLayout({viewRect:r,r:l}),e.each(i,(function(t,n){var i;if(isNaN(t))e.setItemLayout(n,{angle:NaN,startAngle:NaN,endAngle:NaN,clockwise:g,cx:a,cy:s,r0:u,r:y?NaN:l});else{(i="area"!==y?0===d&&v?f:t*f:iM/p)n?a:o,h=Math.abs(l.label.y-n);if(h>=u.maxY){var c=l.label.x-e-l.len2*r,p=i+l.len,f=Math.abs(c)t.unconstrainedWidth?null:d:null;i.setStyle("width",f)}var g=i.getBoundingRect();o.width=g.width;var y=(i.style.margin||0)+2.1;o.height=g.height+y,o.y-=(o.height-c)/2}}}function pM(t){return"center"===t.position}function dM(t){var e,n,i=t.getData(),r=[],o=!1,a=(t.get("minShowLabelAngle")||0)*uM,s=i.getLayout("viewRect"),l=i.getLayout("r"),u=s.width,h=s.x,c=s.y,p=s.height;function d(t){t.ignore=!0}i.each((function(t){var s=i.getItemGraphicEl(t),c=s.shape,p=s.getTextContent(),f=s.getTextGuideLine(),g=i.getItemModel(t),y=g.getModel("label"),v=y.get("position")||g.get(["emphasis","label","position"]),m=y.get("distanceToLabelLine"),x=y.get("alignTo"),_=Ur(y.get("edgeDistance"),u),b=y.get("bleedMargin"),w=g.getModel("labelLine"),S=w.get("length");S=Ur(S,u);var M=w.get("length2");if(M=Ur(M,u),Math.abs(c.endAngle-c.startAngle)0?"right":"left":k>0?"left":"right"}var B=Math.PI,F=0,G=y.get("rotate");if(j(G))F=G*(B/180);else if("center"===v)F=0;else if("radial"===G||!0===G){F=k<0?-A+B:-A}else if("tangential"===G&&"outside"!==v&&"outer"!==v){var W=Math.atan2(k,L);W<0&&(W=2*B+W),L>0&&(W=B+W),F=W-B}if(o=!!F,p.x=I,p.y=T,p.rotation=F,p.setStyle({verticalAlign:"middle"}),P){p.setStyle({align:D});var H=p.states.select;H&&(H.x+=p.x,H.y+=p.y)}else{var Y=p.getBoundingRect().clone();Y.applyTransform(p.getComputedTransform());var U=(p.style.margin||0)+2.1;Y.y-=U/2,Y.height+=U,r.push({label:p,labelLine:f,position:v,len:S,len2:M,minTurnAngle:w.get("minTurnAngle"),maxSurfaceAngle:w.get("maxSurfaceAngle"),surfaceNormal:new Ce(k,L),linePoints:C,textAlign:D,labelDistance:m,labelAlignTo:x,edgeDistance:_,bleedMargin:b,rect:Y,unconstrainedWidth:Y.width,labelStyleWidth:p.style.width})}s.setTextConfig({inside:P})}})),!o&&t.get("avoidLabelOverlap")&&function(t,e,n,i,r,o,a,s){for(var l=[],u=[],h=Number.MAX_VALUE,c=-Number.MAX_VALUE,p=0;p0){for(var l=o.getItemLayout(0),u=1;isNaN(l&&l.startAngle)&&u=n.r0}},e.type="pie",e}(Tg);function vM(t,e,n){e=Y(e)&&{coordDimensions:e}||A({encodeDefine:t.getEncode()},e);var i=t.getSource(),r=nx(i,e).dimensions,o=new ex(r,t);return o.initData(i,n),o}var mM=function(){function t(t,e){this._getDataWithEncodedVisual=t,this._getRawData=e}return t.prototype.getAllNames=function(){var t=this._getRawData();return t.mapArray(t.getName)},t.prototype.containName=function(t){return this._getRawData().indexOfName(t)>=0},t.prototype.indexOfName=function(t){return this._getDataWithEncodedVisual().indexOfName(t)},t.prototype.getItemVisual=function(t,e){return this._getDataWithEncodedVisual().getItemVisual(t,e)},t}(),xM=Po(),_M=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.init=function(e){t.prototype.init.apply(this,arguments),this.legendVisualProvider=new mM(W(this.getData,this),W(this.getRawData,this)),this._defaultLabelLine(e)},e.prototype.mergeOption=function(){t.prototype.mergeOption.apply(this,arguments)},e.prototype.getInitialData=function(){return vM(this,{coordDimensions:["value"],encodeDefaulter:H($p,this)})},e.prototype.getDataParams=function(e){var n=this.getData(),i=xM(n),r=i.seats;if(!r){var o=[];n.each(n.mapDimension("value"),(function(t){o.push(t)})),r=i.seats=$r(o,n.hostModel.get("percentPrecision"))}var a=t.prototype.getDataParams.call(this,e);return a.percent=r[e]||0,a.$vars.push("percent"),a},e.prototype._defaultLabelLine=function(t){bo(t,"labelLine",["show"]);var e=t.labelLine,n=t.emphasis.labelLine;e.show=e.show&&t.label.show,n.show=n.show&&t.emphasis.label.show},e.type="series.pie",e.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,minAngle:0,minShowLabelAngle:0,selectedOffset:10,percentPrecision:2,stillShowZeroSum:!0,left:0,top:0,right:0,bottom:0,width:null,height:null,label:{rotate:0,show:!0,overflow:"truncate",position:"outer",alignTo:"none",edgeDistance:"25%",bleedMargin:10,distanceToLabelLine:5},labelLine:{show:!0,length:15,length2:15,smooth:!1,minTurnAngle:90,maxSurfaceAngle:90,lineStyle:{width:1,type:"solid"}},itemStyle:{borderWidth:1,borderJoin:"round"},showEmptyCircle:!0,emptyCircleStyle:{color:"lightgray",opacity:1},labelLayout:{hideOverlap:!0},emphasis:{scale:!0,scaleSize:5},avoidLabelOverlap:!0,animationType:"expansion",animationDuration:1e3,animationTypeUpdate:"transition",animationEasingUpdate:"cubicInOut",animationDurationUpdate:500,animationEasing:"cubicInOut"},e}(fg);var bM=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.hasSymbolVisual=!0,n}return n(e,t),e.prototype.getInitialData=function(t,e){return hx(null,this,{useEncodeDefaulter:!0})},e.prototype.getProgressive=function(){var t=this.option.progressive;return null==t?this.option.large?5e3:this.get("progressive"):t},e.prototype.getProgressiveThreshold=function(){var t=this.option.progressiveThreshold;return null==t?this.option.large?1e4:this.get("progressiveThreshold"):t},e.prototype.brushSelector=function(t,e,n){return n.point(e.getItemLayout(t))},e.prototype.getZLevelKey=function(){return this.getData().count()>this.getProgressiveThreshold()?this.id:""},e.type="series.scatter",e.dependencies=["grid","polar","geo","singleAxis","calendar"],e.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,symbolSize:10,large:!1,largeThreshold:2e3,itemStyle:{opacity:.8},emphasis:{scale:!0},clip:!0,select:{itemStyle:{borderColor:"#212121"}},universalTransition:{divideShape:"clone"}},e}(fg),wM=function(){},SM=function(t){function e(e){var n=t.call(this,e)||this;return n._off=0,n.hoverDataIdx=-1,n}return n(e,t),e.prototype.getDefaultShape=function(){return new wM},e.prototype.reset=function(){this.notClear=!1,this._off=0},e.prototype.buildPath=function(t,e){var n,i=e.points,r=e.size,o=this.symbolProxy,a=o.shape,s=t.getContext?t.getContext():t,l=s&&r[0]<4,u=this.softClipShape;if(l)this._ctx=s;else{for(this._ctx=null,n=this._off;n=0;s--){var l=2*s,u=i[l]-o/2,h=i[l+1]-a/2;if(t>=u&&e>=h&&t<=u+o&&e<=h+a)return s}return-1},e.prototype.contain=function(t,e){var n=this.transformCoordToLocal(t,e),i=this.getBoundingRect();return t=n[0],e=n[1],i.contain(t,e)?(this.hoverDataIdx=this.findDataIndex(t,e))>=0:(this.hoverDataIdx=-1,!1)},e.prototype.getBoundingRect=function(){var t=this._rect;if(!t){for(var e=this.shape,n=e.points,i=e.size,r=i[0],o=i[1],a=1/0,s=1/0,l=-1/0,u=-1/0,h=0;h=0&&(l.dataIndex=n+(t.startIndex||0))}))},t.prototype.remove=function(){this._clear()},t.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},t}(),IM=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData();this._updateSymbolDraw(i,t).updateData(i,{clipShape:this._getClipShape(t)}),this._finished=!0},e.prototype.incrementalPrepareRender=function(t,e,n){var i=t.getData();this._updateSymbolDraw(i,t).incrementalPrepareUpdate(i),this._finished=!1},e.prototype.incrementalRender=function(t,e,n){this._symbolDraw.incrementalUpdate(t,e.getData(),{clipShape:this._getClipShape(e)}),this._finished=t.end===e.getData().count()},e.prototype.updateTransform=function(t,e,n){var i=t.getData();if(this.group.dirty(),!this._finished||i.count()>1e4)return{update:!0};var r=AS("").reset(t,e,n);r.progress&&r.progress({start:0,end:i.count(),count:i.count()},i),this._symbolDraw.updateLayout(i)},e.prototype.eachRendered=function(t){this._symbolDraw&&this._symbolDraw.eachRendered(t)},e.prototype._getClipShape=function(t){var e=t.coordinateSystem,n=e&&e.getArea&&e.getArea();return t.get("clip",!0)?n:null},e.prototype._updateSymbolDraw=function(t,e){var n=this._symbolDraw,i=e.pipelineContext.large;return n&&i===this._isLargeDraw||(n&&n.remove(),n=this._symbolDraw=i?new MM:new iS,this._isLargeDraw=i,this.group.removeAll()),this.group.add(n.group),n},e.prototype.remove=function(t,e){this._symbolDraw&&this._symbolDraw.remove(!0),this._symbolDraw=null},e.prototype.dispose=function(){},e.type="scatter",e}(Tg),TM=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.type="grid",e.dependencies=["xAxis","yAxis"],e.layoutMode="box",e.defaultOption={show:!1,z:0,left:"10%",top:60,right:"10%",bottom:70,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"},e}(Op),CM=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.getCoordSysModel=function(){return this.getReferringComponents("grid",Eo).models[0]},e.type="cartesian2dAxis",e}(Op);R(CM,m_);var DM={show:!0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#6E7079",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#E0E6F1"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.2)","rgba(210,219,238,0.2)"]}}},AM=C({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},DM),kM=C({boundaryGap:[0,0],axisLine:{show:"auto"},axisTick:{show:"auto"},splitNumber:5,minorTick:{show:!1,splitNumber:5,length:3,lineStyle:{}},minorSplitLine:{show:!1,lineStyle:{color:"#F4F7FD",width:1}}},DM),LM={category:AM,value:kM,time:C({splitNumber:6,axisLabel:{showMinLabel:!1,showMaxLabel:!1,rich:{primary:{fontWeight:"bold"}}},splitLine:{show:!1}},kM),log:k({logBase:10},kM)},PM={value:1,category:1,time:1,log:1};function OM(t,e,i,r){E(PM,(function(o,a){var s=C(C({},LM[a],!0),r,!0),l=function(t){function i(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e+"Axis."+a,n}return n(i,t),i.prototype.mergeDefaultAndTheme=function(t,e){var n=Dp(this),i=n?kp(t):{};C(t,e.getTheme().get(a+"Axis")),C(t,this.getDefaultOption()),t.type=RM(t),n&&Ap(t,i,n)},i.prototype.optionUpdated=function(){"category"===this.option.type&&(this.__ordinalMeta=dx.createByAxisModel(this))},i.prototype.getCategories=function(t){var e=this.option;if("category"===e.type)return t?e.data:this.__ordinalMeta.categories},i.prototype.getOrdinalMeta=function(){return this.__ordinalMeta},i.type=e+"Axis."+a,i.defaultOption=s,i}(i);t.registerComponentModel(l)})),t.registerSubTypeDefaulter(e+"Axis",RM)}function RM(t){return t.type||(t.data?"category":"value")}var NM=function(){function t(t){this.type="cartesian",this._dimList=[],this._axes={},this.name=t||""}return t.prototype.getAxis=function(t){return this._axes[t]},t.prototype.getAxes=function(){return z(this._dimList,(function(t){return this._axes[t]}),this)},t.prototype.getAxesByScale=function(t){return t=t.toLowerCase(),B(this.getAxes(),(function(e){return e.scale.type===t}))},t.prototype.addAxis=function(t){var e=t.dim;this._axes[e]=t,this._dimList.push(e)},t}(),EM=["x","y"];function zM(t){return"interval"===t.type||"time"===t.type}var VM=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="cartesian2d",e.dimensions=EM,e}return n(e,t),e.prototype.calcAffineTransform=function(){this._transform=this._invTransform=null;var t=this.getAxis("x").scale,e=this.getAxis("y").scale;if(zM(t)&&zM(e)){var n=t.getExtent(),i=e.getExtent(),r=this.dataToPoint([n[0],i[0]]),o=this.dataToPoint([n[1],i[1]]),a=n[1]-n[0],s=i[1]-i[0];if(a&&s){var l=(o[0]-r[0])/a,u=(o[1]-r[1])/s,h=r[0]-n[0]*l,c=r[1]-i[0]*u,p=this._transform=[l,0,0,u,h,c];this._invTransform=Me([],p)}}},e.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAxis("x")},e.prototype.containPoint=function(t){var e=this.getAxis("x"),n=this.getAxis("y");return e.contain(e.toLocalCoord(t[0]))&&n.contain(n.toLocalCoord(t[1]))},e.prototype.containData=function(t){return this.getAxis("x").containData(t[0])&&this.getAxis("y").containData(t[1])},e.prototype.containZone=function(t,e){var n=this.dataToPoint(t),i=this.dataToPoint(e),r=this.getArea(),o=new Ee(n[0],n[1],i[0]-n[0],i[1]-n[1]);return r.intersect(o)},e.prototype.dataToPoint=function(t,e,n){n=n||[];var i=t[0],r=t[1];if(this._transform&&null!=i&&isFinite(i)&&null!=r&&isFinite(r))return Wt(n,t,this._transform);var o=this.getAxis("x"),a=this.getAxis("y");return n[0]=o.toGlobalCoord(o.dataToCoord(i,e)),n[1]=a.toGlobalCoord(a.dataToCoord(r,e)),n},e.prototype.clampData=function(t,e){var n=this.getAxis("x").scale,i=this.getAxis("y").scale,r=n.getExtent(),o=i.getExtent(),a=n.parse(t[0]),s=i.parse(t[1]);return(e=e||[])[0]=Math.min(Math.max(Math.min(r[0],r[1]),a),Math.max(r[0],r[1])),e[1]=Math.min(Math.max(Math.min(o[0],o[1]),s),Math.max(o[0],o[1])),e},e.prototype.pointToData=function(t,e){var n=[];if(this._invTransform)return Wt(n,t,this._invTransform);var i=this.getAxis("x"),r=this.getAxis("y");return n[0]=i.coordToData(i.toLocalCoord(t[0]),e),n[1]=r.coordToData(r.toLocalCoord(t[1]),e),n},e.prototype.getOtherAxis=function(t){return this.getAxis("x"===t.dim?"y":"x")},e.prototype.getArea=function(){var t=this.getAxis("x").getGlobalExtent(),e=this.getAxis("y").getGlobalExtent(),n=Math.min(t[0],t[1]),i=Math.min(e[0],e[1]),r=Math.max(t[0],t[1])-n,o=Math.max(e[0],e[1])-i;return new Ee(n,i,r,o)},e}(NM),BM=function(t){function e(e,n,i,r,o){var a=t.call(this,e,n,i)||this;return a.index=0,a.type=r||"value",a.position=o||"bottom",a}return n(e,t),e.prototype.isHorizontal=function(){var t=this.position;return"top"===t||"bottom"===t},e.prototype.getGlobalExtent=function(t){var e=this.getExtent();return e[0]=this.toGlobalCoord(e[0]),e[1]=this.toGlobalCoord(e[1]),t&&e[0]>e[1]&&e.reverse(),e},e.prototype.pointToData=function(t,e){return this.coordToData(this.toLocalCoord(t["x"===this.dim?0:1]),e)},e.prototype.setCategorySortInfo=function(t){if("category"!==this.type)return!1;this.model.option.categorySortInfo=t,this.scale.setSortInfo(t)},e}(q_);function FM(t,e,n){n=n||{};var i=t.coordinateSystem,r=e.axis,o={},a=r.getAxesOnZeroOf()[0],s=r.position,l=a?"onZero":s,u=r.dim,h=i.getRect(),c=[h.x,h.x+h.width,h.y,h.y+h.height],p={left:0,right:1,top:0,bottom:1,onZero:2},d=e.get("offset")||0,f="x"===u?[c[2]-d,c[3]+d]:[c[0]-d,c[1]+d];if(a){var g=a.toGlobalCoord(a.dataToCoord(0));f[p.onZero]=Math.max(Math.min(g,f[1]),f[0])}o.position=["y"===u?f[p[l]]:c[0],"x"===u?f[p[l]]:c[3]],o.rotation=Math.PI/2*("x"===u?0:1);o.labelDirection=o.tickDirection=o.nameDirection={top:-1,bottom:1,left:-1,right:1}[s],o.labelOffset=a?f[p[s]]-f[p.onZero]:0,e.get(["axisTick","inside"])&&(o.tickDirection=-o.tickDirection),it(n.labelInside,e.get(["axisLabel","inside"]))&&(o.labelDirection=-o.labelDirection);var y=e.get(["axisLabel","rotate"]);return o.labelRotate="top"===l?-y:y,o.z2=1,o}function GM(t){return"cartesian2d"===t.get("coordinateSystem")}function WM(t){var e={xAxisModel:null,yAxisModel:null};return E(e,(function(n,i){var r=i.replace(/Model$/,""),o=t.getReferringComponents(r,Eo).models[0];e[i]=o})),e}var HM=Math.log;function YM(t,e,n){var i=Tx.prototype,r=i.getTicks.call(n),o=i.getTicks.call(n,!0),a=r.length-1,s=i.getInterval.call(n),l=u_(t,e),u=l.extent,h=l.fixMin,c=l.fixMax;if("log"===t.type){var p=HM(t.base);u=[HM(u[0])/p,HM(u[1])/p]}t.setExtent(u[0],u[1]),t.calcNiceExtent({splitNumber:a,fixMin:h,fixMax:c});var d=i.getExtent.call(t);h&&(u[0]=d[0]),c&&(u[1]=d[1]);var f=i.getInterval.call(t),g=u[0],y=u[1];if(h&&c)f=(y-g)/a;else if(h)for(y=u[0]+f*a;yu[0]&&isFinite(g)&&isFinite(u[0]);)f=vx(f),g=u[1]-f*a;else{t.getTicks().length-1>a&&(f=vx(f));var v=f*a;(g=Xr((y=Math.ceil(u[1]/f)*f)-v))<0&&u[0]>=0?(g=0,y=Xr(v)):y>0&&u[1]<=0&&(y=0,g=-Xr(v))}var m=(r[0].value-o[0].value)/s,x=(r[a].value-o[a].value)/s;i.setExtent.call(t,g+f*m,y+f*x),i.setInterval.call(t,f),(m||x)&&i.setNiceExtent.call(t,g+f,y-f)}var UM=function(){function t(t,e,n){this.type="grid",this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this.axisPointerEnabled=!0,this.dimensions=EM,this._initCartesian(t,e,n),this.model=t}return t.prototype.getRect=function(){return this._rect},t.prototype.update=function(t,e){var n=this._axesMap;function i(t){var e,n=G(t),i=n.length;if(i){for(var r=[],o=i-1;o>=0;o--){var a=t[+n[o]],s=a.model,l=a.scale;gx(l)&&s.get("alignTicks")&&null==s.get("interval")?r.push(a):(h_(l,s),gx(l)&&(e=a))}r.length&&(e||h_((e=r.pop()).scale,e.model),E(r,(function(t){YM(t.scale,t.model,e.scale)})))}}this._updateScale(t,this.model),i(n.x),i(n.y);var r={};E(n.x,(function(t){ZM(n,"y",t,r)})),E(n.y,(function(t){ZM(n,"x",t,r)})),this.resize(this.model,e)},t.prototype.resize=function(t,e,n){var i=t.getBoxLayoutParams(),r=!n&&t.get("containLabel"),o=Tp(i,{width:e.getWidth(),height:e.getHeight()});this._rect=o;var a=this._axesList;function s(){E(a,(function(t){var e=t.isHorizontal(),n=e?[0,o.width]:[0,o.height],i=t.inverse?1:0;t.setExtent(n[i],n[1-i]),function(t,e){var n=t.getExtent(),i=n[0]+n[1];t.toGlobalCoord="x"===t.dim?function(t){return t+e}:function(t){return i-t+e},t.toLocalCoord="x"===t.dim?function(t){return t-e}:function(t){return i-t+e}}(t,e?o.x:o.y)}))}s(),r&&(E(a,(function(t){if(!t.model.get(["axisLabel","inside"])){var e=function(t){var e=t.model,n=t.scale;if(e.get(["axisLabel","show"])&&!n.isBlank()){var i,r,o=n.getExtent();r=n instanceof Mx?n.count():(i=n.getTicks()).length;var a,s=t.getLabelModel(),l=p_(t),u=1;r>40&&(u=Math.ceil(r/40));for(var h=0;h0&&i>0||n<0&&i<0)}(t)}var qM=Math.PI,KM=function(){function t(t,e){this.group=new Er,this.opt=e,this.axisModel=t,k(e,{labelOffset:0,nameDirection:1,tickDirection:1,labelDirection:1,silent:!0,handleAutoShown:function(){return!0}});var n=new Er({x:e.position[0],y:e.position[1],rotation:e.rotation});n.updateTransform(),this._transformGroup=n}return t.prototype.hasBuilder=function(t){return!!$M[t]},t.prototype.add=function(t){$M[t](this.opt,this.axisModel,this.group,this._transformGroup)},t.prototype.getGroup=function(){return this.group},t.innerTextLayout=function(t,e,n){var i,r,o=to(e-t);return eo(o)?(r=n>0?"top":"bottom",i="center"):eo(o-qM)?(r=n>0?"bottom":"top",i="center"):(r="middle",i=o>0&&o0?"right":"left":n>0?"left":"right"),{rotation:o,textAlign:i,textVerticalAlign:r}},t.makeAxisEventDataBase=function(t){var e={componentType:t.mainType,componentIndex:t.componentIndex};return e[t.mainType+"Index"]=t.componentIndex,e},t.isLabelSilent=function(t){var e=t.get("tooltip");return t.get("silent")||!(t.get("triggerEvent")||e&&e.show)},t}(),$M={axisLine:function(t,e,n,i){var r=e.get(["axisLine","show"]);if("auto"===r&&t.handleAutoShown&&(r=t.handleAutoShown("axisLine")),r){var o=e.axis.getExtent(),a=i.transform,s=[o[0],0],l=[o[1],0],u=s[0]>l[0];a&&(Wt(s,s,a),Wt(l,l,a));var h=A({lineCap:"round"},e.getModel(["axisLine","lineStyle"]).getLineStyle()),c=new Xu({shape:{x1:s[0],y1:s[1],x2:l[0],y2:l[1]},style:h,strokeContainThreshold:t.strokeContainThreshold||5,silent:!0,z2:1});Oh(c.shape,c.style.lineWidth),c.anid="line",n.add(c);var p=e.get(["axisLine","symbol"]);if(null!=p){var d=e.get(["axisLine","symbolSize"]);X(p)&&(p=[p,p]),(X(d)||j(d))&&(d=[d,d]);var f=Fy(e.get(["axisLine","symbolOffset"])||0,d),g=d[0],y=d[1];E([{rotate:t.rotation+Math.PI/2,offset:f[0],r:0},{rotate:t.rotation-Math.PI/2,offset:f[1],r:Math.sqrt((s[0]-l[0])*(s[0]-l[0])+(s[1]-l[1])*(s[1]-l[1]))}],(function(e,i){if("none"!==p[i]&&null!=p[i]){var r=Vy(p[i],-g/2,-y/2,g,y,h.stroke,!0),o=e.r+e.offset,a=u?l:s;r.attr({rotation:e.rotate,x:a[0]+o*Math.cos(t.rotation),y:a[1]-o*Math.sin(t.rotation),silent:!0,z2:11}),n.add(r)}}))}}},axisTickLabel:function(t,e,n,i){var r=function(t,e,n,i){var r=n.axis,o=n.getModel("axisTick"),a=o.get("show");"auto"===a&&i.handleAutoShown&&(a=i.handleAutoShown("axisTick"));if(!a||r.scale.isBlank())return;for(var s=o.getModel("lineStyle"),l=i.tickDirection*o.get("length"),u=eI(r.getTicksCoords(),e.transform,l,k(s.getLineStyle(),{stroke:n.get(["axisLine","lineStyle","color"])}),"ticks"),h=0;hc[1]?-1:1,d=["start"===s?c[0]-p*h:"end"===s?c[1]+p*h:(c[0]+c[1])/2,tI(s)?t.labelOffset+l*h:0],f=e.get("nameRotate");null!=f&&(f=f*qM/180),tI(s)?o=KM.innerTextLayout(t.rotation,null!=f?f:t.rotation,l):(o=function(t,e,n,i){var r,o,a=to(n-t),s=i[0]>i[1],l="start"===e&&!s||"start"!==e&&s;eo(a-qM/2)?(o=l?"bottom":"top",r="center"):eo(a-1.5*qM)?(o=l?"top":"bottom",r="center"):(o="middle",r=a<1.5*qM&&a>qM/2?l?"left":"right":l?"right":"left");return{rotation:a,textAlign:r,textVerticalAlign:o}}(t.rotation,s,f||0,c),null!=(a=t.axisNameAvailableWidth)&&(a=Math.abs(a/Math.sin(o.rotation)),!isFinite(a)&&(a=null)));var g=u.getFont(),y=e.get("nameTruncate",!0)||{},v=y.ellipsis,m=it(t.nameTruncateMaxWidth,y.maxWidth,a),x=new Bs({x:d[0],y:d[1],rotation:o.rotation,silent:KM.isLabelSilent(e),style:ec(u,{text:r,font:g,overflow:"truncate",width:m,ellipsis:v,fill:u.getTextColor()||e.get(["axisLine","lineStyle","color"]),align:u.get("align")||o.textAlign,verticalAlign:u.get("verticalAlign")||o.textVerticalAlign}),z2:1});if(Xh({el:x,componentModel:e,itemName:r}),x.__fullText=r,x.anid="name",e.get("triggerEvent")){var _=KM.makeAxisEventDataBase(e);_.targetType="axisName",_.name=r,Js(x).eventData=_}i.add(x),x.updateTransform(),n.add(x),x.decomposeTransform()}}};function JM(t){t&&(t.ignore=!0)}function QM(t,e){var n=t&&t.getBoundingRect().clone(),i=e&&e.getBoundingRect().clone();if(n&&i){var r=me([]);return we(r,r,-t.rotation),n.applyTransform(_e([],r,t.getLocalTransform())),i.applyTransform(_e([],r,e.getLocalTransform())),n.intersect(i)}}function tI(t){return"middle"===t||"center"===t}function eI(t,e,n,i,r){for(var o=[],a=[],s=[],l=0;l=0||t===e}function rI(t){var e=oI(t);if(e){var n=e.axisPointerModel,i=e.axis.scale,r=n.option,o=n.get("status"),a=n.get("value");null!=a&&(a=i.parse(a));var s=aI(n);null==o&&(r.status=s?"show":"hide");var l=i.getExtent().slice();l[0]>l[1]&&l.reverse(),(null==a||a>l[1])&&(a=l[1]),a0&&!c.min?c.min=0:null!=c.min&&c.min<0&&!c.max&&(c.max=0);var p=a;null!=c.color&&(p=k({color:c.color},a));var d=C(T(c),{boundaryGap:t,splitNumber:e,scale:n,axisLine:i,axisTick:r,axisLabel:o,name:c.text,showName:s,nameLocation:"end",nameGap:u,nameTextStyle:p,triggerEvent:h},!1);if(X(l)){var f=d.name;d.name=l.replace("{value}",null!=f?f:"")}else U(l)&&(d.name=l(d.name,d));var g=new Sc(d,null,this.ecModel);return R(g,m_.prototype),g.mainType="radar",g.componentIndex=this.componentIndex,g}),this);this._indicatorModels=c},e.prototype.getIndicatorModels=function(){return this._indicatorModels},e.type="radar",e.defaultOption={z:0,center:["50%","50%"],radius:"75%",startAngle:90,axisName:{show:!0},boundaryGap:[0,0],splitNumber:5,axisNameGap:15,scale:!1,shape:"polygon",axisLine:C({lineStyle:{color:"#bbb"}},DI.axisLine),axisLabel:AI(DI.axisLabel,!1),axisTick:AI(DI.axisTick,!1),splitLine:AI(DI.splitLine,!0),splitArea:AI(DI.splitArea,!0),indicator:[]},e}(Op),LI=["axisLine","axisTickLabel","axisName"],PI=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){this.group.removeAll(),this._buildAxes(t),this._buildSplitLineAndArea(t)},e.prototype._buildAxes=function(t){var e=t.coordinateSystem;E(z(e.getIndicatorAxes(),(function(t){var n=t.model.get("showName")?t.name:"";return new KM(t.model,{axisName:n,position:[e.cx,e.cy],rotation:t.angle,labelDirection:-1,tickDirection:-1,nameDirection:1})})),(function(t){E(LI,t.add,t),this.group.add(t.getGroup())}),this)},e.prototype._buildSplitLineAndArea=function(t){var e=t.coordinateSystem,n=e.getIndicatorAxes();if(n.length){var i=t.get("shape"),r=t.getModel("splitLine"),o=t.getModel("splitArea"),a=r.getModel("lineStyle"),s=o.getModel("areaStyle"),l=r.get("show"),u=o.get("show"),h=a.get("color"),c=s.get("color"),p=Y(h)?h:[h],d=Y(c)?c:[c],f=[],g=[];if("circle"===i)for(var y=n[0].getTicksCoords(),v=e.cx,m=e.cy,x=0;x3?1.4:r>1?1.2:1.1;FI(this,"zoom","zoomOnMouseWheel",t,{scale:i>0?s:1/s,originX:o,originY:a,isAvailableBehavior:null})}if(n){var l=Math.abs(i);FI(this,"scrollMove","moveOnMouseWheel",t,{scrollDelta:(i>0?1:-1)*(l>3?.4:l>1?.15:.05),originX:o,originY:a,isAvailableBehavior:null})}}},e.prototype._pinchHandler=function(t){zI(this._zr,"globalPan")||FI(this,"zoom",null,t,{scale:t.pinchScale>1?1.1:1/1.1,originX:t.pinchX,originY:t.pinchY,isAvailableBehavior:null})},e}(jt);function FI(t,e,n,i,r){t.pointerChecker&&t.pointerChecker(i,r.originX,r.originY)&&(pe(i.event),GI(t,e,n,i,r))}function GI(t,e,n,i,r){r.isAvailableBehavior=W(WI,null,n,i),t.trigger(e,r)}function WI(t,e,n){var i=n[t];return!t||i&&(!X(i)||e.event[i+"Key"])}function HI(t,e,n){var i=t.target;i.x+=e,i.y+=n,i.dirty()}function YI(t,e,n,i){var r=t.target,o=t.zoomLimit,a=t.zoom=t.zoom||1;if(a*=e,o){var s=o.min||0,l=o.max||1/0;a=Math.max(Math.min(l,a),s)}var u=a/t.zoom;t.zoom=a,r.x-=(n-r.x)*(u-1),r.y-=(i-r.y)*(u-1),r.scaleX*=u,r.scaleY*=u,r.dirty()}var UI,XI={axisPointer:1,tooltip:1,brush:1};function ZI(t,e,n){var i=e.getComponentByElement(t.topTarget),r=i&&i.coordinateSystem;return i&&i!==n&&!XI.hasOwnProperty(i.mainType)&&r&&r.model!==n}function jI(t){X(t)&&(t=(new DOMParser).parseFromString(t,"text/xml"));var e=t;for(9===e.nodeType&&(e=e.firstChild);"svg"!==e.nodeName.toLowerCase()||1!==e.nodeType;)e=e.nextSibling;return e}var qI={fill:"fill",stroke:"stroke","stroke-width":"lineWidth",opacity:"opacity","fill-opacity":"fillOpacity","stroke-opacity":"strokeOpacity","stroke-dasharray":"lineDash","stroke-dashoffset":"lineDashOffset","stroke-linecap":"lineCap","stroke-linejoin":"lineJoin","stroke-miterlimit":"miterLimit","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","text-anchor":"textAlign",visibility:"visibility",display:"display"},KI=G(qI),$I={"alignment-baseline":"textBaseline","stop-color":"stopColor"},JI=G($I),QI=function(){function t(){this._defs={},this._root=null}return t.prototype.parse=function(t,e){e=e||{};var n=jI(t);this._defsUsePending=[];var i=new Er;this._root=i;var r=[],o=n.getAttribute("viewBox")||"",a=parseFloat(n.getAttribute("width")||e.width),s=parseFloat(n.getAttribute("height")||e.height);isNaN(a)&&(a=null),isNaN(s)&&(s=null),oT(n,i,null,!0,!1);for(var l,u,h=n.firstChild;h;)this._parseNode(h,i,r,null,!1,!1),h=h.nextSibling;if(function(t,e){for(var n=0;n=4&&(l={x:parseFloat(c[0]||0),y:parseFloat(c[1]||0),width:parseFloat(c[2]),height:parseFloat(c[3])})}if(l&&null!=a&&null!=s&&(u=fT(l,{x:0,y:0,width:a,height:s}),!e.ignoreViewBox)){var p=i;(i=new Er).add(p),p.scaleX=p.scaleY=u.scale,p.x=u.x,p.y=u.y}return e.ignoreRootClip||null==a||null==s||i.setClipPath(new Es({shape:{x:0,y:0,width:a,height:s}})),{root:i,width:a,height:s,viewBoxRect:l,viewBoxTransform:u,named:r}},t.prototype._parseNode=function(t,e,n,i,r,o){var a,s=t.nodeName.toLowerCase(),l=i;if("defs"===s&&(r=!0),"text"===s&&(o=!0),"defs"===s||"switch"===s)a=e;else{if(!r){var u=UI[s];if(u&&_t(UI,s)){a=u.call(this,t,e);var h=t.getAttribute("name");if(h){var c={name:h,namedFrom:null,svgNodeTagLower:s,el:a};n.push(c),"g"===s&&(l=c)}else i&&n.push({name:i.name,namedFrom:i,svgNodeTagLower:s,el:a});e.add(a)}}var p=tT[s];if(p&&_t(tT,s)){var d=p.call(this,t),f=t.getAttribute("id");f&&(this._defs[f]=d)}}if(a&&a.isGroup)for(var g=t.firstChild;g;)1===g.nodeType?this._parseNode(g,a,n,l,r,o):3===g.nodeType&&o&&this._parseText(g,a),g=g.nextSibling},t.prototype._parseText=function(t,e){var n=new Ts({style:{text:t.textContent},silent:!0,x:this._textX||0,y:this._textY||0});iT(e,n),oT(t,n,this._defsUsePending,!1,!1),function(t,e){var n=e.__selfStyle;if(n){var i=n.textBaseline,r=i;i&&"auto"!==i?"baseline"===i?r="alphabetic":"before-edge"===i||"text-before-edge"===i?r="top":"after-edge"===i||"text-after-edge"===i?r="bottom":"central"!==i&&"mathematical"!==i||(r="middle"):r="alphabetic",t.style.textBaseline=r}var o=e.__inheritedStyle;if(o){var a=o.textAlign,s=a;a&&("middle"===a&&(s="center"),t.style.textAlign=s)}}(n,e);var i=n.style,r=i.fontSize;r&&r<9&&(i.fontSize=9,n.scaleX*=r/9,n.scaleY*=r/9);var o=(i.fontSize||i.fontFamily)&&[i.fontStyle,i.fontWeight,(i.fontSize||12)+"px",i.fontFamily||"sans-serif"].join(" ");i.font=o;var a=n.getBoundingRect();return this._textX+=a.width,e.add(n),n},t.internalField=void(UI={g:function(t,e){var n=new Er;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n},rect:function(t,e){var n=new Es;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({x:parseFloat(t.getAttribute("x")||"0"),y:parseFloat(t.getAttribute("y")||"0"),width:parseFloat(t.getAttribute("width")||"0"),height:parseFloat(t.getAttribute("height")||"0")}),n.silent=!0,n},circle:function(t,e){var n=new xu;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(t.getAttribute("cx")||"0"),cy:parseFloat(t.getAttribute("cy")||"0"),r:parseFloat(t.getAttribute("r")||"0")}),n.silent=!0,n},line:function(t,e){var n=new Xu;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({x1:parseFloat(t.getAttribute("x1")||"0"),y1:parseFloat(t.getAttribute("y1")||"0"),x2:parseFloat(t.getAttribute("x2")||"0"),y2:parseFloat(t.getAttribute("y2")||"0")}),n.silent=!0,n},ellipse:function(t,e){var n=new bu;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(t.getAttribute("cx")||"0"),cy:parseFloat(t.getAttribute("cy")||"0"),rx:parseFloat(t.getAttribute("rx")||"0"),ry:parseFloat(t.getAttribute("ry")||"0")}),n.silent=!0,n},polygon:function(t,e){var n,i=t.getAttribute("points");i&&(n=rT(i));var r=new Gu({shape:{points:n||[]},silent:!0});return iT(e,r),oT(t,r,this._defsUsePending,!1,!1),r},polyline:function(t,e){var n,i=t.getAttribute("points");i&&(n=rT(i));var r=new Hu({shape:{points:n||[]},silent:!0});return iT(e,r),oT(t,r,this._defsUsePending,!1,!1),r},image:function(t,e){var n=new As;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setStyle({image:t.getAttribute("xlink:href")||t.getAttribute("href"),x:+t.getAttribute("x"),y:+t.getAttribute("y"),width:+t.getAttribute("width"),height:+t.getAttribute("height")}),n.silent=!0,n},text:function(t,e){var n=t.getAttribute("x")||"0",i=t.getAttribute("y")||"0",r=t.getAttribute("dx")||"0",o=t.getAttribute("dy")||"0";this._textX=parseFloat(n)+parseFloat(r),this._textY=parseFloat(i)+parseFloat(o);var a=new Er;return iT(e,a),oT(t,a,this._defsUsePending,!1,!0),a},tspan:function(t,e){var n=t.getAttribute("x"),i=t.getAttribute("y");null!=n&&(this._textX=parseFloat(n)),null!=i&&(this._textY=parseFloat(i));var r=t.getAttribute("dx")||"0",o=t.getAttribute("dy")||"0",a=new Er;return iT(e,a),oT(t,a,this._defsUsePending,!1,!0),this._textX+=parseFloat(r),this._textY+=parseFloat(o),a},path:function(t,e){var n=yu(t.getAttribute("d")||"");return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.silent=!0,n}}),t}(),tT={lineargradient:function(t){var e=parseInt(t.getAttribute("x1")||"0",10),n=parseInt(t.getAttribute("y1")||"0",10),i=parseInt(t.getAttribute("x2")||"10",10),r=parseInt(t.getAttribute("y2")||"0",10),o=new eh(e,n,i,r);return eT(t,o),nT(t,o),o},radialgradient:function(t){var e=parseInt(t.getAttribute("cx")||"0",10),n=parseInt(t.getAttribute("cy")||"0",10),i=parseInt(t.getAttribute("r")||"0",10),r=new nh(e,n,i);return eT(t,r),nT(t,r),r}};function eT(t,e){"userSpaceOnUse"===t.getAttribute("gradientUnits")&&(e.global=!0)}function nT(t,e){for(var n=t.firstChild;n;){if(1===n.nodeType&&"stop"===n.nodeName.toLocaleLowerCase()){var i=n.getAttribute("offset"),r=void 0;r=i&&i.indexOf("%")>0?parseInt(i,10)/100:i?parseFloat(i):0;var o={};dT(n,o,o);var a=o.stopColor||n.getAttribute("stop-color")||"#000000";e.colorStops.push({offset:r,color:a})}n=n.nextSibling}}function iT(t,e){t&&t.__inheritedStyle&&(e.__inheritedStyle||(e.__inheritedStyle={}),k(e.__inheritedStyle,t.__inheritedStyle))}function rT(t){for(var e=uT(t),n=[],i=0;i0;o-=2){var a=i[o],s=i[o-1],l=uT(a);switch(r=r||[1,0,0,1,0,0],s){case"translate":be(r,r,[parseFloat(l[0]),parseFloat(l[1]||"0")]);break;case"scale":Se(r,r,[parseFloat(l[0]),parseFloat(l[1]||l[0])]);break;case"rotate":we(r,r,-parseFloat(l[0])*cT);break;case"skewX":_e(r,[1,0,Math.tan(parseFloat(l[0])*cT),1,0,0],r);break;case"skewY":_e(r,[1,Math.tan(parseFloat(l[0])*cT),0,1,0,0],r);break;case"matrix":r[0]=parseFloat(l[0]),r[1]=parseFloat(l[1]),r[2]=parseFloat(l[2]),r[3]=parseFloat(l[3]),r[4]=parseFloat(l[4]),r[5]=parseFloat(l[5])}}e.setLocalTransform(r)}}(t,e),dT(t,a,s),i||function(t,e,n){for(var i=0;i0,f={api:n,geo:s,mapOrGeoModel:t,data:a,isVisualEncodedByVisualMap:d,isGeo:o,transformInfoRaw:c};"geoJSON"===s.resourceType?this._buildGeoJSON(f):"geoSVG"===s.resourceType&&this._buildSVG(f),this._updateController(t,e,n),this._updateMapSelectHandler(t,l,n,i)},t.prototype._buildGeoJSON=function(t){var e=this._regionsGroupByName=yt(),n=yt(),i=this._regionsGroup,r=t.transformInfoRaw,o=t.mapOrGeoModel,a=t.data,s=t.geo.projection,l=s&&s.stream;function u(t,e){return e&&(t=e(t)),t&&[t[0]*r.scaleX+r.x,t[1]*r.scaleY+r.y]}function h(t){for(var e=[],n=!l&&s&&s.project,i=0;i=0)&&(p=r);var d=a?{normal:{align:"center",verticalAlign:"middle"}}:null;Qh(e,tc(i),{labelFetcher:p,labelDataIndex:c,defaultText:n},d);var f=e.getTextContent();if(f&&(NT(f).ignore=f.ignore,e.textConfig&&a)){var g=e.getBoundingRect().clone();e.textConfig.layoutRect=g,e.textConfig.position=[(a[0]-g.x)/g.width*100+"%",(a[1]-g.y)/g.height*100+"%"]}e.disableLabelAnimation=!0}else e.removeTextContent(),e.removeTextConfig(),e.disableLabelAnimation=null}function GT(t,e,n,i,r,o){t.data?t.data.setItemGraphicEl(o,e):Js(e).eventData={componentType:"geo",componentIndex:r.componentIndex,geoIndex:r.componentIndex,name:n,region:i&&i.option||{}}}function WT(t,e,n,i,r){t.data||Xh({el:e,componentModel:r,itemName:n,itemTooltipOption:i.get("tooltip")})}function HT(t,e,n,i,r){e.highDownSilentOnTouch=!!r.get("selectedMode");var o=i.getModel("emphasis"),a=o.get("focus");return Hl(e,a,o.get("blurScope"),o.get("disabled")),t.isGeo&&function(t,e,n){var i=Js(t);i.componentMainType=e.mainType,i.componentIndex=e.componentIndex,i.componentHighDownName=n}(e,r,n),a}function YT(t,e,n){var i,r=[];function o(){i=[]}function a(){i.length&&(r.push(i),i=[])}var s=e({polygonStart:o,polygonEnd:a,lineStart:o,lineEnd:a,point:function(t,e){isFinite(t)&&isFinite(e)&&i.push([t,e])},sphere:function(){}});return!n&&s.polygonStart(),E(t,(function(t){s.lineStart();for(var e=0;e-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2),n},e.type="series.map",e.dependencies=["geo"],e.layoutMode="box",e.defaultOption={z:2,coordinateSystem:"geo",map:"",left:"center",top:"center",aspectScale:null,showLegendSymbol:!0,boundingCoords:null,center:null,zoom:1,scaleLimit:null,selectedMode:!0,label:{show:!1,color:"#000"},itemStyle:{borderWidth:.5,borderColor:"#444",areaColor:"#eee"},emphasis:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{areaColor:"rgba(255,215,0,0.8)"}},select:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{color:"rgba(255,215,0,0.8)"}},nameProperty:"name"},e}(fg);function ZT(t){var e={};t.eachSeriesByType("map",(function(t){var n=t.getHostGeoModel(),i=n?"o"+n.id:"i"+t.getMapType();(e[i]=e[i]||[]).push(t)})),E(e,(function(t,e){for(var n,i,r,o=(n=z(t,(function(t){return t.getData()})),i=t[0].get("mapValueCalculation"),r={},E(n,(function(t){t.each(t.mapDimension("value"),(function(e,n){var i="ec-"+t.getName(n);r[i]=r[i]||[],isNaN(e)||r[i].push(e)}))})),n[0].map(n[0].mapDimension("value"),(function(t,e){for(var o="ec-"+n[0].getName(e),a=0,s=1/0,l=-1/0,u=r[o].length,h=0;h1?(d.width=p,d.height=p/x):(d.height=p,d.width=p*x),d.y=c[1]-d.height/2,d.x=c[0]-d.width/2;else{var b=t.getBoxLayoutParams();b.aspect=x,d=Tp(b,{width:v,height:m})}this.setViewRect(d.x,d.y,d.width,d.height),this.setCenter(t.get("center"),e),this.setZoom(t.get("zoom"))}R(tC,KT);var iC=function(){function t(){this.dimensions=QT}return t.prototype.create=function(t,e){var n=[];function i(t){return{nameProperty:t.get("nameProperty"),aspectScale:t.get("aspectScale"),projection:t.get("projection")}}t.eachComponent("geo",(function(t,r){var o=t.get("map"),a=new tC(o+r,o,A({nameMap:t.get("nameMap")},i(t)));a.zoomLimit=t.get("scaleLimit"),n.push(a),t.coordinateSystem=a,a.model=t,a.resize=nC,a.resize(t,e)})),t.eachSeries((function(t){if("geo"===t.get("coordinateSystem")){var e=t.get("geoIndex")||0;t.coordinateSystem=n[e]}}));var r={};return t.eachSeriesByType("map",(function(t){if(!t.getHostGeoModel()){var e=t.getMapType();r[e]=r[e]||[],r[e].push(t)}})),E(r,(function(t,r){var o=z(t,(function(t){return t.get("nameMap")})),a=new tC(r,r,A({nameMap:D(o)},i(t[0])));a.zoomLimit=it.apply(null,z(t,(function(t){return t.get("scaleLimit")}))),n.push(a),a.resize=nC,a.resize(t[0],e),E(t,(function(t){t.coordinateSystem=a,function(t,e){E(e.get("geoCoord"),(function(e,n){t.addGeoCoord(n,e)}))}(a,t)}))})),n},t.prototype.getFilledRegions=function(t,e,n,i){for(var r=(t||[]).slice(),o=yt(),a=0;a=0;){var o=e[n];o.hierNode.prelim+=i,o.hierNode.modifier+=i,r+=o.hierNode.change,i+=o.hierNode.shift+r}}(t);var o=(n[0].hierNode.prelim+n[n.length-1].hierNode.prelim)/2;r?(t.hierNode.prelim=r.hierNode.prelim+e(t,r),t.hierNode.modifier=t.hierNode.prelim-o):t.hierNode.prelim=o}else r&&(t.hierNode.prelim=r.hierNode.prelim+e(t,r));t.parentNode.hierNode.defaultAncestor=function(t,e,n,i){if(e){for(var r=t,o=t,a=o.parentNode.children[0],s=e,l=r.hierNode.modifier,u=o.hierNode.modifier,h=a.hierNode.modifier,c=s.hierNode.modifier;s=gC(s),o=yC(o),s&&o;){r=gC(r),a=yC(a),r.hierNode.ancestor=t;var p=s.hierNode.prelim+c-o.hierNode.prelim-u+i(s,o);p>0&&(mC(vC(s,t,n),t,p),u+=p,l+=p),c+=s.hierNode.modifier,u+=o.hierNode.modifier,l+=r.hierNode.modifier,h+=a.hierNode.modifier}s&&!gC(r)&&(r.hierNode.thread=s,r.hierNode.modifier+=c-l),o&&!yC(a)&&(a.hierNode.thread=o,a.hierNode.modifier+=u-h,n=t)}return n}(t,r,t.parentNode.hierNode.defaultAncestor||i[0],e)}function pC(t){var e=t.hierNode.prelim+t.parentNode.hierNode.modifier;t.setLayout({x:e},!0),t.hierNode.modifier+=t.parentNode.hierNode.modifier}function dC(t){return arguments.length?t:xC}function fC(t,e){return t-=Math.PI/2,{x:e*Math.cos(t),y:e*Math.sin(t)}}function gC(t){var e=t.children;return e.length&&t.isExpand?e[e.length-1]:t.hierNode.thread}function yC(t){var e=t.children;return e.length&&t.isExpand?e[0]:t.hierNode.thread}function vC(t,e,n){return t.hierNode.ancestor.parentNode===e.parentNode?t.hierNode.ancestor:n}function mC(t,e,n){var i=n/(e.hierNode.i-t.hierNode.i);e.hierNode.change-=i,e.hierNode.shift+=n,e.hierNode.modifier+=n,e.hierNode.prelim+=n,t.hierNode.change+=i}function xC(t,e){return t.parentNode===e.parentNode?1:2}var _C=function(){this.parentPoint=[],this.childPoints=[]},bC=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},e.prototype.getDefaultShape=function(){return new _C},e.prototype.buildPath=function(t,e){var n=e.childPoints,i=n.length,r=e.parentPoint,o=n[0],a=n[i-1];if(1===i)return t.moveTo(r[0],r[1]),void t.lineTo(o[0],o[1]);var s=e.orient,l="TB"===s||"BT"===s?0:1,u=1-l,h=Ur(e.forkPosition,1),c=[];c[l]=r[l],c[u]=r[u]+(a[u]-r[u])*h,t.moveTo(r[0],r[1]),t.lineTo(c[0],c[1]),t.moveTo(o[0],o[1]),c[l]=o[l],t.lineTo(c[0],c[1]),c[l]=a[l],t.lineTo(c[0],c[1]),t.lineTo(a[0],a[1]);for(var p=1;pm.x)||(_-=Math.PI);var S=b?"left":"right",M=s.getModel("label"),I=M.get("rotate"),T=I*(Math.PI/180),C=y.getTextContent();C&&(y.setTextConfig({position:M.get("position")||S,rotation:null==I?-_:T,origin:"center"}),C.setStyle("verticalAlign","middle"))}var D=s.get(["emphasis","focus"]),A="relative"===D?vt(a.getAncestorsIndices(),a.getDescendantIndices()):"ancestor"===D?a.getAncestorsIndices():"descendant"===D?a.getDescendantIndices():null;A&&(Js(n).focus=A),function(t,e,n,i,r,o,a,s){var l=e.getModel(),u=t.get("edgeShape"),h=t.get("layout"),c=t.getOrient(),p=t.get(["lineStyle","curveness"]),d=t.get("edgeForkPosition"),f=l.getModel("lineStyle").getLineStyle(),g=i.__edge;if("curve"===u)e.parentNode&&e.parentNode!==n&&(g||(g=i.__edge=new Ku({shape:DC(h,c,p,r,r)})),dh(g,{shape:DC(h,c,p,o,a)},t));else if("polyline"===u)if("orthogonal"===h){if(e!==n&&e.children&&0!==e.children.length&&!0===e.isExpand){for(var y=e.children,v=[],m=0;me&&(e=i.height)}this.height=e+1},t.prototype.getNodeById=function(t){if(this.getId()===t)return this;for(var e=0,n=this.children,i=n.length;e=0&&this.hostTree.data.setItemLayout(this.dataIndex,t,e)},t.prototype.getLayout=function(){return this.hostTree.data.getItemLayout(this.dataIndex)},t.prototype.getModel=function(t){if(!(this.dataIndex<0))return this.hostTree.data.getItemModel(this.dataIndex).getModel(t)},t.prototype.getLevelModel=function(){return(this.hostTree.levelModels||[])[this.depth]},t.prototype.setVisual=function(t,e){this.dataIndex>=0&&this.hostTree.data.setItemVisual(this.dataIndex,t,e)},t.prototype.getVisual=function(t){return this.hostTree.data.getItemVisual(this.dataIndex,t)},t.prototype.getRawIndex=function(){return this.hostTree.data.getRawIndex(this.dataIndex)},t.prototype.getId=function(){return this.hostTree.data.getId(this.dataIndex)},t.prototype.getChildIndex=function(){if(this.parentNode){for(var t=this.parentNode.children,e=0;e=0){var i=n.getData().tree.root,r=t.targetNode;if(X(r)&&(r=i.getNodeById(r)),r&&i.contains(r))return{node:r};var o=t.targetNodeId;if(null!=o&&(r=i.getNodeById(o)))return{node:r}}}function GC(t){for(var e=[];t;)(t=t.parentNode)&&e.push(t);return e.reverse()}function WC(t,e){return P(GC(t),e)>=0}function HC(t,e){for(var n=[];t;){var i=t.dataIndex;n.push({name:t.name,dataIndex:i,value:e.getRawValue(i)}),t=t.parentNode}return n.reverse(),n}var YC=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.hasSymbolVisual=!0,e.ignoreStyleOnData=!0,e}return n(e,t),e.prototype.getInitialData=function(t){var e={name:t.name,children:t.data},n=t.leaves||{},i=new Sc(n,this,this.ecModel),r=BC.createTree(e,this,(function(t){t.wrapMethod("getItemModel",(function(t,e){var n=r.getNodeByDataIndex(e);return n&&n.children.length&&n.isExpand||(t.parentModel=i),t}))}));var o=0;r.eachNode("preorder",(function(t){t.depth>o&&(o=t.depth)}));var a=t.expandAndCollapse&&t.initialTreeDepth>=0?t.initialTreeDepth:o;return r.root.eachNode("preorder",(function(t){var e=t.hostTree.data.getRawDataItem(t.dataIndex);t.isExpand=e&&null!=e.collapsed?!e.collapsed:t.depth<=a})),r.data},e.prototype.getOrient=function(){var t=this.get("orient");return"horizontal"===t?t="LR":"vertical"===t&&(t="TB"),t},e.prototype.setZoom=function(t){this.option.zoom=t},e.prototype.setCenter=function(t){this.option.center=t},e.prototype.formatTooltip=function(t,e,n){for(var i=this.getData().tree,r=i.root.children[0],o=i.getNodeByDataIndex(t),a=o.getValue(),s=o.name;o&&o!==r;)s=o.parentNode.name+"."+s,o=o.parentNode;return Qf("nameValue",{name:s,value:a,noValue:isNaN(a)||null==a})},e.prototype.getDataParams=function(e){var n=t.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(e);return n.treeAncestors=HC(i,this),n.collapsed=!i.isExpand,n},e.type="series.tree",e.layoutMode="box",e.defaultOption={z:2,coordinateSystem:"view",left:"12%",top:"12%",right:"12%",bottom:"12%",layout:"orthogonal",edgeShape:"curve",edgeForkPosition:"50%",roam:!1,nodeScaleRatio:.4,center:null,zoom:1,orient:"LR",symbol:"emptyCircle",symbolSize:7,expandAndCollapse:!0,initialTreeDepth:2,lineStyle:{color:"#ccc",width:1.5,curveness:.5},itemStyle:{color:"lightsteelblue",borderWidth:1.5},label:{show:!0},animationEasing:"linear",animationDuration:700,animationDurationUpdate:500},e}(fg);function UC(t,e){for(var n,i=[t];n=i.pop();)if(e(n),n.isExpand){var r=n.children;if(r.length)for(var o=r.length-1;o>=0;o--)i.push(r[o])}}function XC(t,e){t.eachSeriesByType("tree",(function(t){!function(t,e){var n=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}(t,e);t.layoutInfo=n;var i=t.get("layout"),r=0,o=0,a=null;"radial"===i?(r=2*Math.PI,o=Math.min(n.height,n.width)/2,a=dC((function(t,e){return(t.parentNode===e.parentNode?1:2)/t.depth}))):(r=n.width,o=n.height,a=dC());var s=t.getData().tree.root,l=s.children[0];if(l){!function(t){var e=t;e.hierNode={defaultAncestor:null,ancestor:e,prelim:0,modifier:0,change:0,shift:0,i:0,thread:null};for(var n,i,r=[e];n=r.pop();)if(i=n.children,n.isExpand&&i.length)for(var o=i.length-1;o>=0;o--){var a=i[o];a.hierNode={defaultAncestor:null,ancestor:a,prelim:0,modifier:0,change:0,shift:0,i:o,thread:null},r.push(a)}}(s),function(t,e,n){for(var i,r=[t],o=[];i=r.pop();)if(o.push(i),i.isExpand){var a=i.children;if(a.length)for(var s=0;sh.getLayout().x&&(h=t),t.depth>c.depth&&(c=t)}));var p=u===h?1:a(u,h)/2,d=p-u.getLayout().x,f=0,g=0,y=0,v=0;if("radial"===i)f=r/(h.getLayout().x+p+d),g=o/(c.depth-1||1),UC(l,(function(t){y=(t.getLayout().x+d)*f,v=(t.depth-1)*g;var e=fC(y,v);t.setLayout({x:e.x,y:e.y,rawX:y,rawY:v},!0)}));else{var m=t.getOrient();"RL"===m||"LR"===m?(g=o/(h.getLayout().x+p+d),f=r/(c.depth-1||1),UC(l,(function(t){v=(t.getLayout().x+d)*g,y="LR"===m?(t.depth-1)*f:r-(t.depth-1)*f,t.setLayout({x:y,y:v},!0)}))):"TB"!==m&&"BT"!==m||(f=r/(h.getLayout().x+p+d),g=o/(c.depth-1||1),UC(l,(function(t){y=(t.getLayout().x+d)*f,v="TB"===m?(t.depth-1)*g:o-(t.depth-1)*g,t.setLayout({x:y,y:v},!0)})))}}}(t,e)}))}function ZC(t){t.eachSeriesByType("tree",(function(t){var e=t.getData();e.tree.eachNode((function(t){var n=t.getModel().getModel("itemStyle").getItemStyle();A(e.ensureUniqueItemVisual(t.dataIndex,"style"),n)}))}))}var jC=["treemapZoomToNode","treemapRender","treemapMove"];function qC(t){var e=t.getData().tree,n={};e.eachNode((function(e){for(var i=e;i&&i.depth>1;)i=i.parentNode;var r=ld(t.ecModel,i.name||i.dataIndex+"",n);e.setVisual("decal",r)}))}var KC=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.preventUsingHoverLayer=!0,n}return n(e,t),e.prototype.getInitialData=function(t,e){var n={name:t.name,children:t.data};$C(n);var i=t.levels||[],r=this.designatedVisualItemStyle={},o=new Sc({itemStyle:r},this,e);i=t.levels=function(t,e){var n,i,r=_o(e.get("color")),o=_o(e.get(["aria","decal","decals"]));if(!r)return;E(t=t||[],(function(t){var e=new Sc(t),r=e.get("color"),o=e.get("decal");(e.get(["itemStyle","color"])||r&&"none"!==r)&&(n=!0),(e.get(["itemStyle","decal"])||o&&"none"!==o)&&(i=!0)}));var a=t[0]||(t[0]={});n||(a.color=r.slice());!i&&o&&(a.decal=o.slice());return t}(i,e);var a=z(i||[],(function(t){return new Sc(t,o,e)}),this),s=BC.createTree(n,this,(function(t){t.wrapMethod("getItemModel",(function(t,e){var n=s.getNodeByDataIndex(e),i=n?a[n.depth]:null;return t.parentModel=i||o,t}))}));return s.data},e.prototype.optionUpdated=function(){this.resetViewRoot()},e.prototype.formatTooltip=function(t,e,n){var i=this.getData(),r=this.getRawValue(t);return Qf("nameValue",{name:i.getName(t),value:r})},e.prototype.getDataParams=function(e){var n=t.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(e);return n.treeAncestors=HC(i,this),n.treePathInfo=n.treeAncestors,n},e.prototype.setLayoutInfo=function(t){this.layoutInfo=this.layoutInfo||{},A(this.layoutInfo,t)},e.prototype.mapIdToIndex=function(t){var e=this._idIndexMap;e||(e=this._idIndexMap=yt(),this._idIndexMapCount=0);var n=e.get(t);return null==n&&e.set(t,n=this._idIndexMapCount++),n},e.prototype.getViewRoot=function(){return this._viewRoot},e.prototype.resetViewRoot=function(t){t?this._viewRoot=t:t=this._viewRoot;var e=this.getRawData().tree.root;t&&(t===e||e.contains(t))||(this._viewRoot=e)},e.prototype.enableAriaDecal=function(){qC(this)},e.type="series.treemap",e.layoutMode="box",e.defaultOption={progressive:0,left:"center",top:"middle",width:"80%",height:"80%",sort:!0,clipWindow:"origin",squareRatio:.5*(1+Math.sqrt(5)),leafDepth:null,drillDownIcon:"▶",zoomToNodeRatio:.1024,roam:!0,nodeClick:"zoomToNode",animation:!0,animationDurationUpdate:900,animationEasing:"quinticInOut",breadcrumb:{show:!0,height:22,left:"center",top:"bottom",emptyItemWidth:25,itemStyle:{color:"rgba(0,0,0,0.7)",textStyle:{color:"#fff"}},emphasis:{itemStyle:{color:"rgba(0,0,0,0.9)"}}},label:{show:!0,distance:0,padding:5,position:"inside",color:"#fff",overflow:"truncate"},upperLabel:{show:!1,position:[0,"50%"],height:20,overflow:"truncate",verticalAlign:"middle"},itemStyle:{color:null,colorAlpha:null,colorSaturation:null,borderWidth:0,gapWidth:0,borderColor:"#fff",borderColorSaturation:null},emphasis:{upperLabel:{show:!0,position:[0,"50%"],overflow:"truncate",verticalAlign:"middle"}},visualDimension:0,visualMin:null,visualMax:null,color:[],colorAlpha:null,colorSaturation:null,colorMappingBy:"index",visibleMin:10,childrenVisibleMin:null,levels:[]},e}(fg);function $C(t){var e=0;E(t.children,(function(t){$C(t);var n=t.value;Y(n)&&(n=n[0]),e+=n}));var n=t.value;Y(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=e),n<0&&(n=0),Y(t.value)?t.value[0]=n:t.value=n}var JC=function(){function t(t){this.group=new Er,t.add(this.group)}return t.prototype.render=function(t,e,n,i){var r=t.getModel("breadcrumb"),o=this.group;if(o.removeAll(),r.get("show")&&n){var a=r.getModel("itemStyle"),s=r.getModel("emphasis"),l=a.getModel("textStyle"),u=s.getModel(["itemStyle","textStyle"]),h={pos:{left:r.get("left"),right:r.get("right"),top:r.get("top"),bottom:r.get("bottom")},box:{width:e.getWidth(),height:e.getHeight()},emptyItemWidth:r.get("emptyItemWidth"),totalWidth:0,renderList:[]};this._prepare(n,h,l),this._renderContent(t,h,a,s,l,u,i),Cp(o,h.pos,h.box)}},t.prototype._prepare=function(t,e,n){for(var i=t;i;i=i.parentNode){var r=Do(i.getModel().get("name"),""),o=n.getTextRect(r),a=Math.max(o.width+16,e.emptyItemWidth);e.totalWidth+=a+8,e.renderList.push({node:i,text:r,width:a})}},t.prototype._renderContent=function(t,e,n,i,r,o,a){for(var s,l,u,h,c,p,d,f,g,y=0,v=e.emptyItemWidth,m=t.get(["breadcrumb","height"]),x=(s=e.pos,l=e.box,h=l.width,c=l.height,p=Ur(s.left,h),d=Ur(s.top,c),f=Ur(s.right,h),g=Ur(s.bottom,c),(isNaN(p)||isNaN(parseFloat(s.left)))&&(p=0),(isNaN(f)||isNaN(parseFloat(s.right)))&&(f=h),(isNaN(d)||isNaN(parseFloat(s.top)))&&(d=0),(isNaN(g)||isNaN(parseFloat(s.bottom)))&&(g=c),u=dp(u||0),{width:Math.max(f-p-u[1]-u[3],0),height:Math.max(g-d-u[0]-u[2],0)}),_=e.totalWidth,b=e.renderList,w=i.getModel("itemStyle").getItemStyle(),S=b.length-1;S>=0;S--){var M=b[S],I=M.node,T=M.width,C=M.text;_>x.width&&(_-=T-v,T=v,C=null);var D=new Gu({shape:{points:QC(y,0,T,m,S===b.length-1,0===S)},style:k(n.getItemStyle(),{lineJoin:"bevel"}),textContent:new Bs({style:ec(r,{text:C})}),textConfig:{position:"inside"},z2:1e5,onclick:H(a,I)});D.disableLabelAnimation=!0,D.getTextContent().ensureState("emphasis").style=ec(o,{text:C}),D.ensureState("emphasis").style=w,Hl(D,i.get("focus"),i.get("blurScope"),i.get("disabled")),this.group.add(D),tD(D,t,I),y+=T+8}},t.prototype.remove=function(){this.group.removeAll()},t}();function QC(t,e,n,i,r,o){var a=[[r?t:t-5,e],[t+n,e],[t+n,e+i],[r?t:t-5,e+i]];return!o&&a.splice(2,0,[t+n+5,e+i/2]),!r&&a.push([t,e+i/2]),a}function tD(t,e,n){Js(t).eventData={componentType:"series",componentSubType:"treemap",componentIndex:e.componentIndex,seriesIndex:e.seriesIndex,seriesName:e.name,seriesType:"treemap",selfType:"breadcrumb",nodeData:{dataIndex:n&&n.dataIndex,name:n&&n.name},treePathInfo:n&&HC(n,e)}}var eD=function(){function t(){this._storage=[],this._elExistsMap={}}return t.prototype.add=function(t,e,n,i,r){return!this._elExistsMap[t.id]&&(this._elExistsMap[t.id]=!0,this._storage.push({el:t,target:e,duration:n,delay:i,easing:r}),!0)},t.prototype.finished=function(t){return this._finishedCallback=t,this},t.prototype.start=function(){for(var t=this,e=this._storage.length,n=function(){--e<=0&&(t._storage.length=0,t._elExistsMap={},t._finishedCallback&&t._finishedCallback())},i=0,r=this._storage.length;i3||Math.abs(t.dy)>3)){var e=this.seriesModel.getData().tree.root;if(!e)return;var n=e.getLayout();if(!n)return;this.api.dispatchAction({type:"treemapMove",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:n.x+t.dx,y:n.y+t.dy,width:n.width,height:n.height}})}},e.prototype._onZoom=function(t){var e=t.originX,n=t.originY;if("animating"!==this._state){var i=this.seriesModel.getData().tree.root;if(!i)return;var r=i.getLayout();if(!r)return;var o=new Ee(r.x,r.y,r.width,r.height),a=this.seriesModel.layoutInfo,s=[1,0,0,1,0,0];be(s,s,[-(e-=a.x),-(n-=a.y)]),Se(s,s,[t.scale,t.scale]),be(s,s,[e,n]),o.applyTransform(s),this.api.dispatchAction({type:"treemapRender",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:o.x,y:o.y,width:o.width,height:o.height}})}},e.prototype._initEvents=function(t){var e=this;t.on("click",(function(t){if("ready"===e._state){var n=e.seriesModel.get("nodeClick",!0);if(n){var i=e.findTarget(t.offsetX,t.offsetY);if(i){var r=i.node;if(r.getLayout().isLeafRoot)e._rootToNode(i);else if("zoomToNode"===n)e._zoomToNode(i);else if("link"===n){var o=r.hostTree.data.getItemModel(r.dataIndex),a=o.get("link",!0),s=o.get("target",!0)||"blank";a&&_p(a,s)}}}}}),this)},e.prototype._renderBreadcrumb=function(t,e,n){var i=this;n||(n=null!=t.get("leafDepth",!0)?{node:t.getViewRoot()}:this.findTarget(e.getWidth()/2,e.getHeight()/2))||(n={node:t.getData().tree.root}),(this._breadcrumb||(this._breadcrumb=new JC(this.group))).render(t,e,n.node,(function(e){"animating"!==i._state&&(WC(t.getViewRoot(),e)?i._rootToNode({node:e}):i._zoomToNode({node:e}))}))},e.prototype.remove=function(){this._clearController(),this._containerGroup&&this._containerGroup.removeAll(),this._storage={nodeGroup:[],background:[],content:[]},this._state="ready",this._breadcrumb&&this._breadcrumb.remove()},e.prototype.dispose=function(){this._clearController()},e.prototype._zoomToNode=function(t){this.api.dispatchAction({type:"treemapZoomToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:t.node})},e.prototype._rootToNode=function(t){this.api.dispatchAction({type:"treemapRootToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:t.node})},e.prototype.findTarget=function(t,e){var n;return this.seriesModel.getViewRoot().eachNode({attr:"viewChildren",order:"preorder"},(function(i){var r=this._storage.background[i.getRawIndex()];if(r){var o=r.transformCoordToLocal(t,e),a=r.shape;if(!(a.x<=o[0]&&o[0]<=a.x+a.width&&a.y<=o[1]&&o[1]<=a.y+a.height))return!1;n={node:i,offsetX:o[0],offsetY:o[1]}}}),this),n},e.type="treemap",e}(Tg);var hD=E,cD=q,pD=-1,dD=function(){function t(e){var n=e.mappingMethod,i=e.type,r=this.option=T(e);this.type=i,this.mappingMethod=n,this._normalizeData=SD[n];var o=t.visualHandlers[i];this.applyVisual=o.applyVisual,this.getColorMapper=o.getColorMapper,this._normalizedToVisual=o._normalizedToVisual[n],"piecewise"===n?(fD(r),function(t){var e=t.pieceList;t.hasSpecialVisual=!1,E(e,(function(e,n){e.originIndex=n,null!=e.visual&&(t.hasSpecialVisual=!0)}))}(r)):"category"===n?r.categories?function(t){var e=t.categories,n=t.categoryMap={},i=t.visual;if(hD(e,(function(t,e){n[t]=e})),!Y(i)){var r=[];q(i)?hD(i,(function(t,e){var i=n[e];r[null!=i?i:pD]=t})):r[-1]=i,i=wD(t,r)}for(var o=e.length-1;o>=0;o--)null==i[o]&&(delete n[e[o]],e.pop())}(r):fD(r,!0):(lt("linear"!==n||r.dataExtent),fD(r))}return t.prototype.mapValueToVisual=function(t){var e=this._normalizeData(t);return this._normalizedToVisual(e,t)},t.prototype.getNormalizer=function(){return W(this._normalizeData,this)},t.listVisualTypes=function(){return G(t.visualHandlers)},t.isValidType=function(e){return t.visualHandlers.hasOwnProperty(e)},t.eachVisual=function(t,e,n){q(t)?E(t,e,n):e.call(n,t)},t.mapVisual=function(e,n,i){var r,o=Y(e)?[]:q(e)?{}:(r=!0,null);return t.eachVisual(e,(function(t,e){var a=n.call(i,t,e);r?o=a:o[e]=a})),o},t.retrieveVisuals=function(e){var n,i={};return e&&hD(t.visualHandlers,(function(t,r){e.hasOwnProperty(r)&&(i[r]=e[r],n=!0)})),n?i:null},t.prepareVisualTypes=function(t){if(Y(t))t=t.slice();else{if(!cD(t))return[];var e=[];hD(t,(function(t,n){e.push(n)})),t=e}return t.sort((function(t,e){return"color"===e&&"color"!==t&&0===t.indexOf("color")?1:-1})),t},t.dependsOn=function(t,e){return"color"===e?!(!t||0!==t.indexOf(e)):t===e},t.findPieceIndex=function(t,e,n){for(var i,r=1/0,o=0,a=e.length;ou[1]&&(u[1]=l);var h=e.get("colorMappingBy"),c={type:a.name,dataExtent:u,visual:a.range};"color"!==c.type||"index"!==h&&"id"!==h?c.mappingMethod="linear":(c.mappingMethod="category",c.loop=!0);var p=new dD(c);return ID(p).drColorMappingBy=h,p}(0,r,o,0,u,d);E(d,(function(t,e){if(t.depth>=n.length||t===n[t.depth]){var o=function(t,e,n,i,r,o){var a=A({},e);if(r){var s=r.type,l="color"===s&&ID(r).drColorMappingBy,u="index"===l?i:"id"===l?o.mapIdToIndex(n.getId()):n.getValue(t.get("visualDimension"));a[s]=r.mapValueToVisual(u)}return a}(r,u,t,e,f,i);CD(t,o,n,i)}}))}else s=DD(u),h.fill=s}}function DD(t){var e=AD(t,"color");if(e){var n=AD(t,"colorAlpha"),i=AD(t,"colorSaturation");return i&&(e=ei(e,null,null,i)),n&&(e=ni(e,n)),e}}function AD(t,e){var n=t[e];if(null!=n&&"none"!==n)return n}function kD(t,e){var n=t.get(e);return Y(n)&&n.length?{name:e,range:n}:null}var LD=Math.max,PD=Math.min,OD=it,RD=E,ND=["itemStyle","borderWidth"],ED=["itemStyle","gapWidth"],zD=["upperLabel","show"],VD=["upperLabel","height"],BD={seriesType:"treemap",reset:function(t,e,n,i){var r=n.getWidth(),o=n.getHeight(),a=t.option,s=Tp(t.getBoxLayoutParams(),{width:n.getWidth(),height:n.getHeight()}),l=a.size||[],u=Ur(OD(s.width,l[0]),r),h=Ur(OD(s.height,l[1]),o),c=i&&i.type,p=FC(i,["treemapZoomToNode","treemapRootToNode"],t),d="treemapRender"===c||"treemapMove"===c?i.rootRect:null,f=t.getViewRoot(),g=GC(f);if("treemapMove"!==c){var y="treemapZoomToNode"===c?function(t,e,n,i,r){var o,a=(e||{}).node,s=[i,r];if(!a||a===n)return s;var l=i*r,u=l*t.option.zoomToNodeRatio;for(;o=a.parentNode;){for(var h=0,c=o.children,p=0,d=c.length;pQr&&(u=Qr),a=o}ua[1]&&(a[1]=e)}))):a=[NaN,NaN];return{sum:i,dataExtent:a}}(e,a,s);if(0===u.sum)return t.viewChildren=[];if(u.sum=function(t,e,n,i,r){if(!i)return n;for(var o=t.get("visibleMin"),a=r.length,s=a,l=a-1;l>=0;l--){var u=r["asc"===i?a-l-1:l].getValue();u/n*ei&&(i=a));var l=t.area*t.area,u=e*e*n;return l?LD(u*i/l,l/(u*r)):1/0}function WD(t,e,n,i,r){var o=e===n.width?0:1,a=1-o,s=["x","y"],l=["width","height"],u=n[s[o]],h=e?t.area/e:0;(r||h>n[l[a]])&&(h=n[l[a]]);for(var c=0,p=t.length;ci&&(i=e);var o=i%2?i+2:i+3;r=[];for(var a=0;a0&&(m[0]=-m[0],m[1]=-m[1]);var _=v[0]<0?-1:1;if("start"!==i.__position&&"end"!==i.__position){var b=-Math.atan2(v[1],v[0]);u[0].8?"left":h[0]<-.8?"right":"center",p=h[1]>.8?"top":h[1]<-.8?"bottom":"middle";break;case"start":i.x=-h[0]*f+l[0],i.y=-h[1]*g+l[1],c=h[0]>.8?"right":h[0]<-.8?"left":"center",p=h[1]>.8?"bottom":h[1]<-.8?"top":"middle";break;case"insideStartTop":case"insideStart":case"insideStartBottom":i.x=f*_+l[0],i.y=l[1]+w,c=v[0]<0?"right":"left",i.originX=-f*_,i.originY=-w;break;case"insideMiddleTop":case"insideMiddle":case"insideMiddleBottom":case"middle":i.x=x[0],i.y=x[1]+w,c="center",i.originY=-w;break;case"insideEndTop":case"insideEnd":case"insideEndBottom":i.x=-f*_+u[0],i.y=u[1]+w,c=v[0]>=0?"right":"left",i.originX=f*_,i.originY=-w}i.scaleX=i.scaleY=r,i.setStyle({verticalAlign:i.__verticalAlign||p,align:i.__align||c})}}}function S(t,e){var n=t.__specifiedRotation;if(null==n){var i=a.tangentAt(e);t.attr("rotation",(1===e?-1:1)*Math.PI/2-Math.atan2(i[1],i[0]))}else t.attr("rotation",n)}},e}(Er),TA=function(){function t(t){this.group=new Er,this._LineCtor=t||IA}return t.prototype.updateData=function(t){var e=this;this._progressiveEls=null;var n=this,i=n.group,r=n._lineData;n._lineData=t,r||i.removeAll();var o=CA(t);t.diff(r).add((function(n){e._doAdd(t,n,o)})).update((function(n,i){e._doUpdate(r,t,i,n,o)})).remove((function(t){i.remove(r.getItemGraphicEl(t))})).execute()},t.prototype.updateLayout=function(){var t=this._lineData;t&&t.eachItemGraphicEl((function(e,n){e.updateLayout(t,n)}),this)},t.prototype.incrementalPrepareUpdate=function(t){this._seriesScope=CA(t),this._lineData=null,this.group.removeAll()},t.prototype.incrementalUpdate=function(t,e){function n(t){t.isGroup||function(t){return t.animators&&t.animators.length>0}(t)||(t.incremental=!0,t.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[];for(var i=t.start;i=0?i+=u:i-=u:f>=0?i-=u:i+=u}return i}function zA(t,e){var n=[],i=Cn,r=[[],[],[]],o=[[],[]],a=[];e/=2,t.eachEdge((function(t,s){var l=t.getLayout(),u=t.getVisual("fromSymbol"),h=t.getVisual("toSymbol");l.__original||(l.__original=[Tt(l[0]),Tt(l[1])],l[2]&&l.__original.push(Tt(l[2])));var c=l.__original;if(null!=l[2]){if(It(r[0],c[0]),It(r[1],c[2]),It(r[2],c[1]),u&&"none"!==u){var p=aA(t.node1),d=EA(r,c[0],p*e);i(r[0][0],r[1][0],r[2][0],d,n),r[0][0]=n[3],r[1][0]=n[4],i(r[0][1],r[1][1],r[2][1],d,n),r[0][1]=n[3],r[1][1]=n[4]}if(h&&"none"!==h){p=aA(t.node2),d=EA(r,c[1],p*e);i(r[0][0],r[1][0],r[2][0],d,n),r[1][0]=n[1],r[2][0]=n[2],i(r[0][1],r[1][1],r[2][1],d,n),r[1][1]=n[1],r[2][1]=n[2]}It(l[0],r[0]),It(l[1],r[2]),It(l[2],r[1])}else{if(It(o[0],c[0]),It(o[1],c[1]),kt(a,o[1],o[0]),Et(a,a),u&&"none"!==u){p=aA(t.node1);At(o[0],o[0],a,p*e)}if(h&&"none"!==h){p=aA(t.node2);At(o[1],o[1],a,-p*e)}It(l[0],o[0]),It(l[1],o[1])}}))}function VA(t){return"view"===t.type}var BA=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(t,e){var n=new iS,i=new TA,r=this.group;this._controller=new BI(e.getZr()),this._controllerHost={target:r},r.add(n.group),r.add(i.group),this._symbolDraw=n,this._lineDraw=i,this._firstRender=!0},e.prototype.render=function(t,e,n){var i=this,r=t.coordinateSystem;this._model=t;var o=this._symbolDraw,a=this._lineDraw,s=this.group;if(VA(r)){var l={x:r.x,y:r.y,scaleX:r.scaleX,scaleY:r.scaleY};this._firstRender?s.attr(l):dh(s,l,t)}zA(t.getGraph(),oA(t));var u=t.getData();o.updateData(u);var h=t.getEdgeData();a.updateData(h),this._updateNodeAndLinkScale(),this._updateController(t,e,n),clearTimeout(this._layoutTimeout);var c=t.forceLayout,p=t.get(["force","layoutAnimation"]);c&&this._startForceLayoutIteration(c,p);var d=t.get("layout");u.graph.eachNode((function(e){var n=e.dataIndex,r=e.getGraphicEl(),o=e.getModel();if(r){r.off("drag").off("dragend");var a=o.get("draggable");a&&r.on("drag",(function(o){switch(d){case"force":c.warmUp(),!i._layouting&&i._startForceLayoutIteration(c,p),c.setFixed(n),u.setItemLayout(n,[r.x,r.y]);break;case"circular":u.setItemLayout(n,[r.x,r.y]),e.setLayout({fixed:!0},!0),uA(t,"symbolSize",e,[o.offsetX,o.offsetY]),i.updateLayout(t);break;default:u.setItemLayout(n,[r.x,r.y]),iA(t.getGraph(),t),i.updateLayout(t)}})).on("dragend",(function(){c&&c.setUnfixed(n)})),r.setDraggable(a,!!o.get("cursor")),"adjacency"===o.get(["emphasis","focus"])&&(Js(r).focus=e.getAdjacentDataIndices())}})),u.graph.eachEdge((function(t){var e=t.getGraphicEl(),n=t.getModel().get(["emphasis","focus"]);e&&"adjacency"===n&&(Js(e).focus={edge:[t.dataIndex],node:[t.node1.dataIndex,t.node2.dataIndex]})}));var f="circular"===t.get("layout")&&t.get(["circular","rotateLabel"]),g=u.getLayout("cx"),y=u.getLayout("cy");u.graph.eachNode((function(t){cA(t,f,g,y)})),this._firstRender=!1},e.prototype.dispose=function(){this._controller&&this._controller.dispose(),this._controllerHost=null},e.prototype._startForceLayoutIteration=function(t,e){var n=this;!function i(){t.step((function(t){n.updateLayout(n._model),(n._layouting=!t)&&(e?n._layoutTimeout=setTimeout(i,16):i())}))}()},e.prototype._updateController=function(t,e,n){var i=this,r=this._controller,o=this._controllerHost,a=this.group;r.setPointerChecker((function(e,i,r){var o=a.getBoundingRect();return o.applyTransform(a.transform),o.contain(i,r)&&!ZI(e,n,t)})),VA(t.coordinateSystem)?(r.enable(t.get("roam")),o.zoomLimit=t.get("scaleLimit"),o.zoom=t.coordinateSystem.getZoom(),r.off("pan").off("zoom").on("pan",(function(e){HI(o,e.dx,e.dy),n.dispatchAction({seriesId:t.id,type:"graphRoam",dx:e.dx,dy:e.dy})})).on("zoom",(function(e){YI(o,e.scale,e.originX,e.originY),n.dispatchAction({seriesId:t.id,type:"graphRoam",zoom:e.scale,originX:e.originX,originY:e.originY}),i._updateNodeAndLinkScale(),zA(t.getGraph(),oA(t)),i._lineDraw.updateLayout(),n.updateLabelLayout()}))):r.disable()},e.prototype._updateNodeAndLinkScale=function(){var t=this._model,e=t.getData(),n=oA(t);e.eachItemGraphicEl((function(t,e){t&&t.setSymbolScale(n)}))},e.prototype.updateLayout=function(t){zA(t.getGraph(),oA(t)),this._symbolDraw.updateLayout(),this._lineDraw.updateLayout()},e.prototype.remove=function(t,e){this._symbolDraw&&this._symbolDraw.remove(),this._lineDraw&&this._lineDraw.remove()},e.type="graph",e}(Tg);function FA(t){return"_EC_"+t}var GA=function(){function t(t){this.type="graph",this.nodes=[],this.edges=[],this._nodesMap={},this._edgesMap={},this._directed=t||!1}return t.prototype.isDirected=function(){return this._directed},t.prototype.addNode=function(t,e){t=null==t?""+e:""+t;var n=this._nodesMap;if(!n[FA(t)]){var i=new WA(t,e);return i.hostGraph=this,this.nodes.push(i),n[FA(t)]=i,i}},t.prototype.getNodeByIndex=function(t){var e=this.data.getRawIndex(t);return this.nodes[e]},t.prototype.getNodeById=function(t){return this._nodesMap[FA(t)]},t.prototype.addEdge=function(t,e,n){var i=this._nodesMap,r=this._edgesMap;if(j(t)&&(t=this.nodes[t]),j(e)&&(e=this.nodes[e]),t instanceof WA||(t=i[FA(t)]),e instanceof WA||(e=i[FA(e)]),t&&e){var o=t.id+"-"+e.id,a=new HA(t,e,n);return a.hostGraph=this,this._directed&&(t.outEdges.push(a),e.inEdges.push(a)),t.edges.push(a),t!==e&&e.edges.push(a),this.edges.push(a),r[o]=a,a}},t.prototype.getEdgeByIndex=function(t){var e=this.edgeData.getRawIndex(t);return this.edges[e]},t.prototype.getEdge=function(t,e){t instanceof WA&&(t=t.id),e instanceof WA&&(e=e.id);var n=this._edgesMap;return this._directed?n[t+"-"+e]:n[t+"-"+e]||n[e+"-"+t]},t.prototype.eachNode=function(t,e){for(var n=this.nodes,i=n.length,r=0;r=0&&t.call(e,n[r],r)},t.prototype.eachEdge=function(t,e){for(var n=this.edges,i=n.length,r=0;r=0&&n[r].node1.dataIndex>=0&&n[r].node2.dataIndex>=0&&t.call(e,n[r],r)},t.prototype.breadthFirstTraverse=function(t,e,n,i){if(e instanceof WA||(e=this._nodesMap[FA(e)]),e){for(var r="out"===n?"outEdges":"in"===n?"inEdges":"edges",o=0;o=0&&n.node2.dataIndex>=0}));for(r=0,o=i.length;r=0&&this[t][e].setItemVisual(this.dataIndex,n,i)},getVisual:function(n){return this[t][e].getItemVisual(this.dataIndex,n)},setLayout:function(n,i){this.dataIndex>=0&&this[t][e].setItemLayout(this.dataIndex,n,i)},getLayout:function(){return this[t][e].getItemLayout(this.dataIndex)},getGraphicEl:function(){return this[t][e].getItemGraphicEl(this.dataIndex)},getRawIndex:function(){return this[t][e].getRawIndex(this.dataIndex)}}}function UA(t,e,n,i,r){for(var o=new GA(i),a=0;a "+p)),u++)}var d,f=n.get("coordinateSystem");if("cartesian2d"===f||"polar"===f)d=hx(t,n);else{var g=vd.get(f),y=g&&g.dimensions||[];P(y,"value")<0&&y.concat(["value"]);var v=nx(t,{coordDimensions:y,encodeDefine:n.getEncode()}).dimensions;(d=new ex(v,n)).initData(t)}var m=new ex(["value"],n);return m.initData(l,s),r&&r(d,m),kC({mainData:d,struct:o,structAttr:"graph",datas:{node:d,edge:m},datasAttr:{node:"data",edge:"edgeData"}}),o.update(),o}R(WA,YA("hostGraph","data")),R(HA,YA("hostGraph","edgeData"));var XA=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.hasSymbolVisual=!0,n}return n(e,t),e.prototype.init=function(e){t.prototype.init.apply(this,arguments);var n=this;function i(){return n._categoriesData}this.legendVisualProvider=new mM(i,i),this.fillDataTextStyle(e.edges||e.links),this._updateCategoriesData()},e.prototype.mergeOption=function(e){t.prototype.mergeOption.apply(this,arguments),this.fillDataTextStyle(e.edges||e.links),this._updateCategoriesData()},e.prototype.mergeDefaultAndTheme=function(e){t.prototype.mergeDefaultAndTheme.apply(this,arguments),bo(e,"edgeLabel",["show"])},e.prototype.getInitialData=function(t,e){var n,i=t.edges||t.links||[],r=t.data||t.nodes||[],o=this;if(r&&i){KD(n=this)&&(n.__curvenessList=[],n.__edgeMap={},$D(n));var a=UA(r,i,this,!0,(function(t,e){t.wrapMethod("getItemModel",(function(t){var e=o._categoriesModels[t.getShallow("category")];return e&&(e.parentModel=t.parentModel,t.parentModel=e),t}));var n=Sc.prototype.getModel;function i(t,e){var i=n.call(this,t,e);return i.resolveParentPath=r,i}function r(t){if(t&&("label"===t[0]||"label"===t[1])){var e=t.slice();return"label"===t[0]?e[0]="edgeLabel":"label"===t[1]&&(e[1]="edgeLabel"),e}return t}e.wrapMethod("getItemModel",(function(t){return t.resolveParentPath=r,t.getModel=i,t}))}));return E(a.edges,(function(t){!function(t,e,n,i){if(KD(n)){var r=JD(t,e,n),o=n.__edgeMap,a=o[QD(r)];o[r]&&!a?o[r].isForward=!0:a&&o[r]&&(a.isForward=!0,o[r].isForward=!1),o[r]=o[r]||[],o[r].push(i)}}(t.node1,t.node2,this,t.dataIndex)}),this),a.data}},e.prototype.getGraph=function(){return this.getData().graph},e.prototype.getEdgeData=function(){return this.getGraph().edgeData},e.prototype.getCategoriesData=function(){return this._categoriesData},e.prototype.formatTooltip=function(t,e,n){if("edge"===n){var i=this.getData(),r=this.getDataParams(t,n),o=i.graph.getEdgeByIndex(t),a=i.getName(o.node1.dataIndex),s=i.getName(o.node2.dataIndex),l=[];return null!=a&&l.push(a),null!=s&&l.push(s),Qf("nameValue",{name:l.join(" > "),value:r.value,noValue:null==r.value})}return cg({series:this,dataIndex:t,multipleSeries:e})},e.prototype._updateCategoriesData=function(){var t=z(this.option.categories||[],(function(t){return null!=t.value?t:A({value:0},t)})),e=new ex(["value"],this);e.initData(t),this._categoriesData=e,this._categoriesModels=e.mapArray((function(t){return e.getItemModel(t)}))},e.prototype.setZoom=function(t){this.option.zoom=t},e.prototype.setCenter=function(t){this.option.center=t},e.prototype.isAnimationEnabled=function(){return t.prototype.isAnimationEnabled.call(this)&&!("force"===this.get("layout")&&this.get(["force","layoutAnimation"]))},e.type="series.graph",e.dependencies=["grid","polar","geo","singleAxis","calendar"],e.defaultOption={z:2,coordinateSystem:"view",legendHoverLink:!0,layout:null,circular:{rotateLabel:!1},force:{initLayout:null,repulsion:[0,50],gravity:.1,friction:.6,edgeLength:30,layoutAnimation:!0},left:"center",top:"center",symbol:"circle",symbolSize:10,edgeSymbol:["none","none"],edgeSymbolSize:10,edgeLabel:{position:"middle",distance:5},draggable:!1,roam:!1,center:null,zoom:1,nodeScaleRatio:.6,label:{show:!1,formatter:"{b}"},itemStyle:{},lineStyle:{color:"#aaa",width:1,opacity:.5},emphasis:{scale:!0,label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}},e}(fg),ZA={type:"graphRoam",event:"graphRoam",update:"none"};var jA=function(){this.angle=0,this.width=10,this.r=10,this.x=0,this.y=0},qA=function(t){function e(e){var n=t.call(this,e)||this;return n.type="pointer",n}return n(e,t),e.prototype.getDefaultShape=function(){return new jA},e.prototype.buildPath=function(t,e){var n=Math.cos,i=Math.sin,r=e.r,o=e.width,a=e.angle,s=e.x-n(a)*o*(o>=r/3?1:2),l=e.y-i(a)*o*(o>=r/3?1:2);a=e.angle-Math.PI/2,t.moveTo(s,l),t.lineTo(e.x+n(a)*o,e.y+i(a)*o),t.lineTo(e.x+n(e.angle)*r,e.y+i(e.angle)*r),t.lineTo(e.x-n(a)*o,e.y-i(a)*o),t.lineTo(s,l)},e}(Ms);function KA(t,e){var n=null==t?"":t+"";return e&&(X(e)?n=e.replace("{value}",n):U(e)&&(n=e(t))),n}var $A=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){this.group.removeAll();var i=t.get(["axisLine","lineStyle","color"]),r=function(t,e){var n=t.get("center"),i=e.getWidth(),r=e.getHeight(),o=Math.min(i,r);return{cx:Ur(n[0],e.getWidth()),cy:Ur(n[1],e.getHeight()),r:Ur(t.get("radius"),o/2)}}(t,n);this._renderMain(t,e,n,i,r),this._data=t.getData()},e.prototype.dispose=function(){},e.prototype._renderMain=function(t,e,n,i,r){var o=this.group,a=t.get("clockwise"),s=-t.get("startAngle")/180*Math.PI,l=-t.get("endAngle")/180*Math.PI,u=t.getModel("axisLine"),h=u.get("roundCap")?ES:Eu,c=u.get("show"),p=u.getModel("lineStyle"),d=p.get("width"),f=[s,l];is(f,!a);for(var g=(l=f[1])-(s=f[0]),y=s,v=[],m=0;c&&m=t&&(0===e?0:i[e-1][0])Math.PI/2&&(V+=Math.PI):"tangential"===z?V=-M-Math.PI/2:j(z)&&(V=z*Math.PI/180),0===V?c.add(new Bs({style:ec(x,{text:O,x:N,y:E,verticalAlign:h<-.8?"top":h>.8?"bottom":"middle",align:u<-.4?"left":u>.4?"right":"center"},{inheritColor:R}),silent:!0})):c.add(new Bs({style:ec(x,{text:O,x:N,y:E,verticalAlign:"middle",align:"center"},{inheritColor:R}),silent:!0,originX:N,originY:E,rotation:V}))}if(m.get("show")&&k!==_){P=(P=m.get("distance"))?P+l:l;for(var B=0;B<=b;B++){u=Math.cos(M),h=Math.sin(M);var F=new Xu({shape:{x1:u*(f-P)+p,y1:h*(f-P)+d,x2:u*(f-S-P)+p,y2:h*(f-S-P)+d},silent:!0,style:D});"auto"===D.stroke&&F.setStyle({stroke:i((k+B/b)/_)}),c.add(F),M+=T}M-=T}else M+=I}},e.prototype._renderPointer=function(t,e,n,i,r,o,a,s,l){var u=this.group,h=this._data,c=this._progressEls,p=[],d=t.get(["pointer","show"]),f=t.getModel("progress"),g=f.get("show"),y=t.getData(),v=y.mapDimension("value"),m=+t.get("min"),x=+t.get("max"),_=[m,x],b=[o,a];function w(e,n){var i,o=y.getItemModel(e).getModel("pointer"),a=Ur(o.get("width"),r.r),s=Ur(o.get("length"),r.r),l=t.get(["pointer","icon"]),u=o.get("offsetCenter"),h=Ur(u[0],r.r),c=Ur(u[1],r.r),p=o.get("keepAspect");return(i=l?Vy(l,h-a/2,c-s,a,s,null,p):new qA({shape:{angle:-Math.PI/2,width:a,r:s,x:h,y:c}})).rotation=-(n+Math.PI/2),i.x=r.cx,i.y=r.cy,i}function S(t,e){var n=f.get("roundCap")?ES:Eu,i=f.get("overlap"),a=i?f.get("width"):l/y.count(),u=i?r.r-a:r.r-(t+1)*a,h=i?r.r:r.r-t*a,c=new n({shape:{startAngle:o,endAngle:e,cx:r.cx,cy:r.cy,clockwise:s,r0:u,r:h}});return i&&(c.z2=x-y.get(v,t)%x),c}(g||d)&&(y.diff(h).add((function(e){var n=y.get(v,e);if(d){var i=w(e,o);fh(i,{rotation:-((isNaN(+n)?b[0]:Yr(n,_,b,!0))+Math.PI/2)},t),u.add(i),y.setItemGraphicEl(e,i)}if(g){var r=S(e,o),a=f.get("clip");fh(r,{shape:{endAngle:Yr(n,_,b,a)}},t),u.add(r),Qs(t.seriesIndex,y.dataType,e,r),p[e]=r}})).update((function(e,n){var i=y.get(v,e);if(d){var r=h.getItemGraphicEl(n),a=r?r.rotation:o,s=w(e,a);s.rotation=a,dh(s,{rotation:-((isNaN(+i)?b[0]:Yr(i,_,b,!0))+Math.PI/2)},t),u.add(s),y.setItemGraphicEl(e,s)}if(g){var l=c[n],m=S(e,l?l.shape.endAngle:o),x=f.get("clip");dh(m,{shape:{endAngle:Yr(i,_,b,x)}},t),u.add(m),Qs(t.seriesIndex,y.dataType,e,m),p[e]=m}})).execute(),y.each((function(t){var e=y.getItemModel(t),n=e.getModel("emphasis"),r=n.get("focus"),o=n.get("blurScope"),a=n.get("disabled");if(d){var s=y.getItemGraphicEl(t),l=y.getItemVisual(t,"style"),u=l.fill;if(s instanceof As){var h=s.style;s.useStyle(A({image:h.image,x:h.x,y:h.y,width:h.width,height:h.height},l))}else s.useStyle(l),"pointer"!==s.type&&s.setColor(u);s.setStyle(e.getModel(["pointer","itemStyle"]).getItemStyle()),"auto"===s.style.fill&&s.setStyle("fill",i(Yr(y.get(v,t),_,[0,1],!0))),s.z2EmphasisLift=0,Zl(s,e),Hl(s,r,o,a)}if(g){var c=p[t];c.useStyle(y.getItemVisual(t,"style")),c.setStyle(e.getModel(["progress","itemStyle"]).getItemStyle()),c.z2EmphasisLift=0,Zl(c,e),Hl(c,r,o,a)}})),this._progressEls=p)},e.prototype._renderAnchor=function(t,e){var n=t.getModel("anchor");if(n.get("show")){var i=n.get("size"),r=n.get("icon"),o=n.get("offsetCenter"),a=n.get("keepAspect"),s=Vy(r,e.cx-i/2+Ur(o[0],e.r),e.cy-i/2+Ur(o[1],e.r),i,i,null,a);s.z2=n.get("showAbove")?1:0,s.setStyle(n.getModel("itemStyle").getItemStyle()),this.group.add(s)}},e.prototype._renderTitleAndDetail=function(t,e,n,i,r){var o=this,a=t.getData(),s=a.mapDimension("value"),l=+t.get("min"),u=+t.get("max"),h=new Er,c=[],p=[],d=t.isAnimationEnabled(),f=t.get(["pointer","showAbove"]);a.diff(this._data).add((function(t){c[t]=new Bs({silent:!0}),p[t]=new Bs({silent:!0})})).update((function(t,e){c[t]=o._titleEls[e],p[t]=o._detailEls[e]})).execute(),a.each((function(e){var n=a.getItemModel(e),o=a.get(s,e),g=new Er,y=i(Yr(o,[l,u],[0,1],!0)),v=n.getModel("title");if(v.get("show")){var m=v.get("offsetCenter"),x=r.cx+Ur(m[0],r.r),_=r.cy+Ur(m[1],r.r);(D=c[e]).attr({z2:f?0:2,style:ec(v,{x:x,y:_,text:a.getName(e),align:"center",verticalAlign:"middle"},{inheritColor:y})}),g.add(D)}var b=n.getModel("detail");if(b.get("show")){var w=b.get("offsetCenter"),S=r.cx+Ur(w[0],r.r),M=r.cy+Ur(w[1],r.r),I=Ur(b.get("width"),r.r),T=Ur(b.get("height"),r.r),C=t.get(["progress","show"])?a.getItemVisual(e,"style").fill:y,D=p[e],A=b.get("formatter");D.attr({z2:f?0:2,style:ec(b,{x:S,y:M,text:KA(o,A),width:isNaN(I)?null:I,height:isNaN(T)?null:T,align:"center",verticalAlign:"middle"},{inheritColor:C})}),uc(D,{normal:b},o,(function(t){return KA(t,A)})),d&&hc(D,e,a,t,{getFormattedLabel:function(t,e,n,i,r,a){return KA(a?a.interpolatedValue:o,A)}}),g.add(D)}h.add(g)})),this.group.add(h),this._titleEls=c,this._detailEls=p},e.type="gauge",e}(Tg),JA=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.visualStyleAccessPath="itemStyle",n}return n(e,t),e.prototype.getInitialData=function(t,e){return vM(this,["value"])},e.type="series.gauge",e.defaultOption={z:2,colorBy:"data",center:["50%","50%"],legendHoverLink:!0,radius:"75%",startAngle:225,endAngle:-45,clockwise:!0,min:0,max:100,splitNumber:10,axisLine:{show:!0,roundCap:!1,lineStyle:{color:[[1,"#E6EBF8"]],width:10}},progress:{show:!1,overlap:!0,width:10,roundCap:!1,clip:!0},splitLine:{show:!0,length:10,distance:10,lineStyle:{color:"#63677A",width:3,type:"solid"}},axisTick:{show:!0,splitNumber:5,length:6,distance:10,lineStyle:{color:"#63677A",width:1,type:"solid"}},axisLabel:{show:!0,distance:15,color:"#464646",fontSize:12,rotate:0},pointer:{icon:null,offsetCenter:[0,0],show:!0,showAbove:!0,length:"60%",width:6,keepAspect:!1},anchor:{show:!1,showAbove:!1,size:6,icon:"circle",offsetCenter:[0,0],keepAspect:!1,itemStyle:{color:"#fff",borderWidth:0,borderColor:"#5470c6"}},title:{show:!0,offsetCenter:[0,"20%"],color:"#464646",fontSize:16,valueAnimation:!1},detail:{show:!0,backgroundColor:"rgba(0,0,0,0)",borderWidth:0,borderColor:"#ccc",width:100,height:null,padding:[5,10],offsetCenter:[0,"40%"],color:"#464646",fontSize:30,fontWeight:"bold",lineHeight:30,valueAnimation:!1}},e}(fg);var QA=["itemStyle","opacity"],tk=function(t){function e(e,n){var i=t.call(this)||this,r=i,o=new Hu,a=new Bs;return r.setTextContent(a),i.setTextGuideLine(o),i.updateData(e,n,!0),i}return n(e,t),e.prototype.updateData=function(t,e,n){var i=this,r=t.hostModel,o=t.getItemModel(e),a=t.getItemLayout(e),s=o.getModel("emphasis"),l=o.get(QA);l=null==l?1:l,n||xh(i),i.useStyle(t.getItemVisual(e,"style")),i.style.lineJoin="round",n?(i.setShape({points:a.points}),i.style.opacity=0,fh(i,{style:{opacity:l}},r,e)):dh(i,{style:{opacity:l},shape:{points:a.points}},r,e),Zl(i,o),this._updateLabel(t,e),Hl(this,s.get("focus"),s.get("blurScope"),s.get("disabled"))},e.prototype._updateLabel=function(t,e){var n=this,i=this.getTextGuideLine(),r=n.getTextContent(),o=t.hostModel,a=t.getItemModel(e),s=t.getItemLayout(e).label,l=t.getItemVisual(e,"style"),u=l.fill;Qh(r,tc(a),{labelFetcher:t.hostModel,labelDataIndex:e,defaultOpacity:l.opacity,defaultText:t.getName(e)},{normal:{align:s.textAlign,verticalAlign:s.verticalAlign}}),n.setTextConfig({local:!0,inside:!!s.inside,insideStroke:u,outsideFill:u});var h=s.linePoints;i.setShape({points:h}),n.textGuideLineConfig={anchor:h?new Ce(h[0][0],h[0][1]):null},dh(r,{style:{x:s.x,y:s.y}},o,e),r.attr({rotation:s.rotation,originX:s.x,originY:s.y,z2:10}),xb(n,_b(a),{stroke:u})},e}(Gu),ek=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.ignoreLabelLineUpdate=!0,n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData(),r=this._data,o=this.group;i.diff(r).add((function(t){var e=new tk(i,t);i.setItemGraphicEl(t,e),o.add(e)})).update((function(t,e){var n=r.getItemGraphicEl(e);n.updateData(i,t),o.add(n),i.setItemGraphicEl(t,n)})).remove((function(e){mh(r.getItemGraphicEl(e),t,e)})).execute(),this._data=i},e.prototype.remove=function(){this.group.removeAll(),this._data=null},e.prototype.dispose=function(){},e.type="funnel",e}(Tg),nk=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(e){t.prototype.init.apply(this,arguments),this.legendVisualProvider=new mM(W(this.getData,this),W(this.getRawData,this)),this._defaultLabelLine(e)},e.prototype.getInitialData=function(t,e){return vM(this,{coordDimensions:["value"],encodeDefaulter:H($p,this)})},e.prototype._defaultLabelLine=function(t){bo(t,"labelLine",["show"]);var e=t.labelLine,n=t.emphasis.labelLine;e.show=e.show&&t.label.show,n.show=n.show&&t.emphasis.label.show},e.prototype.getDataParams=function(e){var n=this.getData(),i=t.prototype.getDataParams.call(this,e),r=n.mapDimension("value"),o=n.getSum(r);return i.percent=o?+(n.get(r,e)/o*100).toFixed(2):0,i.$vars.push("percent"),i},e.type="series.funnel",e.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",left:80,top:60,right:80,bottom:60,minSize:"0%",maxSize:"100%",sort:"descending",orient:"vertical",gap:0,funnelAlign:"center",label:{show:!0,position:"outer"},labelLine:{show:!0,length:20,lineStyle:{width:1}},itemStyle:{borderColor:"#fff",borderWidth:1},emphasis:{label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}},e}(fg);function ik(t,e){t.eachSeriesByType("funnel",(function(t){var n=t.getData(),i=n.mapDimension("value"),r=t.get("sort"),o=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}(t,e),a=t.get("orient"),s=o.width,l=o.height,u=function(t,e){for(var n=t.mapDimension("value"),i=t.mapArray(n,(function(t){return t})),r=[],o="ascending"===e,a=0,s=t.count();a5)return;var i=this._model.coordinateSystem.getSlidedAxisExpandWindow([t.offsetX,t.offsetY]);"none"!==i.behavior&&this._dispatchExpand({axisExpandWindow:i.axisExpandWindow})}this._mouseDownPoint=null},mousemove:function(t){if(!this._mouseDownPoint&&yk(this,"mousemove")){var e=this._model,n=e.coordinateSystem.getSlidedAxisExpandWindow([t.offsetX,t.offsetY]),i=n.behavior;"jump"===i&&this._throttledDispatchExpand.debounceNextCall(e.get("axisExpandDebounce")),this._throttledDispatchExpand("none"===i?null:{axisExpandWindow:n.axisExpandWindow,animation:"jump"===i?null:{duration:0}})}}};function yk(t,e){var n=t._model;return n.get("axisExpandable")&&n.get("axisExpandTriggerOn")===e}var vk=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(){t.prototype.init.apply(this,arguments),this.mergeOption({})},e.prototype.mergeOption=function(t){var e=this.option;t&&C(e,t,!0),this._initDimensions()},e.prototype.contains=function(t,e){var n=t.get("parallelIndex");return null!=n&&e.getComponent("parallel",n)===this},e.prototype.setAxisExpand=function(t){E(["axisExpandable","axisExpandCenter","axisExpandCount","axisExpandWidth","axisExpandWindow"],(function(e){t.hasOwnProperty(e)&&(this.option[e]=t[e])}),this)},e.prototype._initDimensions=function(){var t=this.dimensions=[],e=this.parallelAxisIndex=[];E(B(this.ecModel.queryComponents({mainType:"parallelAxis"}),(function(t){return(t.get("parallelIndex")||0)===this.componentIndex}),this),(function(n){t.push("dim"+n.get("dim")),e.push(n.componentIndex)}))},e.type="parallel",e.dependencies=["parallelAxis"],e.layoutMode="box",e.defaultOption={z:0,left:80,top:60,right:80,bottom:60,layout:"horizontal",axisExpandable:!1,axisExpandCenter:null,axisExpandCount:0,axisExpandWidth:50,axisExpandRate:17,axisExpandDebounce:50,axisExpandSlideTriggerArea:[-.15,.05,.4],axisExpandTriggerOn:"click",parallelAxisDefault:null},e}(Op),mk=function(t){function e(e,n,i,r,o){var a=t.call(this,e,n,i)||this;return a.type=r||"value",a.axisIndex=o,a}return n(e,t),e.prototype.isHorizontal=function(){return"horizontal"!==this.coordinateSystem.getModel().get("layout")},e}(q_);function xk(t,e,n,i,r,o){t=t||0;var a=n[1]-n[0];if(null!=r&&(r=bk(r,[0,a])),null!=o&&(o=Math.max(o,null!=r?r:0)),"all"===i){var s=Math.abs(e[1]-e[0]);s=bk(s,[0,a]),r=o=bk(s,[r,o]),i=0}e[0]=bk(e[0],n),e[1]=bk(e[1],n);var l=_k(e,i);e[i]+=t;var u,h=r||0,c=n.slice();return l.sign<0?c[0]+=h:c[1]-=h,e[i]=bk(e[i],c),u=_k(e,i),null!=r&&(u.sign!==l.sign||u.spano&&(e[1-i]=e[i]+u.sign*o),e}function _k(t,e){var n=t[e]-t[1-e];return{span:Math.abs(n),sign:n>0?-1:n<0?1:e?-1:1}}function bk(t,e){return Math.min(null!=e[1]?e[1]:1/0,Math.max(null!=e[0]?e[0]:-1/0,t))}var wk=E,Sk=Math.min,Mk=Math.max,Ik=Math.floor,Tk=Math.ceil,Ck=Xr,Dk=Math.PI,Ak=function(){function t(t,e,n){this.type="parallel",this._axesMap=yt(),this._axesLayout={},this.dimensions=t.dimensions,this._model=t,this._init(t,e,n)}return t.prototype._init=function(t,e,n){var i=t.dimensions,r=t.parallelAxisIndex;wk(i,(function(t,n){var i=r[n],o=e.getComponent("parallelAxis",i),a=this._axesMap.set(t,new mk(t,c_(o),[0,0],o.get("type"),i)),s="category"===a.type;a.onBand=s&&o.get("boundaryGap"),a.inverse=o.get("inverse"),o.axis=a,a.model=o,a.coordinateSystem=o.coordinateSystem=this}),this)},t.prototype.update=function(t,e){this._updateAxesFromSeries(this._model,t)},t.prototype.containPoint=function(t){var e=this._makeLayoutInfo(),n=e.axisBase,i=e.layoutBase,r=e.pixelDimIndex,o=t[1-r],a=t[r];return o>=n&&o<=n+e.axisLength&&a>=i&&a<=i+e.layoutLength},t.prototype.getModel=function(){return this._model},t.prototype._updateAxesFromSeries=function(t,e){e.eachSeries((function(n){if(t.contains(n,e)){var i=n.getData();wk(this.dimensions,(function(t){var e=this._axesMap.get(t);e.scale.unionExtentFromData(i,i.mapDimension(t)),h_(e.scale,e.model)}),this)}}),this)},t.prototype.resize=function(t,e){this._rect=Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()}),this._layoutAxes()},t.prototype.getRect=function(){return this._rect},t.prototype._makeLayoutInfo=function(){var t,e=this._model,n=this._rect,i=["x","y"],r=["width","height"],o=e.get("layout"),a="horizontal"===o?0:1,s=n[r[a]],l=[0,s],u=this.dimensions.length,h=kk(e.get("axisExpandWidth"),l),c=kk(e.get("axisExpandCount")||0,[0,u]),p=e.get("axisExpandable")&&u>3&&u>c&&c>1&&h>0&&s>0,d=e.get("axisExpandWindow");d?(t=kk(d[1]-d[0],l),d[1]=d[0]+t):(t=kk(h*(c-1),l),(d=[h*(e.get("axisExpandCenter")||Ik(u/2))-t/2])[1]=d[0]+t);var f=(s-t)/(u-c);f<3&&(f=0);var g=[Ik(Ck(d[0]/h,1))+1,Tk(Ck(d[1]/h,1))-1],y=f/h*d[0];return{layout:o,pixelDimIndex:a,layoutBase:n[i[a]],layoutLength:s,axisBase:n[i[1-a]],axisLength:n[r[1-a]],axisExpandable:p,axisExpandWidth:h,axisCollapseWidth:f,axisExpandWindow:d,axisCount:u,winInnerIndices:g,axisExpandWindow0Pos:y}},t.prototype._layoutAxes=function(){var t=this._rect,e=this._axesMap,n=this.dimensions,i=this._makeLayoutInfo(),r=i.layout;e.each((function(t){var e=[0,i.axisLength],n=t.inverse?1:0;t.setExtent(e[n],e[1-n])})),wk(n,(function(e,n){var o=(i.axisExpandable?Pk:Lk)(n,i),a={horizontal:{x:o.position,y:i.axisLength},vertical:{x:0,y:o.position}},s={horizontal:Dk/2,vertical:0},l=[a[r].x+t.x,a[r].y+t.y],u=s[r],h=[1,0,0,1,0,0];we(h,h,u),be(h,h,l),this._axesLayout[e]={position:l,rotation:u,transform:h,axisNameAvailableWidth:o.axisNameAvailableWidth,axisLabelShow:o.axisLabelShow,nameTruncateMaxWidth:o.nameTruncateMaxWidth,tickDirection:1,labelDirection:1}}),this)},t.prototype.getAxis=function(t){return this._axesMap.get(t)},t.prototype.dataToPoint=function(t,e){return this.axisCoordToPoint(this._axesMap.get(e).dataToCoord(t),e)},t.prototype.eachActiveState=function(t,e,n,i){null==n&&(n=0),null==i&&(i=t.count());var r=this._axesMap,o=this.dimensions,a=[],s=[];E(o,(function(e){a.push(t.mapDimension(e)),s.push(r.get(e).model)}));for(var l=this.hasAxisBrushed(),u=n;ur*(1-h[0])?(l="jump",a=s-r*(1-h[2])):(a=s-r*h[1])>=0&&(a=s-r*(1-h[1]))<=0&&(a=0),(a*=e.axisExpandWidth/u)?xk(a,i,o,"all"):l="none";else{var p=i[1]-i[0];(i=[Mk(0,o[1]*s/p-p/2)])[1]=Sk(o[1],i[0]+p),i[0]=i[1]-p}return{axisExpandWindow:i,behavior:l}},t}();function kk(t,e){return Sk(Mk(t,e[0]),e[1])}function Lk(t,e){var n=e.layoutLength/(e.axisCount-1);return{position:n*t,axisNameAvailableWidth:n,axisLabelShow:!0}}function Pk(t,e){var n,i,r=e.layoutLength,o=e.axisExpandWidth,a=e.axisCount,s=e.axisCollapseWidth,l=e.winInnerIndices,u=s,h=!1;return t=0;n--)Zr(e[n])},e.prototype.getActiveState=function(t){var e=this.activeIntervals;if(!e.length)return"normal";if(null==t||isNaN(+t))return"inactive";if(1===e.length){var n=e[0];if(n[0]<=t&&t<=n[1])return"active"}else for(var i=0,r=e.length;i6}(t)||o){if(a&&!o){"single"===s.brushMode&&Qk(t);var l=T(s);l.brushType=yL(l.brushType,a),l.panelId=a===Nk?null:a.panelId,o=t._creatingCover=Uk(t,l),t._covers.push(o)}if(o){var u=xL[yL(t._brushType,a)];o.__brushOption.range=u.getCreatingRange(pL(t,o,t._track)),i&&(Xk(t,o),u.updateCommon(t,o)),Zk(t,o),r={isEnd:i}}}else i&&"single"===s.brushMode&&s.removeOnClick&&$k(t,e,n)&&Qk(t)&&(r={isEnd:i,removeOnClick:!0});return r}function yL(t,e){return"auto"===t?e.defaultBrushType:t}var vL={mousedown:function(t){if(this._dragging)mL(this,t);else if(!t.target||!t.target.draggable){dL(t);var e=this.group.transformCoordToLocal(t.offsetX,t.offsetY);this._creatingCover=null,(this._creatingPanel=$k(this,t,e))&&(this._dragging=!0,this._track=[e.slice()])}},mousemove:function(t){var e=t.offsetX,n=t.offsetY,i=this.group.transformCoordToLocal(e,n);if(function(t,e,n){if(t._brushType&&!function(t,e,n){var i=t._zr;return e<0||e>i.getWidth()||n<0||n>i.getHeight()}(t,e.offsetX,e.offsetY)){var i=t._zr,r=t._covers,o=$k(t,e,n);if(!t._dragging)for(var a=0;a=0&&(o[r[a].depth]=new Sc(r[a],this,e));if(i&&n){var s=UA(i,n,this,!0,(function(t,e){t.wrapMethod("getItemModel",(function(t,e){var n=t.parentModel,i=n.getData().getItemLayout(e);if(i){var r=i.depth,o=n.levelModels[r];o&&(t.parentModel=o)}return t})),e.wrapMethod("getItemModel",(function(t,e){var n=t.parentModel,i=n.getGraph().getEdgeByIndex(e).node1.getLayout();if(i){var r=i.depth,o=n.levelModels[r];o&&(t.parentModel=o)}return t}))}));return s.data}},e.prototype.setNodePosition=function(t,e){var n=(this.option.data||this.option.nodes)[t];n.localX=e[0],n.localY=e[1]},e.prototype.getGraph=function(){return this.getData().graph},e.prototype.getEdgeData=function(){return this.getGraph().edgeData},e.prototype.formatTooltip=function(t,e,n){function i(t){return isNaN(t)||null==t}if("edge"===n){var r=this.getDataParams(t,n),o=r.data,a=r.value;return Qf("nameValue",{name:o.source+" -- "+o.target,value:a,noValue:i(a)})}var s=this.getGraph().getNodeByIndex(t).getLayout().value,l=this.getDataParams(t,n).data.name;return Qf("nameValue",{name:null!=l?l+"":null,value:s,noValue:i(s)})},e.prototype.optionUpdated=function(){},e.prototype.getDataParams=function(e,n){var i=t.prototype.getDataParams.call(this,e,n);if(null==i.value&&"node"===n){var r=this.getGraph().getNodeByIndex(e).getLayout().value;i.value=r}return i},e.type="series.sankey",e.defaultOption={z:2,coordinateSystem:"view",left:"5%",top:"5%",right:"20%",bottom:"5%",orient:"horizontal",nodeWidth:20,nodeGap:8,draggable:!0,layoutIterations:32,label:{show:!0,position:"right",fontSize:12},edgeLabel:{show:!1,fontSize:12},levels:[],nodeAlign:"justify",lineStyle:{color:"#314656",opacity:.2,curveness:.5},emphasis:{label:{show:!0},lineStyle:{opacity:.5}},select:{itemStyle:{borderColor:"#212121"}},animationEasing:"linear",animationDuration:1e3},e}(fg);function RL(t,e){t.eachSeriesByType("sankey",(function(t){var n=t.get("nodeWidth"),i=t.get("nodeGap"),r=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}(t,e);t.layoutInfo=r;var o=r.width,a=r.height,s=t.getGraph(),l=s.nodes,u=s.edges;!function(t){E(t,(function(t){var e=YL(t.outEdges,HL),n=YL(t.inEdges,HL),i=t.getValue()||0,r=Math.max(e,n,i);t.setLayout({value:r},!0)}))}(l),function(t,e,n,i,r,o,a,s,l){(function(t,e,n,i,r,o,a){for(var s=[],l=[],u=[],h=[],c=0,p=0;p=0;v&&y.depth>d&&(d=y.depth),g.setLayout({depth:v?y.depth:c},!0),"vertical"===o?g.setLayout({dy:n},!0):g.setLayout({dx:n},!0);for(var m=0;mc-1?d:c-1;a&&"left"!==a&&function(t,e,n,i){if("right"===e){for(var r=[],o=t,a=0;o.length;){for(var s=0;s0;o--)zL(s,l*=.99,a),EL(s,r,n,i,a),UL(s,l,a),EL(s,r,n,i,a)}(t,e,o,r,i,a,s),function(t,e){var n="vertical"===e?"x":"y";E(t,(function(t){t.outEdges.sort((function(t,e){return t.node2.getLayout()[n]-e.node2.getLayout()[n]})),t.inEdges.sort((function(t,e){return t.node1.getLayout()[n]-e.node1.getLayout()[n]}))})),E(t,(function(t){var e=0,n=0;E(t.outEdges,(function(t){t.setLayout({sy:e},!0),e+=t.getLayout().dy})),E(t.inEdges,(function(t){t.setLayout({ty:n},!0),n+=t.getLayout().dy}))}))}(t,s)}(l,u,n,i,o,a,0!==B(l,(function(t){return 0===t.getLayout().value})).length?0:t.get("layoutIterations"),t.get("orient"),t.get("nodeAlign"))}))}function NL(t){var e=t.hostGraph.data.getRawDataItem(t.dataIndex);return null!=e.depth&&e.depth>=0}function EL(t,e,n,i,r){var o="vertical"===r?"x":"y";E(t,(function(t){var a,s,l;t.sort((function(t,e){return t.getLayout()[o]-e.getLayout()[o]}));for(var u=0,h=t.length,c="vertical"===r?"dx":"dy",p=0;p0&&(a=s.getLayout()[o]+l,"vertical"===r?s.setLayout({x:a},!0):s.setLayout({y:a},!0)),u=s.getLayout()[o]+s.getLayout()[c]+e;if((l=u-e-("vertical"===r?i:n))>0){a=s.getLayout()[o]-l,"vertical"===r?s.setLayout({x:a},!0):s.setLayout({y:a},!0),u=a;for(p=h-2;p>=0;--p)(l=(s=t[p]).getLayout()[o]+s.getLayout()[c]+e-u)>0&&(a=s.getLayout()[o]-l,"vertical"===r?s.setLayout({x:a},!0):s.setLayout({y:a},!0)),u=s.getLayout()[o]}}))}function zL(t,e,n){E(t.slice().reverse(),(function(t){E(t,(function(t){if(t.outEdges.length){var i=YL(t.outEdges,VL,n)/YL(t.outEdges,HL);if(isNaN(i)){var r=t.outEdges.length;i=r?YL(t.outEdges,BL,n)/r:0}if("vertical"===n){var o=t.getLayout().x+(i-WL(t,n))*e;t.setLayout({x:o},!0)}else{var a=t.getLayout().y+(i-WL(t,n))*e;t.setLayout({y:a},!0)}}}))}))}function VL(t,e){return WL(t.node2,e)*t.getValue()}function BL(t,e){return WL(t.node2,e)}function FL(t,e){return WL(t.node1,e)*t.getValue()}function GL(t,e){return WL(t.node1,e)}function WL(t,e){return"vertical"===e?t.getLayout().x+t.getLayout().dx/2:t.getLayout().y+t.getLayout().dy/2}function HL(t){return t.getValue()}function YL(t,e,n){for(var i=0,r=t.length,o=-1;++oo&&(o=e)})),E(n,(function(e){var n=new dD({type:"color",mappingMethod:"linear",dataExtent:[r,o],visual:t.get("color")}).mapValueToVisual(e.getLayout().value),i=e.getModel().get(["itemStyle","color"]);null!=i?(e.setVisual("color",i),e.setVisual("style",{fill:i})):(e.setVisual("color",n),e.setVisual("style",{fill:n}))}))}i.length&&E(i,(function(t){var e=t.getModel().get("lineStyle");t.setVisual("style",e)}))}))}var ZL=function(){function t(){}return t.prototype.getInitialData=function(t,e){var n,i,r=e.getComponent("xAxis",this.get("xAxisIndex")),o=e.getComponent("yAxis",this.get("yAxisIndex")),a=r.get("type"),s=o.get("type");"category"===a?(t.layout="horizontal",n=r.getOrdinalMeta(),i=!0):"category"===s?(t.layout="vertical",n=o.getOrdinalMeta(),i=!0):t.layout=t.layout||"horizontal";var l=["x","y"],u="horizontal"===t.layout?0:1,h=this._baseAxisDim=l[u],c=l[1-u],p=[r,o],d=p[u].get("type"),f=p[1-u].get("type"),g=t.data;if(g&&i){var y=[];E(g,(function(t,e){var n;Y(t)?(n=t.slice(),t.unshift(e)):Y(t.value)?((n=A({},t)).value=n.value.slice(),t.value.unshift(e)):n=t,y.push(n)})),t.data=y}var v=this.defaultValueDimensions,m=[{name:h,type:Rm(d),ordinalMeta:n,otherDims:{tooltip:!1,itemName:0},dimsDef:["base"]},{name:c,type:Rm(f),dimsDef:v.slice()}];return vM(this,{coordDimensions:m,dimensionsCount:v.length+1,encodeDefaulter:H(Kp,m,this)})},t.prototype.getBaseAxis=function(){var t=this._baseAxisDim;return this.ecModel.getComponent(t+"Axis",this.get(t+"AxisIndex")).axis},t}(),jL=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.defaultValueDimensions=[{name:"min",defaultTooltip:!0},{name:"Q1",defaultTooltip:!0},{name:"median",defaultTooltip:!0},{name:"Q3",defaultTooltip:!0},{name:"max",defaultTooltip:!0}],n.visualDrawType="stroke",n}return n(e,t),e.type="series.boxplot",e.dependencies=["xAxis","yAxis","grid"],e.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,boxWidth:[7,50],itemStyle:{color:"#fff",borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2,shadowBlur:5,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0,0,0,0.2)"}},animationDuration:800},e}(fg);R(jL,ZL,!0);var qL=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData(),r=this.group,o=this._data;this._data||r.removeAll();var a="horizontal"===t.get("layout")?1:0;i.diff(o).add((function(t){if(i.hasValue(t)){var e=JL(i.getItemLayout(t),i,t,a,!0);i.setItemGraphicEl(t,e),r.add(e)}})).update((function(t,e){var n=o.getItemGraphicEl(e);if(i.hasValue(t)){var s=i.getItemLayout(t);n?(xh(n),QL(s,n,i,t)):n=JL(s,i,t,a),r.add(n),i.setItemGraphicEl(t,n)}else r.remove(n)})).remove((function(t){var e=o.getItemGraphicEl(t);e&&r.remove(e)})).execute(),this._data=i},e.prototype.remove=function(t){var e=this.group,n=this._data;this._data=null,n&&n.eachItemGraphicEl((function(t){t&&e.remove(t)}))},e.type="boxplot",e}(Tg),KL=function(){},$L=function(t){function e(e){var n=t.call(this,e)||this;return n.type="boxplotBoxPath",n}return n(e,t),e.prototype.getDefaultShape=function(){return new KL},e.prototype.buildPath=function(t,e){var n=e.points,i=0;for(t.moveTo(n[i][0],n[i][1]),i++;i<4;i++)t.lineTo(n[i][0],n[i][1]);for(t.closePath();ig){var _=[v,x];i.push(_)}}}return{boxData:n,outliers:i}}(e.getRawData(),t.config);return[{dimensions:["ItemName","Low","Q1","Q2","Q3","High"],data:i.boxData},{data:i.outliers}]}};var rP=["color","borderColor"],oP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){this.group.removeClipPath(),this._progressiveEls=null,this._updateDrawMode(t),this._isLargeDraw?this._renderLarge(t):this._renderNormal(t)},e.prototype.incrementalPrepareRender=function(t,e,n){this._clear(),this._updateDrawMode(t)},e.prototype.incrementalRender=function(t,e,n,i){this._progressiveEls=[],this._isLargeDraw?this._incrementalRenderLarge(t,e):this._incrementalRenderNormal(t,e)},e.prototype.eachRendered=function(t){jh(this._progressiveEls||this.group,t)},e.prototype._updateDrawMode=function(t){var e=t.pipelineContext.large;null!=this._isLargeDraw&&e===this._isLargeDraw||(this._isLargeDraw=e,this._clear())},e.prototype._renderNormal=function(t){var e=t.getData(),n=this._data,i=this.group,r=e.getLayout("isSimpleBox"),o=t.get("clip",!0),a=t.coordinateSystem,s=a.getArea&&a.getArea();this._data||i.removeAll(),e.diff(n).add((function(n){if(e.hasValue(n)){var a=e.getItemLayout(n);if(o&&uP(s,a))return;var l=lP(a,n,!0);fh(l,{shape:{points:a.ends}},t,n),hP(l,e,n,r),i.add(l),e.setItemGraphicEl(n,l)}})).update((function(a,l){var u=n.getItemGraphicEl(l);if(e.hasValue(a)){var h=e.getItemLayout(a);o&&uP(s,h)?i.remove(u):(u?(dh(u,{shape:{points:h.ends}},t,a),xh(u)):u=lP(h),hP(u,e,a,r),i.add(u),e.setItemGraphicEl(a,u))}else i.remove(u)})).remove((function(t){var e=n.getItemGraphicEl(t);e&&i.remove(e)})).execute(),this._data=e},e.prototype._renderLarge=function(t){this._clear(),fP(t,this.group);var e=t.get("clip",!0)?yS(t.coordinateSystem,!1,t):null;e?this.group.setClipPath(e):this.group.removeClipPath()},e.prototype._incrementalRenderNormal=function(t,e){for(var n,i=e.getData(),r=i.getLayout("isSimpleBox");null!=(n=t.next());){var o=lP(i.getItemLayout(n));hP(o,i,n,r),o.incremental=!0,this.group.add(o),this._progressiveEls.push(o)}},e.prototype._incrementalRenderLarge=function(t,e){fP(e,this.group,this._progressiveEls,!0)},e.prototype.remove=function(t){this._clear()},e.prototype._clear=function(){this.group.removeAll(),this._data=null},e.type="candlestick",e}(Tg),aP=function(){},sP=function(t){function e(e){var n=t.call(this,e)||this;return n.type="normalCandlestickBox",n}return n(e,t),e.prototype.getDefaultShape=function(){return new aP},e.prototype.buildPath=function(t,e){var n=e.points;this.__simpleBox?(t.moveTo(n[4][0],n[4][1]),t.lineTo(n[6][0],n[6][1])):(t.moveTo(n[0][0],n[0][1]),t.lineTo(n[1][0],n[1][1]),t.lineTo(n[2][0],n[2][1]),t.lineTo(n[3][0],n[3][1]),t.closePath(),t.moveTo(n[4][0],n[4][1]),t.lineTo(n[5][0],n[5][1]),t.moveTo(n[6][0],n[6][1]),t.lineTo(n[7][0],n[7][1]))},e}(Ms);function lP(t,e,n){var i=t.ends;return new sP({shape:{points:n?cP(i,t):i},z2:100})}function uP(t,e){for(var n=!0,i=0;i0?"borderColor":"borderColor0"])||n.get(["itemStyle",t>0?"color":"color0"]);0===t&&(r=n.get(["itemStyle","borderColorDoji"]));var o=n.getModel("itemStyle").getItemStyle(rP);e.useStyle(o),e.style.fill=null,e.style.stroke=r}var yP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.defaultValueDimensions=[{name:"open",defaultTooltip:!0},{name:"close",defaultTooltip:!0},{name:"lowest",defaultTooltip:!0},{name:"highest",defaultTooltip:!0}],n}return n(e,t),e.prototype.getShadowDim=function(){return"open"},e.prototype.brushSelector=function(t,e,n){var i=e.getItemLayout(t);return i&&n.rect(i.brushRect)},e.type="series.candlestick",e.dependencies=["xAxis","yAxis","grid"],e.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,clip:!0,itemStyle:{color:"#eb5454",color0:"#47b262",borderColor:"#eb5454",borderColor0:"#47b262",borderColorDoji:null,borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2}},barMaxWidth:null,barMinWidth:null,barWidth:null,large:!0,largeThreshold:600,progressive:3e3,progressiveThreshold:1e4,progressiveChunkMode:"mod",animationEasing:"linear",animationDuration:300},e}(fg);function vP(t){t&&Y(t.series)&&E(t.series,(function(t){q(t)&&"k"===t.type&&(t.type="candlestick")}))}R(yP,ZL,!0);var mP=["itemStyle","borderColor"],xP=["itemStyle","borderColor0"],_P=["itemStyle","borderColorDoji"],bP=["itemStyle","color"],wP=["itemStyle","color0"],SP={seriesType:"candlestick",plan:Sg(),performRawSeries:!0,reset:function(t,e){function n(t,e){return e.get(t>0?bP:wP)}function i(t,e){return e.get(0===t?_P:t>0?mP:xP)}if(!e.isSeriesFiltered(t))return!t.pipelineContext.large&&{progress:function(t,e){for(var r;null!=(r=t.next());){var o=e.getItemModel(r),a=e.getItemLayout(r).sign,s=o.getItemStyle();s.fill=n(a,o),s.stroke=i(a,o)||s.fill,A(e.ensureUniqueItemVisual(r,"style"),s)}}}}},MP={seriesType:"candlestick",plan:Sg(),reset:function(t){var e=t.coordinateSystem,n=t.getData(),i=function(t,e){var n,i=t.getBaseAxis(),r="category"===i.type?i.getBandWidth():(n=i.getExtent(),Math.abs(n[1]-n[0])/e.count()),o=Ur(rt(t.get("barMaxWidth"),r),r),a=Ur(rt(t.get("barMinWidth"),1),r),s=t.get("barWidth");return null!=s?Ur(s,r):Math.max(Math.min(r/2,o),a)}(t,n),r=["x","y"],o=n.getDimensionIndex(n.mapDimension(r[0])),a=z(n.mapDimensionsAll(r[1]),n.getDimensionIndex,n),s=a[0],l=a[1],u=a[2],h=a[3];if(n.setLayout({candleWidth:i,isSimpleBox:i<=1.3}),!(o<0||a.length<4))return{progress:t.pipelineContext.large?function(n,i){var r,a,c=Ax(4*n.count),p=0,d=[],f=[],g=i.getStore(),y=!!t.get(["itemStyle","borderColorDoji"]);for(;null!=(a=n.next());){var v=g.get(o,a),m=g.get(s,a),x=g.get(l,a),_=g.get(u,a),b=g.get(h,a);isNaN(v)||isNaN(_)||isNaN(b)?(c[p++]=NaN,p+=3):(c[p++]=IP(g,a,m,x,l,y),d[0]=v,d[1]=_,r=e.dataToPoint(d,null,f),c[p++]=r?r[0]:NaN,c[p++]=r?r[1]:NaN,d[1]=b,r=e.dataToPoint(d,null,f),c[p++]=r?r[1]:NaN)}i.setLayout("largePoints",c)}:function(t,n){var r,a=n.getStore();for(;null!=(r=t.next());){var c=a.get(o,r),p=a.get(s,r),d=a.get(l,r),f=a.get(u,r),g=a.get(h,r),y=Math.min(p,d),v=Math.max(p,d),m=M(y,c),x=M(v,c),_=M(f,c),b=M(g,c),w=[];I(w,x,0),I(w,m,1),w.push(C(b),C(x),C(_),C(m));var S=!!n.getItemModel(r).get(["itemStyle","borderColorDoji"]);n.setItemLayout(r,{sign:IP(a,r,p,d,l,S),initBaseline:p>d?x[1]:m[1],ends:w,brushRect:T(f,g,c)})}function M(t,n){var i=[];return i[0]=n,i[1]=t,isNaN(n)||isNaN(t)?[NaN,NaN]:e.dataToPoint(i)}function I(t,e,n){var r=e.slice(),o=e.slice();r[0]=Rh(r[0]+i/2,1,!1),o[0]=Rh(o[0]-i/2,1,!0),n?t.push(r,o):t.push(o,r)}function T(t,e,n){var r=M(t,n),o=M(e,n);return r[0]-=i/2,o[0]-=i/2,{x:r[0],y:r[1],width:i,height:o[1]-r[1]}}function C(t){return t[0]=Rh(t[0],1),t}}}}};function IP(t,e,n,i,r,o){return n>i?-1:n0?t.get(r,e-1)<=i?1:-1:1}function TP(t,e){var n=e.rippleEffectColor||e.color;t.eachChild((function(t){t.attr({z:e.z,zlevel:e.zlevel,style:{stroke:"stroke"===e.brushType?n:null,fill:"fill"===e.brushType?n:null}})}))}var CP=function(t){function e(e,n){var i=t.call(this)||this,r=new Jw(e,n),o=new Er;return i.add(r),i.add(o),i.updateData(e,n),i}return n(e,t),e.prototype.stopEffectAnimation=function(){this.childAt(1).removeAll()},e.prototype.startEffectAnimation=function(t){for(var e=t.symbolType,n=t.color,i=t.rippleNumber,r=this.childAt(1),o=0;o0&&(o=this._getLineLength(i)/l*1e3),o!==this._period||a!==this._loop||s!==this._roundTrip){i.stopAnimation();var h=void 0;h=U(u)?u(n):u,i.__t>0&&(h=-o*i.__t),this._animateSymbol(i,o,h,a,s)}this._period=o,this._loop=a,this._roundTrip=s}},e.prototype._animateSymbol=function(t,e,n,i,r){if(e>0){t.__t=0;var o=this,a=t.animate("",i).when(r?2*e:e,{__t:r?2:1}).delay(n).during((function(){o._updateSymbolPosition(t)}));i||a.done((function(){o.remove(t)})),a.start()}},e.prototype._getLineLength=function(t){return Vt(t.__p1,t.__cp1)+Vt(t.__cp1,t.__p2)},e.prototype._updateAnimationPoints=function(t,e){t.__p1=e[0],t.__p2=e[1],t.__cp1=e[2]||[(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2]},e.prototype.updateData=function(t,e,n){this.childAt(0).updateData(t,e,n),this._updateEffectSymbol(t,e)},e.prototype._updateSymbolPosition=function(t){var e=t.__p1,n=t.__p2,i=t.__cp1,r=t.__t<1?t.__t:2-t.__t,o=[t.x,t.y],a=o.slice(),s=Mn,l=In;o[0]=s(e[0],i[0],n[0],r),o[1]=s(e[1],i[1],n[1],r);var u=t.__t<1?l(e[0],i[0],n[0],r):l(n[0],i[0],e[0],1-r),h=t.__t<1?l(e[1],i[1],n[1],r):l(n[1],i[1],e[1],1-r);t.rotation=-Math.atan2(h,u)-Math.PI/2,"line"!==this._symbolType&&"rect"!==this._symbolType&&"roundRect"!==this._symbolType||(void 0!==t.__lastT&&t.__lastT=0&&!(i[o]<=e);o--);o=Math.min(o,r-2)}else{for(o=a;oe);o++);o=Math.min(o-1,r-2)}var s=(e-i[o])/(i[o+1]-i[o]),l=n[o],u=n[o+1];t.x=l[0]*(1-s)+s*u[0],t.y=l[1]*(1-s)+s*u[1];var h=t.__t<1?u[0]-l[0]:l[0]-u[0],c=t.__t<1?u[1]-l[1]:l[1]-u[1];t.rotation=-Math.atan2(c,h)-Math.PI/2,this._lastFrame=o,this._lastFramePercent=e,t.ignore=!1}},e}(kP),OP=function(){this.polyline=!1,this.curveness=0,this.segs=[]},RP=function(t){function e(e){var n=t.call(this,e)||this;return n._off=0,n.hoverDataIdx=-1,n}return n(e,t),e.prototype.reset=function(){this.notClear=!1,this._off=0},e.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},e.prototype.getDefaultShape=function(){return new OP},e.prototype.buildPath=function(t,e){var n,i=e.segs,r=e.curveness;if(e.polyline)for(n=this._off;n0){t.moveTo(i[n++],i[n++]);for(var a=1;a0){var c=(s+u)/2-(l-h)*r,p=(l+h)/2-(u-s)*r;t.quadraticCurveTo(c,p,u,h)}else t.lineTo(u,h)}this.incremental&&(this._off=n,this.notClear=!0)},e.prototype.findDataIndex=function(t,e){var n=this.shape,i=n.segs,r=n.curveness,o=this.style.lineWidth;if(n.polyline)for(var a=0,s=0;s0)for(var u=i[s++],h=i[s++],c=1;c0){if(ss(u,h,(u+p)/2-(h-d)*r,(h+d)/2-(p-u)*r,p,d,o,t,e))return a}else if(os(u,h,p,d,o,t,e))return a;a++}return-1},e.prototype.contain=function(t,e){var n=this.transformCoordToLocal(t,e),i=this.getBoundingRect();return t=n[0],e=n[1],i.contain(t,e)?(this.hoverDataIdx=this.findDataIndex(t,e))>=0:(this.hoverDataIdx=-1,!1)},e.prototype.getBoundingRect=function(){var t=this._rect;if(!t){for(var e=this.shape.segs,n=1/0,i=1/0,r=-1/0,o=-1/0,a=0;a0&&(o.dataIndex=n+t.__startIndex)}))},t.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},t}(),EP={seriesType:"lines",plan:Sg(),reset:function(t){var e=t.coordinateSystem;if(e){var n=t.get("polyline"),i=t.pipelineContext.large;return{progress:function(r,o){var a=[];if(i){var s=void 0,l=r.end-r.start;if(n){for(var u=0,h=r.start;h0&&(l||s.configLayer(o,{motionBlur:!0,lastFrameAlpha:Math.max(Math.min(a/10+.9,1),0)})),r.updateData(i);var u=t.get("clip",!0)&&yS(t.coordinateSystem,!1,t);u?this.group.setClipPath(u):this.group.removeClipPath(),this._lastZlevel=o,this._finished=!0},e.prototype.incrementalPrepareRender=function(t,e,n){var i=t.getData();this._updateLineDraw(i,t).incrementalPrepareUpdate(i),this._clearLayer(n),this._finished=!1},e.prototype.incrementalRender=function(t,e,n){this._lineDraw.incrementalUpdate(t,e.getData()),this._finished=t.end===e.getData().count()},e.prototype.eachRendered=function(t){this._lineDraw&&this._lineDraw.eachRendered(t)},e.prototype.updateTransform=function(t,e,n){var i=t.getData(),r=t.pipelineContext;if(!this._finished||r.large||r.progressiveRender)return{update:!0};var o=EP.reset(t,e,n);o.progress&&o.progress({start:0,end:i.count(),count:i.count()},i),this._lineDraw.updateLayout(),this._clearLayer(n)},e.prototype._updateLineDraw=function(t,e){var n=this._lineDraw,i=this._showEffect(e),r=!!e.get("polyline"),o=e.pipelineContext.large;return n&&i===this._hasEffet&&r===this._isPolyline&&o===this._isLargeDraw||(n&&n.remove(),n=this._lineDraw=o?new NP:new TA(r?i?PP:LP:i?kP:IA),this._hasEffet=i,this._isPolyline=r,this._isLargeDraw=o),this.group.add(n.group),n},e.prototype._showEffect=function(t){return!!t.get(["effect","show"])},e.prototype._clearLayer=function(t){var e=t.getZr();"svg"===e.painter.getType()||null==this._lastZlevel||e.painter.getLayer(this._lastZlevel).clear(!0)},e.prototype.remove=function(t,e){this._lineDraw&&this._lineDraw.remove(),this._lineDraw=null,this._clearLayer(e)},e.prototype.dispose=function(t,e){this.remove(t,e)},e.type="lines",e}(Tg),VP="undefined"==typeof Uint32Array?Array:Uint32Array,BP="undefined"==typeof Float64Array?Array:Float64Array;function FP(t){var e=t.data;e&&e[0]&&e[0][0]&&e[0][0].coord&&(t.data=z(e,(function(t){var e={coords:[t[0].coord,t[1].coord]};return t[0].name&&(e.fromName=t[0].name),t[1].name&&(e.toName=t[1].name),D([e,t[0],t[1]])})))}var GP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.visualStyleAccessPath="lineStyle",n.visualDrawType="stroke",n}return n(e,t),e.prototype.init=function(e){e.data=e.data||[],FP(e);var n=this._processFlatCoordsArray(e.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(e.data=new Float32Array(n.count)),t.prototype.init.apply(this,arguments)},e.prototype.mergeOption=function(e){if(FP(e),e.data){var n=this._processFlatCoordsArray(e.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(e.data=new Float32Array(n.count))}t.prototype.mergeOption.apply(this,arguments)},e.prototype.appendData=function(t){var e=this._processFlatCoordsArray(t.data);e.flatCoords&&(this._flatCoords?(this._flatCoords=vt(this._flatCoords,e.flatCoords),this._flatCoordsOffset=vt(this._flatCoordsOffset,e.flatCoordsOffset)):(this._flatCoords=e.flatCoords,this._flatCoordsOffset=e.flatCoordsOffset),t.data=new Float32Array(e.count)),this.getRawData().appendData(t.data)},e.prototype._getCoordsFromItemModel=function(t){var e=this.getData().getItemModel(t),n=e.option instanceof Array?e.option:e.getShallow("coords");return n},e.prototype.getLineCoordsCount=function(t){return this._flatCoordsOffset?this._flatCoordsOffset[2*t+1]:this._getCoordsFromItemModel(t).length},e.prototype.getLineCoords=function(t,e){if(this._flatCoordsOffset){for(var n=this._flatCoordsOffset[2*t],i=this._flatCoordsOffset[2*t+1],r=0;r ")})},e.prototype.preventIncremental=function(){return!!this.get(["effect","show"])},e.prototype.getProgressive=function(){var t=this.option.progressive;return null==t?this.option.large?1e4:this.get("progressive"):t},e.prototype.getProgressiveThreshold=function(){var t=this.option.progressiveThreshold;return null==t?this.option.large?2e4:this.get("progressiveThreshold"):t},e.prototype.getZLevelKey=function(){var t=this.getModel("effect"),e=t.get("trailLength");return this.getData().count()>this.getProgressiveThreshold()?this.id:t.get("show")&&e>0?e+"":""},e.type="series.lines",e.dependencies=["grid","polar","geo","calendar"],e.defaultOption={coordinateSystem:"geo",z:2,legendHoverLink:!0,xAxisIndex:0,yAxisIndex:0,symbol:["none","none"],symbolSize:[10,10],geoIndex:0,effect:{show:!1,period:4,constantSpeed:0,symbol:"circle",symbolSize:3,loop:!0,trailLength:.2},large:!1,largeThreshold:2e3,polyline:!1,clip:!0,label:{show:!1,position:"end"},lineStyle:{opacity:.5}},e}(fg);function WP(t){return t instanceof Array||(t=[t,t]),t}var HP={seriesType:"lines",reset:function(t){var e=WP(t.get("symbol")),n=WP(t.get("symbolSize")),i=t.getData();return i.setVisual("fromSymbol",e&&e[0]),i.setVisual("toSymbol",e&&e[1]),i.setVisual("fromSymbolSize",n&&n[0]),i.setVisual("toSymbolSize",n&&n[1]),{dataEach:i.hasItemOption?function(t,e){var n=t.getItemModel(e),i=WP(n.getShallow("symbol",!0)),r=WP(n.getShallow("symbolSize",!0));i[0]&&t.setItemVisual(e,"fromSymbol",i[0]),i[1]&&t.setItemVisual(e,"toSymbol",i[1]),r[0]&&t.setItemVisual(e,"fromSymbolSize",r[0]),r[1]&&t.setItemVisual(e,"toSymbolSize",r[1])}:null}}};var YP=function(){function t(){this.blurSize=30,this.pointSize=20,this.maxOpacity=1,this.minOpacity=0,this._gradientPixels={inRange:null,outOfRange:null};var t=h.createCanvas();this.canvas=t}return t.prototype.update=function(t,e,n,i,r,o){var a=this._getBrush(),s=this._getGradient(r,"inRange"),l=this._getGradient(r,"outOfRange"),u=this.pointSize+this.blurSize,h=this.canvas,c=h.getContext("2d"),p=t.length;h.width=e,h.height=n;for(var d=0;d0){var I=o(v)?s:l;v>0&&(v=v*S+w),x[_++]=I[M],x[_++]=I[M+1],x[_++]=I[M+2],x[_++]=I[M+3]*v*256}else _+=4}return c.putImageData(m,0,0),h},t.prototype._getBrush=function(){var t=this._brushCanvas||(this._brushCanvas=h.createCanvas()),e=this.pointSize+this.blurSize,n=2*e;t.width=n,t.height=n;var i=t.getContext("2d");return i.clearRect(0,0,n,n),i.shadowOffsetX=n,i.shadowBlur=this.blurSize,i.shadowColor="#000",i.beginPath(),i.arc(-e,e,this.pointSize,0,2*Math.PI,!0),i.closePath(),i.fill(),t},t.prototype._getGradient=function(t,e){for(var n=this._gradientPixels,i=n[e]||(n[e]=new Uint8ClampedArray(1024)),r=[0,0,0,0],o=0,a=0;a<256;a++)t[e](a/255,!0,r),i[o++]=r[0],i[o++]=r[1],i[o++]=r[2],i[o++]=r[3];return i},t}();function UP(t){var e=t.dimensions;return"lng"===e[0]&&"lat"===e[1]}var XP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i;e.eachComponent("visualMap",(function(e){e.eachTargetSeries((function(n){n===t&&(i=e)}))})),this._progressiveEls=null,this.group.removeAll();var r=t.coordinateSystem;"cartesian2d"===r.type||"calendar"===r.type?this._renderOnCartesianAndCalendar(t,n,0,t.getData().count()):UP(r)&&this._renderOnGeo(r,t,i,n)},e.prototype.incrementalPrepareRender=function(t,e,n){this.group.removeAll()},e.prototype.incrementalRender=function(t,e,n,i){var r=e.coordinateSystem;r&&(UP(r)?this.render(e,n,i):(this._progressiveEls=[],this._renderOnCartesianAndCalendar(e,i,t.start,t.end,!0)))},e.prototype.eachRendered=function(t){jh(this._progressiveEls||this.group,t)},e.prototype._renderOnCartesianAndCalendar=function(t,e,n,i,r){var o,a,s,l,u=t.coordinateSystem,h=vS(u,"cartesian2d");if(h){var c=u.getAxis("x"),p=u.getAxis("y");0,o=c.getBandWidth()+.5,a=p.getBandWidth()+.5,s=c.scale.getExtent(),l=p.scale.getExtent()}for(var d=this.group,f=t.getData(),g=t.getModel(["emphasis","itemStyle"]).getItemStyle(),y=t.getModel(["blur","itemStyle"]).getItemStyle(),v=t.getModel(["select","itemStyle"]).getItemStyle(),m=t.get(["itemStyle","borderRadius"]),x=tc(t),_=t.getModel("emphasis"),b=_.get("focus"),w=_.get("blurScope"),S=_.get("disabled"),M=h?[f.mapDimension("x"),f.mapDimension("y"),f.mapDimension("value")]:[f.mapDimension("time"),f.mapDimension("value")],I=n;Is[1]||Al[1])continue;var k=u.dataToPoint([D,A]);T=new Es({shape:{x:k[0]-o/2,y:k[1]-a/2,width:o,height:a},style:C})}else{if(isNaN(f.get(M[1],I)))continue;T=new Es({z2:1,shape:u.dataToRect([f.get(M[0],I)]).contentShape,style:C})}if(f.hasItemOption){var L=f.getItemModel(I),P=L.getModel("emphasis");g=P.getModel("itemStyle").getItemStyle(),y=L.getModel(["blur","itemStyle"]).getItemStyle(),v=L.getModel(["select","itemStyle"]).getItemStyle(),m=L.get(["itemStyle","borderRadius"]),b=P.get("focus"),w=P.get("blurScope"),S=P.get("disabled"),x=tc(L)}T.shape.r=m;var O=t.getRawValue(I),R="-";O&&null!=O[2]&&(R=O[2]+""),Qh(T,x,{labelFetcher:t,labelDataIndex:I,defaultOpacity:C.opacity,defaultText:R}),T.ensureState("emphasis").style=g,T.ensureState("blur").style=y,T.ensureState("select").style=v,Hl(T,b,w,S),T.incremental=r,r&&(T.states.emphasis.hoverLayer=!0),d.add(T),f.setItemGraphicEl(I,T),this._progressiveEls&&this._progressiveEls.push(T)}},e.prototype._renderOnGeo=function(t,e,n,i){var r=n.targetVisuals.inRange,o=n.targetVisuals.outOfRange,a=e.getData(),s=this._hmLayer||this._hmLayer||new YP;s.blurSize=e.get("blurSize"),s.pointSize=e.get("pointSize"),s.minOpacity=e.get("minOpacity"),s.maxOpacity=e.get("maxOpacity");var l=t.getViewRect().clone(),u=t.getRoamTransform();l.applyTransform(u);var h=Math.max(l.x,0),c=Math.max(l.y,0),p=Math.min(l.width+l.x,i.getWidth()),d=Math.min(l.height+l.y,i.getHeight()),f=p-h,g=d-c,y=[a.mapDimension("lng"),a.mapDimension("lat"),a.mapDimension("value")],v=a.mapArray(y,(function(e,n,i){var r=t.dataToPoint([e,n]);return r[0]-=h,r[1]-=c,r.push(i),r})),m=n.getExtent(),x="visualMap.continuous"===n.type?function(t,e){var n=t[1]-t[0];return e=[(e[0]-t[0])/n,(e[1]-t[0])/n],function(t){return t>=e[0]&&t<=e[1]}}(m,n.option.range):function(t,e,n){var i=t[1]-t[0],r=(e=z(e,(function(e){return{interval:[(e.interval[0]-t[0])/i,(e.interval[1]-t[0])/i]}}))).length,o=0;return function(t){var i;for(i=o;i=0;i--){var a;if((a=e[i].interval)[0]<=t&&t<=a[1]){o=i;break}}return i>=0&&i0?1:-1}(n,o,r,i,c),function(t,e,n,i,r,o,a,s,l,u){var h,c=l.valueDim,p=l.categoryDim,d=Math.abs(n[p.wh]),f=t.getItemVisual(e,"symbolSize");h=Y(f)?f.slice():null==f?["100%","100%"]:[f,f];h[p.index]=Ur(h[p.index],d),h[c.index]=Ur(h[c.index],i?d:Math.abs(o)),u.symbolSize=h,(u.symbolScale=[h[0]/s,h[1]/s])[c.index]*=(l.isHorizontal?-1:1)*a}(t,e,r,o,0,c.boundingLength,c.pxSign,u,i,c),function(t,e,n,i,r){var o=t.get(jP)||0;o&&(KP.attr({scaleX:e[0],scaleY:e[1],rotation:n}),KP.updateTransform(),o/=KP.getLineScale(),o*=e[i.valueDim.index]);r.valueLineWidth=o||0}(n,c.symbolScale,l,i,c);var p=c.symbolSize,d=Fy(n.get("symbolOffset"),p);return function(t,e,n,i,r,o,a,s,l,u,h,c){var p=h.categoryDim,d=h.valueDim,f=c.pxSign,g=Math.max(e[d.index]+s,0),y=g;if(i){var v=Math.abs(l),m=it(t.get("symbolMargin"),"15%")+"",x=!1;m.lastIndexOf("!")===m.length-1&&(x=!0,m=m.slice(0,m.length-1));var _=Ur(m,e[d.index]),b=Math.max(g+2*_,0),w=x?0:2*_,S=ho(i),M=S?i:fO((v+w)/b);b=g+2*(_=(v-M*g)/2/(x?M:Math.max(M-1,1))),w=x?0:2*_,S||"fixed"===i||(M=u?fO((Math.abs(u)+w)/b):0),y=M*b-w,c.repeatTimes=M,c.symbolMargin=_}var I=f*(y/2),T=c.pathPosition=[];T[p.index]=n[p.wh]/2,T[d.index]="start"===a?I:"end"===a?l-I:l/2,o&&(T[0]+=o[0],T[1]+=o[1]);var C=c.bundlePosition=[];C[p.index]=n[p.xy],C[d.index]=n[d.xy];var D=c.barRectShape=A({},n);D[d.wh]=f*Math.max(Math.abs(n[d.wh]),Math.abs(T[d.index]+I)),D[p.wh]=n[p.wh];var k=c.clipShape={};k[p.xy]=-n[p.xy],k[p.wh]=h.ecSize[p.wh],k[d.xy]=0,k[d.wh]=n[d.wh]}(n,p,r,o,0,d,s,c.valueLineWidth,c.boundingLength,c.repeatCutLength,i,c),c}function QP(t,e){return t.toGlobalCoord(t.dataToCoord(t.scale.parse(e)))}function tO(t){var e=t.symbolPatternSize,n=Vy(t.symbolType,-e/2,-e/2,e,e);return n.attr({culling:!0}),"image"!==n.type&&n.setStyle({strokeNoScale:!0}),n}function eO(t,e,n,i){var r=t.__pictorialBundle,o=n.symbolSize,a=n.valueLineWidth,s=n.pathPosition,l=e.valueDim,u=n.repeatTimes||0,h=0,c=o[e.valueDim.index]+a+2*n.symbolMargin;for(cO(t,(function(t){t.__pictorialAnimationIndex=h,t.__pictorialRepeatTimes=u,h0:i<0)&&(r=u-1-t),e[l.index]=c*(r-u/2+.5)+s[l.index],{x:e[0],y:e[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation}}}function nO(t,e,n,i){var r=t.__pictorialBundle,o=t.__pictorialMainPath;o?pO(o,null,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation},n,i):(o=t.__pictorialMainPath=tO(n),r.add(o),pO(o,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:0,scaleY:0,rotation:n.rotation},{scaleX:n.symbolScale[0],scaleY:n.symbolScale[1]},n,i))}function iO(t,e,n){var i=A({},e.barRectShape),r=t.__pictorialBarRect;r?pO(r,null,{shape:i},e,n):((r=t.__pictorialBarRect=new Es({z2:2,shape:i,silent:!0,style:{stroke:"transparent",fill:"transparent",lineWidth:0}})).disableMorphing=!0,t.add(r))}function rO(t,e,n,i){if(n.symbolClip){var r=t.__pictorialClipPath,o=A({},n.clipShape),a=e.valueDim,s=n.animationModel,l=n.dataIndex;if(r)dh(r,{shape:o},s,l);else{o[a.wh]=0,r=new Es({shape:o}),t.__pictorialBundle.setClipPath(r),t.__pictorialClipPath=r;var u={};u[a.wh]=n.clipShape[a.wh],qh[i?"updateProps":"initProps"](r,{shape:u},s,l)}}}function oO(t,e){var n=t.getItemModel(e);return n.getAnimationDelayParams=aO,n.isAnimationEnabled=sO,n}function aO(t){return{index:t.__pictorialAnimationIndex,count:t.__pictorialRepeatTimes}}function sO(){return this.parentModel.isAnimationEnabled()&&!!this.getShallow("animation")}function lO(t,e,n,i){var r=new Er,o=new Er;return r.add(o),r.__pictorialBundle=o,o.x=n.bundlePosition[0],o.y=n.bundlePosition[1],n.symbolRepeat?eO(r,e,n):nO(r,0,n),iO(r,n,i),rO(r,e,n,i),r.__pictorialShapeStr=hO(t,n),r.__pictorialSymbolMeta=n,r}function uO(t,e,n,i){var r=i.__pictorialBarRect;r&&r.removeTextContent();var o=[];cO(i,(function(t){o.push(t)})),i.__pictorialMainPath&&o.push(i.__pictorialMainPath),i.__pictorialClipPath&&(n=null),E(o,(function(t){yh(t,{scaleX:0,scaleY:0},n,e,(function(){i.parent&&i.parent.remove(i)}))})),t.setItemGraphicEl(e,null)}function hO(t,e){return[t.getItemVisual(e.dataIndex,"symbol")||"none",!!e.symbolRepeat,!!e.symbolClip].join(":")}function cO(t,e,n){E(t.__pictorialBundle.children(),(function(i){i!==t.__pictorialBarRect&&e.call(n,i)}))}function pO(t,e,n,i,r,o){e&&t.attr(e),i.symbolClip&&!r?n&&t.attr(n):n&&qh[r?"updateProps":"initProps"](t,n,i.animationModel,i.dataIndex,o)}function dO(t,e,n){var i=n.dataIndex,r=n.itemModel,o=r.getModel("emphasis"),a=o.getModel("itemStyle").getItemStyle(),s=r.getModel(["blur","itemStyle"]).getItemStyle(),l=r.getModel(["select","itemStyle"]).getItemStyle(),u=r.getShallow("cursor"),h=o.get("focus"),c=o.get("blurScope"),p=o.get("scale");cO(t,(function(t){if(t instanceof As){var e=t.style;t.useStyle(A({image:e.image,x:e.x,y:e.y,width:e.width,height:e.height},n.style))}else t.useStyle(n.style);var i=t.ensureState("emphasis");i.style=a,p&&(i.scaleX=1.1*t.scaleX,i.scaleY=1.1*t.scaleY),t.ensureState("blur").style=s,t.ensureState("select").style=l,u&&(t.cursor=u),t.z2=n.z2}));var d=e.valueDim.posDesc[+(n.boundingLength>0)];Qh(t.__pictorialBarRect,tc(r),{labelFetcher:e.seriesModel,labelDataIndex:i,defaultText:Kw(e.seriesModel.getData(),i),inheritColor:n.style.fill,defaultOpacity:n.style.opacity,defaultOutsidePosition:d}),Hl(t,h,c,o.get("disabled"))}function fO(t){var e=Math.round(t);return Math.abs(t-e)<1e-4?e:Math.ceil(t)}var gO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.hasSymbolVisual=!0,n.defaultSymbol="roundRect",n}return n(e,t),e.prototype.getInitialData=function(e){return e.stack=null,t.prototype.getInitialData.apply(this,arguments)},e.type="series.pictorialBar",e.dependencies=["grid"],e.defaultOption=Tc(OS.defaultOption,{symbol:"circle",symbolSize:null,symbolRotate:null,symbolPosition:null,symbolOffset:null,symbolMargin:null,symbolRepeat:!1,symbolRepeatDirection:"end",symbolClip:!1,symbolBoundingData:null,symbolPatternSize:400,barGap:"-100%",progressive:0,emphasis:{scale:!1},select:{itemStyle:{borderColor:"#212121"}}}),e}(OS);var yO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._layers=[],n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData(),r=this,o=this.group,a=t.getLayerSeries(),s=i.getLayout("layoutInfo"),l=s.rect,u=s.boundaryGap;function h(t){return t.name}o.x=0,o.y=l.y+u[0];var c=new Lm(this._layersSeries||[],a,h,h),p=[];function d(e,n,s){var l=r._layers;if("remove"!==e){for(var u,h,c=[],d=[],f=a[n].indices,g=0;go&&(o=s),i.push(s)}for(var u=0;uo&&(o=c)}return{y0:r,max:o}}(l),h=u.y0,c=n/u.max,p=o.length,d=o[0].indices.length,f=0;fMath.PI/2?"right":"left"):S&&"center"!==S?"left"===S?(m=r.r0+w,a>Math.PI/2&&(S="right")):"right"===S&&(m=r.r-w,a>Math.PI/2&&(S="left")):(m=o===2*Math.PI&&0===r.r0?0:(r.r+r.r0)/2,S="center"),g.style.align=S,g.style.verticalAlign=f(p,"verticalAlign")||"middle",g.x=m*s+r.cx,g.y=m*l+r.cy;var M=f(p,"rotate"),I=0;"radial"===M?(I=-a)<-Math.PI/2&&(I+=Math.PI):"tangential"===M?(I=Math.PI/2-a)>Math.PI/2?I-=Math.PI:I<-Math.PI/2&&(I+=Math.PI):j(M)&&(I=M*Math.PI/180),g.rotation=I})),h.dirtyStyle()},e}(Eu),bO="sunburstRootToNode",wO="sunburstHighlight";var SO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n,i){var r=this;this.seriesModel=t,this.api=n,this.ecModel=e;var o=t.getData(),a=o.tree.root,s=t.getViewRoot(),l=this.group,u=t.get("renderLabelForZeroData"),h=[];s.eachNode((function(t){h.push(t)}));var c=this._oldChildren||[];!function(i,r){if(0===i.length&&0===r.length)return;function s(t){return t.getId()}function h(s,h){!function(i,r){u||!i||i.getValue()||(i=null);if(i!==a&&r!==a)if(r&&r.piece)i?(r.piece.updateData(!1,i,t,e,n),o.setItemGraphicEl(i.dataIndex,r.piece)):function(t){if(!t)return;t.piece&&(l.remove(t.piece),t.piece=null)}(r);else if(i){var s=new _O(i,t,e,n);l.add(s),o.setItemGraphicEl(i.dataIndex,s)}}(null==s?null:i[s],null==h?null:r[h])}new Lm(r,i,s,s).add(h).update(h).remove(H(h,null)).execute()}(h,c),function(i,o){o.depth>0?(r.virtualPiece?r.virtualPiece.updateData(!1,i,t,e,n):(r.virtualPiece=new _O(i,t,e,n),l.add(r.virtualPiece)),o.piece.off("click"),r.virtualPiece.on("click",(function(t){r._rootToNode(o.parentNode)}))):r.virtualPiece&&(l.remove(r.virtualPiece),r.virtualPiece=null)}(a,s),this._initEvents(),this._oldChildren=h},e.prototype._initEvents=function(){var t=this;this.group.off("click"),this.group.on("click",(function(e){var n=!1;t.seriesModel.getViewRoot().eachNode((function(i){if(!n&&i.piece&&i.piece===e.target){var r=i.getModel().get("nodeClick");if("rootToNode"===r)t._rootToNode(i);else if("link"===r){var o=i.getModel(),a=o.get("link");if(a)_p(a,o.get("target",!0)||"_blank")}n=!0}}))}))},e.prototype._rootToNode=function(t){t!==this.seriesModel.getViewRoot()&&this.api.dispatchAction({type:bO,from:this.uid,seriesId:this.seriesModel.id,targetNode:t})},e.prototype.containPoint=function(t,e){var n=e.getData().getItemLayout(0);if(n){var i=t[0]-n.cx,r=t[1]-n.cy,o=Math.sqrt(i*i+r*r);return o<=n.r&&o>=n.r0}},e.type="sunburst",e}(Tg),MO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.ignoreStyleOnData=!0,n}return n(e,t),e.prototype.getInitialData=function(t,e){var n={name:t.name,children:t.data};IO(n);var i=this._levelModels=z(t.levels||[],(function(t){return new Sc(t,this,e)}),this),r=BC.createTree(n,this,(function(t){t.wrapMethod("getItemModel",(function(t,e){var n=r.getNodeByDataIndex(e),o=i[n.depth];return o&&(t.parentModel=o),t}))}));return r.data},e.prototype.optionUpdated=function(){this.resetViewRoot()},e.prototype.getDataParams=function(e){var n=t.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(e);return n.treePathInfo=HC(i,this),n},e.prototype.getLevelModel=function(t){return this._levelModels&&this._levelModels[t.depth]},e.prototype.getViewRoot=function(){return this._viewRoot},e.prototype.resetViewRoot=function(t){t?this._viewRoot=t:t=this._viewRoot;var e=this.getRawData().tree.root;t&&(t===e||e.contains(t))||(this._viewRoot=e)},e.prototype.enableAriaDecal=function(){qC(this)},e.type="series.sunburst",e.defaultOption={z:2,center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,minAngle:0,stillShowZeroSum:!0,nodeClick:"rootToNode",renderLabelForZeroData:!1,label:{rotate:"radial",show:!0,opacity:1,align:"center",position:"inside",distance:5,silent:!0},itemStyle:{borderWidth:1,borderColor:"white",borderType:"solid",shadowBlur:0,shadowColor:"rgba(0, 0, 0, 0.2)",shadowOffsetX:0,shadowOffsetY:0,opacity:1},emphasis:{focus:"descendant"},blur:{itemStyle:{opacity:.2},label:{opacity:.1}},animationType:"expansion",animationDuration:1e3,animationDurationUpdate:500,data:[],sort:"desc"},e}(fg);function IO(t){var e=0;E(t.children,(function(t){IO(t);var n=t.value;Y(n)&&(n=n[0]),e+=n}));var n=t.value;Y(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=e),n<0&&(n=0),Y(t.value)?t.value[0]=n:t.value=n}var TO=Math.PI/180;function CO(t,e,n){e.eachSeriesByType(t,(function(t){var e=t.get("center"),i=t.get("radius");Y(i)||(i=[0,i]),Y(e)||(e=[e,e]);var r=n.getWidth(),o=n.getHeight(),a=Math.min(r,o),s=Ur(e[0],r),l=Ur(e[1],o),u=Ur(i[0],a/2),h=Ur(i[1],a/2),c=-t.get("startAngle")*TO,p=t.get("minAngle")*TO,d=t.getData().tree.root,f=t.getViewRoot(),g=f.depth,y=t.get("sort");null!=y&&DO(f,y);var v=0;E(f.children,(function(t){!isNaN(t.getValue())&&v++}));var m=f.getValue(),x=Math.PI/(m||v)*2,_=f.depth>0,b=f.height-(_?-1:1),w=(h-u)/(b||1),S=t.get("clockwise"),M=t.get("stillShowZeroSum"),I=S?1:-1,T=function(e,n){if(e){var i=n;if(e!==d){var r=e.getValue(),o=0===m&&M?x:r*x;o1;)r=r.parentNode;var o=n.getColorFromPalette(r.name||r.dataIndex+"",e);return t.depth>1&&X(o)&&(o=Kn(o,(t.depth-1)/(i-1)*.5)),o}(r,t,i.root.height)),A(n.ensureUniqueItemVisual(r.dataIndex,"style"),o)}))}))}var kO={color:"fill",borderColor:"stroke"},LO={symbol:1,symbolSize:1,symbolKeepAspect:1,legendIcon:1,visualMeta:1,liftZ:1,decal:1},PO=Po(),OO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.optionUpdated=function(){this.currentZLevel=this.get("zlevel",!0),this.currentZ=this.get("z",!0)},e.prototype.getInitialData=function(t,e){return hx(null,this)},e.prototype.getDataParams=function(e,n,i){var r=t.prototype.getDataParams.call(this,e,n);return i&&(r.info=PO(i).info),r},e.type="series.custom",e.dependencies=["grid","polar","geo","singleAxis","calendar"],e.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,clip:!1},e}(fg);function RO(t,e){return e=e||[0,0],z(["x","y"],(function(n,i){var r=this.getAxis(n),o=e[i],a=t[i]/2;return"category"===r.type?r.getBandWidth():Math.abs(r.dataToCoord(o-a)-r.dataToCoord(o+a))}),this)}function NO(t,e){return e=e||[0,0],z([0,1],(function(n){var i=e[n],r=t[n]/2,o=[],a=[];return o[n]=i-r,a[n]=i+r,o[1-n]=a[1-n]=e[1-n],Math.abs(this.dataToPoint(o)[n]-this.dataToPoint(a)[n])}),this)}function EO(t,e){var n=this.getAxis(),i=e instanceof Array?e[0]:e,r=(t instanceof Array?t[0]:t)/2;return"category"===n.type?n.getBandWidth():Math.abs(n.dataToCoord(i-r)-n.dataToCoord(i+r))}function zO(t,e){return e=e||[0,0],z(["Radius","Angle"],(function(n,i){var r=this["get"+n+"Axis"](),o=e[i],a=t[i]/2,s="category"===r.type?r.getBandWidth():Math.abs(r.dataToCoord(o-a)-r.dataToCoord(o+a));return"Angle"===n&&(s=s*Math.PI/180),s}),this)}function VO(t,e,n,i){return t&&(t.legacy||!1!==t.legacy&&!n&&!i&&"tspan"!==e&&("text"===e||_t(t,"text")))}function BO(t,e,n){var i,r,o,a=t;if("text"===e)o=a;else{o={},_t(a,"text")&&(o.text=a.text),_t(a,"rich")&&(o.rich=a.rich),_t(a,"textFill")&&(o.fill=a.textFill),_t(a,"textStroke")&&(o.stroke=a.textStroke),_t(a,"fontFamily")&&(o.fontFamily=a.fontFamily),_t(a,"fontSize")&&(o.fontSize=a.fontSize),_t(a,"fontStyle")&&(o.fontStyle=a.fontStyle),_t(a,"fontWeight")&&(o.fontWeight=a.fontWeight),r={type:"text",style:o,silent:!0},i={};var s=_t(a,"textPosition");n?i.position=s?a.textPosition:"inside":s&&(i.position=a.textPosition),_t(a,"textPosition")&&(i.position=a.textPosition),_t(a,"textOffset")&&(i.offset=a.textOffset),_t(a,"textRotation")&&(i.rotation=a.textRotation),_t(a,"textDistance")&&(i.distance=a.textDistance)}return FO(o,t),E(o.rich,(function(t){FO(t,t)})),{textConfig:i,textContent:r}}function FO(t,e){e&&(e.font=e.textFont||e.font,_t(e,"textStrokeWidth")&&(t.lineWidth=e.textStrokeWidth),_t(e,"textAlign")&&(t.align=e.textAlign),_t(e,"textVerticalAlign")&&(t.verticalAlign=e.textVerticalAlign),_t(e,"textLineHeight")&&(t.lineHeight=e.textLineHeight),_t(e,"textWidth")&&(t.width=e.textWidth),_t(e,"textHeight")&&(t.height=e.textHeight),_t(e,"textBackgroundColor")&&(t.backgroundColor=e.textBackgroundColor),_t(e,"textPadding")&&(t.padding=e.textPadding),_t(e,"textBorderColor")&&(t.borderColor=e.textBorderColor),_t(e,"textBorderWidth")&&(t.borderWidth=e.textBorderWidth),_t(e,"textBorderRadius")&&(t.borderRadius=e.textBorderRadius),_t(e,"textBoxShadowColor")&&(t.shadowColor=e.textBoxShadowColor),_t(e,"textBoxShadowBlur")&&(t.shadowBlur=e.textBoxShadowBlur),_t(e,"textBoxShadowOffsetX")&&(t.shadowOffsetX=e.textBoxShadowOffsetX),_t(e,"textBoxShadowOffsetY")&&(t.shadowOffsetY=e.textBoxShadowOffsetY))}function GO(t,e,n){var i=t;i.textPosition=i.textPosition||n.position||"inside",null!=n.offset&&(i.textOffset=n.offset),null!=n.rotation&&(i.textRotation=n.rotation),null!=n.distance&&(i.textDistance=n.distance);var r=i.textPosition.indexOf("inside")>=0,o=t.fill||"#000";WO(i,e);var a=null==i.textFill;return r?a&&(i.textFill=n.insideFill||"#fff",!i.textStroke&&n.insideStroke&&(i.textStroke=n.insideStroke),!i.textStroke&&(i.textStroke=o),null==i.textStrokeWidth&&(i.textStrokeWidth=2)):(a&&(i.textFill=t.fill||n.outsideFill||"#000"),!i.textStroke&&n.outsideStroke&&(i.textStroke=n.outsideStroke)),i.text=e.text,i.rich=e.rich,E(e.rich,(function(t){WO(t,t)})),i}function WO(t,e){e&&(_t(e,"fill")&&(t.textFill=e.fill),_t(e,"stroke")&&(t.textStroke=e.fill),_t(e,"lineWidth")&&(t.textStrokeWidth=e.lineWidth),_t(e,"font")&&(t.font=e.font),_t(e,"fontStyle")&&(t.fontStyle=e.fontStyle),_t(e,"fontWeight")&&(t.fontWeight=e.fontWeight),_t(e,"fontSize")&&(t.fontSize=e.fontSize),_t(e,"fontFamily")&&(t.fontFamily=e.fontFamily),_t(e,"align")&&(t.textAlign=e.align),_t(e,"verticalAlign")&&(t.textVerticalAlign=e.verticalAlign),_t(e,"lineHeight")&&(t.textLineHeight=e.lineHeight),_t(e,"width")&&(t.textWidth=e.width),_t(e,"height")&&(t.textHeight=e.height),_t(e,"backgroundColor")&&(t.textBackgroundColor=e.backgroundColor),_t(e,"padding")&&(t.textPadding=e.padding),_t(e,"borderColor")&&(t.textBorderColor=e.borderColor),_t(e,"borderWidth")&&(t.textBorderWidth=e.borderWidth),_t(e,"borderRadius")&&(t.textBorderRadius=e.borderRadius),_t(e,"shadowColor")&&(t.textBoxShadowColor=e.shadowColor),_t(e,"shadowBlur")&&(t.textBoxShadowBlur=e.shadowBlur),_t(e,"shadowOffsetX")&&(t.textBoxShadowOffsetX=e.shadowOffsetX),_t(e,"shadowOffsetY")&&(t.textBoxShadowOffsetY=e.shadowOffsetY),_t(e,"textShadowColor")&&(t.textShadowColor=e.textShadowColor),_t(e,"textShadowBlur")&&(t.textShadowBlur=e.textShadowBlur),_t(e,"textShadowOffsetX")&&(t.textShadowOffsetX=e.textShadowOffsetX),_t(e,"textShadowOffsetY")&&(t.textShadowOffsetY=e.textShadowOffsetY))}var HO={position:["x","y"],scale:["scaleX","scaleY"],origin:["originX","originY"]},YO=G(HO),UO=(V(gr,(function(t,e){return t[e]=1,t}),{}),gr.join(", "),["","style","shape","extra"]),XO=Po();function ZO(t,e,n,i,r){var o=t+"Animation",a=ch(t,i,r)||{},s=XO(e).userDuring;return a.duration>0&&(a.during=s?W(tR,{el:e,userDuring:s}):null,a.setToFinal=!0,a.scope=t),A(a,n[o]),a}function jO(t,e,n,i){var r=(i=i||{}).dataIndex,o=i.isInit,a=i.clearStyle,s=n.isAnimationEnabled(),l=XO(t),u=e.style;l.userDuring=e.during;var h={},c={};if(function(t,e,n){for(var i=0;i=0)){var c=t.getAnimationStyleProps(),p=c?c.style:null;if(p){!r&&(r=i.style={});var d=G(n);for(u=0;u0&&t.animateFrom(p,d)}else!function(t,e,n,i,r){if(r){var o=ZO("update",t,e,i,n);o.duration>0&&t.animateFrom(r,o)}}(t,e,r||0,n,h);qO(t,e),u?t.dirty():t.markRedraw()}function qO(t,e){for(var n=XO(t).leaveToProps,i=0;i=0){!o&&(o=i[t]={});var p=G(a);for(h=0;hi[1]&&i.reverse(),{coordSys:{type:"polar",cx:t.cx,cy:t.cy,r:i[1],r0:i[0]},api:{coord:function(i){var r=e.dataToRadius(i[0]),o=n.dataToAngle(i[1]),a=t.coordToPoint([r,o]);return a.push(r,o*Math.PI/180),a},size:W(zO,t)}}},calendar:function(t){var e=t.getRect(),n=t.getRangeInfo();return{coordSys:{type:"calendar",x:e.x,y:e.y,width:e.width,height:e.height,cellWidth:t.getCellWidth(),cellHeight:t.getCellHeight(),rangeInfo:{start:n.start,end:n.end,weeks:n.weeks,dayCount:n.allDay}},api:{coord:function(e,n){return t.dataToPoint(e,n)}}}}};function mR(t){return t instanceof Ms}function xR(t){return t instanceof wa}var _R=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n,i){this._progressiveEls=null;var r=this._data,o=t.getData(),a=this.group,s=IR(t,o,e,n);r||a.removeAll(),o.diff(r).add((function(e){CR(n,null,e,s(e,i),t,a,o)})).remove((function(e){var n=r.getItemGraphicEl(e);n&&KO(n,PO(n).option,t)})).update((function(e,l){var u=r.getItemGraphicEl(l);CR(n,u,e,s(e,i),t,a,o)})).execute();var l=t.get("clip",!0)?yS(t.coordinateSystem,!1,t):null;l?a.setClipPath(l):a.removeClipPath(),this._data=o},e.prototype.incrementalPrepareRender=function(t,e,n){this.group.removeAll(),this._data=null},e.prototype.incrementalRender=function(t,e,n,i,r){var o=e.getData(),a=IR(e,o,n,i),s=this._progressiveEls=[];function l(t){t.isGroup||(t.incremental=!0,t.ensureState("emphasis").hoverLayer=!0)}for(var u=t.start;u=0?e.getStore().get(r,n):void 0}var o=e.get(i.name,n),a=i&&i.ordinalMeta;return a?a.categories[o]:o},styleEmphasis:function(n,i){0;null==i&&(i=s);var r=m(i,lR).getItemStyle(),o=x(i,lR),a=ec(o,null,null,!0,!0);a.text=o.getShallow("show")?ot(t.getFormattedLabel(i,lR),t.getFormattedLabel(i,uR),Kw(e,i)):null;var l=nc(o,null,!0);return b(n,r),r=GO(r,a,l),n&&_(r,n),r.legacy=!0,r},visual:function(t,n){if(null==n&&(n=s),_t(kO,t)){var i=e.getItemVisual(n,"style");return i?i[kO[t]]:null}if(_t(LO,t))return e.getItemVisual(n,t)},barLayout:function(t){if("cartesian2d"===o.type){return function(t){var e=[],n=t.axis,i="axis0";if("category"===n.type){for(var r=n.getBandWidth(),o=0;o=c;f--){var g=e.childAt(f);OR(e,g,r)}}(t,c,n,i,r),a>=0?o.replaceAt(c,a):o.add(c),c}function AR(t,e,n){var i,r=PO(t),o=e.type,a=e.shape,s=e.style;return n.isUniversalTransitionEnabled()||null!=o&&o!==r.customGraphicType||"path"===o&&((i=a)&&(_t(i,"pathData")||_t(i,"d")))&&zR(a)!==r.customPathData||"image"===o&&_t(s,"image")&&s.image!==r.customImagePath}function kR(t,e,n){var i=e?LR(t,e):t,r=e?PR(t,i,lR):t.style,o=t.type,a=i?i.textConfig:null,s=t.textContent,l=s?e?LR(s,e):s:null;if(r&&(n.isLegacy||VO(r,o,!!a,!!l))){n.isLegacy=!0;var u=BO(r,o,!e);!a&&u.textConfig&&(a=u.textConfig),!l&&u.textContent&&(l=u.textContent)}if(!e&&l){var h=l;!h.type&&(h.type="text")}var c=e?n[e]:n.normal;c.cfg=a,c.conOpt=l}function LR(t,e){return e?t?t[e]:null:t}function PR(t,e,n){var i=e&&e.style;return null==i&&n===lR&&t&&(i=t.styleEmphasis),i}function OR(t,e,n){e&&KO(e,PO(t).option,n)}function RR(t,e){var n=t&&t.name;return null!=n?n:"e\0\0"+e}function NR(t,e){var n=this.context,i=null!=t?n.newChildren[t]:null,r=null!=e?n.oldChildren[e]:null;DR(n.api,r,n.dataIndex,i,n.seriesModel,n.group)}function ER(t){var e=this.context,n=e.oldChildren[t];n&&KO(n,PO(n).option,e.seriesModel)}function zR(t){return t&&(t.pathData||t.d)}var VR=Po(),BR=T,FR=W,GR=function(){function t(){this._dragging=!1,this.animationThreshold=15}return t.prototype.render=function(t,e,n,i){var r=e.get("value"),o=e.get("status");if(this._axisModel=t,this._axisPointerModel=e,this._api=n,i||this._lastValue!==r||this._lastStatus!==o){this._lastValue=r,this._lastStatus=o;var a=this._group,s=this._handle;if(!o||"hide"===o)return a&&a.hide(),void(s&&s.hide());a&&a.show(),s&&s.show();var l={};this.makeElOption(l,r,t,e,n);var u=l.graphicKey;u!==this._lastGraphicKey&&this.clear(n),this._lastGraphicKey=u;var h=this._moveAnimation=this.determineAnimation(t,e);if(a){var c=H(WR,e,h);this.updatePointerEl(a,l,c),this.updateLabelEl(a,l,c,e)}else a=this._group=new Er,this.createPointerEl(a,l,t,e),this.createLabelEl(a,l,t,e),n.getZr().add(a);XR(a,e,!0),this._renderHandle(r)}},t.prototype.remove=function(t){this.clear(t)},t.prototype.dispose=function(t){this.clear(t)},t.prototype.determineAnimation=function(t,e){var n=e.get("animation"),i=t.axis,r="category"===i.type,o=e.get("snap");if(!o&&!r)return!1;if("auto"===n||null==n){var a=this.animationThreshold;if(r&&i.getBandWidth()>a)return!0;if(o){var s=oI(t).seriesDataCount,l=i.getExtent();return Math.abs(l[0]-l[1])/s>a}return!1}return!0===n},t.prototype.makeElOption=function(t,e,n,i,r){},t.prototype.createPointerEl=function(t,e,n,i){var r=e.pointer;if(r){var o=VR(t).pointerEl=new qh[r.type](BR(e.pointer));t.add(o)}},t.prototype.createLabelEl=function(t,e,n,i){if(e.label){var r=VR(t).labelEl=new Bs(BR(e.label));t.add(r),YR(r,i)}},t.prototype.updatePointerEl=function(t,e,n){var i=VR(t).pointerEl;i&&e.pointer&&(i.setStyle(e.pointer.style),n(i,{shape:e.pointer.shape}))},t.prototype.updateLabelEl=function(t,e,n,i){var r=VR(t).labelEl;r&&(r.setStyle(e.label.style),n(r,{x:e.label.x,y:e.label.y}),YR(r,i))},t.prototype._renderHandle=function(t){if(!this._dragging&&this.updateHandleTransform){var e,n=this._axisPointerModel,i=this._api.getZr(),r=this._handle,o=n.getModel("handle"),a=n.get("status");if(!o.get("show")||!a||"hide"===a)return r&&i.remove(r),void(this._handle=null);this._handle||(e=!0,r=this._handle=Wh(o.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(t){pe(t.event)},onmousedown:FR(this._onHandleDragMove,this,0,0),drift:FR(this._onHandleDragMove,this),ondragend:FR(this._onHandleDragEnd,this)}),i.add(r)),XR(r,n,!1),r.setStyle(o.getItemStyle(null,["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"]));var s=o.get("size");Y(s)||(s=[s,s]),r.scaleX=s[0]/2,r.scaleY=s[1]/2,Eg(this,"_doDispatchAxisPointer",o.get("throttle")||0,"fixRate"),this._moveHandleToValue(t,e)}},t.prototype._moveHandleToValue=function(t,e){WR(this._axisPointerModel,!e&&this._moveAnimation,this._handle,UR(this.getHandleTransform(t,this._axisModel,this._axisPointerModel)))},t.prototype._onHandleDragMove=function(t,e){var n=this._handle;if(n){this._dragging=!0;var i=this.updateHandleTransform(UR(n),[t,e],this._axisModel,this._axisPointerModel);this._payloadInfo=i,n.stopAnimation(),n.attr(UR(i)),VR(n).lastProp=null,this._doDispatchAxisPointer()}},t.prototype._doDispatchAxisPointer=function(){if(this._handle){var t=this._payloadInfo,e=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:t.cursorPoint[0],y:t.cursorPoint[1],tooltipOption:t.tooltipOption,axesInfo:[{axisDim:e.axis.dim,axisIndex:e.componentIndex}]})}},t.prototype._onHandleDragEnd=function(){if(this._dragging=!1,this._handle){var t=this._axisPointerModel.get("value");this._moveHandleToValue(t),this._api.dispatchAction({type:"hideTip"})}},t.prototype.clear=function(t){this._lastValue=null,this._lastStatus=null;var e=t.getZr(),n=this._group,i=this._handle;e&&n&&(this._lastGraphicKey=null,n&&e.remove(n),i&&e.remove(i),this._group=null,this._handle=null,this._payloadInfo=null),zg(this,"_doDispatchAxisPointer")},t.prototype.doClear=function(){},t.prototype.buildLabel=function(t,e,n){return{x:t[n=n||0],y:t[1-n],width:e[n],height:e[1-n]}},t}();function WR(t,e,n,i){HR(VR(n).lastProp,i)||(VR(n).lastProp=i,e?dh(n,i,t):(n.stopAnimation(),n.attr(i)))}function HR(t,e){if(q(t)&&q(e)){var n=!0;return E(e,(function(e,i){n=n&&HR(t[i],e)})),!!n}return t===e}function YR(t,e){t[e.get(["label","show"])?"show":"hide"]()}function UR(t){return{x:t.x||0,y:t.y||0,rotation:t.rotation||0}}function XR(t,e,n){var i=e.get("z"),r=e.get("zlevel");t&&t.traverse((function(t){"group"!==t.type&&(null!=i&&(t.z=i),null!=r&&(t.zlevel=r),t.silent=n)}))}function ZR(t){var e,n=t.get("type"),i=t.getModel(n+"Style");return"line"===n?(e=i.getLineStyle()).fill=null:"shadow"===n&&((e=i.getAreaStyle()).stroke=null),e}function jR(t,e,n,i,r){var o=qR(n.get("value"),e.axis,e.ecModel,n.get("seriesDataIndices"),{precision:n.get(["label","precision"]),formatter:n.get(["label","formatter"])}),a=n.getModel("label"),s=dp(a.get("padding")||0),l=a.getFont(),u=_r(o,l),h=r.position,c=u.width+s[1]+s[3],p=u.height+s[0]+s[2],d=r.align;"right"===d&&(h[0]-=c),"center"===d&&(h[0]-=c/2);var f=r.verticalAlign;"bottom"===f&&(h[1]-=p),"middle"===f&&(h[1]-=p/2),function(t,e,n,i){var r=i.getWidth(),o=i.getHeight();t[0]=Math.min(t[0]+e,r)-e,t[1]=Math.min(t[1]+n,o)-n,t[0]=Math.max(t[0],0),t[1]=Math.max(t[1],0)}(h,c,p,i);var g=a.get("backgroundColor");g&&"auto"!==g||(g=e.get(["axisLine","lineStyle","color"])),t.label={x:h[0],y:h[1],style:ec(a,{text:o,font:l,fill:a.getTextColor(),padding:s,backgroundColor:g}),z2:10}}function qR(t,e,n,i,r){t=e.scale.parse(t);var o=e.scale.getLabel({value:t},{precision:r.precision}),a=r.formatter;if(a){var s={value:d_(e,{value:t}),axisDimension:e.dim,axisIndex:e.index,seriesData:[]};E(i,(function(t){var e=n.getSeriesByIndex(t.seriesIndex),i=t.dataIndexInside,r=e&&e.getDataParams(i);r&&s.seriesData.push(r)})),X(a)?o=a.replace("{value}",o):U(a)&&(o=a(s))}return o}function KR(t,e,n){var i=[1,0,0,1,0,0];return we(i,i,n.rotation),be(i,i,n.position),Eh([t.dataToCoord(e),(n.labelOffset||0)+(n.labelDirection||1)*(n.labelMargin||0)],i)}function $R(t,e,n,i,r,o){var a=KM.innerTextLayout(n.rotation,0,n.labelDirection);n.labelMargin=r.get(["label","margin"]),jR(e,i,r,o,{position:KR(i.axis,t,n),align:a.textAlign,verticalAlign:a.textVerticalAlign})}function JR(t,e,n){return{x1:t[n=n||0],y1:t[1-n],x2:e[n],y2:e[1-n]}}function QR(t,e,n){return{x:t[n=n||0],y:t[1-n],width:e[n],height:e[1-n]}}function tN(t,e,n,i,r,o){return{cx:t,cy:e,r0:n,r:i,startAngle:r,endAngle:o,clockwise:!0}}var eN=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.makeElOption=function(t,e,n,i,r){var o=n.axis,a=o.grid,s=i.get("type"),l=nN(a,o).getOtherAxis(o).getGlobalExtent(),u=o.toGlobalCoord(o.dataToCoord(e,!0));if(s&&"none"!==s){var h=ZR(i),c=iN[s](o,u,l);c.style=h,t.graphicKey=c.type,t.pointer=c}$R(e,t,FM(a.model,n),n,i,r)},e.prototype.getHandleTransform=function(t,e,n){var i=FM(e.axis.grid.model,e,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var r=KR(e.axis,t,i);return{x:r[0],y:r[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},e.prototype.updateHandleTransform=function(t,e,n,i){var r=n.axis,o=r.grid,a=r.getGlobalExtent(!0),s=nN(o,r).getOtherAxis(r).getGlobalExtent(),l="x"===r.dim?0:1,u=[t.x,t.y];u[l]+=e[l],u[l]=Math.min(a[1],u[l]),u[l]=Math.max(a[0],u[l]);var h=(s[1]+s[0])/2,c=[h,h];c[l]=u[l];return{x:u[0],y:u[1],rotation:t.rotation,cursorPoint:c,tooltipOption:[{verticalAlign:"middle"},{align:"center"}][l]}},e}(GR);function nN(t,e){var n={};return n[e.dim+"AxisIndex"]=e.index,t.getCartesian(n)}var iN={line:function(t,e,n){return{type:"Line",subPixelOptimize:!0,shape:JR([e,n[0]],[e,n[1]],rN(t))}},shadow:function(t,e,n){var i=Math.max(1,t.getBandWidth()),r=n[1]-n[0];return{type:"Rect",shape:QR([e-i/2,n[0]],[i,r],rN(t))}}};function rN(t){return"x"===t.dim?0:1}var oN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="axisPointer",e.defaultOption={show:"auto",z:50,type:"line",snap:!1,triggerTooltip:!0,value:null,status:null,link:[],animation:null,animationDurationUpdate:200,lineStyle:{color:"#B9BEC9",width:1,type:"dashed"},shadowStyle:{color:"rgba(210,219,238,0.2)"},label:{show:!0,formatter:null,precision:"auto",margin:3,color:"#fff",padding:[5,7,5,7],backgroundColor:"auto",borderColor:null,borderWidth:0,borderRadius:3},handle:{show:!1,icon:"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.4h1.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.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z",size:45,margin:50,color:"#333",shadowBlur:3,shadowColor:"#aaa",shadowOffsetX:0,shadowOffsetY:2,throttle:40}},e}(Op),aN=Po(),sN=E;function lN(t,e,n){if(!r.node){var i=e.getZr();aN(i).records||(aN(i).records={}),function(t,e){if(aN(t).initialized)return;function n(n,i){t.on(n,(function(n){var r=function(t){var e={showTip:[],hideTip:[]},n=function(i){var r=e[i.type];r?r.push(i):(i.dispatchAction=n,t.dispatchAction(i))};return{dispatchAction:n,pendings:e}}(e);sN(aN(t).records,(function(t){t&&i(t,n,r.dispatchAction)})),function(t,e){var n,i=t.showTip.length,r=t.hideTip.length;i?n=t.showTip[i-1]:r&&(n=t.hideTip[r-1]);n&&(n.dispatchAction=null,e.dispatchAction(n))}(r.pendings,e)}))}aN(t).initialized=!0,n("click",H(hN,"click")),n("mousemove",H(hN,"mousemove")),n("globalout",uN)}(i,e),(aN(i).records[t]||(aN(i).records[t]={})).handler=n}}function uN(t,e,n){t.handler("leave",null,n)}function hN(t,e,n,i){e.handler(t,n,i)}function cN(t,e){if(!r.node){var n=e.getZr();(aN(n).records||{})[t]&&(aN(n).records[t]=null)}}var pN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=e.getComponent("tooltip"),r=t.get("triggerOn")||i&&i.get("triggerOn")||"mousemove|click";lN("axisPointer",n,(function(t,e,n){"none"!==r&&("leave"===t||r.indexOf(t)>=0)&&n({type:"updateAxisPointer",currTrigger:t,x:e&&e.offsetX,y:e&&e.offsetY})}))},e.prototype.remove=function(t,e){cN("axisPointer",e)},e.prototype.dispose=function(t,e){cN("axisPointer",e)},e.type="axisPointer",e}(wg);function dN(t,e){var n,i=[],r=t.seriesIndex;if(null==r||!(n=e.getSeriesByIndex(r)))return{point:[]};var o=n.getData(),a=Lo(o,t);if(null==a||a<0||Y(a))return{point:[]};var s=o.getItemGraphicEl(a),l=n.coordinateSystem;if(n.getTooltipPosition)i=n.getTooltipPosition(a)||[];else if(l&&l.dataToPoint)if(t.isStacked){var u=l.getBaseAxis(),h=l.getOtherAxis(u).dim,c=u.dim,p="x"===h||"radius"===h?1:0,d=o.mapDimension(c),f=[];f[p]=o.get(d,a),f[1-p]=o.get(o.getCalculationInfo("stackResultDimension"),a),i=l.dataToPoint(f)||[]}else i=l.dataToPoint(o.getValues(z(l.dimensions,(function(t){return o.mapDimension(t)})),a))||[];else if(s){var g=s.getBoundingRect().clone();g.applyTransform(s.transform),i=[g.x+g.width/2,g.y+g.height/2]}return{point:i,el:s}}var fN=Po();function gN(t,e,n){var i=t.currTrigger,r=[t.x,t.y],o=t,a=t.dispatchAction||W(n.dispatchAction,n),s=e.getComponent("axisPointer").coordSysAxesInfo;if(s){_N(r)&&(r=dN({seriesIndex:o.seriesIndex,dataIndex:o.dataIndex},e).point);var l=_N(r),u=o.axesInfo,h=s.axesInfo,c="leave"===i||_N(r),p={},d={},f={list:[],map:{}},g={showPointer:H(vN,d),showTooltip:H(mN,f)};E(s.coordSysMap,(function(t,e){var n=l||t.containPoint(r);E(s.coordSysAxesInfo[e],(function(t,e){var i=t.axis,o=function(t,e){for(var n=0;n<(t||[]).length;n++){var i=t[n];if(e.axis.dim===i.axisDim&&e.axis.model.componentIndex===i.axisIndex)return i}}(u,t);if(!c&&n&&(!u||o)){var a=o&&o.value;null!=a||l||(a=i.pointToData(r)),null!=a&&yN(t,a,g,!1,p)}}))}));var y={};return E(h,(function(t,e){var n=t.linkGroup;n&&!d[e]&&E(n.axesInfo,(function(e,i){var r=d[i];if(e!==t&&r){var o=r.value;n.mapper&&(o=t.axis.scale.parse(n.mapper(o,xN(e),xN(t)))),y[t.key]=o}}))})),E(y,(function(t,e){yN(h[e],t,g,!0,p)})),function(t,e,n){var i=n.axesInfo=[];E(e,(function(e,n){var r=e.axisPointerModel.option,o=t[n];o?(!e.useHandle&&(r.status="show"),r.value=o.value,r.seriesDataIndices=(o.payloadBatch||[]).slice()):!e.useHandle&&(r.status="hide"),"show"===r.status&&i.push({axisDim:e.axis.dim,axisIndex:e.axis.model.componentIndex,value:r.value})}))}(d,h,p),function(t,e,n,i){if(_N(e)||!t.list.length)return void i({type:"hideTip"});var r=((t.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};i({type:"showTip",escapeConnect:!0,x:e[0],y:e[1],tooltipOption:n.tooltipOption,position:n.position,dataIndexInside:r.dataIndexInside,dataIndex:r.dataIndex,seriesIndex:r.seriesIndex,dataByCoordSys:t.list})}(f,r,t,a),function(t,e,n){var i=n.getZr(),r="axisPointerLastHighlights",o=fN(i)[r]||{},a=fN(i)[r]={};E(t,(function(t,e){var n=t.axisPointerModel.option;"show"===n.status&&E(n.seriesDataIndices,(function(t){var e=t.seriesIndex+" | "+t.dataIndex;a[e]=t}))}));var s=[],l=[];E(o,(function(t,e){!a[e]&&l.push(t)})),E(a,(function(t,e){!o[e]&&s.push(t)})),l.length&&n.dispatchAction({type:"downplay",escapeConnect:!0,notBlur:!0,batch:l}),s.length&&n.dispatchAction({type:"highlight",escapeConnect:!0,notBlur:!0,batch:s})}(h,0,n),p}}function yN(t,e,n,i,r){var o=t.axis;if(!o.scale.isBlank()&&o.containData(e))if(t.involveSeries){var a=function(t,e){var n=e.axis,i=n.dim,r=t,o=[],a=Number.MAX_VALUE,s=-1;return E(e.seriesModels,(function(e,l){var u,h,c=e.getData().mapDimensionsAll(i);if(e.getAxisTooltipData){var p=e.getAxisTooltipData(c,t,n);h=p.dataIndices,u=p.nestestValue}else{if(!(h=e.getData().indicesOfNearest(c[0],t,"category"===n.type?.5:null)).length)return;u=e.getData().get(c[0],h[0])}if(null!=u&&isFinite(u)){var d=t-u,f=Math.abs(d);f<=a&&((f=0&&s<0)&&(a=f,s=d,r=u,o.length=0),E(h,(function(t){o.push({seriesIndex:e.seriesIndex,dataIndexInside:t,dataIndex:e.getData().getRawIndex(t)})})))}})),{payloadBatch:o,snapToValue:r}}(e,t),s=a.payloadBatch,l=a.snapToValue;s[0]&&null==r.seriesIndex&&A(r,s[0]),!i&&t.snap&&o.containData(l)&&null!=l&&(e=l),n.showPointer(t,e,s),n.showTooltip(t,a,l)}else n.showPointer(t,e)}function vN(t,e,n,i){t[e.key]={value:n,payloadBatch:i}}function mN(t,e,n,i){var r=n.payloadBatch,o=e.axis,a=o.model,s=e.axisPointerModel;if(e.triggerTooltip&&r.length){var l=e.coordSys.model,u=sI(l),h=t.map[u];h||(h=t.map[u]={coordSysId:l.id,coordSysIndex:l.componentIndex,coordSysType:l.type,coordSysMainType:l.mainType,dataByAxis:[]},t.list.push(h)),h.dataByAxis.push({axisDim:o.dim,axisIndex:a.componentIndex,axisType:a.type,axisId:a.id,value:i,valueLabelOpt:{precision:s.get(["label","precision"]),formatter:s.get(["label","formatter"])},seriesDataIndices:r.slice()})}}function xN(t){var e=t.axis.model,n={},i=n.axisDim=t.axis.dim;return n.axisIndex=n[i+"AxisIndex"]=e.componentIndex,n.axisName=n[i+"AxisName"]=e.name,n.axisId=n[i+"AxisId"]=e.id,n}function _N(t){return!t||null==t[0]||isNaN(t[0])||null==t[1]||isNaN(t[1])}function bN(t){uI.registerAxisPointerClass("CartesianAxisPointer",eN),t.registerComponentModel(oN),t.registerComponentView(pN),t.registerPreprocessor((function(t){if(t){(!t.axisPointer||0===t.axisPointer.length)&&(t.axisPointer={});var e=t.axisPointer.link;e&&!Y(e)&&(t.axisPointer.link=[e])}})),t.registerProcessor(t.PRIORITY.PROCESSOR.STATISTIC,(function(t,e){t.getComponent("axisPointer").coordSysAxesInfo=nI(t,e)})),t.registerAction({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},gN)}var wN=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.makeElOption=function(t,e,n,i,r){var o=n.axis;"angle"===o.dim&&(this.animationThreshold=Math.PI/18);var a=o.polar,s=a.getOtherAxis(o).getExtent(),l=o.dataToCoord(e),u=i.get("type");if(u&&"none"!==u){var h=ZR(i),c=SN[u](o,a,l,s);c.style=h,t.graphicKey=c.type,t.pointer=c}var p=function(t,e,n,i,r){var o=e.axis,a=o.dataToCoord(t),s=i.getAngleAxis().getExtent()[0];s=s/180*Math.PI;var l,u,h,c=i.getRadiusAxis().getExtent();if("radius"===o.dim){var p=[1,0,0,1,0,0];we(p,p,s),be(p,p,[i.cx,i.cy]),l=Eh([a,-r],p);var d=e.getModel("axisLabel").get("rotate")||0,f=KM.innerTextLayout(s,d*Math.PI/180,-1);u=f.textAlign,h=f.textVerticalAlign}else{var g=c[1];l=i.coordToPoint([g+r,a]);var y=i.cx,v=i.cy;u=Math.abs(l[0]-y)/g<.3?"center":l[0]>y?"left":"right",h=Math.abs(l[1]-v)/g<.3?"middle":l[1]>v?"top":"bottom"}return{position:l,align:u,verticalAlign:h}}(e,n,0,a,i.get(["label","margin"]));jR(t,n,i,r,p)},e}(GR);var SN={line:function(t,e,n,i){return"angle"===t.dim?{type:"Line",shape:JR(e.coordToPoint([i[0],n]),e.coordToPoint([i[1],n]))}:{type:"Circle",shape:{cx:e.cx,cy:e.cy,r:n}}},shadow:function(t,e,n,i){var r=Math.max(1,t.getBandWidth()),o=Math.PI/180;return"angle"===t.dim?{type:"Sector",shape:tN(e.cx,e.cy,i[0],i[1],(-n-r/2)*o,(r/2-n)*o)}:{type:"Sector",shape:tN(e.cx,e.cy,n-r/2,n+r/2,0,2*Math.PI)}}},MN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.findAxisModel=function(t){var e;return this.ecModel.eachComponent(t,(function(t){t.getCoordSysModel()===this&&(e=t)}),this),e},e.type="polar",e.dependencies=["radiusAxis","angleAxis"],e.defaultOption={z:0,center:["50%","50%"],radius:"80%"},e}(Op),IN=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.getCoordSysModel=function(){return this.getReferringComponents("polar",Eo).models[0]},e.type="polarAxis",e}(Op);R(IN,m_);var TN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="angleAxis",e}(IN),CN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="radiusAxis",e}(IN),DN=function(t){function e(e,n){return t.call(this,"radius",e,n)||this}return n(e,t),e.prototype.pointToData=function(t,e){return this.polar.pointToData(t,e)["radius"===this.dim?0:1]},e}(q_);DN.prototype.dataToRadius=q_.prototype.dataToCoord,DN.prototype.radiusToData=q_.prototype.coordToData;var AN=Po(),kN=function(t){function e(e,n){return t.call(this,"angle",e,n||[0,360])||this}return n(e,t),e.prototype.pointToData=function(t,e){return this.polar.pointToData(t,e)["radius"===this.dim?0:1]},e.prototype.calculateCategoryInterval=function(){var t=this,e=t.getLabelModel(),n=t.scale,i=n.getExtent(),r=n.count();if(i[1]-i[0]<1)return 0;var o=i[0],a=t.dataToCoord(o+1)-t.dataToCoord(o),s=Math.abs(a),l=_r(null==o?"":o+"",e.getFont(),"center","top"),u=Math.max(l.height,7)/s;isNaN(u)&&(u=1/0);var h=Math.max(0,Math.floor(u)),c=AN(t.model),p=c.lastAutoInterval,d=c.lastTickCount;return null!=p&&null!=d&&Math.abs(p-h)<=1&&Math.abs(d-r)<=1&&p>h?h=p:(c.lastTickCount=r,c.lastAutoInterval=h),h},e}(q_);kN.prototype.dataToAngle=q_.prototype.dataToCoord,kN.prototype.angleToData=q_.prototype.coordToData;var LN=["radius","angle"],PN=function(){function t(t){this.dimensions=LN,this.type="polar",this.cx=0,this.cy=0,this._radiusAxis=new DN,this._angleAxis=new kN,this.axisPointerEnabled=!0,this.name=t||"",this._radiusAxis.polar=this._angleAxis.polar=this}return t.prototype.containPoint=function(t){var e=this.pointToCoord(t);return this._radiusAxis.contain(e[0])&&this._angleAxis.contain(e[1])},t.prototype.containData=function(t){return this._radiusAxis.containData(t[0])&&this._angleAxis.containData(t[1])},t.prototype.getAxis=function(t){return this["_"+t+"Axis"]},t.prototype.getAxes=function(){return[this._radiusAxis,this._angleAxis]},t.prototype.getAxesByScale=function(t){var e=[],n=this._angleAxis,i=this._radiusAxis;return n.scale.type===t&&e.push(n),i.scale.type===t&&e.push(i),e},t.prototype.getAngleAxis=function(){return this._angleAxis},t.prototype.getRadiusAxis=function(){return this._radiusAxis},t.prototype.getOtherAxis=function(t){var e=this._angleAxis;return t===e?this._radiusAxis:e},t.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAngleAxis()},t.prototype.getTooltipAxes=function(t){var e=null!=t&&"auto"!==t?this.getAxis(t):this.getBaseAxis();return{baseAxes:[e],otherAxes:[this.getOtherAxis(e)]}},t.prototype.dataToPoint=function(t,e){return this.coordToPoint([this._radiusAxis.dataToRadius(t[0],e),this._angleAxis.dataToAngle(t[1],e)])},t.prototype.pointToData=function(t,e){var n=this.pointToCoord(t);return[this._radiusAxis.radiusToData(n[0],e),this._angleAxis.angleToData(n[1],e)]},t.prototype.pointToCoord=function(t){var e=t[0]-this.cx,n=t[1]-this.cy,i=this.getAngleAxis(),r=i.getExtent(),o=Math.min(r[0],r[1]),a=Math.max(r[0],r[1]);i.inverse?o=a-360:a=o+360;var s=Math.sqrt(e*e+n*n);e/=s,n/=s;for(var l=Math.atan2(-n,e)/Math.PI*180,u=la;)l+=360*u;return[s,l]},t.prototype.coordToPoint=function(t){var e=t[0],n=t[1]/180*Math.PI;return[Math.cos(n)*e+this.cx,-Math.sin(n)*e+this.cy]},t.prototype.getArea=function(){var t=this.getAngleAxis(),e=this.getRadiusAxis().getExtent().slice();e[0]>e[1]&&e.reverse();var n=t.getExtent(),i=Math.PI/180;return{cx:this.cx,cy:this.cy,r0:e[0],r:e[1],startAngle:-n[0]*i,endAngle:-n[1]*i,clockwise:t.inverse,contain:function(t,e){var n=t-this.cx,i=e-this.cy,r=n*n+i*i-1e-4,o=this.r,a=this.r0;return r<=o*o&&r>=a*a}}},t.prototype.convertToPixel=function(t,e,n){return ON(e)===this?this.dataToPoint(n):null},t.prototype.convertFromPixel=function(t,e,n){return ON(e)===this?this.pointToData(n):null},t}();function ON(t){var e=t.seriesModel,n=t.polarModel;return n&&n.coordinateSystem||e&&e.coordinateSystem}function RN(t,e){var n=this,i=n.getAngleAxis(),r=n.getRadiusAxis();if(i.scale.setExtent(1/0,-1/0),r.scale.setExtent(1/0,-1/0),t.eachSeries((function(t){if(t.coordinateSystem===n){var e=t.getData();E(v_(e,"radius"),(function(t){r.scale.unionExtentFromData(e,t)})),E(v_(e,"angle"),(function(t){i.scale.unionExtentFromData(e,t)}))}})),h_(i.scale,i.model),h_(r.scale,r.model),"category"===i.type&&!i.onBand){var o=i.getExtent(),a=360/i.scale.count();i.inverse?o[1]+=a:o[1]-=a,i.setExtent(o[0],o[1])}}function NN(t,e){if(t.type=e.get("type"),t.scale=c_(e),t.onBand=e.get("boundaryGap")&&"category"===t.type,t.inverse=e.get("inverse"),function(t){return"angleAxis"===t.mainType}(e)){t.inverse=t.inverse!==e.get("clockwise");var n=e.get("startAngle");t.setExtent(n,n+(t.inverse?-360:360))}e.axis=t,t.model=e}var EN={dimensions:LN,create:function(t,e){var n=[];return t.eachComponent("polar",(function(t,i){var r=new PN(i+"");r.update=RN;var o=r.getRadiusAxis(),a=r.getAngleAxis(),s=t.findAxisModel("radiusAxis"),l=t.findAxisModel("angleAxis");NN(o,s),NN(a,l),function(t,e,n){var i=e.get("center"),r=n.getWidth(),o=n.getHeight();t.cx=Ur(i[0],r),t.cy=Ur(i[1],o);var a=t.getRadiusAxis(),s=Math.min(r,o)/2,l=e.get("radius");null==l?l=[0,"100%"]:Y(l)||(l=[0,l]);var u=[Ur(l[0],s),Ur(l[1],s)];a.inverse?a.setExtent(u[1],u[0]):a.setExtent(u[0],u[1])}(r,t,e),n.push(r),t.coordinateSystem=r,r.model=t})),t.eachSeries((function(t){if("polar"===t.get("coordinateSystem")){var e=t.getReferringComponents("polar",Eo).models[0];0,t.coordinateSystem=e.coordinateSystem}})),n}},zN=["axisLine","axisLabel","axisTick","minorTick","splitLine","minorSplitLine","splitArea"];function VN(t,e,n){e[1]>e[0]&&(e=e.slice().reverse());var i=t.coordToPoint([e[0],n]),r=t.coordToPoint([e[1],n]);return{x1:i[0],y1:i[1],x2:r[0],y2:r[1]}}function BN(t){return t.getRadiusAxis().inverse?0:1}function FN(t){var e=t[0],n=t[t.length-1];e&&n&&Math.abs(Math.abs(e.coord-n.coord)-360)<1e-4&&t.pop()}var GN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.axisPointerClass="PolarAxisPointer",n}return n(e,t),e.prototype.render=function(t,e){if(this.group.removeAll(),t.get("show")){var n=t.axis,i=n.polar,r=i.getRadiusAxis().getExtent(),o=n.getTicksCoords(),a=n.getMinorTicksCoords(),s=z(n.getViewLabels(),(function(t){t=T(t);var e=n.scale,i="ordinal"===e.type?e.getRawOrdinalNumber(t.tickValue):t.tickValue;return t.coord=n.dataToCoord(i),t}));FN(s),FN(o),E(zN,(function(e){!t.get([e,"show"])||n.scale.isBlank()&&"axisLine"!==e||WN[e](this.group,t,i,o,a,r,s)}),this)}},e.type="angleAxis",e}(uI),WN={axisLine:function(t,e,n,i,r,o){var a,s=e.getModel(["axisLine","lineStyle"]),l=BN(n),u=l?0:1;(a=0===o[u]?new xu({shape:{cx:n.cx,cy:n.cy,r:o[l]},style:s.getLineStyle(),z2:1,silent:!0}):new Vu({shape:{cx:n.cx,cy:n.cy,r:o[l],r0:o[u]},style:s.getLineStyle(),z2:1,silent:!0})).style.fill=null,t.add(a)},axisTick:function(t,e,n,i,r,o){var a=e.getModel("axisTick"),s=(a.get("inside")?-1:1)*a.get("length"),l=o[BN(n)],u=z(i,(function(t){return new Xu({shape:VN(n,[l,l+s],t.coord)})}));t.add(Lh(u,{style:k(a.getModel("lineStyle").getLineStyle(),{stroke:e.get(["axisLine","lineStyle","color"])})}))},minorTick:function(t,e,n,i,r,o){if(r.length){for(var a=e.getModel("axisTick"),s=e.getModel("minorTick"),l=(a.get("inside")?-1:1)*s.get("length"),u=o[BN(n)],h=[],c=0;cf?"left":"right",v=Math.abs(d[1]-g)/p<.3?"middle":d[1]>g?"top":"bottom";if(s&&s[c]){var m=s[c];q(m)&&m.textStyle&&(a=new Sc(m.textStyle,l,l.ecModel))}var x=new Bs({silent:KM.isLabelSilent(e),style:ec(a,{x:d[0],y:d[1],fill:a.getTextColor()||e.get(["axisLine","lineStyle","color"]),text:i.formattedLabel,align:y,verticalAlign:v})});if(t.add(x),h){var _=KM.makeAxisEventDataBase(e);_.targetType="axisLabel",_.value=i.rawLabel,Js(x).eventData=_}}),this)},splitLine:function(t,e,n,i,r,o){var a=e.getModel("splitLine").getModel("lineStyle"),s=a.get("color"),l=0;s=s instanceof Array?s:[s];for(var u=[],h=0;h=0?"p":"n",T=_;m&&(i[s][M]||(i[s][M]={p:_,n:_}),T=i[s][M][I]);var C=void 0,D=void 0,A=void 0,k=void 0;if("radius"===c.dim){var L=c.dataToCoord(S)-_,P=o.dataToCoord(M);Math.abs(L)=k})}}}))}var KN={startAngle:90,clockwise:!0,splitNumber:12,axisLabel:{rotate:0}},$N={splitNumber:5},JN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="polar",e}(wg);function QN(t,e){e=e||{};var n=t.coordinateSystem,i=t.axis,r={},o=i.position,a=i.orient,s=n.getRect(),l=[s.x,s.x+s.width,s.y,s.y+s.height],u={horizontal:{top:l[2],bottom:l[3]},vertical:{left:l[0],right:l[1]}};r.position=["vertical"===a?u.vertical[o]:l[0],"horizontal"===a?u.horizontal[o]:l[3]];r.rotation=Math.PI/2*{horizontal:0,vertical:1}[a];r.labelDirection=r.tickDirection=r.nameDirection={top:-1,bottom:1,right:1,left:-1}[o],t.get(["axisTick","inside"])&&(r.tickDirection=-r.tickDirection),it(e.labelInside,t.get(["axisLabel","inside"]))&&(r.labelDirection=-r.labelDirection);var h=e.rotate;return null==h&&(h=t.get(["axisLabel","rotate"])),r.labelRotation="top"===o?-h:h,r.z2=1,r}var tE=["axisLine","axisTickLabel","axisName"],eE=["splitArea","splitLine"],nE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.axisPointerClass="SingleAxisPointer",n}return n(e,t),e.prototype.render=function(e,n,i,r){var o=this.group;o.removeAll();var a=this._axisGroup;this._axisGroup=new Er;var s=QN(e),l=new KM(e,s);E(tE,l.add,l),o.add(this._axisGroup),o.add(l.getGroup()),E(eE,(function(t){e.get([t,"show"])&&iE[t](this,this.group,this._axisGroup,e)}),this),Bh(a,this._axisGroup,e),t.prototype.render.call(this,e,n,i,r)},e.prototype.remove=function(){pI(this)},e.type="singleAxis",e}(uI),iE={splitLine:function(t,e,n,i){var r=i.axis;if(!r.scale.isBlank()){var o=i.getModel("splitLine"),a=o.getModel("lineStyle"),s=a.get("color");s=s instanceof Array?s:[s];for(var l=a.get("width"),u=i.coordinateSystem.getRect(),h=r.isHorizontal(),c=[],p=0,d=r.getTicksCoords({tickModel:o}),f=[],g=[],y=0;y=e.y&&t[1]<=e.y+e.height:n.contain(n.toLocalCoord(t[1]))&&t[0]>=e.y&&t[0]<=e.y+e.height},t.prototype.pointToData=function(t){var e=this.getAxis();return[e.coordToData(e.toLocalCoord(t["horizontal"===e.orient?0:1]))]},t.prototype.dataToPoint=function(t){var e=this.getAxis(),n=this.getRect(),i=[],r="horizontal"===e.orient?0:1;return t instanceof Array&&(t=t[0]),i[r]=e.toGlobalCoord(e.dataToCoord(+t)),i[1-r]=0===r?n.y+n.height/2:n.x+n.width/2,i},t.prototype.convertToPixel=function(t,e,n){return lE(e)===this?this.dataToPoint(n):null},t.prototype.convertFromPixel=function(t,e,n){return lE(e)===this?this.pointToData(n):null},t}();function lE(t){var e=t.seriesModel,n=t.singleAxisModel;return n&&n.coordinateSystem||e&&e.coordinateSystem}var uE={create:function(t,e){var n=[];return t.eachComponent("singleAxis",(function(i,r){var o=new sE(i,t,e);o.name="single_"+r,o.resize(i,e),i.coordinateSystem=o,n.push(o)})),t.eachSeries((function(t){if("singleAxis"===t.get("coordinateSystem")){var e=t.getReferringComponents("singleAxis",Eo).models[0];t.coordinateSystem=e&&e.coordinateSystem}})),n},dimensions:aE},hE=["x","y"],cE=["width","height"],pE=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.makeElOption=function(t,e,n,i,r){var o=n.axis,a=o.coordinateSystem,s=gE(a,1-fE(o)),l=a.dataToPoint(e)[0],u=i.get("type");if(u&&"none"!==u){var h=ZR(i),c=dE[u](o,l,s);c.style=h,t.graphicKey=c.type,t.pointer=c}$R(e,t,QN(n),n,i,r)},e.prototype.getHandleTransform=function(t,e,n){var i=QN(e,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var r=KR(e.axis,t,i);return{x:r[0],y:r[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},e.prototype.updateHandleTransform=function(t,e,n,i){var r=n.axis,o=r.coordinateSystem,a=fE(r),s=gE(o,a),l=[t.x,t.y];l[a]+=e[a],l[a]=Math.min(s[1],l[a]),l[a]=Math.max(s[0],l[a]);var u=gE(o,1-a),h=(u[1]+u[0])/2,c=[h,h];return c[a]=l[a],{x:l[0],y:l[1],rotation:t.rotation,cursorPoint:c,tooltipOption:{verticalAlign:"middle"}}},e}(GR),dE={line:function(t,e,n){return{type:"Line",subPixelOptimize:!0,shape:JR([e,n[0]],[e,n[1]],fE(t))}},shadow:function(t,e,n){var i=t.getBandWidth(),r=n[1]-n[0];return{type:"Rect",shape:QR([e-i/2,n[0]],[i,r],fE(t))}}};function fE(t){return t.isHorizontal()?0:1}function gE(t,e){var n=t.getRect();return[n[hE[e]],n[hE[e]]+n[cE[e]]]}var yE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="single",e}(wg);var vE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(e,n,i){var r=kp(e);t.prototype.init.apply(this,arguments),mE(e,r)},e.prototype.mergeOption=function(e){t.prototype.mergeOption.apply(this,arguments),mE(this.option,e)},e.prototype.getCellSize=function(){return this.option.cellSize},e.type="calendar",e.defaultOption={z:2,left:80,top:60,cellSize:20,orient:"horizontal",splitLine:{show:!0,lineStyle:{color:"#000",width:1,type:"solid"}},itemStyle:{color:"#fff",borderWidth:1,borderColor:"#ccc"},dayLabel:{show:!0,firstDay:0,position:"start",margin:"50%",color:"#000"},monthLabel:{show:!0,position:"start",margin:5,align:"center",formatter:null,color:"#000"},yearLabel:{show:!0,position:null,margin:30,formatter:null,color:"#ccc",fontFamily:"sans-serif",fontWeight:"bolder",fontSize:20}},e}(Op);function mE(t,e){var n,i=t.cellSize;1===(n=Y(i)?i:t.cellSize=[i,i]).length&&(n[1]=n[0]);var r=z([0,1],(function(t){return function(t,e){return null!=t[Sp[e][0]]||null!=t[Sp[e][1]]&&null!=t[Sp[e][2]]}(e,t)&&(n[t]="auto"),null!=n[t]&&"auto"!==n[t]}));Ap(t,e,{type:"box",ignoreSize:r})}var xE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=this.group;i.removeAll();var r=t.coordinateSystem,o=r.getRangeInfo(),a=r.getOrient(),s=e.getLocaleModel();this._renderDayRect(t,o,i),this._renderLines(t,o,a,i),this._renderYearText(t,o,a,i),this._renderMonthText(t,s,a,i),this._renderWeekText(t,s,o,a,i)},e.prototype._renderDayRect=function(t,e,n){for(var i=t.coordinateSystem,r=t.getModel("itemStyle").getItemStyle(),o=i.getCellWidth(),a=i.getCellHeight(),s=e.start.time;s<=e.end.time;s=i.getNextNDay(s,1).time){var l=i.dataToRect([s],!1).tl,u=new Es({shape:{x:l[0],y:l[1],width:o,height:a},cursor:"default",style:r});n.add(u)}},e.prototype._renderLines=function(t,e,n,i){var r=this,o=t.coordinateSystem,a=t.getModel(["splitLine","lineStyle"]).getLineStyle(),s=t.get(["splitLine","show"]),l=a.lineWidth;this._tlpoints=[],this._blpoints=[],this._firstDayOfMonth=[],this._firstDayPoints=[];for(var u=e.start,h=0;u.time<=e.end.time;h++){p(u.formatedDate),0===h&&(u=o.getDateInfo(e.start.y+"-"+e.start.m));var c=u.date;c.setMonth(c.getMonth()+1),u=o.getDateInfo(c)}function p(e){r._firstDayOfMonth.push(o.getDateInfo(e)),r._firstDayPoints.push(o.dataToRect([e],!1).tl);var l=r._getLinePointsOfOneWeek(t,e,n);r._tlpoints.push(l[0]),r._blpoints.push(l[l.length-1]),s&&r._drawSplitline(l,a,i)}p(o.getNextNDay(e.end.time,1).formatedDate),s&&this._drawSplitline(r._getEdgesPoints(r._tlpoints,l,n),a,i),s&&this._drawSplitline(r._getEdgesPoints(r._blpoints,l,n),a,i)},e.prototype._getEdgesPoints=function(t,e,n){var i=[t[0].slice(),t[t.length-1].slice()],r="horizontal"===n?0:1;return i[0][r]=i[0][r]-e/2,i[1][r]=i[1][r]+e/2,i},e.prototype._drawSplitline=function(t,e,n){var i=new Hu({z2:20,shape:{points:t},style:e});n.add(i)},e.prototype._getLinePointsOfOneWeek=function(t,e,n){for(var i=t.coordinateSystem,r=i.getDateInfo(e),o=[],a=0;a<7;a++){var s=i.getNextNDay(r.time,a),l=i.dataToRect([s.time],!1);o[2*s.day]=l.tl,o[2*s.day+1]=l["horizontal"===n?"bl":"tr"]}return o},e.prototype._formatterLabel=function(t,e){return X(t)&&t?(n=t,E(e,(function(t,e){n=n.replace("{"+e+"}",i?ie(t):t)})),n):U(t)?t(e):e.nameMap;var n,i},e.prototype._yearTextPositionControl=function(t,e,n,i,r){var o=e[0],a=e[1],s=["center","bottom"];"bottom"===i?(a+=r,s=["center","top"]):"left"===i?o-=r:"right"===i?(o+=r,s=["center","top"]):a-=r;var l=0;return"left"!==i&&"right"!==i||(l=Math.PI/2),{rotation:l,x:o,y:a,style:{align:s[0],verticalAlign:s[1]}}},e.prototype._renderYearText=function(t,e,n,i){var r=t.getModel("yearLabel");if(r.get("show")){var o=r.get("margin"),a=r.get("position");a||(a="horizontal"!==n?"top":"left");var s=[this._tlpoints[this._tlpoints.length-1],this._blpoints[0]],l=(s[0][0]+s[1][0])/2,u=(s[0][1]+s[1][1])/2,h="horizontal"===n?0:1,c={top:[l,s[h][1]],bottom:[l,s[1-h][1]],left:[s[1-h][0],u],right:[s[h][0],u]},p=e.start.y;+e.end.y>+e.start.y&&(p=p+"-"+e.end.y);var d=r.get("formatter"),f={start:e.start.y,end:e.end.y,nameMap:p},g=this._formatterLabel(d,f),y=new Bs({z2:30,style:ec(r,{text:g})});y.attr(this._yearTextPositionControl(y,c[a],n,a,o)),i.add(y)}},e.prototype._monthTextPositionControl=function(t,e,n,i,r){var o="left",a="top",s=t[0],l=t[1];return"horizontal"===n?(l+=r,e&&(o="center"),"start"===i&&(a="bottom")):(s+=r,e&&(a="middle"),"start"===i&&(o="right")),{x:s,y:l,align:o,verticalAlign:a}},e.prototype._renderMonthText=function(t,e,n,i){var r=t.getModel("monthLabel");if(r.get("show")){var o=r.get("nameMap"),a=r.get("margin"),s=r.get("position"),l=r.get("align"),u=[this._tlpoints,this._blpoints];o&&!X(o)||(o&&(e=Rc(o)||e),o=e.get(["time","monthAbbr"])||[]);var h="start"===s?0:1,c="horizontal"===n?0:1;a="start"===s?-a:a;for(var p="center"===l,d=0;d=i.start.time&&n.timea.end.time&&t.reverse(),t},t.prototype._getRangeInfo=function(t){var e,n=[this.getDateInfo(t[0]),this.getDateInfo(t[1])];n[0].time>n[1].time&&(e=!0,n.reverse());var i=Math.floor(n[1].time/_E)-Math.floor(n[0].time/_E)+1,r=new Date(n[0].time),o=r.getDate(),a=n[1].date.getDate();r.setDate(o+i-1);var s=r.getDate();if(s!==a)for(var l=r.getTime()-n[1].time>0?1:-1;(s=r.getDate())!==a&&(r.getTime()-n[1].time)*l>0;)i-=l,r.setDate(s-l);var u=Math.floor((i+n[0].day+6)/7),h=e?1-u:u-1;return e&&n.reverse(),{range:[n[0].formatedDate,n[1].formatedDate],start:n[0],end:n[1],allDay:i,weeks:u,nthWeek:h,fweek:n[0].day,lweek:n[1].day}},t.prototype._getDateByWeeksAndDay=function(t,e,n){var i=this._getRangeInfo(n);if(t>i.weeks||0===t&&ei.lweek)return null;var r=7*(t-1)-i.fweek+e,o=new Date(i.start.time);return o.setDate(+i.start.d+r),this.getDateInfo(o)},t.create=function(e,n){var i=[];return e.eachComponent("calendar",(function(r){var o=new t(r,e,n);i.push(o),r.coordinateSystem=o})),e.eachSeries((function(t){"calendar"===t.get("coordinateSystem")&&(t.coordinateSystem=i[t.get("calendarIndex")||0])})),i},t.dimensions=["time","value"],t}();function wE(t){var e=t.calendarModel,n=t.seriesModel;return e?e.coordinateSystem:n?n.coordinateSystem:null}function SE(t,e){var n;return E(e,(function(e){null!=t[e]&&"auto"!==t[e]&&(n=!0)})),n}var ME=["transition","enterFrom","leaveTo"],IE=ME.concat(["enterAnimation","updateAnimation","leaveAnimation"]);function TE(t,e,n){if(n&&(!t[n]&&e[n]&&(t[n]={}),t=t[n],e=e[n]),t&&e)for(var i=n?ME:IE,r=0;r=0;l--){var p,d,f;if(f=null!=(d=Do((p=n[l]).id,null))?r.get(d):null){var g=f.parent,y=(c=AE(g),{}),v=Cp(f,p,g===i?{width:o,height:a}:{width:c.width,height:c.height},null,{hv:p.hv,boundingMode:p.bounding},y);if(!AE(f).isNew&&v){for(var m=p.transition,x={},_=0;_=0)?x[b]=w:f[b]=w}dh(f,x,t,0)}else f.attr(y)}}},e.prototype._clear=function(){var t=this,e=this._elMap;e.each((function(n){OE(n,AE(n).option,e,t._lastGraphicModel)})),this._elMap=yt()},e.prototype.dispose=function(){this._clear()},e.type="graphic",e}(wg);function LE(t){var e=_t(DE,t)?DE[t]:Ch(t);var n=new e({});return AE(n).type=t,n}function PE(t,e,n,i){var r=LE(n);return e.add(r),i.set(t,r),AE(r).id=t,AE(r).isNew=!0,r}function OE(t,e,n,i){t&&t.parent&&("group"===t.type&&t.traverse((function(t){OE(t,e,n,i)})),KO(t,e,i),n.removeKey(AE(t).id))}function RE(t,e,n,i){t.isGroup||E([["cursor",wa.prototype.cursor],["zlevel",i||0],["z",n||0],["z2",0]],(function(n){var i=n[0];_t(e,i)?t[i]=rt(e[i],n[1]):null==t[i]&&(t[i]=n[1])})),E(G(e),(function(n){if(0===n.indexOf("on")){var i=e[n];t[n]=U(i)?i:null}})),_t(e,"draggable")&&(t.draggable=e.draggable),null!=e.name&&(t.name=e.name),null!=e.id&&(t.id=e.id)}var NE=["x","y","radius","angle","single"],EE=["cartesian2d","polar","singleAxis"];function zE(t){return t+"Axis"}function VE(t,e){var n,i=yt(),r=[],o=yt();t.eachComponent({mainType:"dataZoom",query:e},(function(t){o.get(t.uid)||s(t)}));do{n=!1,t.eachComponent("dataZoom",a)}while(n);function a(t){!o.get(t.uid)&&function(t){var e=!1;return t.eachTargetAxis((function(t,n){var r=i.get(t);r&&r[n]&&(e=!0)})),e}(t)&&(s(t),n=!0)}function s(t){o.set(t.uid,!0),r.push(t),t.eachTargetAxis((function(t,e){(i.get(t)||i.set(t,[]))[e]=!0}))}return r}function BE(t){var e=t.ecModel,n={infoList:[],infoMap:yt()};return t.eachTargetAxis((function(t,i){var r=e.getComponent(zE(t),i);if(r){var o=r.getCoordSysModel();if(o){var a=o.uid,s=n.infoMap.get(a);s||(s={model:o,axisModels:[]},n.infoList.push(s),n.infoMap.set(a,s)),s.axisModels.push(r)}}})),n}var FE=function(){function t(){this.indexList=[],this.indexMap=[]}return t.prototype.add=function(t){this.indexMap[t]||(this.indexList.push(t),this.indexMap[t]=!0)},t}(),GE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._autoThrottle=!0,n._noTarget=!0,n._rangePropMode=["percent","percent"],n}return n(e,t),e.prototype.init=function(t,e,n){var i=WE(t);this.settledOption=i,this.mergeDefaultAndTheme(t,n),this._doInit(i)},e.prototype.mergeOption=function(t){var e=WE(t);C(this.option,t,!0),C(this.settledOption,e,!0),this._doInit(e)},e.prototype._doInit=function(t){var e=this.option;this._setDefaultThrottle(t),this._updateRangeUse(t);var n=this.settledOption;E([["start","startValue"],["end","endValue"]],(function(t,i){"value"===this._rangePropMode[i]&&(e[t[0]]=n[t[0]]=null)}),this),this._resetTarget()},e.prototype._resetTarget=function(){var t=this.get("orient",!0),e=this._targetAxisInfoMap=yt();this._fillSpecifiedTargetAxis(e)?this._orient=t||this._makeAutoOrientByTargetAxis():(this._orient=t||"horizontal",this._fillAutoTargetAxisByOrient(e,this._orient)),this._noTarget=!0,e.each((function(t){t.indexList.length&&(this._noTarget=!1)}),this)},e.prototype._fillSpecifiedTargetAxis=function(t){var e=!1;return E(NE,(function(n){var i=this.getReferringComponents(zE(n),zo);if(i.specified){e=!0;var r=new FE;E(i.models,(function(t){r.add(t.componentIndex)})),t.set(n,r)}}),this),e},e.prototype._fillAutoTargetAxisByOrient=function(t,e){var n=this.ecModel,i=!0;if(i){var r="vertical"===e?"y":"x";o(n.findComponents({mainType:r+"Axis"}),r)}i&&o(n.findComponents({mainType:"singleAxis",filter:function(t){return t.get("orient",!0)===e}}),"single");function o(e,n){var r=e[0];if(r){var o=new FE;if(o.add(r.componentIndex),t.set(n,o),i=!1,"x"===n||"y"===n){var a=r.getReferringComponents("grid",Eo).models[0];a&&E(e,(function(t){r.componentIndex!==t.componentIndex&&a===t.getReferringComponents("grid",Eo).models[0]&&o.add(t.componentIndex)}))}}}i&&E(NE,(function(e){if(i){var r=n.findComponents({mainType:zE(e),filter:function(t){return"category"===t.get("type",!0)}});if(r[0]){var o=new FE;o.add(r[0].componentIndex),t.set(e,o),i=!1}}}),this)},e.prototype._makeAutoOrientByTargetAxis=function(){var t;return this.eachTargetAxis((function(e){!t&&(t=e)}),this),"y"===t?"vertical":"horizontal"},e.prototype._setDefaultThrottle=function(t){if(t.hasOwnProperty("throttle")&&(this._autoThrottle=!1),this._autoThrottle){var e=this.ecModel.option;this.option.throttle=e.animation&&e.animationDurationUpdate>0?100:20}},e.prototype._updateRangeUse=function(t){var e=this._rangePropMode,n=this.get("rangeMode");E([["start","startValue"],["end","endValue"]],(function(i,r){var o=null!=t[i[0]],a=null!=t[i[1]];o&&!a?e[r]="percent":!o&&a?e[r]="value":n?e[r]=n[r]:o&&(e[r]="percent")}))},e.prototype.noTarget=function(){return this._noTarget},e.prototype.getFirstTargetAxisModel=function(){var t;return this.eachTargetAxis((function(e,n){null==t&&(t=this.ecModel.getComponent(zE(e),n))}),this),t},e.prototype.eachTargetAxis=function(t,e){this._targetAxisInfoMap.each((function(n,i){E(n.indexList,(function(n){t.call(e,i,n)}))}))},e.prototype.getAxisProxy=function(t,e){var n=this.getAxisModel(t,e);if(n)return n.__dzAxisProxy},e.prototype.getAxisModel=function(t,e){var n=this._targetAxisInfoMap.get(t);if(n&&n.indexMap[e])return this.ecModel.getComponent(zE(t),e)},e.prototype.setRawRange=function(t){var e=this.option,n=this.settledOption;E([["start","startValue"],["end","endValue"]],(function(i){null==t[i[0]]&&null==t[i[1]]||(e[i[0]]=n[i[0]]=t[i[0]],e[i[1]]=n[i[1]]=t[i[1]])}),this),this._updateRangeUse(t)},e.prototype.setCalculatedRange=function(t){var e=this.option;E(["start","startValue","end","endValue"],(function(n){e[n]=t[n]}))},e.prototype.getPercentRange=function(){var t=this.findRepresentativeAxisProxy();if(t)return t.getDataPercentWindow()},e.prototype.getValueRange=function(t,e){if(null!=t||null!=e)return this.getAxisProxy(t,e).getDataValueWindow();var n=this.findRepresentativeAxisProxy();return n?n.getDataValueWindow():void 0},e.prototype.findRepresentativeAxisProxy=function(t){if(t)return t.__dzAxisProxy;for(var e,n=this._targetAxisInfoMap.keys(),i=0;i=0}(e)){var n=zE(this._dimName),i=e.getReferringComponents(n,Eo).models[0];i&&this._axisIndex===i.componentIndex&&t.push(e)}}),this),t},t.prototype.getAxisModel=function(){return this.ecModel.getComponent(this._dimName+"Axis",this._axisIndex)},t.prototype.getMinMaxSpan=function(){return T(this._minMaxSpan)},t.prototype.calculateDataWindow=function(t){var e,n=this._dataExtent,i=this.getAxisModel().axis.scale,r=this._dataZoomModel.getRangePropMode(),o=[0,100],a=[],s=[];XE(["start","end"],(function(l,u){var h=t[l],c=t[l+"Value"];"percent"===r[u]?(null==h&&(h=o[u]),c=i.parse(Yr(h,o,n))):(e=!0,h=Yr(c=null==c?n[u]:i.parse(c),n,o)),s[u]=null==c||isNaN(c)?n[u]:c,a[u]=null==h||isNaN(h)?o[u]:h})),ZE(s),ZE(a);var l=this._minMaxSpan;function u(t,e,n,r,o){var a=o?"Span":"ValueSpan";xk(0,t,n,"all",l["min"+a],l["max"+a]);for(var s=0;s<2;s++)e[s]=Yr(t[s],n,r,!0),o&&(e[s]=i.parse(e[s]))}return e?u(s,a,n,o,!1):u(a,s,o,n,!0),{valueWindow:s,percentWindow:a}},t.prototype.reset=function(t){if(t===this._dataZoomModel){var e=this.getTargetSeriesModels();this._dataExtent=function(t,e,n){var i=[1/0,-1/0];XE(n,(function(t){!function(t,e,n){e&&E(v_(e,n),(function(n){var i=e.getApproximateExtent(n);i[0]t[1]&&(t[1]=i[1])}))}(i,t.getData(),e)}));var r=t.getAxisModel(),o=s_(r.axis.scale,r,i).calculate();return[o.min,o.max]}(this,this._dimName,e),this._updateMinMaxSpan();var n=this.calculateDataWindow(t.settledOption);this._valueWindow=n.valueWindow,this._percentWindow=n.percentWindow,this._setAxisModel()}},t.prototype.filterData=function(t,e){if(t===this._dataZoomModel){var n=this._dimName,i=this.getTargetSeriesModels(),r=t.get("filterMode"),o=this._valueWindow;"none"!==r&&XE(i,(function(t){var e=t.getData(),i=e.mapDimensionsAll(n);if(i.length){if("weakFilter"===r){var a=e.getStore(),s=z(i,(function(t){return e.getDimensionIndex(t)}),e);e.filterSelf((function(t){for(var e,n,r,l=0;lo[1];if(h&&!c&&!p)return!0;h&&(r=!0),c&&(e=!0),p&&(n=!0)}return r&&e&&n}))}else XE(i,(function(n){if("empty"===r)t.setData(e=e.map(n,(function(t){return function(t){return t>=o[0]&&t<=o[1]}(t)?t:NaN})));else{var i={};i[n]=o,e.selectRange(i)}}));XE(i,(function(t){e.setApproximateExtent(o,t)}))}}))}},t.prototype._updateMinMaxSpan=function(){var t=this._minMaxSpan={},e=this._dataZoomModel,n=this._dataExtent;XE(["min","max"],(function(i){var r=e.get(i+"Span"),o=e.get(i+"ValueSpan");null!=o&&(o=this.getAxisModel().axis.scale.parse(o)),null!=o?r=Yr(n[0]+o,n,[0,100],!0):null!=r&&(o=Yr(r,[0,100],n,!0)-n[0]),t[i+"Span"]=r,t[i+"ValueSpan"]=o}),this)},t.prototype._setAxisModel=function(){var t=this.getAxisModel(),e=this._percentWindow,n=this._valueWindow;if(e){var i=Kr(n,[0,500]);i=Math.min(i,20);var r=t.axis.scale.rawExtentInfo;0!==e[0]&&r.setDeterminedMinMax("min",+n[0].toFixed(i)),100!==e[1]&&r.setDeterminedMinMax("max",+n[1].toFixed(i)),r.freeze()}},t}();var qE={getTargetSeries:function(t){function e(e){t.eachComponent("dataZoom",(function(n){n.eachTargetAxis((function(i,r){var o=t.getComponent(zE(i),r);e(i,r,o,n)}))}))}e((function(t,e,n,i){n.__dzAxisProxy=null}));var n=[];e((function(e,i,r,o){r.__dzAxisProxy||(r.__dzAxisProxy=new jE(e,i,o,t),n.push(r.__dzAxisProxy))}));var i=yt();return E(n,(function(t){E(t.getTargetSeriesModels(),(function(t){i.set(t.uid,t)}))})),i},overallReset:function(t,e){t.eachComponent("dataZoom",(function(t){t.eachTargetAxis((function(e,n){t.getAxisProxy(e,n).reset(t)})),t.eachTargetAxis((function(n,i){t.getAxisProxy(n,i).filterData(t,e)}))})),t.eachComponent("dataZoom",(function(t){var e=t.findRepresentativeAxisProxy();if(e){var n=e.getDataPercentWindow(),i=e.getDataValueWindow();t.setCalculatedRange({start:n[0],end:n[1],startValue:i[0],endValue:i[1]})}}))}};var KE=!1;function $E(t){KE||(KE=!0,t.registerProcessor(t.PRIORITY.PROCESSOR.FILTER,qE),function(t){t.registerAction("dataZoom",(function(t,e){E(VE(e,t),(function(e){e.setRawRange({start:t.start,end:t.end,startValue:t.startValue,endValue:t.endValue})}))}))}(t),t.registerSubTypeDefaulter("dataZoom",(function(){return"slider"})))}function JE(t){t.registerComponentModel(HE),t.registerComponentView(UE),$E(t)}var QE=function(){},tz={};function ez(t,e){tz[t]=e}function nz(t){return tz[t]}var iz=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.optionUpdated=function(){t.prototype.optionUpdated.apply(this,arguments);var e=this.ecModel;E(this.option.feature,(function(t,n){var i=nz(n);i&&(i.getDefaultOption&&(i.defaultOption=i.getDefaultOption(e)),C(t,i.defaultOption))}))},e.type="toolbox",e.layoutMode={type:"box",ignoreSize:!0},e.defaultOption={show:!0,z:6,orient:"horizontal",left:"right",top:"top",backgroundColor:"transparent",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemSize:15,itemGap:8,showTitle:!0,iconStyle:{borderColor:"#666",color:"none"},emphasis:{iconStyle:{borderColor:"#3E98C5"}},tooltip:{show:!1,position:"bottom"}},e}(Op);function rz(t,e){var n=dp(e.get("padding")),i=e.getItemStyle(["color","opacity"]);return i.fill=e.get("backgroundColor"),t=new Es({shape:{x:t.x-n[3],y:t.y-n[0],width:t.width+n[1]+n[3],height:t.height+n[0]+n[2],r:e.get("borderRadius")},style:i,silent:!0,z2:-1})}var oz=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.render=function(t,e,n,i){var r=this.group;if(r.removeAll(),t.get("show")){var o=+t.get("itemSize"),a="vertical"===t.get("orient"),s=t.get("feature")||{},l=this._features||(this._features={}),u=[];E(s,(function(t,e){u.push(e)})),new Lm(this._featureNames||[],u).add(h).update(h).remove(H(h,null)).execute(),this._featureNames=u,function(t,e,n){var i=e.getBoxLayoutParams(),r=e.get("padding"),o={width:n.getWidth(),height:n.getHeight()},a=Tp(i,o,r);Ip(e.get("orient"),t,e.get("itemGap"),a.width,a.height),Cp(t,i,o,r)}(r,t,n),r.add(rz(r.getBoundingRect(),t)),a||r.eachChild((function(t){var e=t.__title,i=t.ensureState("emphasis"),a=i.textConfig||(i.textConfig={}),s=t.getTextContent(),l=s&&s.ensureState("emphasis");if(l&&!U(l)&&e){var u=l.style||(l.style={}),h=_r(e,Bs.makeFont(u)),c=t.x+r.x,p=!1;t.y+r.y+o+h.height>n.getHeight()&&(a.position="top",p=!0);var d=p?-5-h.height:o+10;c+h.width/2>n.getWidth()?(a.position=["100%",d],u.align="right"):c-h.width/2<0&&(a.position=[0,d],u.align="left")}}))}function h(h,c){var p,d=u[h],f=u[c],g=s[d],y=new Sc(g,t,t.ecModel);if(i&&null!=i.newTitle&&i.featureName===d&&(g.title=i.newTitle),d&&!f){if(function(t){return 0===t.indexOf("my")}(d))p={onclick:y.option.onclick,featureName:d};else{var v=nz(d);if(!v)return;p=new v}l[d]=p}else if(!(p=l[f]))return;p.uid=Ic("toolbox-feature"),p.model=y,p.ecModel=e,p.api=n;var m=p instanceof QE;d||!f?!y.get("show")||m&&p.unusable?m&&p.remove&&p.remove(e,n):(!function(i,s,l){var u,h,c=i.getModel("iconStyle"),p=i.getModel(["emphasis","iconStyle"]),d=s instanceof QE&&s.getIcons?s.getIcons():i.get("icon"),f=i.get("title")||{};X(d)?(u={})[l]=d:u=d;X(f)?(h={})[l]=f:h=f;var g=i.iconPaths={};E(u,(function(l,u){var d=Wh(l,{},{x:-o/2,y:-o/2,width:o,height:o});d.setStyle(c.getItemStyle()),d.ensureState("emphasis").style=p.getItemStyle();var f=new Bs({style:{text:h[u],align:p.get("textAlign"),borderRadius:p.get("textBorderRadius"),padding:p.get("textPadding"),fill:null},ignore:!0});d.setTextContent(f),Xh({el:d,componentModel:t,itemName:u,formatterParamsExtra:{title:h[u]}}),d.__title=h[u],d.on("mouseover",(function(){var e=p.getItemStyle(),i=a?null==t.get("right")&&"right"!==t.get("left")?"right":"left":null==t.get("bottom")&&"bottom"!==t.get("top")?"bottom":"top";f.setStyle({fill:p.get("textFill")||e.fill||e.stroke||"#000",backgroundColor:p.get("textBackgroundColor")}),d.setTextConfig({position:p.get("textPosition")||i}),f.ignore=!t.get("showTitle"),n.enterEmphasis(this)})).on("mouseout",(function(){"emphasis"!==i.get(["iconStatus",u])&&n.leaveEmphasis(this),f.hide()})),("emphasis"===i.get(["iconStatus",u])?Al:kl)(d),r.add(d),d.on("click",W(s.onclick,s,e,n,u)),g[u]=d}))}(y,p,d),y.setIconStatus=function(t,e){var n=this.option,i=this.iconPaths;n.iconStatus=n.iconStatus||{},n.iconStatus[t]=e,i[t]&&("emphasis"===e?Al:kl)(i[t])},p instanceof QE&&p.render&&p.render(y,e,n,i)):m&&p.dispose&&p.dispose(e,n)}},e.prototype.updateView=function(t,e,n,i){E(this._features,(function(t){t instanceof QE&&t.updateView&&t.updateView(t.model,e,n,i)}))},e.prototype.remove=function(t,e){E(this._features,(function(n){n instanceof QE&&n.remove&&n.remove(t,e)})),this.group.removeAll()},e.prototype.dispose=function(t,e){E(this._features,(function(n){n instanceof QE&&n.dispose&&n.dispose(t,e)}))},e.type="toolbox",e}(wg);var az=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.onclick=function(t,e){var n=this.model,i=n.get("name")||t.get("title.0.text")||"echarts",o="svg"===e.getZr().painter.getType(),a=o?"svg":n.get("type",!0)||"png",s=e.getConnectedDataURL({type:a,backgroundColor:n.get("backgroundColor",!0)||t.get("backgroundColor")||"#fff",connectedBackgroundColor:n.get("connectedBackgroundColor"),excludeComponents:n.get("excludeComponents"),pixelRatio:n.get("pixelRatio")}),l=r.browser;if(U(MouseEvent)&&(l.newEdge||!l.ie&&!l.edge)){var u=document.createElement("a");u.download=i+"."+a,u.target="_blank",u.href=s;var h=new MouseEvent("click",{view:document.defaultView,bubbles:!0,cancelable:!1});u.dispatchEvent(h)}else if(window.navigator.msSaveOrOpenBlob||o){var c=s.split(","),p=c[0].indexOf("base64")>-1,d=o?decodeURIComponent(c[1]):c[1];p&&(d=window.atob(d));var f=i+"."+a;if(window.navigator.msSaveOrOpenBlob){for(var g=d.length,y=new Uint8Array(g);g--;)y[g]=d.charCodeAt(g);var v=new Blob([y]);window.navigator.msSaveOrOpenBlob(v,f)}else{var m=document.createElement("iframe");document.body.appendChild(m);var x=m.contentWindow,_=x.document;_.open("image/svg+xml","replace"),_.write(d),_.close(),x.focus(),_.execCommand("SaveAs",!0,f),document.body.removeChild(m)}}else{var b=n.get("lang"),w='',S=window.open();S.document.write(w),S.document.title=i}},e.getDefaultOption=function(t){return{show:!0,icon:"M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0",title:t.getLocaleModel().get(["toolbox","saveAsImage","title"]),type:"png",connectedBackgroundColor:"#fff",name:"",excludeComponents:["toolbox"],lang:t.getLocaleModel().get(["toolbox","saveAsImage","lang"])}},e}(QE),sz="__ec_magicType_stack__",lz=[["line","bar"],["stack"]],uz=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.getIcons=function(){var t=this.model,e=t.get("icon"),n={};return E(t.get("type"),(function(t){e[t]&&(n[t]=e[t])})),n},e.getDefaultOption=function(t){return{show:!0,type:[],icon:{line:"M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4",bar:"M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7",stack:"M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z"},title:t.getLocaleModel().get(["toolbox","magicType","title"]),option:{},seriesIndex:{}}},e.prototype.onclick=function(t,e,n){var i=this.model,r=i.get(["seriesIndex",n]);if(hz[n]){var o,a={series:[]};E(lz,(function(t){P(t,n)>=0&&E(t,(function(t){i.setIconStatus(t,"normal")}))})),i.setIconStatus(n,"emphasis"),t.eachComponent({mainType:"series",query:null==r?null:{seriesIndex:r}},(function(t){var e=t.subType,r=t.id,o=hz[n](e,r,t,i);o&&(k(o,t.option),a.series.push(o));var s=t.coordinateSystem;if(s&&"cartesian2d"===s.type&&("line"===n||"bar"===n)){var l=s.getAxesByScale("ordinal")[0];if(l){var u=l.dim+"Axis",h=t.getReferringComponents(u,Eo).models[0].componentIndex;a[u]=a[u]||[];for(var c=0;c<=h;c++)a[u][h]=a[u][h]||{};a[u][h].boundaryGap="bar"===n}}}));var s=n;"stack"===n&&(o=C({stack:i.option.title.tiled,tiled:i.option.title.stack},i.option.title),"emphasis"!==i.get(["iconStatus",n])&&(s="tiled")),e.dispatchAction({type:"changeMagicType",currentType:s,newOption:a,newTitle:o,featureName:"magicType"})}},e}(QE),hz={line:function(t,e,n,i){if("bar"===t)return C({id:e,type:"line",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","line"])||{},!0)},bar:function(t,e,n,i){if("line"===t)return C({id:e,type:"bar",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","bar"])||{},!0)},stack:function(t,e,n,i){var r=n.get("stack")===sz;if("line"===t||"bar"===t)return i.setIconStatus("stack",r?"normal":"emphasis"),C({id:e,stack:r?"":sz},i.get(["option","stack"])||{},!0)}};vm({type:"changeMagicType",event:"magicTypeChanged",update:"prepareAndUpdate"},(function(t,e){e.mergeOption(t.newOption)}));var cz=new Array(60).join("-"),pz="\t";function dz(t){return t.replace(/^\s\s*/,"").replace(/\s\s*$/,"")}var fz=new RegExp("[\t]+","g");function gz(t,e){var n=t.split(new RegExp("\n*"+cz+"\n*","g")),i={series:[]};return E(n,(function(t,n){if(function(t){if(t.slice(0,t.indexOf("\n")).indexOf(pz)>=0)return!0}(t)){var r=function(t){for(var e=t.split(/\n+/g),n=[],i=z(dz(e.shift()).split(fz),(function(t){return{name:t,data:[]}})),r=0;r=0)&&t(r,i._targetInfoList)}))}return t.prototype.setOutputRanges=function(t,e){return this.matchOutputRanges(t,e,(function(t,e,n){if((t.coordRanges||(t.coordRanges=[])).push(e),!t.coordRange){t.coordRange=e;var i=Az[t.brushType](0,n,e);t.__rangeOffset={offset:Lz[t.brushType](i.values,t.range,[1,1]),xyMinMax:i.xyMinMax}}})),t},t.prototype.matchOutputRanges=function(t,e,n){E(t,(function(t){var i=this.findTargetInfo(t,e);i&&!0!==i&&E(i.coordSyses,(function(i){var r=Az[t.brushType](1,i,t.range,!0);n(t,r.values,i,e)}))}),this)},t.prototype.setInputRanges=function(t,e){E(t,(function(t){var n,i,r,o,a,s=this.findTargetInfo(t,e);if(t.range=t.range||[],s&&!0!==s){t.panelId=s.panelId;var l=Az[t.brushType](0,s.coordSys,t.coordRange),u=t.__rangeOffset;t.range=u?Lz[t.brushType](l.values,u.offset,(n=l.xyMinMax,i=u.xyMinMax,r=Oz(n),o=Oz(i),a=[r[0]/o[0],r[1]/o[1]],isNaN(a[0])&&(a[0]=1),isNaN(a[1])&&(a[1]=1),a)):l.values}}),this)},t.prototype.makePanelOpts=function(t,e){return z(this._targetInfoList,(function(n){var i=n.getPanelRect();return{panelId:n.panelId,defaultBrushType:e?e(n):null,clipPath:bL(i),isTargetByCursor:SL(i,t,n.coordSysModel),getLinearBrushOtherExtent:wL(i)}}))},t.prototype.controlSeries=function(t,e,n){var i=this.findTargetInfo(t,n);return!0===i||i&&P(i.coordSyses,e.coordinateSystem)>=0},t.prototype.findTargetInfo=function(t,e){for(var n=this._targetInfoList,i=Iz(e,t),r=0;rt[1]&&t.reverse(),t}function Iz(t,e){return Ro(t,e,{includeMainTypes:wz})}var Tz={grid:function(t,e){var n=t.xAxisModels,i=t.yAxisModels,r=t.gridModels,o=yt(),a={},s={};(n||i||r)&&(E(n,(function(t){var e=t.axis.grid.model;o.set(e.id,e),a[e.id]=!0})),E(i,(function(t){var e=t.axis.grid.model;o.set(e.id,e),s[e.id]=!0})),E(r,(function(t){o.set(t.id,t),a[t.id]=!0,s[t.id]=!0})),o.each((function(t){var r=t.coordinateSystem,o=[];E(r.getCartesians(),(function(t,e){(P(n,t.getAxis("x").model)>=0||P(i,t.getAxis("y").model)>=0)&&o.push(t)})),e.push({panelId:"grid--"+t.id,gridModel:t,coordSysModel:t,coordSys:o[0],coordSyses:o,getPanelRect:Dz.grid,xAxisDeclared:a[t.id],yAxisDeclared:s[t.id]})})))},geo:function(t,e){E(t.geoModels,(function(t){var n=t.coordinateSystem;e.push({panelId:"geo--"+t.id,geoModel:t,coordSysModel:t,coordSys:n,coordSyses:[n],getPanelRect:Dz.geo})}))}},Cz=[function(t,e){var n=t.xAxisModel,i=t.yAxisModel,r=t.gridModel;return!r&&n&&(r=n.axis.grid.model),!r&&i&&(r=i.axis.grid.model),r&&r===e.gridModel},function(t,e){var n=t.geoModel;return n&&n===e.geoModel}],Dz={grid:function(){return this.coordSys.master.getRect().clone()},geo:function(){var t=this.coordSys,e=t.getBoundingRect().clone();return e.applyTransform(Nh(t)),e}},Az={lineX:H(kz,0),lineY:H(kz,1),rect:function(t,e,n,i){var r=t?e.pointToData([n[0][0],n[1][0]],i):e.dataToPoint([n[0][0],n[1][0]],i),o=t?e.pointToData([n[0][1],n[1][1]],i):e.dataToPoint([n[0][1],n[1][1]],i),a=[Mz([r[0],o[0]]),Mz([r[1],o[1]])];return{values:a,xyMinMax:a}},polygon:function(t,e,n,i){var r=[[1/0,-1/0],[1/0,-1/0]];return{values:z(n,(function(n){var o=t?e.pointToData(n,i):e.dataToPoint(n,i);return r[0][0]=Math.min(r[0][0],o[0]),r[1][0]=Math.min(r[1][0],o[1]),r[0][1]=Math.max(r[0][1],o[0]),r[1][1]=Math.max(r[1][1],o[1]),o})),xyMinMax:r}}};function kz(t,e,n,i){var r=n.getAxis(["x","y"][t]),o=Mz(z([0,1],(function(t){return e?r.coordToData(r.toLocalCoord(i[t]),!0):r.toGlobalCoord(r.dataToCoord(i[t]))}))),a=[];return a[t]=o,a[1-t]=[NaN,NaN],{values:o,xyMinMax:a}}var Lz={lineX:H(Pz,0),lineY:H(Pz,1),rect:function(t,e,n){return[[t[0][0]-n[0]*e[0][0],t[0][1]-n[0]*e[0][1]],[t[1][0]-n[1]*e[1][0],t[1][1]-n[1]*e[1][1]]]},polygon:function(t,e,n){return z(t,(function(t,i){return[t[0]-n[0]*e[i][0],t[1]-n[1]*e[i][1]]}))}};function Pz(t,e,n,i){return[e[0]-i[t]*n[0],e[1]-i[t]*n[1]]}function Oz(t){return t?[t[0][1]-t[0][0],t[1][1]-t[1][0]]:[NaN,NaN]}var Rz,Nz,Ez=E,zz=xo+"toolbox-dataZoom_",Vz=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.render=function(t,e,n,i){this._brushController||(this._brushController=new Yk(n.getZr()),this._brushController.on("brush",W(this._onBrush,this)).mount()),function(t,e,n,i,r){var o=n._isZoomActive;i&&"takeGlobalCursor"===i.type&&(o="dataZoomSelect"===i.key&&i.dataZoomSelectActive);n._isZoomActive=o,t.setIconStatus("zoom",o?"emphasis":"normal");var a=new Sz(Fz(t),e,{include:["grid"]}).makePanelOpts(r,(function(t){return t.xAxisDeclared&&!t.yAxisDeclared?"lineX":!t.xAxisDeclared&&t.yAxisDeclared?"lineY":"rect"}));n._brushController.setPanels(a).enableBrush(!(!o||!a.length)&&{brushType:"auto",brushStyle:t.getModel("brushStyle").getItemStyle()})}(t,e,this,i,n),function(t,e){t.setIconStatus("back",function(t){return _z(t).length}(e)>1?"emphasis":"normal")}(t,e)},e.prototype.onclick=function(t,e,n){Bz[n].call(this)},e.prototype.remove=function(t,e){this._brushController&&this._brushController.unmount()},e.prototype.dispose=function(t,e){this._brushController&&this._brushController.dispose()},e.prototype._onBrush=function(t){var e=t.areas;if(t.isEnd&&e.length){var n={},i=this.ecModel;this._brushController.updateCovers([]),new Sz(Fz(this.model),i,{include:["grid"]}).matchOutputRanges(e,i,(function(t,e,n){if("cartesian2d"===n.type){var i=t.brushType;"rect"===i?(r("x",n,e[0]),r("y",n,e[1])):r({lineX:"x",lineY:"y"}[i],n,e)}})),function(t,e){var n=_z(t);mz(e,(function(e,i){for(var r=n.length-1;r>=0&&!n[r][i];r--);if(r<0){var o=t.queryComponents({mainType:"dataZoom",subType:"select",id:i})[0];if(o){var a=o.getPercentRange();n[0][i]={dataZoomId:i,start:a[0],end:a[1]}}}})),n.push(e)}(i,n),this._dispatchZoomAction(n)}function r(t,e,r){var o=e.getAxis(t),a=o.model,s=function(t,e,n){var i;return n.eachComponent({mainType:"dataZoom",subType:"select"},(function(n){n.getAxisModel(t,e.componentIndex)&&(i=n)})),i}(t,a,i),l=s.findRepresentativeAxisProxy(a).getMinMaxSpan();null==l.minValueSpan&&null==l.maxValueSpan||(r=xk(0,r.slice(),o.scale.getExtent(),0,l.minValueSpan,l.maxValueSpan)),s&&(n[s.id]={dataZoomId:s.id,startValue:r[0],endValue:r[1]})}},e.prototype._dispatchZoomAction=function(t){var e=[];Ez(t,(function(t,n){e.push(T(t))})),e.length&&this.api.dispatchAction({type:"dataZoom",from:this.uid,batch:e})},e.getDefaultOption=function(t){return{show:!0,filterMode:"filter",icon:{zoom:"M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1",back:"M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26"},title:t.getLocaleModel().get(["toolbox","dataZoom","title"]),brushStyle:{borderWidth:0,color:"rgba(210,219,238,0.2)"}}},e}(QE),Bz={zoom:function(){var t=!this._isZoomActive;this.api.dispatchAction({type:"takeGlobalCursor",key:"dataZoomSelect",dataZoomSelectActive:t})},back:function(){this._dispatchZoomAction(function(t){var e=_z(t),n=e[e.length-1];e.length>1&&e.pop();var i={};return mz(n,(function(t,n){for(var r=e.length-1;r>=0;r--)if(t=e[r][n]){i[n]=t;break}})),i}(this.ecModel))}};function Fz(t){var e={xAxisIndex:t.get("xAxisIndex",!0),yAxisIndex:t.get("yAxisIndex",!0),xAxisId:t.get("xAxisId",!0),yAxisId:t.get("yAxisId",!0)};return null==e.xAxisIndex&&null==e.xAxisId&&(e.xAxisIndex="all"),null==e.yAxisIndex&&null==e.yAxisId&&(e.yAxisIndex="all"),e}Rz="dataZoom",Nz=function(t){var e=t.getComponent("toolbox",0),n=["feature","dataZoom"];if(e&&null!=e.get(n)){var i=e.getModel(n),r=[],o=Ro(t,Fz(i));return Ez(o.xAxisModels,(function(t){return a(t,"xAxis","xAxisIndex")})),Ez(o.yAxisModels,(function(t){return a(t,"yAxis","yAxisIndex")})),r}function a(t,e,n){var o=t.componentIndex,a={type:"select",$fromToolbox:!0,filterMode:i.get("filterMode",!0)||"filter",id:zz+e+o};a[n]=o,r.push(a)}},lt(null==ed.get(Rz)&&Nz),ed.set(Rz,Nz);var Gz=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="tooltip",e.dependencies=["axisPointer"],e.defaultOption={z:60,show:!0,showContent:!0,trigger:"item",triggerOn:"mousemove|click",alwaysShowContent:!1,displayMode:"single",renderMode:"auto",confine:null,showDelay:0,hideDelay:100,transitionDuration:.4,enterable:!1,backgroundColor:"#fff",shadowBlur:10,shadowColor:"rgba(0, 0, 0, .2)",shadowOffsetX:1,shadowOffsetY:2,borderRadius:4,borderWidth:1,padding:null,extraCssText:"",axisPointer:{type:"line",axis:"auto",animation:"auto",animationDurationUpdate:200,animationEasingUpdate:"exponentialOut",crossStyle:{color:"#999",width:1,type:"dashed",textStyle:{}}},textStyle:{color:"#666",fontSize:14}},e}(Op);function Wz(t){var e=t.get("confine");return null!=e?!!e:"richText"===t.get("renderMode")}function Hz(t){if(r.domSupported)for(var e=document.documentElement.style,n=0,i=t.length;n-1?(u+="top:50%",h+="translateY(-50%) rotate("+(a="left"===s?-225:-45)+"deg)"):(u+="left:50%",h+="translateX(-50%) rotate("+(a="top"===s?225:45)+"deg)");var c=a*Math.PI/180,p=l+r,d=p*Math.abs(Math.cos(c))+p*Math.abs(Math.sin(c)),f=e+" solid "+r+"px;";return'
                    '}(n,i,r)),X(t))o.innerHTML=t+a;else if(t){o.innerHTML="",Y(t)||(t=[t]);for(var s=0;s=0?this._tryShow(n,i):"leave"===e&&this._hide(i))}),this))},e.prototype._keepShow=function(){var t=this._tooltipModel,e=this._ecModel,n=this._api,i=t.get("triggerOn");if(null!=this._lastX&&null!=this._lastY&&"none"!==i&&"click"!==i){var r=this;clearTimeout(this._refreshUpdateTimeout),this._refreshUpdateTimeout=setTimeout((function(){!n.isDisposed()&&r.manuallyShowTip(t,e,n,{x:r._lastX,y:r._lastY,dataByCoordSys:r._lastDataByCoordSys})}))}},e.prototype.manuallyShowTip=function(t,e,n,i){if(i.from!==this.uid&&!r.node&&n.getDom()){var o=aV(i,n);this._ticket="";var a=i.dataByCoordSys,s=function(t,e,n){var i=No(t).queryOptionMap,r=i.keys()[0];if(!r||"series"===r)return;var o,a=Vo(e,r,i.get(r),{useDefault:!1,enableAll:!1,enableNone:!1}).models[0];if(!a)return;if(n.getViewOfComponentModel(a).group.traverse((function(e){var n=Js(e).tooltipConfig;if(n&&n.name===t.name)return o=e,!0})),o)return{componentMainType:r,componentIndex:a.componentIndex,el:o}}(i,e,n);if(s){var l=s.el.getBoundingRect().clone();l.applyTransform(s.el.transform),this._tryShow({offsetX:l.x+l.width/2,offsetY:l.y+l.height/2,target:s.el,position:i.position,positionDefault:"bottom"},o)}else if(i.tooltip&&null!=i.x&&null!=i.y){var u=iV;u.x=i.x,u.y=i.y,u.update(),Js(u).tooltipConfig={name:null,option:i.tooltip},this._tryShow({offsetX:i.x,offsetY:i.y,target:u},o)}else if(a)this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,dataByCoordSys:a,tooltipOption:i.tooltipOption},o);else if(null!=i.seriesIndex){if(this._manuallyAxisShowTip(t,e,n,i))return;var h=dN(i,e),c=h.point[0],p=h.point[1];null!=c&&null!=p&&this._tryShow({offsetX:c,offsetY:p,target:h.el,position:i.position,positionDefault:"bottom"},o)}else null!=i.x&&null!=i.y&&(n.dispatchAction({type:"updateAxisPointer",x:i.x,y:i.y}),this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,target:n.getZr().findHover(i.x,i.y).target},o))}},e.prototype.manuallyHideTip=function(t,e,n,i){var r=this._tooltipContent;!this._alwaysShowContent&&this._tooltipModel&&r.hideLater(this._tooltipModel.get("hideDelay")),this._lastX=this._lastY=this._lastDataByCoordSys=null,i.from!==this.uid&&this._hide(aV(i,n))},e.prototype._manuallyAxisShowTip=function(t,e,n,i){var r=i.seriesIndex,o=i.dataIndex,a=e.getComponent("axisPointer").coordSysAxesInfo;if(null!=r&&null!=o&&null!=a){var s=e.getSeriesByIndex(r);if(s)if("axis"===oV([s.getData().getItemModel(o),s,(s.coordinateSystem||{}).model],this._tooltipModel).get("trigger"))return n.dispatchAction({type:"updateAxisPointer",seriesIndex:r,dataIndex:o,position:i.position}),!0}},e.prototype._tryShow=function(t,e){var n=t.target;if(this._tooltipModel){this._lastX=t.offsetX,this._lastY=t.offsetY;var i=t.dataByCoordSys;if(i&&i.length)this._showAxisTooltip(i,t);else if(n){var r,o;this._lastDataByCoordSys=null,Ty(n,(function(t){return null!=Js(t).dataIndex?(r=t,!0):null!=Js(t).tooltipConfig?(o=t,!0):void 0}),!0),r?this._showSeriesItemTooltip(t,r,e):o?this._showComponentItemTooltip(t,o,e):this._hide(e)}else this._lastDataByCoordSys=null,this._hide(e)}},e.prototype._showOrMove=function(t,e){var n=t.get("showDelay");e=W(e,this),clearTimeout(this._showTimout),n>0?this._showTimout=setTimeout(e,n):e()},e.prototype._showAxisTooltip=function(t,e){var n=this._ecModel,i=this._tooltipModel,r=[e.offsetX,e.offsetY],o=oV([e.tooltipOption],i),a=this._renderMode,s=[],l=Qf("section",{blocks:[],noHeader:!0}),u=[],h=new hg;E(t,(function(t){E(t.dataByAxis,(function(t){var e=n.getComponent(t.axisDim+"Axis",t.axisIndex),r=t.value;if(e&&null!=r){var o=qR(r,e.axis,n,t.seriesDataIndices,t.valueLabelOpt),c=Qf("section",{header:o,noHeader:!ut(o),sortBlocks:!0,blocks:[]});l.blocks.push(c),E(t.seriesDataIndices,(function(l){var p=n.getSeriesByIndex(l.seriesIndex),d=l.dataIndexInside,f=p.getDataParams(d);if(!(f.dataIndex<0)){f.axisDim=t.axisDim,f.axisIndex=t.axisIndex,f.axisType=t.axisType,f.axisId=t.axisId,f.axisValue=d_(e.axis,{value:r}),f.axisValueLabel=o,f.marker=h.makeTooltipMarker("item",xp(f.color),a);var g=yf(p.formatTooltip(d,!0,null)),y=g.frag;if(y){var v=oV([p],i).get("valueFormatter");c.blocks.push(v?A({valueFormatter:v},y):y)}g.text&&u.push(g.text),s.push(f)}}))}}))})),l.blocks.reverse(),u.reverse();var c=e.position,p=o.get("order"),d=og(l,h,a,p,n.get("useUTC"),o.get("textStyle"));d&&u.unshift(d);var f="richText"===a?"\n\n":"
                    ",g=u.join(f);this._showOrMove(o,(function(){this._updateContentNotChangedOnAxis(t,s)?this._updatePosition(o,c,r[0],r[1],this._tooltipContent,s):this._showTooltipContent(o,g,s,Math.random()+"",r[0],r[1],c,null,h)}))},e.prototype._showSeriesItemTooltip=function(t,e,n){var i=this._ecModel,r=Js(e),o=r.seriesIndex,a=i.getSeriesByIndex(o),s=r.dataModel||a,l=r.dataIndex,u=r.dataType,h=s.getData(u),c=this._renderMode,p=t.positionDefault,d=oV([h.getItemModel(l),s,a&&(a.coordinateSystem||{}).model],this._tooltipModel,p?{position:p}:null),f=d.get("trigger");if(null==f||"item"===f){var g=s.getDataParams(l,u),y=new hg;g.marker=y.makeTooltipMarker("item",xp(g.color),c);var v=yf(s.formatTooltip(l,!1,u)),m=d.get("order"),x=d.get("valueFormatter"),_=v.frag,b=_?og(x?A({valueFormatter:x},_):_,y,c,m,i.get("useUTC"),d.get("textStyle")):v.text,w="item_"+s.name+"_"+l;this._showOrMove(d,(function(){this._showTooltipContent(d,b,g,w,t.offsetX,t.offsetY,t.position,t.target,y)})),n({type:"showTip",dataIndexInside:l,dataIndex:h.getRawIndex(l),seriesIndex:o,from:this.uid})}},e.prototype._showComponentItemTooltip=function(t,e,n){var i=Js(e),r=i.tooltipConfig.option||{};if(X(r)){r={content:r,formatter:r}}var o=[r],a=this._ecModel.getComponent(i.componentMainType,i.componentIndex);a&&o.push(a),o.push({formatter:r.content});var s=t.positionDefault,l=oV(o,this._tooltipModel,s?{position:s}:null),u=l.get("content"),h=Math.random()+"",c=new hg;this._showOrMove(l,(function(){var n=T(l.get("formatterParams")||{});this._showTooltipContent(l,u,n,h,t.offsetX,t.offsetY,t.position,e,c)})),n({type:"showTip",from:this.uid})},e.prototype._showTooltipContent=function(t,e,n,i,r,o,a,s,l){if(this._ticket="",t.get("showContent")&&t.get("show")){var u=this._tooltipContent;u.setEnterable(t.get("enterable"));var h=t.get("formatter");a=a||t.get("position");var c=e,p=this._getNearestPoint([r,o],n,t.get("trigger"),t.get("borderColor")).color;if(h)if(X(h)){var d=t.ecModel.get("useUTC"),f=Y(n)?n[0]:n;c=h,f&&f.axisType&&f.axisType.indexOf("time")>=0&&(c=jc(f.axisValue,c,d)),c=vp(c,n,!0)}else if(U(h)){var g=W((function(e,i){e===this._ticket&&(u.setContent(i,l,t,p,a),this._updatePosition(t,a,r,o,u,n,s))}),this);this._ticket=i,c=h(n,i,g)}else c=h;u.setContent(c,l,t,p,a),u.show(t,p),this._updatePosition(t,a,r,o,u,n,s)}},e.prototype._getNearestPoint=function(t,e,n,i){return"axis"===n||Y(e)?{color:i||("html"===this._renderMode?"#fff":"none")}:Y(e)?void 0:{color:i||e.color||e.borderColor}},e.prototype._updatePosition=function(t,e,n,i,r,o,a){var s=this._api.getWidth(),l=this._api.getHeight();e=e||t.get("position");var u=r.getSize(),h=t.get("align"),c=t.get("verticalAlign"),p=a&&a.getBoundingRect().clone();if(a&&p.applyTransform(a.transform),U(e)&&(e=e([n,i],o,r.el,p,{viewSize:[s,l],contentSize:u.slice()})),Y(e))n=Ur(e[0],s),i=Ur(e[1],l);else if(q(e)){var d=e;d.width=u[0],d.height=u[1];var f=Tp(d,{width:s,height:l});n=f.x,i=f.y,h=null,c=null}else if(X(e)&&a){var g=function(t,e,n,i){var r=n[0],o=n[1],a=Math.ceil(Math.SQRT2*i)+8,s=0,l=0,u=e.width,h=e.height;switch(t){case"inside":s=e.x+u/2-r/2,l=e.y+h/2-o/2;break;case"top":s=e.x+u/2-r/2,l=e.y-o-a;break;case"bottom":s=e.x+u/2-r/2,l=e.y+h+a;break;case"left":s=e.x-r-a,l=e.y+h/2-o/2;break;case"right":s=e.x+u+a,l=e.y+h/2-o/2}return[s,l]}(e,p,u,t.get("borderWidth"));n=g[0],i=g[1]}else{g=function(t,e,n,i,r,o,a){var s=n.getSize(),l=s[0],u=s[1];null!=o&&(t+l+o+2>i?t-=l+o:t+=o);null!=a&&(e+u+a>r?e-=u+a:e+=a);return[t,e]}(n,i,r,s,l,h?null:20,c?null:20);n=g[0],i=g[1]}if(h&&(n-=sV(h)?u[0]/2:"right"===h?u[0]:0),c&&(i-=sV(c)?u[1]/2:"bottom"===c?u[1]:0),Wz(t)){g=function(t,e,n,i,r){var o=n.getSize(),a=o[0],s=o[1];return t=Math.min(t+a,i)-a,e=Math.min(e+s,r)-s,t=Math.max(t,0),e=Math.max(e,0),[t,e]}(n,i,r,s,l);n=g[0],i=g[1]}r.moveTo(n,i)},e.prototype._updateContentNotChangedOnAxis=function(t,e){var n=this._lastDataByCoordSys,i=this._cbParamsList,r=!!n&&n.length===t.length;return r&&E(n,(function(n,o){var a=n.dataByAxis||[],s=(t[o]||{}).dataByAxis||[];(r=r&&a.length===s.length)&&E(a,(function(t,n){var o=s[n]||{},a=t.seriesDataIndices||[],l=o.seriesDataIndices||[];(r=r&&t.value===o.value&&t.axisType===o.axisType&&t.axisId===o.axisId&&a.length===l.length)&&E(a,(function(t,e){var n=l[e];r=r&&t.seriesIndex===n.seriesIndex&&t.dataIndex===n.dataIndex})),i&&E(t.seriesDataIndices,(function(t){var n=t.seriesIndex,o=e[n],a=i[n];o&&a&&a.data!==o.data&&(r=!1)}))}))})),this._lastDataByCoordSys=t,this._cbParamsList=e,!!r},e.prototype._hide=function(t){this._lastDataByCoordSys=null,t({type:"hideTip",from:this.uid})},e.prototype.dispose=function(t,e){!r.node&&e.getDom()&&(zg(this,"_updatePosition"),this._tooltipContent.dispose(),cN("itemTooltip",e))},e.type="tooltip",e}(wg);function oV(t,e,n){var i,r=e.ecModel;n?(i=new Sc(n,r,r),i=new Sc(e.option,i,r)):i=e;for(var o=t.length-1;o>=0;o--){var a=t[o];a&&(a instanceof Sc&&(a=a.get("tooltip",!0)),X(a)&&(a={formatter:a}),a&&(i=new Sc(a,i,r)))}return i}function aV(t,e){return t.dispatchAction||W(e.dispatchAction,e)}function sV(t){return"center"===t||"middle"===t}var lV=["rect","polygon","keep","clear"];function uV(t,e){var n=_o(t?t.brush:[]);if(n.length){var i=[];E(n,(function(t){var e=t.hasOwnProperty("toolbox")?t.toolbox:[];e instanceof Array&&(i=i.concat(e))}));var r=t&&t.toolbox;Y(r)&&(r=r[0]),r||(r={feature:{}},t.toolbox=[r]);var o=r.feature||(r.feature={}),a=o.brush||(o.brush={}),s=a.type||(a.type=[]);s.push.apply(s,i),function(t){var e={};E(t,(function(t){e[t]=1})),t.length=0,E(e,(function(e,n){t.push(n)}))}(s),e&&!s.length&&s.push.apply(s,lV)}}var hV=E;function cV(t){if(t)for(var e in t)if(t.hasOwnProperty(e))return!0}function pV(t,e,n){var i={};return hV(e,(function(e){var r,o=i[e]=((r=function(){}).prototype.__hidden=r.prototype,new r);hV(t[e],(function(t,i){if(dD.isValidType(i)){var r={type:i,visual:t};n&&n(r,e),o[i]=new dD(r),"opacity"===i&&((r=T(r)).type="colorAlpha",o.__hidden.__alphaForOpacity=new dD(r))}}))})),i}function dV(t,e,n){var i;E(n,(function(t){e.hasOwnProperty(t)&&cV(e[t])&&(i=!0)})),i&&E(n,(function(n){e.hasOwnProperty(n)&&cV(e[n])?t[n]=T(e[n]):delete t[n]}))}var fV={lineX:gV(0),lineY:gV(1),rect:{point:function(t,e,n){return t&&n.boundingRect.contain(t[0],t[1])},rect:function(t,e,n){return t&&n.boundingRect.intersect(t)}},polygon:{point:function(t,e,n){return t&&n.boundingRect.contain(t[0],t[1])&&w_(n.range,t[0],t[1])},rect:function(t,e,n){var i=n.range;if(!t||i.length<=1)return!1;var r=t.x,o=t.y,a=t.width,s=t.height,l=i[0];return!!(w_(i,r,o)||w_(i,r+a,o)||w_(i,r,o+s)||w_(i,r+a,o+s)||Ee.create(t).contain(l[0],l[1])||Hh(r,o,r+a,o,i)||Hh(r,o,r,o+s,i)||Hh(r+a,o,r+a,o+s,i)||Hh(r,o+s,r+a,o+s,i))||void 0}}};function gV(t){var e=["x","y"],n=["width","height"];return{point:function(e,n,i){if(e){var r=i.range;return yV(e[t],r)}},rect:function(i,r,o){if(i){var a=o.range,s=[i[e[t]],i[e[t]]+i[n[t]]];return s[1]e[0][1]&&(e[0][1]=o[0]),o[1]e[1][1]&&(e[1][1]=o[1])}return e&&IV(e)}};function IV(t){return new Ee(t[0][0],t[1][0],t[0][1]-t[0][0],t[1][1]-t[1][0])}var TV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(t,e){this.ecModel=t,this.api=e,this.model,(this._brushController=new Yk(e.getZr())).on("brush",W(this._onBrush,this)).mount()},e.prototype.render=function(t,e,n,i){this.model=t,this._updateController(t,e,n,i)},e.prototype.updateTransform=function(t,e,n,i){_V(e),this._updateController(t,e,n,i)},e.prototype.updateVisual=function(t,e,n,i){this.updateTransform(t,e,n,i)},e.prototype.updateView=function(t,e,n,i){this._updateController(t,e,n,i)},e.prototype._updateController=function(t,e,n,i){(!i||i.$from!==t.id)&&this._brushController.setPanels(t.brushTargetManager.makePanelOpts(n)).enableBrush(t.brushOption).updateCovers(t.areas.slice())},e.prototype.dispose=function(){this._brushController.dispose()},e.prototype._onBrush=function(t){var e=this.model.id,n=this.model.brushTargetManager.setOutputRanges(t.areas,this.ecModel);(!t.isEnd||t.removeOnClick)&&this.api.dispatchAction({type:"brush",brushId:e,areas:T(n),$from:e}),t.isEnd&&this.api.dispatchAction({type:"brushEnd",brushId:e,areas:T(n),$from:e})},e.type="brush",e}(wg),CV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.areas=[],n.brushOption={},n}return n(e,t),e.prototype.optionUpdated=function(t,e){var n=this.option;!e&&dV(n,t,["inBrush","outOfBrush"]);var i=n.inBrush=n.inBrush||{};n.outOfBrush=n.outOfBrush||{color:"#ddd"},i.hasOwnProperty("liftZ")||(i.liftZ=5)},e.prototype.setAreas=function(t){t&&(this.areas=z(t,(function(t){return DV(this.option,t)}),this))},e.prototype.setBrushOption=function(t){this.brushOption=DV(this.option,t),this.brushType=this.brushOption.brushType},e.type="brush",e.dependencies=["geo","grid","xAxis","yAxis","parallel","series"],e.defaultOption={seriesIndex:"all",brushType:"rect",brushMode:"single",transformable:!0,brushStyle:{borderWidth:1,color:"rgba(210,219,238,0.3)",borderColor:"#D2DBEE"},throttleType:"fixRate",throttleDelay:0,removeOnClick:!0,z:1e4},e}(Op);function DV(t,e){return C({brushType:t.brushType,brushMode:t.brushMode,transformable:t.transformable,brushStyle:new Sc(t.brushStyle).getItemStyle(),removeOnClick:t.removeOnClick,z:t.z},e,!0)}var AV=["rect","polygon","lineX","lineY","keep","clear"],kV=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.render=function(t,e,n){var i,r,o;e.eachComponent({mainType:"brush"},(function(t){i=t.brushType,r=t.brushOption.brushMode||"single",o=o||!!t.areas.length})),this._brushType=i,this._brushMode=r,E(t.get("type",!0),(function(e){t.setIconStatus(e,("keep"===e?"multiple"===r:"clear"===e?o:e===i)?"emphasis":"normal")}))},e.prototype.updateView=function(t,e,n){this.render(t,e,n)},e.prototype.getIcons=function(){var t=this.model,e=t.get("icon",!0),n={};return E(t.get("type",!0),(function(t){e[t]&&(n[t]=e[t])})),n},e.prototype.onclick=function(t,e,n){var i=this._brushType,r=this._brushMode;"clear"===n?(e.dispatchAction({type:"axisAreaSelect",intervals:[]}),e.dispatchAction({type:"brush",command:"clear",areas:[]})):e.dispatchAction({type:"takeGlobalCursor",key:"brush",brushOption:{brushType:"keep"===n?i:i!==n&&n,brushMode:"keep"===n?"multiple"===r?"single":"multiple":r}})},e.getDefaultOption=function(t){return{show:!0,type:AV.slice(),icon:{rect:"M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13",polygon:"M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2",lineX:"M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4",lineY:"M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4",keep:"M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z",clear:"M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2"},title:t.getLocaleModel().get(["toolbox","brush","title"])}},e}(QE);var LV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.layoutMode={type:"box",ignoreSize:!0},n}return n(e,t),e.type="title",e.defaultOption={z:6,show:!0,text:"",target:"blank",subtext:"",subtarget:"blank",left:0,top:0,backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,padding:5,itemGap:10,textStyle:{fontSize:18,fontWeight:"bold",color:"#464646"},subtextStyle:{fontSize:12,color:"#6E7079"}},e}(Op),PV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){if(this.group.removeAll(),t.get("show")){var i=this.group,r=t.getModel("textStyle"),o=t.getModel("subtextStyle"),a=t.get("textAlign"),s=rt(t.get("textBaseline"),t.get("textVerticalAlign")),l=new Bs({style:ec(r,{text:t.get("text"),fill:r.getTextColor()},{disableBox:!0}),z2:10}),u=l.getBoundingRect(),h=t.get("subtext"),c=new Bs({style:ec(o,{text:h,fill:o.getTextColor(),y:u.height+t.get("itemGap"),verticalAlign:"top"},{disableBox:!0}),z2:10}),p=t.get("link"),d=t.get("sublink"),f=t.get("triggerEvent",!0);l.silent=!p&&!f,c.silent=!d&&!f,p&&l.on("click",(function(){_p(p,"_"+t.get("target"))})),d&&c.on("click",(function(){_p(d,"_"+t.get("subtarget"))})),Js(l).eventData=Js(c).eventData=f?{componentType:"title",componentIndex:t.componentIndex}:null,i.add(l),h&&i.add(c);var g=i.getBoundingRect(),y=t.getBoxLayoutParams();y.width=g.width,y.height=g.height;var v=Tp(y,{width:n.getWidth(),height:n.getHeight()},t.get("padding"));a||("middle"===(a=t.get("left")||t.get("right"))&&(a="center"),"right"===a?v.x+=v.width:"center"===a&&(v.x+=v.width/2)),s||("center"===(s=t.get("top")||t.get("bottom"))&&(s="middle"),"bottom"===s?v.y+=v.height:"middle"===s&&(v.y+=v.height/2),s=s||"top"),i.x=v.x,i.y=v.y,i.markRedraw();var m={align:a,verticalAlign:s};l.setStyle(m),c.setStyle(m),g=i.getBoundingRect();var x=v.margin,_=t.getItemStyle(["color","opacity"]);_.fill=t.get("backgroundColor");var b=new Es({shape:{x:g.x-x[3],y:g.y-x[0],width:g.width+x[1]+x[3],height:g.height+x[0]+x[2],r:t.get("borderRadius")},style:_,subPixelOptimize:!0,silent:!0});i.add(b)}},e.type="title",e}(wg);var OV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.layoutMode="box",n}return n(e,t),e.prototype.init=function(t,e,n){this.mergeDefaultAndTheme(t,n),this._initData()},e.prototype.mergeOption=function(e){t.prototype.mergeOption.apply(this,arguments),this._initData()},e.prototype.setCurrentIndex=function(t){null==t&&(t=this.option.currentIndex);var e=this._data.count();this.option.loop?t=(t%e+e)%e:(t>=e&&(t=e-1),t<0&&(t=0)),this.option.currentIndex=t},e.prototype.getCurrentIndex=function(){return this.option.currentIndex},e.prototype.isIndexMax=function(){return this.getCurrentIndex()>=this._data.count()-1},e.prototype.setPlayState=function(t){this.option.autoPlay=!!t},e.prototype.getPlayState=function(){return!!this.option.autoPlay},e.prototype._initData=function(){var t,e=this.option,n=e.data||[],i=e.axisType,r=this._names=[];"category"===i?(t=[],E(n,(function(e,n){var i,o=Do(So(e),"");q(e)?(i=T(e)).value=n:i=n,t.push(i),r.push(o)}))):t=n;var o={category:"ordinal",time:"time",value:"number"}[i]||"number";(this._data=new ex([{name:"value",type:o}],this)).initData(t,r)},e.prototype.getData=function(){return this._data},e.prototype.getCategories=function(){if("category"===this.get("axisType"))return this._names.slice()},e.type="timeline",e.defaultOption={z:4,show:!0,axisType:"time",realtime:!0,left:"20%",top:null,right:"20%",bottom:0,width:null,height:40,padding:5,controlPosition:"left",autoPlay:!1,rewind:!1,loop:!0,playInterval:2e3,currentIndex:0,itemStyle:{},label:{color:"#000"},data:[]},e}(Op),RV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="timeline.slider",e.defaultOption=Tc(OV.defaultOption,{backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,orient:"horizontal",inverse:!1,tooltip:{trigger:"item"},symbol:"circle",symbolSize:12,lineStyle:{show:!0,width:2,color:"#DAE1F5"},label:{position:"auto",show:!0,interval:"auto",rotate:0,color:"#A4B1D7"},itemStyle:{color:"#A4B1D7",borderWidth:1},checkpointStyle:{symbol:"circle",symbolSize:15,color:"#316bf3",borderColor:"#fff",borderWidth:2,shadowBlur:2,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0, 0, 0, 0.3)",animation:!0,animationDuration:300,animationEasing:"quinticInOut"},controlStyle:{show:!0,showPlayBtn:!0,showPrevBtn:!0,showNextBtn:!0,itemSize:24,itemGap:12,position:"left",playIcon:"path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z",stopIcon:"path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z",nextIcon:"M2,18.5A1.52,1.52,0,0,1,.92,18a1.49,1.49,0,0,1,0-2.12L7.81,9.36,1,3.11A1.5,1.5,0,1,1,3,.89l8,7.34a1.48,1.48,0,0,1,.49,1.09,1.51,1.51,0,0,1-.46,1.1L3,18.08A1.5,1.5,0,0,1,2,18.5Z",prevIcon:"M10,.5A1.52,1.52,0,0,1,11.08,1a1.49,1.49,0,0,1,0,2.12L4.19,9.64,11,15.89a1.5,1.5,0,1,1-2,2.22L1,10.77A1.48,1.48,0,0,1,.5,9.68,1.51,1.51,0,0,1,1,8.58L9,.92A1.5,1.5,0,0,1,10,.5Z",prevBtnSize:18,nextBtnSize:18,color:"#A4B1D7",borderColor:"#A4B1D7",borderWidth:1},emphasis:{label:{show:!0,color:"#6f778d"},itemStyle:{color:"#316BF3"},controlStyle:{color:"#316BF3",borderColor:"#316BF3",borderWidth:2}},progress:{lineStyle:{color:"#316BF3"},itemStyle:{color:"#316BF3"},label:{color:"#6f778d"}},data:[]}),e}(OV);R(RV,gf.prototype);var NV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="timeline",e}(wg),EV=function(t){function e(e,n,i,r){var o=t.call(this,e,n,i)||this;return o.type=r||"value",o}return n(e,t),e.prototype.getLabelModel=function(){return this.model.getModel("label")},e.prototype.isHorizontal=function(){return"horizontal"===this.model.get("orient")},e}(q_),zV=Math.PI,VV=Po(),BV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(t,e){this.api=e},e.prototype.render=function(t,e,n){if(this.model=t,this.api=n,this.ecModel=e,this.group.removeAll(),t.get("show",!0)){var i=this._layout(t,n),r=this._createGroup("_mainGroup"),o=this._createGroup("_labelGroup"),a=this._axis=this._createAxis(i,t);t.formatTooltip=function(t){return Qf("nameValue",{noName:!0,value:a.scale.getLabel({value:t})})},E(["AxisLine","AxisTick","Control","CurrentPointer"],(function(e){this["_render"+e](i,r,a,t)}),this),this._renderAxisLabel(i,o,a,t),this._position(i,t)}this._doPlayStop(),this._updateTicksStatus()},e.prototype.remove=function(){this._clearTimer(),this.group.removeAll()},e.prototype.dispose=function(){this._clearTimer()},e.prototype._layout=function(t,e){var n,i,r,o,a=t.get(["label","position"]),s=t.get("orient"),l=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()},t.get("padding"))}(t,e),u={horizontal:"center",vertical:(n=null==a||"auto"===a?"horizontal"===s?l.y+l.height/2=0||"+"===n?"left":"right"},h={horizontal:n>=0||"+"===n?"top":"bottom",vertical:"middle"},c={horizontal:0,vertical:zV/2},p="vertical"===s?l.height:l.width,d=t.getModel("controlStyle"),f=d.get("show",!0),g=f?d.get("itemSize"):0,y=f?d.get("itemGap"):0,v=g+y,m=t.get(["label","rotate"])||0;m=m*zV/180;var x=d.get("position",!0),_=f&&d.get("showPlayBtn",!0),b=f&&d.get("showPrevBtn",!0),w=f&&d.get("showNextBtn",!0),S=0,M=p;"left"===x||"bottom"===x?(_&&(i=[0,0],S+=v),b&&(r=[S,0],S+=v),w&&(o=[M-g,0],M-=v)):(_&&(i=[M-g,0],M-=v),b&&(r=[0,0],S+=v),w&&(o=[M-g,0],M-=v));var I=[S,M];return t.get("inverse")&&I.reverse(),{viewRect:l,mainLength:p,orient:s,rotation:c[s],labelRotation:m,labelPosOpt:n,labelAlign:t.get(["label","align"])||u[s],labelBaseline:t.get(["label","verticalAlign"])||t.get(["label","baseline"])||h[s],playPosition:i,prevBtnPosition:r,nextBtnPosition:o,axisExtent:I,controlSize:g,controlGap:y}},e.prototype._position=function(t,e){var n=this._mainGroup,i=this._labelGroup,r=t.viewRect;if("vertical"===t.orient){var o=[1,0,0,1,0,0],a=r.x,s=r.y+r.height;be(o,o,[-a,-s]),we(o,o,-zV/2),be(o,o,[a,s]),(r=r.clone()).applyTransform(o)}var l=y(r),u=y(n.getBoundingRect()),h=y(i.getBoundingRect()),c=[n.x,n.y],p=[i.x,i.y];p[0]=c[0]=l[0][0];var d,f=t.labelPosOpt;null==f||X(f)?(v(c,u,l,1,d="+"===f?0:1),v(p,h,l,1,1-d)):(v(c,u,l,1,d=f>=0?0:1),p[1]=c[1]+f);function g(t){t.originX=l[0][0]-t.x,t.originY=l[1][0]-t.y}function y(t){return[[t.x,t.x+t.width],[t.y,t.y+t.height]]}function v(t,e,n,i,r){t[i]+=n[i][r]-e[i][r]}n.setPosition(c),i.setPosition(p),n.rotation=i.rotation=t.rotation,g(n),g(i)},e.prototype._createAxis=function(t,e){var n=e.getData(),i=e.get("axisType"),r=function(t,e){if(e=e||t.get("type"))switch(e){case"category":return new Mx({ordinalMeta:t.getCategories(),extent:[1/0,-1/0]});case"time":return new Fx({locale:t.ecModel.getLocaleModel(),useUTC:t.ecModel.get("useUTC")});default:return new Tx}}(e,i);r.getTicks=function(){return n.mapArray(["value"],(function(t){return{value:t}}))};var o=n.getDataExtent("value");r.setExtent(o[0],o[1]),r.calcNiceTicks();var a=new EV("value",r,t.axisExtent,i);return a.model=e,a},e.prototype._createGroup=function(t){var e=this[t]=new Er;return this.group.add(e),e},e.prototype._renderAxisLine=function(t,e,n,i){var r=n.getExtent();if(i.get(["lineStyle","show"])){var o=new Xu({shape:{x1:r[0],y1:0,x2:r[1],y2:0},style:A({lineCap:"round"},i.getModel("lineStyle").getLineStyle()),silent:!0,z2:1});e.add(o);var a=this._progressLine=new Xu({shape:{x1:r[0],x2:this._currentPointer?this._currentPointer.x:r[0],y1:0,y2:0},style:k({lineCap:"round",lineWidth:o.style.lineWidth},i.getModel(["progress","lineStyle"]).getLineStyle()),silent:!0,z2:1});e.add(a)}},e.prototype._renderAxisTick=function(t,e,n,i){var r=this,o=i.getData(),a=n.scale.getTicks();this._tickSymbols=[],E(a,(function(t){var a=n.dataToCoord(t.value),s=o.getItemModel(t.value),l=s.getModel("itemStyle"),u=s.getModel(["emphasis","itemStyle"]),h=s.getModel(["progress","itemStyle"]),c={x:a,y:0,onclick:W(r._changeTimeline,r,t.value)},p=FV(s,l,e,c);p.ensureState("emphasis").style=u.getItemStyle(),p.ensureState("progress").style=h.getItemStyle(),Wl(p);var d=Js(p);s.get("tooltip")?(d.dataIndex=t.value,d.dataModel=i):d.dataIndex=d.dataModel=null,r._tickSymbols.push(p)}))},e.prototype._renderAxisLabel=function(t,e,n,i){var r=this;if(n.getLabelModel().get("show")){var o=i.getData(),a=n.getViewLabels();this._tickLabels=[],E(a,(function(i){var a=i.tickValue,s=o.getItemModel(a),l=s.getModel("label"),u=s.getModel(["emphasis","label"]),h=s.getModel(["progress","label"]),c=n.dataToCoord(i.tickValue),p=new Bs({x:c,y:0,rotation:t.labelRotation-t.rotation,onclick:W(r._changeTimeline,r,a),silent:!1,style:ec(l,{text:i.formattedLabel,align:t.labelAlign,verticalAlign:t.labelBaseline})});p.ensureState("emphasis").style=ec(u),p.ensureState("progress").style=ec(h),e.add(p),Wl(p),VV(p).dataIndex=a,r._tickLabels.push(p)}))}},e.prototype._renderControl=function(t,e,n,i){var r=t.controlSize,o=t.rotation,a=i.getModel("controlStyle").getItemStyle(),s=i.getModel(["emphasis","controlStyle"]).getItemStyle(),l=i.getPlayState(),u=i.get("inverse",!0);function h(t,n,l,u){if(t){var h=Mr(rt(i.get(["controlStyle",n+"BtnSize"]),r),r),c=function(t,e,n,i){var r=i.style,o=Wh(t.get(["controlStyle",e]),i||{},new Ee(n[0],n[1],n[2],n[3]));r&&o.setStyle(r);return o}(i,n+"Icon",[0,-h/2,h,h],{x:t[0],y:t[1],originX:r/2,originY:0,rotation:u?-o:0,rectHover:!0,style:a,onclick:l});c.ensureState("emphasis").style=s,e.add(c),Wl(c)}}h(t.nextBtnPosition,"next",W(this._changeTimeline,this,u?"-":"+")),h(t.prevBtnPosition,"prev",W(this._changeTimeline,this,u?"+":"-")),h(t.playPosition,l?"stop":"play",W(this._handlePlayClick,this,!l),!0)},e.prototype._renderCurrentPointer=function(t,e,n,i){var r=i.getData(),o=i.getCurrentIndex(),a=r.getItemModel(o).getModel("checkpointStyle"),s=this,l={onCreate:function(t){t.draggable=!0,t.drift=W(s._handlePointerDrag,s),t.ondragend=W(s._handlePointerDragend,s),GV(t,s._progressLine,o,n,i,!0)},onUpdate:function(t){GV(t,s._progressLine,o,n,i)}};this._currentPointer=FV(a,a,this._mainGroup,{},this._currentPointer,l)},e.prototype._handlePlayClick=function(t){this._clearTimer(),this.api.dispatchAction({type:"timelinePlayChange",playState:t,from:this.uid})},e.prototype._handlePointerDrag=function(t,e,n){this._clearTimer(),this._pointerChangeTimeline([n.offsetX,n.offsetY])},e.prototype._handlePointerDragend=function(t){this._pointerChangeTimeline([t.offsetX,t.offsetY],!0)},e.prototype._pointerChangeTimeline=function(t,e){var n=this._toAxisCoord(t)[0],i=Zr(this._axis.getExtent().slice());n>i[1]&&(n=i[1]),n=0&&(a[o]=+a[o].toFixed(c)),[a,h]}var JV={min:H($V,"min"),max:H($V,"max"),average:H($V,"average"),median:H($V,"median")};function QV(t,e){if(e){var n=t.getData(),i=t.coordinateSystem,r=i.dimensions;if(!function(t){return!isNaN(parseFloat(t.x))&&!isNaN(parseFloat(t.y))}(e)&&!Y(e.coord)&&i){var o=tB(e,n,i,t);if((e=T(e)).type&&JV[e.type]&&o.baseAxis&&o.valueAxis){var a=P(r,o.baseAxis.dim),s=P(r,o.valueAxis.dim),l=JV[e.type](n,o.baseDataDim,o.valueDataDim,a,s);e.coord=l[0],e.value=l[1]}else e.coord=[null!=e.xAxis?e.xAxis:e.radiusAxis,null!=e.yAxis?e.yAxis:e.angleAxis]}if(null==e.coord)e.coord=[];else for(var u=e.coord,h=0;h<2;h++)JV[u[h]]&&(u[h]=iB(n,n.mapDimension(r[h]),u[h]));return e}}function tB(t,e,n,i){var r={};return null!=t.valueIndex||null!=t.valueDim?(r.valueDataDim=null!=t.valueIndex?e.getDimension(t.valueIndex):t.valueDim,r.valueAxis=n.getAxis(function(t,e){var n=t.getData().getDimensionInfo(e);return n&&n.coordDim}(i,r.valueDataDim)),r.baseAxis=n.getOtherAxis(r.valueAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim)):(r.baseAxis=i.getBaseAxis(),r.valueAxis=n.getOtherAxis(r.baseAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim),r.valueDataDim=e.mapDimension(r.valueAxis.dim)),r}function eB(t,e){return!(t&&t.containData&&e.coord&&!KV(e))||t.containData(e.coord)}function nB(t,e){return t?function(t,n,i,r){return _f(r<2?t.coord&&t.coord[r]:t.value,e[r])}:function(t,n,i,r){return _f(t.value,e[r])}}function iB(t,e,n){if("average"===n){var i=0,r=0;return t.each(e,(function(t,e){isNaN(t)||(i+=t,r++)})),i/r}return"median"===n?t.getMedian(e):t.getDataExtent(e)["max"===n?1:0]}var rB=Po(),oB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(){this.markerGroupMap=yt()},e.prototype.render=function(t,e,n){var i=this,r=this.markerGroupMap;r.each((function(t){rB(t).keep=!1})),e.eachSeries((function(t){var r=jV.getMarkerModelFromSeries(t,i.type);r&&i.renderSeries(t,r,e,n)})),r.each((function(t){!rB(t).keep&&i.group.remove(t.group)}))},e.prototype.markKeep=function(t){rB(t).keep=!0},e.prototype.toggleBlurSeries=function(t,e){var n=this;E(t,(function(t){var i=jV.getMarkerModelFromSeries(t,n.type);i&&i.getData().eachItemGraphicEl((function(t){t&&(e?Ll(t):Pl(t))}))}))},e.type="marker",e}(wg);function aB(t,e,n){var i=e.coordinateSystem;t.each((function(r){var o,a=t.getItemModel(r),s=Ur(a.get("x"),n.getWidth()),l=Ur(a.get("y"),n.getHeight());if(isNaN(s)||isNaN(l)){if(e.getMarkerPosition)o=e.getMarkerPosition(t.getValues(t.dimensions,r));else if(i){var u=t.get(i.dimensions[0],r),h=t.get(i.dimensions[1],r);o=i.dataToPoint([u,h])}}else o=[s,l];isNaN(s)||(o[0]=s),isNaN(l)||(o[1]=l),t.setItemLayout(r,o)}))}var sB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.updateTransform=function(t,e,n){e.eachSeries((function(t){var e=jV.getMarkerModelFromSeries(t,"markPoint");e&&(aB(e.getData(),t,n),this.markerGroupMap.get(t.id).updateLayout())}),this)},e.prototype.renderSeries=function(t,e,n,i){var r=t.coordinateSystem,o=t.id,a=t.getData(),s=this.markerGroupMap,l=s.get(o)||s.set(o,new iS),u=function(t,e,n){var i;i=t?z(t&&t.dimensions,(function(t){return A(A({},e.getData().getDimensionInfo(e.getData().mapDimension(t))||{}),{name:t,ordinalMeta:null})})):[{name:"value",type:"float"}];var r=new ex(i,n),o=z(n.get("data"),H(QV,e));t&&(o=B(o,H(eB,t)));var a=nB(!!t,i);return r.initData(o,null,a),r}(r,t,e);e.setData(u),aB(e.getData(),t,i),u.each((function(t){var n=u.getItemModel(t),i=n.getShallow("symbol"),r=n.getShallow("symbolSize"),o=n.getShallow("symbolRotate"),s=n.getShallow("symbolOffset"),l=n.getShallow("symbolKeepAspect");if(U(i)||U(r)||U(o)||U(s)){var h=e.getRawValue(t),c=e.getDataParams(t);U(i)&&(i=i(h,c)),U(r)&&(r=r(h,c)),U(o)&&(o=o(h,c)),U(s)&&(s=s(h,c))}var p=n.getModel("itemStyle").getItemStyle(),d=wy(a,"color");p.fill||(p.fill=d),u.setItemVisual(t,{symbol:i,symbolSize:r,symbolRotate:o,symbolOffset:s,symbolKeepAspect:l,style:p})})),l.updateData(u),this.group.add(l.group),u.eachItemGraphicEl((function(t){t.traverse((function(t){Js(t).dataModel=e}))})),this.markKeep(l),l.group.silent=e.get("silent")||t.get("silent")},e.type="markPoint",e}(oB);var lB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.createMarkerModelFromSeries=function(t,n,i){return new e(t,n,i)},e.type="markLine",e.defaultOption={z:5,symbol:["circle","arrow"],symbolSize:[8,16],symbolOffset:0,precision:2,tooltip:{trigger:"item"},label:{show:!0,position:"end",distance:5},lineStyle:{type:"dashed"},emphasis:{label:{show:!0},lineStyle:{width:3}},animationEasing:"linear"},e}(jV),uB=Po(),hB=function(t,e,n,i){var r,o=t.getData();if(Y(i))r=i;else{var a=i.type;if("min"===a||"max"===a||"average"===a||"median"===a||null!=i.xAxis||null!=i.yAxis){var s=void 0,l=void 0;if(null!=i.yAxis||null!=i.xAxis)s=e.getAxis(null!=i.yAxis?"y":"x"),l=it(i.yAxis,i.xAxis);else{var u=tB(i,o,e,t);s=u.valueAxis,l=iB(o,ux(o,u.valueDataDim),a)}var h="x"===s.dim?0:1,c=1-h,p=T(i),d={coord:[]};p.type=null,p.coord=[],p.coord[c]=-1/0,d.coord[c]=1/0;var f=n.get("precision");f>=0&&j(l)&&(l=+l.toFixed(Math.min(f,20))),p.coord[h]=d.coord[h]=l,r=[p,d,{type:a,valueIndex:i.valueIndex,value:l}]}else r=[]}var g=[QV(t,r[0]),QV(t,r[1]),A({},r[2])];return g[2].type=g[2].type||null,C(g[2],g[0]),C(g[2],g[1]),g};function cB(t){return!isNaN(t)&&!isFinite(t)}function pB(t,e,n,i){var r=1-t,o=i.dimensions[t];return cB(e[r])&&cB(n[r])&&e[t]===n[t]&&i.getAxis(o).containData(e[t])}function dB(t,e){if("cartesian2d"===t.type){var n=e[0].coord,i=e[1].coord;if(n&&i&&(pB(1,n,i,t)||pB(0,n,i,t)))return!0}return eB(t,e[0])&&eB(t,e[1])}function fB(t,e,n,i,r){var o,a=i.coordinateSystem,s=t.getItemModel(e),l=Ur(s.get("x"),r.getWidth()),u=Ur(s.get("y"),r.getHeight());if(isNaN(l)||isNaN(u)){if(i.getMarkerPosition)o=i.getMarkerPosition(t.getValues(t.dimensions,e));else{var h=a.dimensions,c=t.get(h[0],e),p=t.get(h[1],e);o=a.dataToPoint([c,p])}if(vS(a,"cartesian2d")){var d=a.getAxis("x"),f=a.getAxis("y");h=a.dimensions;cB(t.get(h[0],e))?o[0]=d.toGlobalCoord(d.getExtent()[n?0:1]):cB(t.get(h[1],e))&&(o[1]=f.toGlobalCoord(f.getExtent()[n?0:1]))}isNaN(l)||(o[0]=l),isNaN(u)||(o[1]=u)}else o=[l,u];t.setItemLayout(e,o)}var gB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.updateTransform=function(t,e,n){e.eachSeries((function(t){var e=jV.getMarkerModelFromSeries(t,"markLine");if(e){var i=e.getData(),r=uB(e).from,o=uB(e).to;r.each((function(e){fB(r,e,!0,t,n),fB(o,e,!1,t,n)})),i.each((function(t){i.setItemLayout(t,[r.getItemLayout(t),o.getItemLayout(t)])})),this.markerGroupMap.get(t.id).updateLayout()}}),this)},e.prototype.renderSeries=function(t,e,n,i){var r=t.coordinateSystem,o=t.id,a=t.getData(),s=this.markerGroupMap,l=s.get(o)||s.set(o,new TA);this.group.add(l.group);var u=function(t,e,n){var i;i=t?z(t&&t.dimensions,(function(t){return A(A({},e.getData().getDimensionInfo(e.getData().mapDimension(t))||{}),{name:t,ordinalMeta:null})})):[{name:"value",type:"float"}];var r=new ex(i,n),o=new ex(i,n),a=new ex([],n),s=z(n.get("data"),H(hB,e,t,n));t&&(s=B(s,H(dB,t)));var l=nB(!!t,i);return r.initData(z(s,(function(t){return t[0]})),null,l),o.initData(z(s,(function(t){return t[1]})),null,l),a.initData(z(s,(function(t){return t[2]}))),a.hasItemOption=!0,{from:r,to:o,line:a}}(r,t,e),h=u.from,c=u.to,p=u.line;uB(e).from=h,uB(e).to=c,e.setData(p);var d=e.get("symbol"),f=e.get("symbolSize"),g=e.get("symbolRotate"),y=e.get("symbolOffset");function v(e,n,r){var o=e.getItemModel(n);fB(e,n,r,t,i);var s=o.getModel("itemStyle").getItemStyle();null==s.fill&&(s.fill=wy(a,"color")),e.setItemVisual(n,{symbolKeepAspect:o.get("symbolKeepAspect"),symbolOffset:rt(o.get("symbolOffset",!0),y[r?0:1]),symbolRotate:rt(o.get("symbolRotate",!0),g[r?0:1]),symbolSize:rt(o.get("symbolSize"),f[r?0:1]),symbol:rt(o.get("symbol",!0),d[r?0:1]),style:s})}Y(d)||(d=[d,d]),Y(f)||(f=[f,f]),Y(g)||(g=[g,g]),Y(y)||(y=[y,y]),u.from.each((function(t){v(h,t,!0),v(c,t,!1)})),p.each((function(t){var e=p.getItemModel(t).getModel("lineStyle").getLineStyle();p.setItemLayout(t,[h.getItemLayout(t),c.getItemLayout(t)]),null==e.stroke&&(e.stroke=h.getItemVisual(t,"style").fill),p.setItemVisual(t,{fromSymbolKeepAspect:h.getItemVisual(t,"symbolKeepAspect"),fromSymbolOffset:h.getItemVisual(t,"symbolOffset"),fromSymbolRotate:h.getItemVisual(t,"symbolRotate"),fromSymbolSize:h.getItemVisual(t,"symbolSize"),fromSymbol:h.getItemVisual(t,"symbol"),toSymbolKeepAspect:c.getItemVisual(t,"symbolKeepAspect"),toSymbolOffset:c.getItemVisual(t,"symbolOffset"),toSymbolRotate:c.getItemVisual(t,"symbolRotate"),toSymbolSize:c.getItemVisual(t,"symbolSize"),toSymbol:c.getItemVisual(t,"symbol"),style:e})})),l.updateData(p),u.line.eachItemGraphicEl((function(t){Js(t).dataModel=e,t.traverse((function(t){Js(t).dataModel=e}))})),this.markKeep(l),l.group.silent=e.get("silent")||t.get("silent")},e.type="markLine",e}(oB);var yB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.createMarkerModelFromSeries=function(t,n,i){return new e(t,n,i)},e.type="markArea",e.defaultOption={z:1,tooltip:{trigger:"item"},animation:!1,label:{show:!0,position:"top"},itemStyle:{borderWidth:0},emphasis:{label:{show:!0,position:"top"}}},e}(jV),vB=Po(),mB=function(t,e,n,i){var r=i[0],o=i[1];if(r&&o){var a=QV(t,r),s=QV(t,o),l=a.coord,u=s.coord;l[0]=it(l[0],-1/0),l[1]=it(l[1],-1/0),u[0]=it(u[0],1/0),u[1]=it(u[1],1/0);var h=D([{},a,s]);return h.coord=[a.coord,s.coord],h.x0=a.x,h.y0=a.y,h.x1=s.x,h.y1=s.y,h}};function xB(t){return!isNaN(t)&&!isFinite(t)}function _B(t,e,n,i){var r=1-t;return xB(e[r])&&xB(n[r])}function bB(t,e){var n=e.coord[0],i=e.coord[1],r={coord:n,x:e.x0,y:e.y0},o={coord:i,x:e.x1,y:e.y1};return vS(t,"cartesian2d")?!(!n||!i||!_B(1,n,i)&&!_B(0,n,i))||function(t,e,n){return!(t&&t.containZone&&e.coord&&n.coord&&!KV(e)&&!KV(n))||t.containZone(e.coord,n.coord)}(t,r,o):eB(t,r)||eB(t,o)}function wB(t,e,n,i,r){var o,a=i.coordinateSystem,s=t.getItemModel(e),l=Ur(s.get(n[0]),r.getWidth()),u=Ur(s.get(n[1]),r.getHeight());if(isNaN(l)||isNaN(u)){if(i.getMarkerPosition){var h=t.getValues(["x0","y0"],e),c=t.getValues(["x1","y1"],e),p=a.clampData(h),d=a.clampData(c),f=[];"x0"===n[0]?f[0]=p[0]>d[0]?c[0]:h[0]:f[0]=p[0]>d[0]?h[0]:c[0],"y0"===n[1]?f[1]=p[1]>d[1]?c[1]:h[1]:f[1]=p[1]>d[1]?h[1]:c[1],o=i.getMarkerPosition(f,n,!0)}else{var g=[m=t.get(n[0],e),x=t.get(n[1],e)];a.clampData&&a.clampData(g,g),o=a.dataToPoint(g,!0)}if(vS(a,"cartesian2d")){var y=a.getAxis("x"),v=a.getAxis("y"),m=t.get(n[0],e),x=t.get(n[1],e);xB(m)?o[0]=y.toGlobalCoord(y.getExtent()["x0"===n[0]?0:1]):xB(x)&&(o[1]=v.toGlobalCoord(v.getExtent()["y0"===n[1]?0:1]))}isNaN(l)||(o[0]=l),isNaN(u)||(o[1]=u)}else o=[l,u];return o}var SB=[["x0","y0"],["x1","y0"],["x1","y1"],["x0","y1"]],MB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.updateTransform=function(t,e,n){e.eachSeries((function(t){var e=jV.getMarkerModelFromSeries(t,"markArea");if(e){var i=e.getData();i.each((function(e){var r=z(SB,(function(r){return wB(i,e,r,t,n)}));i.setItemLayout(e,r),i.getItemGraphicEl(e).setShape("points",r)}))}}),this)},e.prototype.renderSeries=function(t,e,n,i){var r=t.coordinateSystem,o=t.id,a=t.getData(),s=this.markerGroupMap,l=s.get(o)||s.set(o,{group:new Er});this.group.add(l.group),this.markKeep(l);var u=function(t,e,n){var i,r,o=["x0","y0","x1","y1"];if(t){var a=z(t&&t.dimensions,(function(t){var n=e.getData();return A(A({},n.getDimensionInfo(n.mapDimension(t))||{}),{name:t,ordinalMeta:null})}));r=z(o,(function(t,e){return{name:t,type:a[e%2].type}})),i=new ex(r,n)}else i=new ex(r=[{name:"value",type:"float"}],n);var s=z(n.get("data"),H(mB,e,t,n));t&&(s=B(s,H(bB,t)));var l=t?function(t,e,n,i){return _f(t.coord[Math.floor(i/2)][i%2],r[i])}:function(t,e,n,i){return _f(t.value,r[i])};return i.initData(s,null,l),i.hasItemOption=!0,i}(r,t,e);e.setData(u),u.each((function(e){var n=z(SB,(function(n){return wB(u,e,n,t,i)})),o=r.getAxis("x").scale,s=r.getAxis("y").scale,l=o.getExtent(),h=s.getExtent(),c=[o.parse(u.get("x0",e)),o.parse(u.get("x1",e))],p=[s.parse(u.get("y0",e)),s.parse(u.get("y1",e))];Zr(c),Zr(p);var d=!!(l[0]>c[1]||l[1]p[1]||h[1]=0},e.prototype.getOrient=function(){return"vertical"===this.get("orient")?{index:1,name:"vertical"}:{index:0,name:"horizontal"}},e.type="legend.plain",e.dependencies=["series"],e.defaultOption={z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,symbolRotate:"inherit",symbolKeepAspect:!0,inactiveColor:"#ccc",inactiveBorderColor:"#ccc",inactiveBorderWidth:"auto",itemStyle:{color:"inherit",opacity:"inherit",borderColor:"inherit",borderWidth:"auto",borderCap:"inherit",borderJoin:"inherit",borderDashOffset:"inherit",borderMiterLimit:"inherit"},lineStyle:{width:"auto",color:"inherit",inactiveColor:"#ccc",inactiveWidth:2,opacity:"inherit",type:"inherit",cap:"inherit",join:"inherit",dashOffset:"inherit",miterLimit:"inherit"},textStyle:{color:"#333"},selectedMode:!0,selector:!1,selectorLabel:{show:!0,borderRadius:10,padding:[3,5,3,5],fontSize:12,fontFamily:"sans-serif",color:"#666",borderWidth:1,borderColor:"#666"},emphasis:{selectorLabel:{show:!0,color:"#eee",backgroundColor:"#666"}},selectorPosition:"auto",selectorItemGap:7,selectorButtonGap:10,tooltip:{show:!1}},e}(Op),TB=H,CB=E,DB=Er,AB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.newlineDisabled=!1,n}return n(e,t),e.prototype.init=function(){this.group.add(this._contentGroup=new DB),this.group.add(this._selectorGroup=new DB),this._isFirstRender=!0},e.prototype.getContentGroup=function(){return this._contentGroup},e.prototype.getSelectorGroup=function(){return this._selectorGroup},e.prototype.render=function(t,e,n){var i=this._isFirstRender;if(this._isFirstRender=!1,this.resetInner(),t.get("show",!0)){var r=t.get("align"),o=t.get("orient");r&&"auto"!==r||(r="right"===t.get("left")&&"vertical"===o?"right":"left");var a=t.get("selector",!0),s=t.get("selectorPosition",!0);!a||s&&"auto"!==s||(s="horizontal"===o?"end":"start"),this.renderInner(r,t,e,n,a,o,s);var l=t.getBoxLayoutParams(),u={width:n.getWidth(),height:n.getHeight()},h=t.get("padding"),c=Tp(l,u,h),p=this.layoutInner(t,r,c,i,a,s),d=Tp(k({width:p.width,height:p.height},l),u,h);this.group.x=d.x-p.x,this.group.y=d.y-p.y,this.group.markRedraw(),this.group.add(this._backgroundEl=rz(p,t))}},e.prototype.resetInner=function(){this.getContentGroup().removeAll(),this._backgroundEl&&this.group.remove(this._backgroundEl),this.getSelectorGroup().removeAll()},e.prototype.renderInner=function(t,e,n,i,r,o,a){var s=this.getContentGroup(),l=yt(),u=e.get("selectedMode"),h=[];n.eachRawSeries((function(t){!t.get("legendHoverLink")&&h.push(t.id)})),CB(e.getData(),(function(r,o){var a=r.get("name");if(!this.newlineDisabled&&(""===a||"\n"===a)){var c=new DB;return c.newline=!0,void s.add(c)}var p=n.getSeriesByName(a)[0];if(!l.get(a)){if(p){var d=p.getData(),f=d.getVisual("legendLineStyle")||{},g=d.getVisual("legendIcon"),y=d.getVisual("style");this._createItem(p,a,o,r,e,t,f,y,g,u,i).on("click",TB(kB,a,null,i,h)).on("mouseover",TB(PB,p.name,null,i,h)).on("mouseout",TB(OB,p.name,null,i,h)),l.set(a,!0)}else n.eachRawSeries((function(n){if(!l.get(a)&&n.legendVisualProvider){var s=n.legendVisualProvider;if(!s.containName(a))return;var c=s.indexOfName(a),p=s.getItemVisual(c,"style"),d=s.getItemVisual(c,"legendIcon"),f=jn(p.fill);f&&0===f[3]&&(f[3]=.2,p=A(A({},p),{fill:ii(f,"rgba")})),this._createItem(n,a,o,r,e,t,{},p,d,u,i).on("click",TB(kB,null,a,i,h)).on("mouseover",TB(PB,null,a,i,h)).on("mouseout",TB(OB,null,a,i,h)),l.set(a,!0)}}),this);0}}),this),r&&this._createSelector(r,e,i,o,a)},e.prototype._createSelector=function(t,e,n,i,r){var o=this.getSelectorGroup();CB(t,(function(t){var i=t.type,r=new Bs({style:{x:0,y:0,align:"center",verticalAlign:"middle"},onclick:function(){n.dispatchAction({type:"all"===i?"legendAllSelect":"legendInverseSelect"})}});o.add(r),Qh(r,{normal:e.getModel("selectorLabel"),emphasis:e.getModel(["emphasis","selectorLabel"])},{defaultText:t.title}),Wl(r)}))},e.prototype._createItem=function(t,e,n,i,r,o,a,s,l,u,h){var c=t.visualDrawType,p=r.get("itemWidth"),d=r.get("itemHeight"),f=r.isSelected(e),g=i.get("symbolRotate"),y=i.get("symbolKeepAspect"),v=i.get("icon"),m=function(t,e,n,i,r,o,a){function s(t,e){"auto"===t.lineWidth&&(t.lineWidth=e.lineWidth>0?2:0),CB(t,(function(n,i){"inherit"===t[i]&&(t[i]=e[i])}))}var l=e.getModel("itemStyle"),u=l.getItemStyle(),h=0===t.lastIndexOf("empty",0)?"fill":"stroke",c=l.getShallow("decal");u.decal=c&&"inherit"!==c?cv(c,a):i.decal,"inherit"===u.fill&&(u.fill=i[r]);"inherit"===u.stroke&&(u.stroke=i[h]);"inherit"===u.opacity&&(u.opacity=("fill"===r?i:n).opacity);s(u,i);var p=e.getModel("lineStyle"),d=p.getLineStyle();if(s(d,n),"auto"===u.fill&&(u.fill=i.fill),"auto"===u.stroke&&(u.stroke=i.fill),"auto"===d.stroke&&(d.stroke=i.fill),!o){var f=e.get("inactiveBorderWidth"),g=u[h];u.lineWidth="auto"===f?i.lineWidth>0&&g?2:0:u.lineWidth,u.fill=e.get("inactiveColor"),u.stroke=e.get("inactiveBorderColor"),d.stroke=p.get("inactiveColor"),d.lineWidth=p.get("inactiveWidth")}return{itemStyle:u,lineStyle:d}}(l=v||l||"roundRect",i,a,s,c,f,h),x=new DB,_=i.getModel("textStyle");if(!U(t.getLegendIcon)||v&&"inherit"!==v){var b="inherit"===v&&t.getData().getVisual("symbol")?"inherit"===g?t.getData().getVisual("symbolRotate"):g:0;x.add(function(t){var e=t.icon||"roundRect",n=Vy(e,0,0,t.itemWidth,t.itemHeight,t.itemStyle.fill,t.symbolKeepAspect);n.setStyle(t.itemStyle),n.rotation=(t.iconRotate||0)*Math.PI/180,n.setOrigin([t.itemWidth/2,t.itemHeight/2]),e.indexOf("empty")>-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2);return n}({itemWidth:p,itemHeight:d,icon:l,iconRotate:b,itemStyle:m.itemStyle,lineStyle:m.lineStyle,symbolKeepAspect:y}))}else x.add(t.getLegendIcon({itemWidth:p,itemHeight:d,icon:l,iconRotate:g,itemStyle:m.itemStyle,lineStyle:m.lineStyle,symbolKeepAspect:y}));var w="left"===o?p+5:-5,S=o,M=r.get("formatter"),I=e;X(M)&&M?I=M.replace("{name}",null!=e?e:""):U(M)&&(I=M(e));var T=i.get("inactiveColor");x.add(new Bs({style:ec(_,{text:I,x:w,y:d/2,fill:f?_.getTextColor():T,align:S,verticalAlign:"middle"})}));var C=new Es({shape:x.getBoundingRect(),invisible:!0}),D=i.getModel("tooltip");return D.get("show")&&Xh({el:C,componentModel:r,itemName:e,itemTooltipOption:D.option}),x.add(C),x.eachChild((function(t){t.silent=!0})),C.silent=!u,this.getContentGroup().add(x),Wl(x),x.__legendDataIndex=n,x},e.prototype.layoutInner=function(t,e,n,i,r,o){var a=this.getContentGroup(),s=this.getSelectorGroup();Ip(t.get("orient"),a,t.get("itemGap"),n.width,n.height);var l=a.getBoundingRect(),u=[-l.x,-l.y];if(s.markRedraw(),a.markRedraw(),r){Ip("horizontal",s,t.get("selectorItemGap",!0));var h=s.getBoundingRect(),c=[-h.x,-h.y],p=t.get("selectorButtonGap",!0),d=t.getOrient().index,f=0===d?"width":"height",g=0===d?"height":"width",y=0===d?"y":"x";"end"===o?c[d]+=l[f]+p:u[d]+=h[f]+p,c[1-d]+=l[g]/2-h[g]/2,s.x=c[0],s.y=c[1],a.x=u[0],a.y=u[1];var v={x:0,y:0};return v[f]=l[f]+p+h[f],v[g]=Math.max(l[g],h[g]),v[y]=Math.min(0,h[y]+c[1-d]),v}return a.x=u[0],a.y=u[1],this.group.getBoundingRect()},e.prototype.remove=function(){this.getContentGroup().removeAll(),this._isFirstRender=!0},e.type="legend.plain",e}(wg);function kB(t,e,n,i){OB(t,e,n,i),n.dispatchAction({type:"legendToggleSelect",name:null!=t?t:e}),PB(t,e,n,i)}function LB(t){for(var e,n=t.getZr().storage.getDisplayList(),i=0,r=n.length;in[r],f=[-c.x,-c.y];e||(f[i]=l[s]);var g=[0,0],y=[-p.x,-p.y],v=rt(t.get("pageButtonGap",!0),t.get("itemGap",!0));d&&("end"===t.get("pageButtonPosition",!0)?y[i]+=n[r]-p[r]:g[i]+=p[r]+v);y[1-i]+=c[o]/2-p[o]/2,l.setPosition(f),u.setPosition(g),h.setPosition(y);var m={x:0,y:0};if(m[r]=d?n[r]:c[r],m[o]=Math.max(c[o],p[o]),m[a]=Math.min(0,p[a]+y[1-i]),u.__rectSize=n[r],d){var x={x:0,y:0};x[r]=Math.max(n[r]-p[r]-v,0),x[o]=m[o],u.setClipPath(new Es({shape:x})),u.__rectSize=x[r]}else h.eachChild((function(t){t.attr({invisible:!0,silent:!0})}));var _=this._getPageInfo(t);return null!=_.pageIndex&&dh(l,{x:_.contentPosition[0],y:_.contentPosition[1]},d?t:null),this._updatePageInfoView(t,_),m},e.prototype._pageGo=function(t,e,n){var i=this._getPageInfo(e)[t];null!=i&&n.dispatchAction({type:"legendScroll",scrollDataIndex:i,legendId:e.id})},e.prototype._updatePageInfoView=function(t,e){var n=this._controllerGroup;E(["pagePrev","pageNext"],(function(i){var r=null!=e[i+"DataIndex"],o=n.childOfName(i);o&&(o.setStyle("fill",r?t.get("pageIconColor",!0):t.get("pageIconInactiveColor",!0)),o.cursor=r?"pointer":"default")}));var i=n.childOfName("pageText"),r=t.get("pageFormatter"),o=e.pageIndex,a=null!=o?o+1:0,s=e.pageCount;i&&r&&i.setStyle("text",X(r)?r.replace("{current}",null==a?"":a+"").replace("{total}",null==s?"":s+""):r({current:a,total:s}))},e.prototype._getPageInfo=function(t){var e=t.get("scrollDataIndex",!0),n=this.getContentGroup(),i=this._containerGroup.__rectSize,r=t.getOrient().index,o=FB[r],a=GB[r],s=this._findTargetItemIndex(e),l=n.children(),u=l[s],h=l.length,c=h?1:0,p={contentPosition:[n.x,n.y],pageCount:c,pageIndex:c-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!u)return p;var d=m(u);p.contentPosition[r]=-d.s;for(var f=s+1,g=d,y=d,v=null;f<=h;++f)(!(v=m(l[f]))&&y.e>g.s+i||v&&!x(v,g.s))&&(g=y.i>g.i?y:v)&&(null==p.pageNextDataIndex&&(p.pageNextDataIndex=g.i),++p.pageCount),y=v;for(f=s-1,g=d,y=d,v=null;f>=-1;--f)(v=m(l[f]))&&x(y,v.s)||!(g.i=e&&t.s<=e+i}},e.prototype._findTargetItemIndex=function(t){return this._showController?(this.getContentGroup().eachChild((function(i,r){var o=i.__legendDataIndex;null==n&&null!=o&&(n=r),o===t&&(e=r)})),null!=e?e:n):0;var e,n},e.type="legend.scroll",e}(AB);function HB(t){Dm(EB),t.registerComponentModel(zB),t.registerComponentView(WB),function(t){t.registerAction("legendScroll","legendscroll",(function(t,e){var n=t.scrollDataIndex;null!=n&&e.eachComponent({mainType:"legend",subType:"scroll",query:t},(function(t){t.setScrollDataIndex(n)}))}))}(t)}var YB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="dataZoom.inside",e.defaultOption=Tc(GE.defaultOption,{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),e}(GE),UB=Po();function XB(t,e,n){UB(t).coordSysRecordMap.each((function(t){var i=t.dataZoomInfoMap.get(e.uid);i&&(i.getRange=n)}))}function ZB(t,e){if(e){t.removeKey(e.model.uid);var n=e.controller;n&&n.dispose()}}function jB(t,e){t.isDisposed()||t.dispatchAction({type:"dataZoom",animation:{easing:"cubicOut",duration:100},batch:e})}function qB(t,e,n,i){return t.coordinateSystem.containPoint([n,i])}function KB(t){t.registerProcessor(t.PRIORITY.PROCESSOR.FILTER,(function(t,e){var n=UB(e),i=n.coordSysRecordMap||(n.coordSysRecordMap=yt());i.each((function(t){t.dataZoomInfoMap=null})),t.eachComponent({mainType:"dataZoom",subType:"inside"},(function(t){E(BE(t).infoList,(function(n){var r=n.model.uid,o=i.get(r)||i.set(r,function(t,e){var n={model:e,containsPoint:H(qB,e),dispatchAction:H(jB,t),dataZoomInfoMap:null,controller:null},i=n.controller=new BI(t.getZr());return E(["pan","zoom","scrollMove"],(function(t){i.on(t,(function(e){var i=[];n.dataZoomInfoMap.each((function(r){if(e.isAvailableBehavior(r.model.option)){var o=(r.getRange||{})[t],a=o&&o(r.dzReferCoordSysInfo,n.model.mainType,n.controller,e);!r.model.get("disabled",!0)&&a&&i.push({dataZoomId:r.model.id,start:a[0],end:a[1]})}})),i.length&&n.dispatchAction(i)}))})),n}(e,n.model));(o.dataZoomInfoMap||(o.dataZoomInfoMap=yt())).set(t.uid,{dzReferCoordSysInfo:n,model:t,getRange:null})}))})),i.each((function(t){var e,n=t.controller,r=t.dataZoomInfoMap;if(r){var o=r.keys()[0];null!=o&&(e=r.get(o))}if(e){var a=function(t){var e,n="type_",i={type_true:2,type_move:1,type_false:0,type_undefined:-1},r=!0;return t.each((function(t){var o=t.model,a=!o.get("disabled",!0)&&(!o.get("zoomLock",!0)||"move");i[n+a]>i[n+e]&&(e=a),r=r&&o.get("preventDefaultMouseMove",!0)})),{controlType:e,opt:{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!0,preventDefaultMouseMove:!!r}}}(r);n.enable(a.controlType,a.opt),n.setPointerChecker(t.containsPoint),Eg(t,"dispatchAction",e.model.get("throttle",!0),"fixRate")}else ZB(i,t)}))}))}var $B=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="dataZoom.inside",e}return n(e,t),e.prototype.render=function(e,n,i){t.prototype.render.apply(this,arguments),e.noTarget()?this._clear():(this.range=e.getPercentRange(),XB(i,e,{pan:W(JB.pan,this),zoom:W(JB.zoom,this),scrollMove:W(JB.scrollMove,this)}))},e.prototype.dispose=function(){this._clear(),t.prototype.dispose.apply(this,arguments)},e.prototype._clear=function(){!function(t,e){for(var n=UB(t).coordSysRecordMap,i=n.keys(),r=0;r0?s.pixelStart+s.pixelLength-s.pixel:s.pixel-s.pixelStart)/s.pixelLength*(o[1]-o[0])+o[0],u=Math.max(1/i.scale,0);o[0]=(o[0]-l)*u+l,o[1]=(o[1]-l)*u+l;var h=this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();return xk(0,o,[0,100],0,h.minSpan,h.maxSpan),this.range=o,r[0]!==o[0]||r[1]!==o[1]?o:void 0}},pan:QB((function(t,e,n,i,r,o){var a=tF[i]([o.oldX,o.oldY],[o.newX,o.newY],e,r,n);return a.signal*(t[1]-t[0])*a.pixel/a.pixelLength})),scrollMove:QB((function(t,e,n,i,r,o){return tF[i]([0,0],[o.scrollDelta,o.scrollDelta],e,r,n).signal*(t[1]-t[0])*o.scrollDelta}))};function QB(t){return function(e,n,i,r){var o=this.range,a=o.slice(),s=e.axisModels[0];if(s)return xk(t(a,s,e,n,i,r),a,[0,100],"all"),this.range=a,o[0]!==a[0]||o[1]!==a[1]?a:void 0}}var tF={grid:function(t,e,n,i,r){var o=n.axis,a={},s=r.model.coordinateSystem.getRect();return t=t||[0,0],"x"===o.dim?(a.pixel=e[0]-t[0],a.pixelLength=s.width,a.pixelStart=s.x,a.signal=o.inverse?1:-1):(a.pixel=e[1]-t[1],a.pixelLength=s.height,a.pixelStart=s.y,a.signal=o.inverse?-1:1),a},polar:function(t,e,n,i,r){var o=n.axis,a={},s=r.model.coordinateSystem,l=s.getRadiusAxis().getExtent(),u=s.getAngleAxis().getExtent();return t=t?s.pointToCoord(t):[0,0],e=s.pointToCoord(e),"radiusAxis"===n.mainType?(a.pixel=e[0]-t[0],a.pixelLength=l[1]-l[0],a.pixelStart=l[0],a.signal=o.inverse?1:-1):(a.pixel=e[1]-t[1],a.pixelLength=u[1]-u[0],a.pixelStart=u[0],a.signal=o.inverse?-1:1),a},singleAxis:function(t,e,n,i,r){var o=n.axis,a=r.model.coordinateSystem.getRect(),s={};return t=t||[0,0],"horizontal"===o.orient?(s.pixel=e[0]-t[0],s.pixelLength=a.width,s.pixelStart=a.x,s.signal=o.inverse?1:-1):(s.pixel=e[1]-t[1],s.pixelLength=a.height,s.pixelStart=a.y,s.signal=o.inverse?-1:1),s}};function eF(t){$E(t),t.registerComponentModel(YB),t.registerComponentView($B),KB(t)}var nF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="dataZoom.slider",e.layoutMode="box",e.defaultOption=Tc(GE.defaultOption,{show:!0,right:"ph",top:"ph",width:"ph",height:"ph",left:null,bottom:null,borderColor:"#d2dbee",borderRadius:3,backgroundColor:"rgba(47,69,84,0)",dataBackground:{lineStyle:{color:"#d2dbee",width:.5},areaStyle:{color:"#d2dbee",opacity:.2}},selectedDataBackground:{lineStyle:{color:"#8fb0f7",width:.5},areaStyle:{color:"#8fb0f7",opacity:.2}},fillerColor:"rgba(135,175,274,0.2)",handleIcon:"path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z",handleSize:"100%",handleStyle:{color:"#fff",borderColor:"#ACB8D1"},moveHandleSize:7,moveHandleIcon:"path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z",moveHandleStyle:{color:"#D2DBEE",opacity:.7},showDetail:!0,showDataShadow:"auto",realtime:!0,zoomLock:!1,textStyle:{color:"#6E7079"},brushSelect:!0,brushStyle:{color:"rgba(135,175,274,0.15)"},emphasis:{handleStyle:{borderColor:"#8FB0F7"},moveHandleStyle:{color:"#8FB0F7"}}}),e}(GE),iF=Es,rF="horizontal",oF="vertical",aF=["line","bar","candlestick","scatter"],sF={easing:"cubicOut",duration:100,delay:0},lF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._displayables={},n}return n(e,t),e.prototype.init=function(t,e){this.api=e,this._onBrush=W(this._onBrush,this),this._onBrushEnd=W(this._onBrushEnd,this)},e.prototype.render=function(e,n,i,r){if(t.prototype.render.apply(this,arguments),Eg(this,"_dispatchZoomAction",e.get("throttle"),"fixRate"),this._orient=e.getOrient(),!1!==e.get("show")){if(e.noTarget())return this._clear(),void this.group.removeAll();r&&"dataZoom"===r.type&&r.from===this.uid||this._buildView(),this._updateView()}else this.group.removeAll()},e.prototype.dispose=function(){this._clear(),t.prototype.dispose.apply(this,arguments)},e.prototype._clear=function(){zg(this,"_dispatchZoomAction");var t=this.api.getZr();t.off("mousemove",this._onBrush),t.off("mouseup",this._onBrushEnd)},e.prototype._buildView=function(){var t=this.group;t.removeAll(),this._brushing=!1,this._displayables.brushRect=null,this._resetLocation(),this._resetInterval();var e=this._displayables.sliderGroup=new Er;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),t.add(e),this._positionGroup()},e.prototype._resetLocation=function(){var t=this.dataZoomModel,e=this.api,n=t.get("brushSelect")?7:0,i=this._findCoordRect(),r={width:e.getWidth(),height:e.getHeight()},o=this._orient===rF?{right:r.width-i.x-i.width,top:r.height-30-7-n,width:i.width,height:30}:{right:7,top:i.y,width:30,height:i.height},a=kp(t.option);E(["right","top","width","height"],(function(t){"ph"===a[t]&&(a[t]=o[t])}));var s=Tp(a,r);this._location={x:s.x,y:s.y},this._size=[s.width,s.height],this._orient===oF&&this._size.reverse()},e.prototype._positionGroup=function(){var t=this.group,e=this._location,n=this._orient,i=this.dataZoomModel.getFirstTargetAxisModel(),r=i&&i.get("inverse"),o=this._displayables.sliderGroup,a=(this._dataShadowInfo||{}).otherAxisInverse;o.attr(n!==rF||r?n===rF&&r?{scaleY:a?1:-1,scaleX:-1}:n!==oF||r?{scaleY:a?-1:1,scaleX:-1,rotation:Math.PI/2}:{scaleY:a?-1:1,scaleX:1,rotation:Math.PI/2}:{scaleY:a?1:-1,scaleX:1});var s=t.getBoundingRect([o]);t.x=e.x-s.x,t.y=e.y-s.y,t.markRedraw()},e.prototype._getViewExtent=function(){return[0,this._size[0]]},e.prototype._renderBackground=function(){var t=this.dataZoomModel,e=this._size,n=this._displayables.sliderGroup,i=t.get("brushSelect");n.add(new iF({silent:!0,shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:t.get("backgroundColor")},z2:-40}));var r=new iF({shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:"transparent"},z2:0,onclick:W(this._onClickPanel,this)}),o=this.api.getZr();i?(r.on("mousedown",this._onBrushStart,this),r.cursor="crosshair",o.on("mousemove",this._onBrush),o.on("mouseup",this._onBrushEnd)):(o.off("mousemove",this._onBrush),o.off("mouseup",this._onBrushEnd)),n.add(r)},e.prototype._renderDataShadow=function(){var t=this._dataShadowInfo=this._prepareDataShadowInfo();if(this._displayables.dataShadowSegs=[],t){var e=this._size,n=this._shadowSize||[],i=t.series,r=i.getRawData(),o=i.getShadowDim&&i.getShadowDim(),a=o&&r.getDimensionInfo(o)?i.getShadowDim():t.otherDim;if(null!=a){var s=this._shadowPolygonPts,l=this._shadowPolylinePts;if(r!==this._shadowData||a!==this._shadowDim||e[0]!==n[0]||e[1]!==n[1]){var u=r.getDataExtent(a),h=.3*(u[1]-u[0]);u=[u[0]-h,u[1]+h];var c,p=[0,e[1]],d=[0,e[0]],f=[[e[0],0],[0,0]],g=[],y=d[1]/(r.count()-1),v=0,m=Math.round(r.count()/e[0]);r.each([a],(function(t,e){if(m>0&&e%m)v+=y;else{var n=null==t||isNaN(t)||""===t,i=n?0:Yr(t,u,p,!0);n&&!c&&e?(f.push([f[f.length-1][0],0]),g.push([g[g.length-1][0],0])):!n&&c&&(f.push([v,0]),g.push([v,0])),f.push([v,i]),g.push([v,i]),v+=y,c=n}})),s=this._shadowPolygonPts=f,l=this._shadowPolylinePts=g}this._shadowData=r,this._shadowDim=a,this._shadowSize=[e[0],e[1]];for(var x=this.dataZoomModel,_=0;_<3;_++){var b=w(1===_);this._displayables.sliderGroup.add(b),this._displayables.dataShadowSegs.push(b)}}}function w(t){var e=x.getModel(t?"selectedDataBackground":"dataBackground"),n=new Er,i=new Gu({shape:{points:s},segmentIgnoreThreshold:1,style:e.getModel("areaStyle").getAreaStyle(),silent:!0,z2:-20}),r=new Hu({shape:{points:l},segmentIgnoreThreshold:1,style:e.getModel("lineStyle").getLineStyle(),silent:!0,z2:-19});return n.add(i),n.add(r),n}},e.prototype._prepareDataShadowInfo=function(){var t=this.dataZoomModel,e=t.get("showDataShadow");if(!1!==e){var n,i=this.ecModel;return t.eachTargetAxis((function(r,o){E(t.getAxisProxy(r,o).getTargetSeriesModels(),(function(t){if(!(n||!0!==e&&P(aF,t.get("type"))<0)){var a,s=i.getComponent(zE(r),o).axis,l={x:"y",y:"x",radius:"angle",angle:"radius"}[r],u=t.coordinateSystem;null!=l&&u.getOtherAxis&&(a=u.getOtherAxis(s).inverse),l=t.getData().mapDimension(l),n={thisAxis:s,series:t,thisDim:r,otherDim:l,otherAxisInverse:a}}}),this)}),this),n}},e.prototype._renderHandle=function(){var t=this.group,e=this._displayables,n=e.handles=[null,null],i=e.handleLabels=[null,null],r=this._displayables.sliderGroup,o=this._size,a=this.dataZoomModel,s=this.api,l=a.get("borderRadius")||0,u=a.get("brushSelect"),h=e.filler=new iF({silent:u,style:{fill:a.get("fillerColor")},textConfig:{position:"inside"}});r.add(h),r.add(new iF({silent:!0,subPixelOptimize:!0,shape:{x:0,y:0,width:o[0],height:o[1],r:l},style:{stroke:a.get("dataBackgroundColor")||a.get("borderColor"),lineWidth:1,fill:"rgba(0,0,0,0)"}})),E([0,1],(function(e){var o=a.get("handleIcon");!Ny[o]&&o.indexOf("path://")<0&&o.indexOf("image://")<0&&(o="path://"+o);var s=Vy(o,-1,0,2,2,null,!0);s.attr({cursor:uF(this._orient),draggable:!0,drift:W(this._onDragMove,this,e),ondragend:W(this._onDragEnd,this),onmouseover:W(this._showDataInfo,this,!0),onmouseout:W(this._showDataInfo,this,!1),z2:5});var l=s.getBoundingRect(),u=a.get("handleSize");this._handleHeight=Ur(u,this._size[1]),this._handleWidth=l.width/l.height*this._handleHeight,s.setStyle(a.getModel("handleStyle").getItemStyle()),s.style.strokeNoScale=!0,s.rectHover=!0,s.ensureState("emphasis").style=a.getModel(["emphasis","handleStyle"]).getItemStyle(),Wl(s);var h=a.get("handleColor");null!=h&&(s.style.fill=h),r.add(n[e]=s);var c=a.getModel("textStyle");t.add(i[e]=new Bs({silent:!0,invisible:!0,style:ec(c,{x:0,y:0,text:"",verticalAlign:"middle",align:"center",fill:c.getTextColor(),font:c.getFont()}),z2:10}))}),this);var c=h;if(u){var p=Ur(a.get("moveHandleSize"),o[1]),d=e.moveHandle=new Es({style:a.getModel("moveHandleStyle").getItemStyle(),silent:!0,shape:{r:[0,0,2,2],y:o[1]-.5,height:p}}),f=.8*p,g=e.moveHandleIcon=Vy(a.get("moveHandleIcon"),-f/2,-f/2,f,f,"#fff",!0);g.silent=!0,g.y=o[1]+p/2-.5,d.ensureState("emphasis").style=a.getModel(["emphasis","moveHandleStyle"]).getItemStyle();var y=Math.min(o[1]/2,Math.max(p,10));(c=e.moveZone=new Es({invisible:!0,shape:{y:o[1]-y,height:p+y}})).on("mouseover",(function(){s.enterEmphasis(d)})).on("mouseout",(function(){s.leaveEmphasis(d)})),r.add(d),r.add(g),r.add(c)}c.attr({draggable:!0,cursor:uF(this._orient),drift:W(this._onDragMove,this,"all"),ondragstart:W(this._showDataInfo,this,!0),ondragend:W(this._onDragEnd,this),onmouseover:W(this._showDataInfo,this,!0),onmouseout:W(this._showDataInfo,this,!1)})},e.prototype._resetInterval=function(){var t=this._range=this.dataZoomModel.getPercentRange(),e=this._getViewExtent();this._handleEnds=[Yr(t[0],[0,100],e,!0),Yr(t[1],[0,100],e,!0)]},e.prototype._updateInterval=function(t,e){var n=this.dataZoomModel,i=this._handleEnds,r=this._getViewExtent(),o=n.findRepresentativeAxisProxy().getMinMaxSpan(),a=[0,100];xk(e,i,r,n.get("zoomLock")?"all":t,null!=o.minSpan?Yr(o.minSpan,a,r,!0):null,null!=o.maxSpan?Yr(o.maxSpan,a,r,!0):null);var s=this._range,l=this._range=Zr([Yr(i[0],r,a,!0),Yr(i[1],r,a,!0)]);return!s||s[0]!==l[0]||s[1]!==l[1]},e.prototype._updateView=function(t){var e=this._displayables,n=this._handleEnds,i=Zr(n.slice()),r=this._size;E([0,1],(function(t){var i=e.handles[t],o=this._handleHeight;i.attr({scaleX:o/2,scaleY:o/2,x:n[t]+(t?-1:1),y:r[1]/2-o/2})}),this),e.filler.setShape({x:i[0],y:0,width:i[1]-i[0],height:r[1]});var o={x:i[0],width:i[1]-i[0]};e.moveHandle&&(e.moveHandle.setShape(o),e.moveZone.setShape(o),e.moveZone.getBoundingRect(),e.moveHandleIcon&&e.moveHandleIcon.attr("x",o.x+o.width/2));for(var a=e.dataShadowSegs,s=[0,i[0],i[1],r[0]],l=0;le[0]||n[1]<0||n[1]>e[1])){var i=this._handleEnds,r=(i[0]+i[1])/2,o=this._updateInterval("all",n[0]-r);this._updateView(),o&&this._dispatchZoomAction(!1)}},e.prototype._onBrushStart=function(t){var e=t.offsetX,n=t.offsetY;this._brushStart=new Ce(e,n),this._brushing=!0,this._brushStartTime=+new Date},e.prototype._onBrushEnd=function(t){if(this._brushing){var e=this._displayables.brushRect;if(this._brushing=!1,e){e.attr("ignore",!0);var n=e.shape;if(!(+new Date-this._brushStartTime<200&&Math.abs(n.width)<5)){var i=this._getViewExtent(),r=[0,100];this._range=Zr([Yr(n.x,i,r,!0),Yr(n.x+n.width,i,r,!0)]),this._handleEnds=[n.x,n.x+n.width],this._updateView(),this._dispatchZoomAction(!1)}}}},e.prototype._onBrush=function(t){this._brushing&&(pe(t.event),this._updateBrushRect(t.offsetX,t.offsetY))},e.prototype._updateBrushRect=function(t,e){var n=this._displayables,i=this.dataZoomModel,r=n.brushRect;r||(r=n.brushRect=new iF({silent:!0,style:i.getModel("brushStyle").getItemStyle()}),n.sliderGroup.add(r)),r.attr("ignore",!1);var o=this._brushStart,a=this._displayables.sliderGroup,s=a.transformCoordToLocal(t,e),l=a.transformCoordToLocal(o.x,o.y),u=this._size;s[0]=Math.max(Math.min(u[0],s[0]),0),r.setShape({x:l[0],y:0,width:s[0]-l[0],height:u[1]})},e.prototype._dispatchZoomAction=function(t){var e=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,animation:t?sF:null,start:e[0],end:e[1]})},e.prototype._findCoordRect=function(){var t,e=BE(this.dataZoomModel).infoList;if(!t&&e.length){var n=e[0].model.coordinateSystem;t=n.getRect&&n.getRect()}if(!t){var i=this.api.getWidth(),r=this.api.getHeight();t={x:.2*i,y:.2*r,width:.6*i,height:.6*r}}return t},e.type="dataZoom.slider",e}(YE);function uF(t){return"vertical"===t?"ns-resize":"ew-resize"}function hF(t){t.registerComponentModel(nF),t.registerComponentView(lF),$E(t)}var cF=function(t,e,n){var i=T((pF[t]||{})[e]);return n&&Y(i)?i[i.length-1]:i},pF={color:{active:["#006edd","#e0ffff"],inactive:["rgba(0,0,0,0)"]},colorHue:{active:[0,360],inactive:[0,0]},colorSaturation:{active:[.3,1],inactive:[0,0]},colorLightness:{active:[.9,.5],inactive:[0,0]},colorAlpha:{active:[.3,1],inactive:[0,0]},opacity:{active:[.3,1],inactive:[0,0]},symbol:{active:["circle","roundRect","diamond"],inactive:["none"]},symbolSize:{active:[10,50],inactive:[0,0]}},dF=dD.mapVisual,fF=dD.eachVisual,gF=Y,yF=E,vF=Zr,mF=Yr,xF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.stateList=["inRange","outOfRange"],n.replacableOptionKeys=["inRange","outOfRange","target","controller","color"],n.layoutMode={type:"box",ignoreSize:!0},n.dataBound=[-1/0,1/0],n.targetVisuals={},n.controllerVisuals={},n}return n(e,t),e.prototype.init=function(t,e,n){this.mergeDefaultAndTheme(t,n)},e.prototype.optionUpdated=function(t,e){var n=this.option;!e&&dV(n,t,this.replacableOptionKeys),this.textStyleModel=this.getModel("textStyle"),this.resetItemSize(),this.completeVisualOption()},e.prototype.resetVisual=function(t){var e=this.stateList;t=W(t,this),this.controllerVisuals=pV(this.option.controller,e,t),this.targetVisuals=pV(this.option.target,e,t)},e.prototype.getItemSymbol=function(){return null},e.prototype.getTargetSeriesIndices=function(){var t=this.option.seriesIndex,e=[];return null==t||"all"===t?this.ecModel.eachSeries((function(t,n){e.push(n)})):e=_o(t),e},e.prototype.eachTargetSeries=function(t,e){E(this.getTargetSeriesIndices(),(function(n){var i=this.ecModel.getSeriesByIndex(n);i&&t.call(e,i)}),this)},e.prototype.isTargetSeries=function(t){var e=!1;return this.eachTargetSeries((function(n){n===t&&(e=!0)})),e},e.prototype.formatValueText=function(t,e,n){var i,r=this.option,o=r.precision,a=this.dataBound,s=r.formatter;n=n||["<",">"],Y(t)&&(t=t.slice(),i=!0);var l=e?t:i?[u(t[0]),u(t[1])]:u(t);return X(s)?s.replace("{value}",i?l[0]:l).replace("{value2}",i?l[1]:l):U(s)?i?s(t[0],t[1]):s(t):i?t[0]===a[0]?n[0]+" "+l[1]:t[1]===a[1]?n[1]+" "+l[0]:l[0]+" - "+l[1]:l;function u(t){return t===a[0]?"min":t===a[1]?"max":(+t).toFixed(Math.min(o,20))}},e.prototype.resetExtent=function(){var t=this.option,e=vF([t.min,t.max]);this._dataExtent=e},e.prototype.getDataDimensionIndex=function(t){var e=this.option.dimension;if(null!=e)return t.getDimensionIndex(e);for(var n=t.dimensions,i=n.length-1;i>=0;i--){var r=n[i],o=t.getDimensionInfo(r);if(!o.isCalculationCoord)return o.storeDimIndex}},e.prototype.getExtent=function(){return this._dataExtent.slice()},e.prototype.completeVisualOption=function(){var t=this.ecModel,e=this.option,n={inRange:e.inRange,outOfRange:e.outOfRange},i=e.target||(e.target={}),r=e.controller||(e.controller={});C(i,n),C(r,n);var o=this.isCategory();function a(n){gF(e.color)&&!n.inRange&&(n.inRange={color:e.color.slice().reverse()}),n.inRange=n.inRange||{color:t.get("gradientColor")}}a.call(this,i),a.call(this,r),function(t,e,n){var i=t[e],r=t[n];i&&!r&&(r=t[n]={},yF(i,(function(t,e){if(dD.isValidType(e)){var n=cF(e,"inactive",o);null!=n&&(r[e]=n,"color"!==e||r.hasOwnProperty("opacity")||r.hasOwnProperty("colorAlpha")||(r.opacity=[0,0]))}})))}.call(this,i,"inRange","outOfRange"),function(t){var e=(t.inRange||{}).symbol||(t.outOfRange||{}).symbol,n=(t.inRange||{}).symbolSize||(t.outOfRange||{}).symbolSize,i=this.get("inactiveColor"),r=this.getItemSymbol()||"roundRect";yF(this.stateList,(function(a){var s=this.itemSize,l=t[a];l||(l=t[a]={color:o?i:[i]}),null==l.symbol&&(l.symbol=e&&T(e)||(o?r:[r])),null==l.symbolSize&&(l.symbolSize=n&&T(n)||(o?s[0]:[s[0],s[0]])),l.symbol=dF(l.symbol,(function(t){return"none"===t?r:t}));var u=l.symbolSize;if(null!=u){var h=-1/0;fF(u,(function(t){t>h&&(h=t)})),l.symbolSize=dF(u,(function(t){return mF(t,[0,h],[0,s[0]],!0)}))}}),this)}.call(this,r)},e.prototype.resetItemSize=function(){this.itemSize=[parseFloat(this.get("itemWidth")),parseFloat(this.get("itemHeight"))]},e.prototype.isCategory=function(){return!!this.option.categories},e.prototype.setSelected=function(t){},e.prototype.getSelected=function(){return null},e.prototype.getValueState=function(t){return null},e.prototype.getVisualMeta=function(t){return null},e.type="visualMap",e.dependencies=["series"],e.defaultOption={show:!0,z:4,seriesIndex:"all",min:0,max:200,left:0,right:null,top:null,bottom:0,itemWidth:null,itemHeight:null,inverse:!1,orient:"vertical",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",contentColor:"#5793f3",inactiveColor:"#aaa",borderWidth:0,padding:5,textGap:10,precision:0,textStyle:{color:"#333"}},e}(Op),_F=[20,140],bF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.optionUpdated=function(e,n){t.prototype.optionUpdated.apply(this,arguments),this.resetExtent(),this.resetVisual((function(t){t.mappingMethod="linear",t.dataExtent=this.getExtent()})),this._resetRange()},e.prototype.resetItemSize=function(){t.prototype.resetItemSize.apply(this,arguments);var e=this.itemSize;(null==e[0]||isNaN(e[0]))&&(e[0]=_F[0]),(null==e[1]||isNaN(e[1]))&&(e[1]=_F[1])},e.prototype._resetRange=function(){var t=this.getExtent(),e=this.option.range;!e||e.auto?(t.auto=1,this.option.range=t):Y(e)&&(e[0]>e[1]&&e.reverse(),e[0]=Math.max(e[0],t[0]),e[1]=Math.min(e[1],t[1]))},e.prototype.completeVisualOption=function(){t.prototype.completeVisualOption.apply(this,arguments),E(this.stateList,(function(t){var e=this.option.controller[t].symbolSize;e&&e[0]!==e[1]&&(e[0]=e[1]/3)}),this)},e.prototype.setSelected=function(t){this.option.range=t.slice(),this._resetRange()},e.prototype.getSelected=function(){var t=this.getExtent(),e=Zr((this.get("range")||[]).slice());return e[0]>t[1]&&(e[0]=t[1]),e[1]>t[1]&&(e[1]=t[1]),e[0]=n[1]||t<=e[1])?"inRange":"outOfRange"},e.prototype.findTargetDataIndices=function(t){var e=[];return this.eachTargetSeries((function(n){var i=[],r=n.getData();r.each(this.getDataDimensionIndex(r),(function(e,n){t[0]<=e&&e<=t[1]&&i.push(n)}),this),e.push({seriesId:n.id,dataIndex:i})}),this),e},e.prototype.getVisualMeta=function(t){var e=wF(this,"outOfRange",this.getExtent()),n=wF(this,"inRange",this.option.range.slice()),i=[];function r(e,n){i.push({value:e,color:t(e,n)})}for(var o=0,a=0,s=n.length,l=e.length;at[1])break;n.push({color:this.getControllerVisual(o,"color",e),offset:r/100})}return n.push({color:this.getControllerVisual(t[1],"color",e),offset:1}),n},e.prototype._createBarPoints=function(t,e){var n=this.visualMapModel.itemSize;return[[n[0]-e[0],t[0]],[n[0],t[0]],[n[0],t[1]],[n[0]-e[1],t[1]]]},e.prototype._createBarGroup=function(t){var e=this._orient,n=this.visualMapModel.get("inverse");return new Er("horizontal"!==e||n?"horizontal"===e&&n?{scaleX:"bottom"===t?-1:1,rotation:-Math.PI/2}:"vertical"!==e||n?{scaleX:"left"===t?1:-1}:{scaleX:"left"===t?1:-1,scaleY:-1}:{scaleX:"bottom"===t?1:-1,rotation:Math.PI/2})},e.prototype._updateHandle=function(t,e){if(this._useHandle){var n=this._shapes,i=this.visualMapModel,r=n.handleThumbs,o=n.handleLabels,a=i.itemSize,s=i.getExtent();DF([0,1],(function(l){var u=r[l];u.setStyle("fill",e.handlesColor[l]),u.y=t[l];var h=CF(t[l],[0,a[1]],s,!0),c=this.getControllerVisual(h,"symbolSize");u.scaleX=u.scaleY=c/a[0],u.x=a[0]-c/2;var p=Eh(n.handleLabelPoints[l],Nh(u,this.group));o[l].setStyle({x:p[0],y:p[1],text:i.formatValueText(this._dataInterval[l]),verticalAlign:"middle",align:"vertical"===this._orient?this._applyTransform("left",n.mainGroup):"center"})}),this)}},e.prototype._showIndicator=function(t,e,n,i){var r=this.visualMapModel,o=r.getExtent(),a=r.itemSize,s=[0,a[1]],l=this._shapes,u=l.indicator;if(u){u.attr("invisible",!1);var h=this.getControllerVisual(t,"color",{convertOpacityToAlpha:!0}),c=this.getControllerVisual(t,"symbolSize"),p=CF(t,o,s,!0),d=a[0]-c/2,f={x:u.x,y:u.y};u.y=p,u.x=d;var g=Eh(l.indicatorLabelPoint,Nh(u,this.group)),y=l.indicatorLabel;y.attr("invisible",!1);var v=this._applyTransform("left",l.mainGroup),m="horizontal"===this._orient;y.setStyle({text:(n||"")+r.formatValueText(e),verticalAlign:m?v:"middle",align:m?"center":v});var x={x:d,y:p,style:{fill:h}},_={style:{x:g[0],y:g[1]}};if(r.ecModel.isAnimationEnabled()&&!this._firstShowIndicator){var b={duration:100,easing:"cubicInOut",additive:!0};u.x=f.x,u.y=f.y,u.animateTo(x,b),y.animateTo(_,b)}else u.attr(x),y.attr(_);this._firstShowIndicator=!1;var w=this._shapes.handleLabels;if(w)for(var S=0;Sr[1]&&(u[1]=1/0),e&&(u[0]===-1/0?this._showIndicator(l,u[1],"< ",a):u[1]===1/0?this._showIndicator(l,u[0],"> ",a):this._showIndicator(l,l,"≈ ",a));var h=this._hoverLinkDataIndices,c=[];(e||OF(n))&&(c=this._hoverLinkDataIndices=n.findTargetDataIndices(u));var p=function(t,e){var n={},i={};return r(t||[],n),r(e||[],i,n),[o(n),o(i)];function r(t,e,n){for(var i=0,r=t.length;i=0&&(r.dimension=o,i.push(r))}})),t.getData().setVisual("visualMeta",i)}}];function VF(t,e,n,i){for(var r=e.targetVisuals[i],o=dD.prepareVisualTypes(r),a={color:wy(t.getData(),"color")},s=0,l=o.length;s0:t.splitNumber>0)&&!t.calculable?"piecewise":"continuous"})),t.registerAction(NF,EF),E(zF,(function(e){t.registerVisual(t.PRIORITY.VISUAL.COMPONENT,e)})),t.registerPreprocessor(FF))}function YF(t){t.registerComponentModel(bF),t.registerComponentView(LF),HF(t)}var UF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._pieceList=[],n}return n(e,t),e.prototype.optionUpdated=function(e,n){t.prototype.optionUpdated.apply(this,arguments),this.resetExtent();var i=this._mode=this._determineMode();this._pieceList=[],XF[this._mode].call(this,this._pieceList),this._resetSelected(e,n);var r=this.option.categories;this.resetVisual((function(t,e){"categories"===i?(t.mappingMethod="category",t.categories=T(r)):(t.dataExtent=this.getExtent(),t.mappingMethod="piecewise",t.pieceList=z(this._pieceList,(function(t){return t=T(t),"inRange"!==e&&(t.visual=null),t})))}))},e.prototype.completeVisualOption=function(){var e=this.option,n={},i=dD.listVisualTypes(),r=this.isCategory();function o(t,e,n){return t&&t[e]&&t[e].hasOwnProperty(n)}E(e.pieces,(function(t){E(i,(function(e){t.hasOwnProperty(e)&&(n[e]=1)}))})),E(n,(function(t,n){var i=!1;E(this.stateList,(function(t){i=i||o(e,t,n)||o(e.target,t,n)}),this),!i&&E(this.stateList,(function(t){(e[t]||(e[t]={}))[n]=cF(n,"inRange"===t?"active":"inactive",r)}))}),this),t.prototype.completeVisualOption.apply(this,arguments)},e.prototype._resetSelected=function(t,e){var n=this.option,i=this._pieceList,r=(e?n:t).selected||{};if(n.selected=r,E(i,(function(t,e){var n=this.getSelectedMapKey(t);r.hasOwnProperty(n)||(r[n]=!0)}),this),"single"===n.selectedMode){var o=!1;E(i,(function(t,e){var n=this.getSelectedMapKey(t);r[n]&&(o?r[n]=!1:o=!0)}),this)}},e.prototype.getItemSymbol=function(){return this.get("itemSymbol")},e.prototype.getSelectedMapKey=function(t){return"categories"===this._mode?t.value+"":t.index+""},e.prototype.getPieceList=function(){return this._pieceList},e.prototype._determineMode=function(){var t=this.option;return t.pieces&&t.pieces.length>0?"pieces":this.option.categories?"categories":"splitNumber"},e.prototype.setSelected=function(t){this.option.selected=T(t)},e.prototype.getValueState=function(t){var e=dD.findPieceIndex(t,this._pieceList);return null!=e&&this.option.selected[this.getSelectedMapKey(this._pieceList[e])]?"inRange":"outOfRange"},e.prototype.findTargetDataIndices=function(t){var e=[],n=this._pieceList;return this.eachTargetSeries((function(i){var r=[],o=i.getData();o.each(this.getDataDimensionIndex(o),(function(e,i){dD.findPieceIndex(e,n)===t&&r.push(i)}),this),e.push({seriesId:i.id,dataIndex:r})}),this),e},e.prototype.getRepresentValue=function(t){var e;if(this.isCategory())e=t.value;else if(null!=t.value)e=t.value;else{var n=t.interval||[];e=n[0]===-1/0&&n[1]===1/0?0:(n[0]+n[1])/2}return e},e.prototype.getVisualMeta=function(t){if(!this.isCategory()){var e=[],n=["",""],i=this,r=this._pieceList.slice();if(r.length){var o=r[0].interval[0];o!==-1/0&&r.unshift({interval:[-1/0,o]}),(o=r[r.length-1].interval[1])!==1/0&&r.push({interval:[o,1/0]})}else r.push({interval:[-1/0,1/0]});var a=-1/0;return E(r,(function(t){var e=t.interval;e&&(e[0]>a&&s([a,e[0]],"outOfRange"),s(e.slice()),a=e[1])}),this),{stops:e,outerColors:n}}function s(r,o){var a=i.getRepresentValue({interval:r});o||(o=i.getValueState(a));var s=t(a,o);r[0]===-1/0?n[0]=s:r[1]===1/0?n[1]=s:e.push({value:r[0],color:s},{value:r[1],color:s})}},e.type="visualMap.piecewise",e.defaultOption=Tc(xF.defaultOption,{selected:null,minOpen:!1,maxOpen:!1,align:"auto",itemWidth:20,itemHeight:14,itemSymbol:"roundRect",pieces:null,categories:null,splitNumber:5,selectedMode:"multiple",itemGap:10,hoverLink:!0}),e}(xF),XF={splitNumber:function(t){var e=this.option,n=Math.min(e.precision,20),i=this.getExtent(),r=e.splitNumber;r=Math.max(parseInt(r,10),1),e.splitNumber=r;for(var o=(i[1]-i[0])/r;+o.toFixed(n)!==o&&n<5;)n++;e.precision=n,o=+o.toFixed(n),e.minOpen&&t.push({interval:[-1/0,i[0]],close:[0,0]});for(var a=0,s=i[0];a","≥"][e[0]]];t.text=t.text||this.formatValueText(null!=t.value?t.value:t.interval,!1,n)}),this)}};function ZF(t,e){var n=t.inverse;("vertical"===t.orient?!n:n)&&e.reverse()}var jF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.doRender=function(){var t=this.group;t.removeAll();var e=this.visualMapModel,n=e.get("textGap"),i=e.textStyleModel,r=i.getFont(),o=i.getTextColor(),a=this._getItemAlign(),s=e.itemSize,l=this._getViewData(),u=l.endsText,h=it(e.get("showLabel",!0),!u);u&&this._renderEndsText(t,u[0],s,h,a),E(l.viewPieceList,(function(i){var l=i.piece,u=new Er;u.onclick=W(this._onItemClick,this,l),this._enableHoverLink(u,i.indexInModelPieceList);var c=e.getRepresentValue(l);if(this._createItemSymbol(u,c,[0,0,s[0],s[1]]),h){var p=this.visualMapModel.getValueState(c);u.add(new Bs({style:{x:"right"===a?-n:s[0]+n,y:s[1]/2,text:l.text,verticalAlign:"middle",align:a,font:r,fill:o,opacity:"outOfRange"===p?.5:1}}))}t.add(u)}),this),u&&this._renderEndsText(t,u[1],s,h,a),Ip(e.get("orient"),t,e.get("itemGap")),this.renderBackground(t),this.positionGroup(t)},e.prototype._enableHoverLink=function(t,e){var n=this;t.on("mouseover",(function(){return i("highlight")})).on("mouseout",(function(){return i("downplay")}));var i=function(t){var i=n.visualMapModel;i.option.hoverLink&&n.api.dispatchAction({type:t,batch:TF(i.findTargetDataIndices(e),i)})}},e.prototype._getItemAlign=function(){var t=this.visualMapModel,e=t.option;if("vertical"===e.orient)return IF(t,this.api,t.itemSize);var n=e.align;return n&&"auto"!==n||(n="left"),n},e.prototype._renderEndsText=function(t,e,n,i,r){if(e){var o=new Er,a=this.visualMapModel.textStyleModel;o.add(new Bs({style:ec(a,{x:i?"right"===r?n[0]:0:n[0]/2,y:n[1]/2,verticalAlign:"middle",align:i?r:"center",text:e})})),t.add(o)}},e.prototype._getViewData=function(){var t=this.visualMapModel,e=z(t.getPieceList(),(function(t,e){return{piece:t,indexInModelPieceList:e}})),n=t.get("text"),i=t.get("orient"),r=t.get("inverse");return("horizontal"===i?r:!r)?e.reverse():n&&(n=n.slice().reverse()),{viewPieceList:e,endsText:n}},e.prototype._createItemSymbol=function(t,e,n){t.add(Vy(this.getControllerVisual(e,"symbol"),n[0],n[1],n[2],n[3],this.getControllerVisual(e,"color")))},e.prototype._onItemClick=function(t){var e=this.visualMapModel,n=e.option,i=n.selectedMode;if(i){var r=T(n.selected),o=e.getSelectedMapKey(t);"single"===i||!0===i?(r[o]=!0,E(r,(function(t,e){r[e]=e===o}))):r[o]=!r[o],this.api.dispatchAction({type:"selectDataRange",from:this.uid,visualMapId:this.visualMapModel.id,selected:r})}},e.type="visualMap.piecewise",e}(SF);function qF(t){t.registerComponentModel(UF),t.registerComponentView(jF),HF(t)}var KF={label:{enabled:!0},decal:{show:!1}},$F=Po(),JF={};function QF(t,e){var n=t.getModel("aria");if(n.get("enabled")){var i=T(KF);C(i.label,t.getLocaleModel().get("aria"),!1),C(n.option,i,!1),function(){if(n.getModel("decal").get("show")){var e=yt();t.eachSeries((function(t){if(!t.isColorBySeries()){var n=e.get(t.type);n||(n={},e.set(t.type,n)),$F(t).scope=n}})),t.eachRawSeries((function(e){if(!t.isSeriesFiltered(e))if(U(e.enableAriaDecal))e.enableAriaDecal();else{var n=e.getData();if(e.isColorBySeries()){var i=ld(e.ecModel,e.name,JF,t.getSeriesCount()),r=n.getVisual("decal");n.setVisual("decal",u(r,i))}else{var o=e.getRawData(),a={},s=$F(e).scope;n.each((function(t){var e=n.getRawIndex(t);a[e]=t}));var l=o.count();o.each((function(t){var i=a[t],r=o.getName(t)||t+"",h=ld(e.ecModel,r,s,l),c=n.getItemVisual(i,"decal");n.setItemVisual(i,"decal",u(c,h))}))}}function u(t,e){var n=t?A(A({},e),t):e;return n.dirty=!0,n}}))}}(),function(){var i=t.getLocaleModel().get("aria"),o=n.getModel("label");if(o.option=k(o.option,i),!o.get("enabled"))return;var a=e.getZr().dom;if(o.get("description"))return void a.setAttribute("aria-label",o.get("description"));var s,l=t.getSeriesCount(),u=o.get(["data","maxCount"])||10,h=o.get(["series","maxCount"])||10,c=Math.min(l,h);if(l<1)return;var p=function(){var e=t.get("title");e&&e.length&&(e=e[0]);return e&&e.text}();if(p){var d=o.get(["general","withTitle"]);s=r(d,{title:p})}else s=o.get(["general","withoutTitle"]);var f=[],g=l>1?o.get(["series","multiple","prefix"]):o.get(["series","single","prefix"]);s+=r(g,{seriesCount:l}),t.eachSeries((function(e,n){if(n1?o.get(["series","multiple",a]):o.get(["series","single",a]),{seriesId:e.seriesIndex,seriesName:e.get("name"),seriesType:(x=e.subType,t.getLocaleModel().get(["series","typeNames"])[x]||"自定义图")});var s=e.getData();if(s.count()>u)i+=r(o.get(["data","partialData"]),{displayCnt:u});else i+=o.get(["data","allData"]);for(var h=o.get(["data","separator","middle"]),p=o.get(["data","separator","end"]),d=[],g=0;g":"gt",">=":"gte","=":"eq","!=":"ne","<>":"ne"},nG=function(){function t(t){if(null==(this._condVal=X(t)?new RegExp(t):et(t)?t:null)){var e="";0,yo(e)}}return t.prototype.evaluate=function(t){var e=typeof t;return X(e)?this._condVal.test(t):!!j(e)&&this._condVal.test(t+"")},t}(),iG=function(){function t(){}return t.prototype.evaluate=function(){return this.value},t}(),rG=function(){function t(){}return t.prototype.evaluate=function(){for(var t=this.children,e=0;e2&&l.push(e),e=[t,n]}function f(t,n,i,r){vG(t,i)&&vG(n,r)||e.push(t,n,i,r,i,r)}function g(t,n,i,r,o,a){var s=Math.abs(n-t),l=4*Math.tan(s/4)/3,u=nM:C2&&l.push(e),l}function xG(t,e,n,i,r,o,a,s,l,u){if(vG(t,n)&&vG(e,i)&&vG(r,a)&&vG(o,s))l.push(a,s);else{var h=2/u,c=h*h,p=a-t,d=s-e,f=Math.sqrt(p*p+d*d);p/=f,d/=f;var g=n-t,y=i-e,v=r-a,m=o-s,x=g*g+y*y,_=v*v+m*m;if(x=0&&_-w*w=0)l.push(a,s);else{var S=[],M=[];bn(t,n,r,a,.5,S),bn(e,i,o,s,.5,M),xG(S[0],M[0],S[1],M[1],S[2],M[2],S[3],M[3],l,u),xG(S[4],M[4],S[5],M[5],S[6],M[6],S[7],M[7],l,u)}}}}function _G(t,e,n){var i=t[e],r=t[1-e],o=Math.abs(i/r),a=Math.ceil(Math.sqrt(o*n)),s=Math.floor(n/a);0===s&&(s=1,a=n);for(var l=[],u=0;u0)for(u=0;uMath.abs(u),c=_G([l,u],h?0:1,e),p=(h?s:u)/c.length,d=0;d1?null:new Ce(d*l+t,d*u+e)}function MG(t,e,n){var i=new Ce;Ce.sub(i,n,e),i.normalize();var r=new Ce;return Ce.sub(r,t,e),r.dot(i)}function IG(t,e){var n=t[t.length-1];n&&n[0]===e[0]&&n[1]===e[1]||t.push(e)}function TG(t){var e=t.points,n=[],i=[];Oa(e,n,i);var r=new Ee(n[0],n[1],i[0]-n[0],i[1]-n[1]),o=r.width,a=r.height,s=r.x,l=r.y,u=new Ce,h=new Ce;return o>a?(u.x=h.x=s+o/2,u.y=l,h.y=l+a):(u.y=h.y=l+a/2,u.x=s,h.x=s+o),function(t,e,n){for(var i=t.length,r=[],o=0;or,a=_G([i,r],o?0:1,e),s=o?"width":"height",l=o?"height":"width",u=o?"x":"y",h=o?"y":"x",c=t[s]/a.length,p=0;p0)for(var b=i/n,w=-i/2;w<=i/2;w+=b){var S=Math.sin(w),M=Math.cos(w),I=0;for(x=0;x0;l/=2){var u=0,h=0;(t&l)>0&&(u=1),(e&l)>0&&(h=1),s+=l*l*(3*u^h),0===h&&(1===u&&(t=l-1-t,e=l-1-e),a=t,t=e,e=a)}return s}function HG(t){var e=1/0,n=1/0,i=-1/0,r=-1/0,o=z(t,(function(t){var o=t.getBoundingRect(),a=t.getComputedTransform(),s=o.x+o.width/2+(a?a[4]:0),l=o.y+o.height/2+(a?a[5]:0);return e=Math.min(s,e),n=Math.min(l,n),i=Math.max(s,i),r=Math.max(l,r),[s,l]}));return z(o,(function(o,a){return{cp:o,z:WG(o[0],o[1],e,n,i,r),path:t[a]}})).sort((function(t,e){return t.z-e.z})).map((function(t){return t.path}))}function YG(t){return AG(t.path,t.count)}function UG(t){return Y(t[0])}function XG(t,e){for(var n=[],i=t.length,r=0;r=0;r--)if(!n[r].many.length){var l=n[s].many;if(l.length<=1){if(!s)return n;s=0}o=l.length;var u=Math.ceil(o/2);n[r].many=l.slice(u,o),n[s].many=l.slice(0,u),s++}return n}var ZG={clone:function(t){for(var e=[],n=1-Math.pow(1-t.path.style.opacity,1/t.count),i=0;i0){var s,l,u=i.getModel("universalTransition").get("delay"),h=Object.assign({setToFinal:!0},a);UG(t)&&(s=t,l=e),UG(e)&&(s=e,l=t);for(var c=s?s===t:t.length>e.length,p=s?XG(l,s):XG(c?e:t,[c?t:e]),d=0,f=0;f1e4))for(var i=n.getIndices(),r=function(t){for(var e=t.dimensions,n=0;n0&&i.group.traverse((function(t){t instanceof Ms&&!t.animators.length&&t.animateFrom({style:{opacity:0}},r)}))}))}function iW(t){var e=t.getModel("universalTransition").get("seriesKey");return e||t.id}function rW(t){return Y(t)?t.sort().join(","):t}function oW(t){if(t.hostModel)return t.hostModel.getModel("universalTransition").get("divideShape")}function aW(t,e){for(var n=0;n=0&&r.push({dataGroupId:e.oldDataGroupIds[n],data:e.oldData[n],divide:oW(e.oldData[n]),dim:t.dimension})})),E(_o(t.to),(function(t){var i=aW(n.updatedSeries,t);if(i>=0){var r=n.updatedSeries[i].getData();o.push({dataGroupId:e.oldDataGroupIds[i],data:r,divide:oW(r),dim:t.dimension})}})),r.length>0&&o.length>0&&nW(r,o,i)}(t,i,n,e)}));else{var o=function(t,e){var n=yt(),i=yt(),r=yt();return E(t.oldSeries,(function(e,n){var o=t.oldDataGroupIds[n],a=t.oldData[n],s=iW(e),l=rW(s);i.set(l,{dataGroupId:o,data:a}),Y(s)&&E(s,(function(t){r.set(t,{key:l,dataGroupId:o,data:a})}))})),E(e.updatedSeries,(function(t){if(t.isUniversalTransitionEnabled()&&t.isAnimationEnabled()){var e=t.get("dataGroupId"),o=t.getData(),a=iW(t),s=rW(a),l=i.get(s);if(l)n.set(s,{oldSeries:[{dataGroupId:l.dataGroupId,divide:oW(l.data),data:l.data}],newSeries:[{dataGroupId:e,divide:oW(o),data:o}]});else if(Y(a)){var u=[];E(a,(function(t){var e=i.get(t);e.data&&u.push({dataGroupId:e.dataGroupId,divide:oW(e.data),data:e.data})})),u.length&&n.set(s,{oldSeries:u,newSeries:[{dataGroupId:e,data:o,divide:oW(o)}]})}else{var h=r.get(a);if(h){var c=n.get(h.key);c||(c={oldSeries:[{dataGroupId:h.dataGroupId,data:h.data,divide:oW(h.data)}],newSeries:[]},n.set(h.key,c)),c.newSeries.push({dataGroupId:e,data:o,divide:oW(o)})}}}})),n}(i,n);E(o.keys(),(function(t){var n=o.get(t);nW(n.oldSeries,n.newSeries,e)}))}E(n.updatedSeries,(function(t){t.__universalTransitionEnabled&&(t.__universalTransitionEnabled=!1)}))}for(var a=t.getSeries(),s=i.oldSeries=[],l=i.oldDataGroupIds=[],u=i.oldData=[],h=0;h)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
                    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
                    a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
                    t
                    ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
                    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t -}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
                    ","
                    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
                    "],tr:[2,"","
                    "],col:[2,"","
                    "],td:[3,"","
                    "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
                    ","
                    "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); -u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}e.vessel(f,function(n,r,d){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){l.parents("."+s[0])[0]||(l.data("display",l.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+s[0]+a).find("."+s[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=d),e.layero=i("#"+s[0]+a),t.scrollbar||s.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",l[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),s.anim[t.anim]&&e.layero.addClass(s.anim[t.anim]).data("anim",!0)}},l.pt.auto=function(e){function t(e){e=l.find(e),e.height(f[1]-c-d-2*(0|parseFloat(e.css("padding"))))}var a=this,o=a.config,l=i("#"+s[0]+e);""===o.area[0]&&o.maxWidth>0&&(r.ie&&r.ie<8&&o.btn&&l.width(l.innerWidth()),l.outerWidth()>o.maxWidth&&l.width(o.maxWidth));var f=[l.innerWidth(),l.innerHeight()],c=l.find(s[1]).outerHeight()||0,d=l.find("."+s[6]).outerHeight()||0;switch(o.type){case 2:t("iframe");break;default:""===o.area[1]?o.fixed&&f[1]>=n.height()&&(f[1]=n.height(),t("."+s[5])):t("."+s[5])}return a},l.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(s[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},l.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var l={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),l.autoLeft=function(){l.left+o[0]-n.width()>0?(l.tipLeft=l.left+l.width-o[0],f.css({right:12,left:"auto"})):l.tipLeft=l.left},l.where=[function(){l.autoLeft(),l.tipTop=l.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left+l.width+10,l.tipTop=l.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){l.autoLeft(),l.tipTop=l.top+l.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left-o[0]-10,l.tipTop=l.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],l.where[c-1](),1===c?l.top-(n.scrollTop()+o[1]+16)<0&&l.where[2]():2===c?n.width()-(l.left+l.width+o[0]+16)>0||l.where[3]():3===c?l.top-n.scrollTop()+l.height+o[1]+16-n.height()>0&&l.where[0]():4===c&&o[0]+16-l.left>0&&l.where[1](),a.find("."+s[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:l.tipLeft-(t.fixed?n.scrollLeft():0),top:l.tipTop-(t.fixed?n.scrollTop():0)})},l.pt.move=function(){var e=this,t=e.config,a=i(document),l=e.layero,s=l.find(t.move),f=l.find(".layui-layer-resize"),c={};return t.move&&s.css("cursor","move"),s.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(l.css("left")),e.clientY-parseFloat(l.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[l.outerWidth(),l.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],s="fixed"===l.css("position");if(i.preventDefault(),c.stX=s?0:n.scrollLeft(),c.stY=s?0:n.scrollTop(),!t.moveOut){var f=n.width()-l.outerWidth()+c.stX,d=n.height()-l.outerHeight()+c.stY;af&&(a=f),od&&(o=d)}l.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd()),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},l.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+s[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+s[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+s[0])[0]||1==n.attr("layer")&&i("."+s[0]).length<1&&n.removeAttr("layer").show(),n=null})},l.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+s[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},l.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){s.html.attr("layer-full")==e&&(s.html[0].style.removeProperty?s.html[0].style.removeProperty("overflow"):s.html[0].style.removeAttribute("overflow"),s.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+s[4]).attr("times"),i("#"+s[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+s[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+s[0]+e),a=n.find(s[1]).outerHeight()||0,o=n.find("."+s[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+s[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+s[0]+e),r=a.find(".layui-layer-content"),l=a.attr("type"),f=a.find(s[1]).outerHeight()||0,c=a.find("."+s[6]).outerHeight()||0;a.attr("minLeft");l!==o.type[3]&&l!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+s[6]).outerHeight(),l===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+s[0]+e),l=a.find(s[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:l,left:f,top:n.height()-l,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(s[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+s[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(s[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+s[0]+e);o.record(a),s.html.attr("layer-full")||s.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+s[0]+(t||r.index)).find(s[1]);n.html(e)},r.close=function(e){var t=i("#"+s[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var l="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+s[5]+")").remove();for(var a=t.find("."+l),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(l)}else{if(n===o.type[2])try{var f=i("#"+s[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+s[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("anim")&&t.addClass(a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),setTimeout(function(){f()},r.ie&&r.ie<10||!t.data("anim")?0:200)}},r.closeAll=function(e){i.each(i("."+s[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var l,s=2==e.formType?'":function(){return''}();return r.open(i.extend({type:1,btn:["确定","取消"],content:s,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){l=e.find(".layui-layer-input"),l.focus()},resize:!1,yes:function(i){var n=l.val();""===n?l.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",l,{tips:1}):t&&t(n,i,l)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{};return r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,n="";if(e>0)for(n=''+t[0].title+"";i"+t[i].title+"";return n}(),content:'
                      '+function(){var e=t.length,i=1,n="";if(e>0)for(n='
                    • '+(t[0].content||"no content")+"
                    • ";i'+(t[i].content||"no content")+"";return n}()+"
                    ",success:function(t){var n=t.find(".layui-layer-title").children(),a=t.find(".layui-layer-tabmain").children();n.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var n=i(this),o=n.index();n.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),a.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)})}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var l={};if(t=t||{},t.photos){var s=t.photos.constructor===Object,f=s?t.photos:{},d=f.data||[],u=f.start||0;if(l.imgIndex=(0|u)+1,t.img=t.img||"img",s){if(0===d.length)return r.msg("没有图片")}else{var y=i(t.photos),p=function(){d=[],y.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),d.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(p(),0===d.length)return;if(n||y.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:d,tab:t.tab},full:t.full}),!0),p()}),!n)return}l.imgprev=function(e){l.imgIndex--,l.imgIndex<1&&(l.imgIndex=d.length),l.tabimg(e)},l.imgnext=function(e,t){l.imgIndex++,l.imgIndex>d.length&&(l.imgIndex=1,t)||l.tabimg(e)},l.keyup=function(e){if(!l.end){var t=e.keyCode;e.preventDefault(),37===t?l.imgprev(!0):39===t?l.imgnext(!0):27===t&&r.close(l.index)}},l.tabimg=function(e){d.length<=1||(f.start=l.imgIndex-1,r.close(l.index),r.photos(t,!0,e))},l.event=function(){l.bigimg.hover(function(){l.imgsee.show()},function(){l.imgsee.hide()}),l.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),l.imgprev()}),l.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),l.imgnext()}),i(document).on("keyup",l.keyup)},l.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(d[u].src,function(n){r.close(l.loadi),l.index=r.open(i.extend({type:1,area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(d[u].alt||
                    '+(d.length>1?'':"")+'
                    '+(d[u].alt||"")+""+l.imgIndex+"/"+d.length+"
                    ",success:function(e,i){l.bigimg=e.find(".layui-layer-phimg"),l.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),l.event(e),t.tab&&t.tab(d[u],e)},end:function(){l.end=!0,i(document).off("keyup",l.keyup)}},t))},function(){r.close(l.loadi),r.msg("当前图片地址异常
                    是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){d.length>1&&l.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),s.html=i("html"),r.open=function(e){var t=new l(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.jquery),e.layer=r,t("layer",r)})):"function"==typeof define?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); \ No newline at end of file diff --git a/route/static/layer/mobile/index.html b/route/static/layer/mobile/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/layer/mobile/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/layer/mobile/layer.js b/route/static/layer/mobile/layer.js deleted file mode 100755 index f9cf69313..000000000 --- a/route/static/layer/mobile/layer.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */ - ;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'

                    '+(e?n.title[0]:n.title)+"

                    ":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e=''+n.btn[0]+"",2===t&&(e=''+n.btn[1]+""+e),'
                    '+e+"
                    "):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='

                    '+(n.content||"")+"

                    "),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"
                    ':"")+'
                    "+l+'
                    '+n.content+"
                    "+c+"
                    ",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;o -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    - \ No newline at end of file diff --git a/route/static/layer/mobile/need/layer.css b/route/static/layer/mobile/need/layer.css deleted file mode 100755 index b9dbf2010..000000000 --- a/route/static/layer/mobile/need/layer.css +++ /dev/null @@ -1 +0,0 @@ -.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} \ No newline at end of file diff --git a/route/static/layer/skin/default/close.png b/route/static/layer/skin/default/close.png deleted file mode 100755 index c1162cba9aaa136e5e5f0d00360ee3af5c011a1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 581 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 Tf=!_z00000NkvXXu0mjfKHW0S diff --git a/route/static/layer/skin/default/icon.png b/route/static/layer/skin/default/icon.png deleted file mode 100755 index 9afc7fda82a4097e9edf6444fdd5bafeda4d7a4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6864 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 diff --git a/route/static/layer/skin/default/index.html b/route/static/layer/skin/default/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/layer/skin/default/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/layer/skin/default/layer.css b/route/static/layer/skin/default/layer.css deleted file mode 100755 index 08f3a0c9a..000000000 --- a/route/static/layer/skin/default/layer.css +++ /dev/null @@ -1 +0,0 @@ -.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}*html{background-image:url(about:blank);background-attachment:fixed}html #layuicss-skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #b2b2b2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;display:none;cursor:se-resize}.layui-layer{border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#f8f8f8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2e2d3c;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{background-image:url(close.png);position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;transition:All .4s ease-in-out;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-image:url(close_hover.png);transform:rotate(180deg)}.layui-layer-btn{background-color:#f6f8f8;border-top:1px solid #edf1f2;float:left;padding:9px 18px 10px;pointer-events:auto;text-align:right;width:100%;user-select:none;-webkit-user-select:none}.layui-layer-btn a{background-color:#20a53a;border-radius:3px;color:#fff;cursor:pointer;float:right;font-size:12px;font-weight:700;height:30px;line-height:28px;margin-left:8px;padding:0 12px;text-decoration:none}.layui-layer-btn a:hover{background-color:#10952a;border-color:#398439;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn1{border-color:#cbcbcb;background-color:#cbcbcb;color:#fff}.layui-layer-btn .layui-layer-btn1:hover{background-color:#c9302c}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:350px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8d8d8d;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #d3d4d3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:0}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0;box-shadow:none;border:0}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0;box-shadow:none;border:0}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476a7;color:#fff;border:0}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #e9e7e7}.layui-layer-lan .layui-layer-btn a{background:#bbb5b5;border:0}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:0}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} \ No newline at end of file diff --git a/route/static/layer/skin/default/loading-0.gif b/route/static/layer/skin/default/loading-0.gif deleted file mode 100755 index fe1097f721b475b3ca38373bc8fc3cdb9193d3c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5310 zcmbW4XFyYD_rUMXfSa2HXa=C7h6u7EB+MitLP#J1!xj-#2qBQL5<_IKkOab%DSH=D z8RBYD)FNsvR_ktERjeJhj%vsI+Q}Piq5aT*Km7A0cRc4g=Xd6F0|fz0mdG2h1im)` z{(C(0)$Q{?Of>u+Y5I$T{q2{Z&U1nJcHrlVz+XpPzn>kRPX=DMr~a(TC!l%g)VEWJpvZN^VAaW)?el167nHPn56? z*9(tbsFXKLRB0QiehV)s3p)MfN(sfonZ8a;_oP!i8O{thcP}q58pV~)pu5mrT^O$G z7+!2QS2mqa`SV9LXj3L7vm>|xf9f*aZJMty$cGE6EZoWeSx{o=I8QQIsg(rP@F>So#M-h9W|iB2{HAZJwFs z{07QdEYEPs6)9X8&U6>$n>7o3i&8@2ze{9fEUnH`MM(ZK{vY?xipo<+Tp}b{@?A=? z#IStL-&Fm(qJ@QjGO&drWtzkg#Q;%ymgLO~p^zPFFkY4^O6SVOyD}u1Dgk!`)ga=W zBuirZ`TKLZegSR_o-5sx$zU*<{+`?bj{tv;JKfd8owc~fKgoKq{OKH)n+J>M>B(U5 z7%T?YljX_d(CGmzI*%8yC@aX!Qi(Ffl105TgWf-7-TzgV?XQ%GRB~mMT%Nv|P+?NJ zN}eT^D=7Yv9u%QSEX!QDUx>DW$w14MC}r7_qyVKngYw4lY}r5P=X!Z~dIiuKZm$0q z_aCxJ{~w>Z7>sdQ$hE)a-r^%eLN8n{Rg&RmsTL)fhR`Vul?KlL{@bs={QT4F|NiI4 zAHM(Y)yr>RJb(7|o3EdI_2n0zKmP2|+`|W--oJPE&h1Y=zIF4)^=ntJe02HJhZko* zxbXh@b7yDHOuu*f)XAw6$0y%?=h)FBhYw9093LAUIk11<@ZO=f2M7B5dV9LNIy>6i zTKBXxH#IiY@2;z@sjjNjSCs3tnlg21NpVqOL4ICtPWCQUmU5>;o|%!JmMW8`BukRS ziK2w~9ox6X#cqw+61_PpG9o-IG(@;5I7q+`4B&D7IexwyeKvTnXS0}Io*wRQu7)ai zUPp6sbg;KuYiqNHYE4Vz1gIpa+C;2PmXeW`u}ETFFcH&~j4^LZ>+D)j?AtZ?_SU__jQs~B=B;BL1lEKL zzUPSXK*g9*eeKjHV&j1p`Po3=0`?O4-e%&KHW5K_Ve>`prR|k>KbiWR@UVFW0u_r= zlbOT#ax&a2!Q^fvgMt#&=qqga^oNbhkjC#^J&zU5RG6&^x?~3a=>}7Ph6WG-9UvLp zaP6hR4J|wd_|Az0`v~;mt|xaO@jX~pL(r|qX#1b?6=-~W{i_db>H>MSjrUmi(V#Hq zCadw={xfzW?_SLi%6BOF%H7zxUu?tne$k99GV(Ti_N}W~>$PiWWL{`Ey~rM4k^*Z& zxu!`Kc3D+1KyAowTw_zqp0c(uLV06lk1;H4+Xi>p4I3}(&m@jxo2xK{19_{7o#Q>h z=#wGHaH8)xE?OE#M)a*mZ6l8j@qk^4AHxrBS-^@Y+8u@cJ=2qF%iim@5>2}M_^SWZU=A@cH$YNI zE8eay^U$VLgo9N)PC;#*F~+pPg9xN#RYY!Xh)Hg#;DByr$RiL}$!C z5R7#D^_^-z(d1jRhFI!*hL(Ph5O0eM$oPc2(>nd}uhY?+2$Rcp?zuIyQ_AkFXH*}DZ5Gx5a-9-Tb>{$3n^Dxh%Bl`xLA^}$u)khP9Q_MS`&$Bs6j zlBUej(@ml3h(V&`1y8y?fN;JGo&V$k7k8EhT>}=W4A}@TQ5{@}(-4nAX@gry6pd&( z5>bvma`NR!>?K+6r6@p9!mN!M4ex_8`I@ZKEjY>7Y=KES;l}S{#K5Q(ERzpxBGhI; zFZK*K%*vzrwk8q%n@8@^�ZxeL|}aJmNVEgijL}&Bp*VlO^*n&;2s`pB=- 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 diff --git a/route/static/layer/skin/default/loading-1.gif b/route/static/layer/skin/default/loading-1.gif deleted file mode 100755 index a6abc9e5731a534085d00dc745d00de4d28d7f8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 699 zcmZ?wbhEHbRAo?Q_{hYt_w)@mZNLBj|1&T!DE{a6a}5c0b_{Se(lcOY1PT3QVdY|A zV$cDyff`g99GE(%^shWE#VB=( zRe!i3s$b+Nhm4r`@ku8xSE|)Jy~<$Y4J(|rY-tqp>1De<-B`_Su8G8S#M`c2c2sf|OWHA2;(Ka@N5nMI7td3YG$T9HCLd=3TC_<`B8x3+S}@FY z3=BE~Kqs;=a5FG4vGxF67Xx)SP)3x2m4OQ^Bih2>D6#kEozB9bRK2zHz~3r zREjeQGVm}k!^}XE5oHjCnjy@RH1l8Px# diff --git a/route/static/layer/skin/default/loading-2.gif b/route/static/layer/skin/default/loading-2.gif deleted file mode 100755 index e1691ac9812b124b689d37bc25cf4d83060bdb8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1785 zcmZXUdr(tn8i&tGPLh*^#atx>2mv&NOAwLRfVC!ZgC+(96De{j36=&CFaf1RvE&LQ zfQDN@%d&*FHVD=WYPl2}F;HdQ>UgWR9VOUWD`n^|b=7rt+~aI#HnaQv`+f60&pW^O zeK$$d1Sx7Zzy_9xfY<9C9v;4P=Z??k8yg!l8jZ=x$sh=ZgoG%S%C4@iSFc_HekbA; zF+-wMY!qcmk`fZpAaWJ)0>WSbA`t)j+3RnG&?D^?=|Jl7C(Pz63KQG97P={fhS+p% zz@FbG?}Mv1Yi2VH&X$#<3#FZ8)XEe=bX<64|F4OkHic)iy=hfn>t$~k@ z!k2A*v&Djm!cwbuoM;4P>#FlXfHtn)eozj;Y<}|>Oz=RhZoHkd`_FWMkhI1kcmulq z&5fIf&DV|<_U2rkppL7k;3wlhQ&4fA(ALYl#vgrs&KlFD%keJ#G}y}#=_3E9bOsOv z08k}TdK`g2zsoUTJ!7L4ZJXAWOuyaR)*o56Mb6Hp--+%cK4S{A$9noa5)){T) z`ooUaw)Rc?50L+x!0R}|fWY51G>QEAi8kpDPdX|qnA^#|%%xieu5Q|?r*P0@j|}VF zBEI{ga>VsVa1F1d2_!~*7_b)gaLsm=kz>6iS`Cx-Q{R){%#HNo-ceqfQ)GD(q0)Ag30qI>@yF}2Z*zKj;I?& zhIEZ+VD1JM-7K>)OHIo~(Ro~@?OW6JSY8q3wk%~t+DFXA8sSVJJ>4@KT176>@N{V_ zIzRkJ$DB1}w7MpuYw;x?XEebZ4w@Qpb;L%T#gC4Z4^RLGLNCtbo<2Sy;C=!B*cADW zEX&}_!9T*=^KMhP`DEVF>4j&9K*dmhA|ul<>1j)3*0b$P?m;>A?D3}q3;VyMTWaLD zTdp<6GS#qQ0#o?8RxmR5G!U$0bmjq&KICvaxcThbCZO3gdFyX5Az;?_YuvLq- zSE`j9KdEH^h(r;NNF6}J@b)pxq%+~6zG5i4xr5YC3(7B>0%LHBXGtBL@zLatIN@cz z(x{O4=clf>x|L*$?4)~l$3K5bMPtpA!GY1e#g{f4`&P{Clz~RU*6Alx>Q%0&RMr6; zb+10yM8)onYP2*p)Kc+>87({(j4RI|;B;I3d|;$M8Aw0?RECVoJVDT|etDJc)3eXF zl-RMc1e*13u9kQ8R^Kga60ZI`v8SB}G>E@axk;)?t53T*o|J+0ENK+GbA;Lz+l1L zw$`(Bk)vQrM`KAW;Ob1U=x7Y)6vz%kI2&!n^QCMO z*PN5YYmWCye;E&11^@W1*HaO~vB!umJiot?t>f>7!Kw<19@$V~4RusV!>Xs_v4?6a z9eh6-09f|>sAw`>8|TLa^La1tsW|$=lHSKDv=}>EMr1C=X{0SH+@v!8rNzkapQ8+N z&#~2v1nc9K!mz%cYm$z00!jv6BafRPxlNAAD`sa9`ia)LuWA)gdU@bpDU@BxBw%*F z!2~eU5I;5uz({Aj9+dpV9))w_xl`|$nJv;bfR=8PqvU*6IY+50e^B$;qPvK(Ry_A? zkZd$L7rRCR2+`LUtFmYWqeRtow1D?r+2dTcVU&$}L17iPYfclJcA23{Vub3#A7Hc} z5CBj}_+bbT*^;A5S55EEr2Cs@)2Hg2sb>n@D8jBy!Fg?wjS&f+i!yv@0=NVPOj1jY ziufS&Pf3o%oRdcev(sD^qc?sHlyGT4lu^HMdWfDSzc(b@g8G|vOxOpYKnV+N*vWzn VreKue;JXHW8d%Lm+sVMXe*?P3dK&-$ diff --git a/route/static/layer/skin/index.html b/route/static/layer/skin/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/layer/skin/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                    -

                    目标URL

                    -

                    发送域名

                    -

                    内容替换

                    -
                    -
                      -
                    • 目标Url必需是可以访问的,否则将直接502
                    • -
                    • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                    • -
                    • 若您是被动代理,请在发送域名处填写上目标站点的域名
                    • -
                    • 若您不需要内容替换功能,请直接留空
                    • -
                    • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                    • -
                    -
                    \ No newline at end of file diff --git a/route/static/layer/skin/layer.css b/route/static/layer/skin/layer.css deleted file mode 100755 index 6985801d3..000000000 --- a/route/static/layer/skin/layer.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - - @Name: layer's style - @Author: 贤心 - @Blog: sentsin.com - - */html{background-image:url(about:blank);background-attachment:fixed}html #layui_layer_skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{top:150px;left:50%;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3);-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #b2b2b2;border:1px solid rgba(0,0,0,.3);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-moves{position:absolute;border:3px solid #666;border:3px solid rgba(0,0,0,.5);cursor:move;background-color:#fff;background-color:rgba(255,255,255,.3);filter:alpha(opacity=50)}.layui-layer-load{background:url(default/loading-0.gif) center center no-repeat #fff}.layui-layer-ico{background:url(default/icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);-ms-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layui-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layui-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layui-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 10px;height:40px;line-height:40px;border-bottom:1px solid #edf1f2;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#f6f8f8}.layui-layer-setwin{position:absolute;right:10px;*right:0;top:10px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:14px;height:14px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#919191;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-45px -55px}.layui-layer-setwin .layui-layer-max:hover{background-position:-95px -55px}.layui-layer-setwin .layui-layer-maxmin{background-position:-68px -55px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-118px -55px}.layui-layer-setwin .layui-layer-close1{width:14px;height:14px;background-position:-5px -55px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{background-position:-25px -55px}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-23px;top:-23px;width:30px;height:30px;margin-left:0;background-position:-149px -44px;*right:-18px;_right:-15px;_top:-23px;_width:14px;_height:14px;_background-position:-5px -55px}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -44px;_background-position:-25px -55px}.layui-layer-btn{background-color:#f6f8f8;border-top:1px solid #edf1f2;float:left;padding:9px 20px 10px;pointer-events:auto;text-align:right;width:100%}.layui-layer-btn a{height:30px;line-height:30px;margin:0 8px;padding:0 20px;background:#5fbfe7;color:#fff;font-size:14px;font-weight:700;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.7}.layui-layer-btn .layui-layer-btn1{background:#a6bbce}.layui-layer-dialog{min-width:240px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;font-size:14px;overflow:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:15px;left:15px;_left:-50px;width:39px;height:39px}.layui-layer-ico1{background-position:-46px 0}.layui-layer-ico2{background-position:-93px 0}.layui-layer-ico3{background-position:-145px 0}.layui-layer-ico4{background-position:-191px 0}.layui-layer-ico5{background-position:-239px 0}.layui-layer-ico6{background-position:-287px 0}.layui-layer-rim{border:6px solid #8d8d8d;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:170px;border-radius:3px;border:5px solid #8d8d8d;border:5px solid rgba(0,0,0,.4)}.layui-layer-msg .layui-layer-content .layui-layer-ico{top:10px}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:0}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:22px 20px 22px 65px;text-align:left}.layui-layer-msg .layui-layer-padding{padding:17px 20px 17px 65px}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0}.layui-layer-iframe .layui-layer-content{overflow:hidden}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0;box-shadow:none;border:0}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(default/loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(default/loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(default/loading-2.gif) no-repeat}.layui-layer-tips{background:0;box-shadow:none;border:0}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:3px;box-shadow:1px 1px 3px rgba(0,0,0,.3);background-color:#F90;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#F90}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#F90}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476a7;color:#fff;border:0}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #e9e7e7}.layui-layer-lan .layui-layer-btn a{background:#bbb5b5}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:0}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1} \ No newline at end of file diff --git a/route/static/layer/skin/layer.ext.css b/route/static/layer/skin/layer.ext.css deleted file mode 100755 index 45a32a80e..000000000 --- a/route/static/layer/skin/layer.ext.css +++ /dev/null @@ -1,8 +0,0 @@ -/*! - - @Name: layer拓展样式 - @Date: 2012.12.13 - @Author: 贤心 - @blog: sentsin.com - - */.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span{text-overflow:ellipsis;white-space:nowrap}.layui-layer-iconext{background:url(default/icon-ext.png) no-repeat}html #layui_layer_skinlayerextcss{display:none;position:absolute;width:1989px}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:36px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s;background:url(default/xubox_loading1.gif) center center no-repeat #000}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal} \ No newline at end of file diff --git a/route/static/logo.png b/route/static/logo.png deleted file mode 100644 index 34548a078d377180ea04d9d3f4c0f21a42f2e2f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22782 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

                    s+(K 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 diff --git a/route/static/mdw.jpg b/route/static/mdw.jpg deleted file mode 100644 index f21a10aca04387ff102301cea600d79916fd6632..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66905 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 - - - - 抱歉,管理员已关闭面板 - - -

                    开启命令

                    -

                    mw open

                    -
                    - - - \ No newline at end of file diff --git a/route/templates/default/config.html b/route/templates/default/config.html deleted file mode 100755 index 655630e3c..000000000 --- a/route/templates/default/config.html +++ /dev/null @@ -1,196 +0,0 @@ -{% extends "layout.html" %} -{% block content %} -
                    -
                    -
                    -
                    首页/面板设置
                    -
                    -
                    -
                    -
                    - 关闭面板 -
                    - - -
                    -
                    -
                    - 开发模式 -
                    - - -
                    -
                    - -
                    - 监听IPv6 -
                    - - -
                    -
                    -
                    -
                    -
                    -

                    设置

                    -
                    -

                    为了提高安全,修改面板密码!

                    -
                    -
                    -
                    -

                    - 别名 - - - 面板名称 -

                    - -

                    - 服务器IP - - - 默认为外网IP,若您在本地虚拟机测试,请填写虚拟机内网IP! -

                    - -

                    - 面板端口 - - - 建议端口范围7200 - 65535 -

                    - -

                    - 安全入口 - - - 面板管理入口,设置后只能通过指定安全入口登录面板,如: /abc -

                    - -

                    - 默认建站目录 - - - 新创建的站点,默认将保存到该目录的下级目录! -

                    - -

                    - 默认备份目录 - - - 网站和数据库的备份目录! -

                    - -

                    - 服务器时间 - - - 设置/同步当前服务器时间 -

                    -

                    - 面板用户 - - - 设置面板账号 -

                    -

                    - 面板密码 - - - 设置面板密码 -

                    - -
                    - -

                    安全

                    -
                    - -

                    - 绑定域名 - - - 为面板绑定一个访问域名,注意:一旦绑定域名,只能通过域名访问面板 -

                    - -

                    - 面板SSL - - - - 为面板设置https协议访问,提升面板访问安全性 -

                    - -

                    - BasicAuth认证 - - - 为面板增加一道基于BasicAuth的认证服务,有效防止面板被扫描 -

                    - -

                    - API接口 - - - - 提供面板API接口访问的支持 -

                    - -

                    - 未认证响应状态 - - - 用于在未登录且未正确输入安全入口时的响应,可用于隐藏面板特征 -

                    - -

                    - 临时访问授权 - - 为非管理员临时提供面板访问权限 -

                    - -

                    - 二次验证 - - - - 二次验证,加强安全登录 -

                    -
                    - - -

                    通知

                    - -
                    - -

                    - TG机器人通知 - - - - Telegram Bot机器人通知【国内可能无法使用】 -

                    - -

                    - 邮件通知 - - - - 邮件通知 -

                    - -
                    - - -
                    -
                    -
                    - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/control.html b/route/templates/default/control.html deleted file mode 100755 index e6fb4da21..000000000 --- a/route/templates/default/control.html +++ /dev/null @@ -1,176 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -
                    -
                    -
                    -
                    - 首页监控/系统监控 -
                    -
                    -
                    -
                    -
                    - 开启监控 -
                    -
                    -
                    -
                    -
                    - 保存天数: - -
                    -
                    - -
                    -
                    -
                    -
                    -
                    - 仅统计外网 -
                    -
                    -
                    - - -
                    -
                    -
                    -
                    -
                    -
                    -
                    -

                    平均负载

                    -
                    - 区间检索: - 昨天 - 今天 - 最近7天 - 最近30天 -
                    - 自定义时间 -
                    - 时间范围 - - -
                    提交
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -

                    CPU

                    -
                    - 区间检索: - 昨天 - 今天 - 最近7天 - 最近30天 -
                    - 自定义时间 -
                    - 时间范围 - - -
                    提交
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -

                    内存

                    -
                    - 区间检索: - 昨天 - 今天、 - 最近7天 - 最近30天 -
                    - 自定义时间 -
                    - 时间范围 - - -
                    提交
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -

                    磁盘IO

                    -
                    - $data['lan']['S1'] - 昨天 - 今天 - 最近7天 - 最近30天 -
                    - 自定义时间 -
                    - 时间范围 - - -
                    提交
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -

                    网络IO

                    -
                    - $data['lan']['S1'] - 昨天 - 今天 - 最近7天 - 最近30天 -
                    - 自定义时间 -
                    - 时间范围 - - -
                    提交
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/crontab.html b/route/templates/default/crontab.html deleted file mode 100755 index 7284ec8ee..000000000 --- a/route/templates/default/crontab.html +++ /dev/null @@ -1,155 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -
                    -
                    -
                    -
                    - 首页/计划任务 -
                    -
                    -
                    -
                    -

                    添加计划任务

                    -
                    -
                    -
                    - 任务类型 - - *任务类型包含以下部分:Shell脚本、备份网站、备份数据库、备份目录、日志切割、释放内存、访问URL -
                    -
                    - 任务名称 -
                    -
                    -
                    - 执行周期 - -
                    - -
                    - - 小时 -
                    -
                    - - 分钟 -
                    -
                    -
                    -
                    - 脚本内容 -
                    -
                    -
                    -
                    添加任务
                    -
                    -
                      -
                    • 当添加完备份任务,应该手动运行一次,并检查备份包是否完整
                    • -
                    • 磁盘容量不够、数据库密码错误、网络不稳定等原因,可能导致数据备份不完整
                    • -
                    -
                    - - - - - - - - - - - - - - - - -
                    -
                    -
                    -

                    任务列表

                    - -
                    -
                    -
                    - - - - - - - - - - - - - - -
                    任务名称状态周期执行时机保存数量备份到添加时间操作
                    - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/files.html b/route/templates/default/files.html deleted file mode 100755 index e074d3004..000000000 --- a/route/templates/default/files.html +++ /dev/null @@ -1,113 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -
                    -
                    -
                    -
                    -
                    -
                    -
                    - - - - -
                    -
                    - -
                    -
                    -
                    - - - - - -
                    - - -
                    -
                    -
                    - -
                    - -
                    -
                    -
                    - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/firewall.html b/route/templates/default/firewall.html deleted file mode 100755 index e0b1e5224..000000000 --- a/route/templates/default/firewall.html +++ /dev/null @@ -1,117 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - - -
                    -
                    -
                    -
                    - 首页/系统安全 -
                    - -
                    -
                    -
                    - -
                    -
                    - SSH管理 -
                    -
                    -
                    -
                    - -
                    - SSH端口: - -
                    -
                    - -
                    -
                    -
                    -
                    - 启用禁ping -
                    - -
                    -
                    -
                    - -
                    -
                    -
                    - 启用防火墙 -
                    - -
                    -
                    -
                    -
                    - -
                    -
                    - -
                    -
                    -
                    -
                    -

                    防火墙

                    -
                    - -
                    -
                    - - - - - - 说明: 支持放行端口范围,如: 3000:3500 -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    -
                    - -
                    -
                    - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/index.html b/route/templates/default/index.html deleted file mode 100755 index dd74bd55d..000000000 --- a/route/templates/default/index.html +++ /dev/null @@ -1,171 +0,0 @@ -{% extends "layout.html" %} {% block content %} -
                    - -
                    -
                    - 系统:正在获取中...   -
                    - - {{config.version}} - 更新 | - - 重启 - -
                    - -
                    - -
                    - -
                    - -
                    -
                    -

                    状态

                    -
                    -
                    -
                      -
                    • -

                      负载状态?

                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      0%
                      -
                      -

                      获取中...

                      -
                    • -
                    • -

                      CPU使用率

                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      0%
                      -
                      -

                      获取中...

                      -
                    • -
                    • -

                      内存使用率

                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      0%
                      - -
                      -
                      -

                      获取中...

                      -
                    • -
                    -
                    -
                    - - -
                    -
                    概览
                    -
                    - -
                    -
                    - -
                    -
                    -
                    -
                    软件
                    -
                    -
                    - -
                    -
                    -
                    -
                    -
                    -
                    - -
                    -
                    -
                    -
                    -
                    - 流量 - 磁盘IO -
                    - -
                    - - -
                    -
                    - -
                    -
                    -
                    -

                    上行

                    0
                    -

                    下行

                    0
                    -

                    总发送

                    0
                    -

                    总接收

                    0
                    -
                    -
                    -
                    - -
                    -
                    -

                    读取

                    0
                    -

                    写入

                    0
                    -

                    每秒读写

                    0
                    -

                    读写IO延迟

                    0
                    -
                    -
                    -
                    -
                    - -
                    -
                    -
                    -
                    -
                    - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/layout.html b/route/templates/default/layout.html deleted file mode 100755 index f86b59826..000000000 --- a/route/templates/default/layout.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - -{{data['title']}} - - - - - - - - -{% for menu in data['hook_menu'] %} -{% if menu['css_path'] and data['hook_tag'] == menu['name'] %} - - -{% endif %} -{% endfor %} - - -{% for menu in data['hook_global_static'] %} -{% if menu['css_path'] %} - -{% endif %} -{% endfor %} - - - -
                    -
                    - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% block content %}{% endblock %} - -
                    -
                    - - - - - - - - - - - -{% for menu in data['hook_menu'] %} -{% if menu['js_path'] and data['hook_tag'] == menu['name'] %} - -{% endif %} -{% endfor %} - - -{% for menu in data['hook_global_static'] %} -{% if menu['js_path'] %} - -{% endif %} -{% endfor %} - - - - - - - - - \ No newline at end of file diff --git a/route/templates/default/login.html b/route/templates/default/login.html deleted file mode 100755 index aaf5f544a..000000000 --- a/route/templates/default/login.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - -{{data['title']}} - - - - - - -
                    - -
                    - - - - - - - - \ No newline at end of file diff --git a/route/templates/default/logs.html b/route/templates/default/logs.html deleted file mode 100644 index ac3e0b876..000000000 --- a/route/templates/default/logs.html +++ /dev/null @@ -1,189 +0,0 @@ -{% extends "layout.html" %} -{% block content %} - - - -
                    -
                    -
                    -
                    -
                    面板日志
                    -
                    日志审计
                    -
                    -
                    - - -
                    - - -
                    -
                    -
                    -
                    - - -
                    -
                    -
                    - - - - - - - - -
                    编号操作类型详情操作时间
                    -
                    -
                    -
                    -
                    1共1条
                    -
                    -
                    - - - -
                    -
                    -
                    - -
                    -
                    -
                    -
                    -
                    - -
                    -
                    - -
                    -
                    -
                    - - - - - - - - - - - - -
                    用户来源端口时间
                    -
                    -
                    -
                    -
                    -
                    第 1 页
                    -
                    -
                    -
                    -
                    - -
                    -
                    -
                    -
                    -
                    -
                    - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/path.html b/route/templates/default/path.html deleted file mode 100755 index 7998d53fb..000000000 --- a/route/templates/default/path.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - 安全入口校验失败 - - -

                    请使用正确的入口登录面板

                    -

                    错误原因:当前新安装的已经开启了安全入口登录,新装机器都会随机一个8位字符的安全入口名称,亦可以在面板设置处修改,如您没记录或不记得了,可以使用以下方式解决

                    -

                    解决方法:在SSH终端输入以下一种命令来解决

                    -

                    1.查看面板入口:mw default

                    -

                    2.关闭安全入口:mw close_admin_path

                    -

                    注意:【关闭安全入口】将使您的面板登录地址被直接暴露在互联网上,非常危险,请谨慎操作

                    - - \ No newline at end of file diff --git a/route/templates/default/plugin_menu.html b/route/templates/default/plugin_menu.html deleted file mode 100644 index 647c28616..000000000 --- a/route/templates/default/plugin_menu.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "layout.html" %} -{% block content %} -{{data['plugin_content']|safe }} -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/site.html b/route/templates/default/site.html deleted file mode 100755 index aa91828b4..000000000 --- a/route/templates/default/site.html +++ /dev/null @@ -1,75 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -
                    -
                    -
                    -
                    - 首页/网站 -
                    - -
                    -
                    -
                    -

                    创建站点时会自动创建权限配置,统一使用www用户。

                    -
                    -   - - - - - - - -
                    - -
                    - -
                    -
                    - - - - - - - - - - - - - - -
                    域名网站状态备份网站目录到期日期备注操作
                    -
                    - - -
                    - 站点分类: - -
                    -
                    -
                    -
                    -
                    - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/soft.html b/route/templates/default/soft.html deleted file mode 100755 index 21f31853f..000000000 --- a/route/templates/default/soft.html +++ /dev/null @@ -1,58 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -
                    -
                    -
                    -
                    - 首页/软件管理 -
                    - -
                    -
                    -
                    -
                    - - - - - -
                    - - - - - - - - - - - - -
                    软件名称说明位置状态首页显示操作
                    - -
                    -
                    -
                    -
                    -
                    - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/index.html b/route/templates/index.html deleted file mode 100644 index b2d525b29..000000000 --- a/route/templates/index.html +++ /dev/null @@ -1 +0,0 @@ -index \ No newline at end of file From 080e4ad696cc29126fb4bf4ca1f9a8c9c237d14b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:38:28 +0800 Subject: [PATCH 220/965] Update tool_task.py --- plugins/webstats/tool_task.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py index 72b65366a..fe9185f58 100644 --- a/plugins/webstats/tool_task.py +++ b/plugins/webstats/tool_task.py @@ -6,6 +6,8 @@ import os import time import json +from utils.crontab import crontab as MwCrontab + sys.path.append(os.getcwd() + "/class/core") import mw @@ -58,11 +60,11 @@ def createBgTask(): if res and res["id"] == cfg["task_id"]: print("计划任务已经存在!") return True - import crontab_api - api = crontab_api.crontab_api() - cmd = "cd " + mw.getRunDir() + " && nice -n 10 python3 " + \ - getPluginDir() + "/tool_task.py execute" + + + + cmd = "cd " + mw.getPanelDir() + " && nice -n 10 python3 " + getPluginDir() + "/tool_task.py execute" params = { 'name': name, 'type': 'day', @@ -78,7 +80,7 @@ def createBgTask(): 'urladdress': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: cfg["task_id"] = task_id mw.writeFile(getTaskConf(), json.dumps(cfg)) @@ -90,10 +92,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]: # print(data[1]) cfg["task_id"] = -1 From 2d14184c82e3d7e29181029a8cea41ee859739a6 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:40:21 +0800 Subject: [PATCH 221/965] Update index.py --- plugins/webstats/index.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/webstats/index.py b/plugins/webstats/index.py index 3c272cce5..11fbfe40e 100755 --- a/plugins/webstats/index.py +++ b/plugins/webstats/index.py @@ -7,8 +7,12 @@ import time 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 From 7eb88d6a9215711bbdf40905d4faaf812e4b227d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:43:00 +0800 Subject: [PATCH 222/965] Update tool_task.py --- plugins/webstats/tool_task.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py index fe9185f58..614c34ccb 100644 --- a/plugins/webstats/tool_task.py +++ b/plugins/webstats/tool_task.py @@ -6,10 +6,14 @@ import os import time import json -from utils.crontab import crontab as MwCrontab -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 From d32523c451ed71361e4c52058b8885fe2843d3bd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:43:22 +0800 Subject: [PATCH 223/965] up --- plugins/webstats/tool_migrate.py | 8 ++++++-- plugins/webstats/webstats_index.py | 12 +++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/webstats/tool_migrate.py b/plugins/webstats/tool_migrate.py index 926cd6474..1c02f8438 100644 --- a/plugins/webstats/tool_migrate.py +++ b/plugins/webstats/tool_migrate.py @@ -6,9 +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 if mw.isAppleSystem(): diff --git a/plugins/webstats/webstats_index.py b/plugins/webstats/webstats_index.py index 411c8297d..5c5083519 100644 --- a/plugins/webstats/webstats_index.py +++ b/plugins/webstats/webstats_index.py @@ -8,11 +8,13 @@ import json import re -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 +from utils.crontab import crontab as MwCrontab app_debug = False From e4d7ebbe2514b31df9fb34e32f795494eba19645 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:44:24 +0800 Subject: [PATCH 224/965] Update tool_task.py --- plugins/webstats/tool_task.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py index 614c34ccb..fdfa64393 100644 --- a/plugins/webstats/tool_task.py +++ b/plugins/webstats/tool_task.py @@ -66,8 +66,6 @@ def createBgTask(): return True - - cmd = "cd " + mw.getPanelDir() + " && nice -n 10 python3 " + getPluginDir() + "/tool_task.py execute" params = { 'name': name, From 5218468b96fa545a92cfb4a28b6544c9761a6e04 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:44:31 +0800 Subject: [PATCH 225/965] Update tool_task.py --- plugins/webstats/tool_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py index fdfa64393..2be6f244f 100644 --- a/plugins/webstats/tool_task.py +++ b/plugins/webstats/tool_task.py @@ -79,7 +79,7 @@ def createBgTask(): 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } task_id = MwCrontab.instance().add(params) From 5a072d7cda93d5449058532892f0f6c792435afc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:46:40 +0800 Subject: [PATCH 226/965] Update crontab.py --- web/utils/crontab.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/utils/crontab.py b/web/utils/crontab.py index 4a8b6035f..8ce71a511 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -16,8 +16,6 @@ import json import threading import multiprocessing -from admin import model - import core.mw as mw import thisdb From 2893f0273598baffcf6960b1008c4ebf03a725c5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:51:29 +0800 Subject: [PATCH 227/965] update --- plugins/webstats/tool_task.py | 3 +-- web/admin/crontab/__init__.py | 6 +++++- web/utils/crontab.py | 4 +--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py index 2be6f244f..c57f517da 100644 --- a/plugins/webstats/tool_task.py +++ b/plugins/webstats/tool_task.py @@ -59,8 +59,7 @@ def createBgTask(): return True if "task_id" in cfg.keys() and cfg["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (cfg["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: print("计划任务已经存在!") return True diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index 17f506fba..b6184606f 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -115,6 +115,10 @@ def add(): request_data['sname'] = request.form.get('sName', '') request_data['sbody'] = request.form.get('sBody', '') request_data['url_address'] = request.form.get('urladdress', '') - return MwCrontab.instance().add(request_data) + + cron_id = MwCrontab.instance().add(request_data) + if cron_id > 0: + return mw.returnData(True, '添加成功') + return mw.returnData(False, '添加失败') diff --git a/web/utils/crontab.py b/web/utils/crontab.py index 8ce71a511..2e5175bdb 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -184,9 +184,7 @@ class crontab(object): add_dbdata['url_address'] = data['url_address'] tid = thisdb.addCrontab(add_dbdata) - if tid > 0: - return mw.returnData(True, '添加成功') - return mw.returnData(False, '添加失败') + return tid def delete(self, tid): data = thisdb.getCrond(tid) From aa2126e7e6cca606c67052b4fccdf944065ba18b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 04:57:24 +0800 Subject: [PATCH 228/965] Update crontab.py --- web/utils/crontab.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/utils/crontab.py b/web/utils/crontab.py index 2e5175bdb..f61dc41ff 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -164,9 +164,9 @@ class crontab(object): cmd += ' ' + cron_path + '/' + cron_name + ' >> ' + cron_path + '/' + cron_name + '.log 2>&1' if not mw.isAppleSystem(): - data = self.writeShell(cmd) - if not data['status']: - return data + sh_data = self.writeShell(cmd) + if not sh_data['status']: + return sh_data self.crondReload() add_dbdata = {} From c249da061d9d6bc2accae7f2964844bbce201577 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:01:42 +0800 Subject: [PATCH 229/965] Update mw.py --- web/core/mw.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/core/mw.py b/web/core/mw.py index fc9efa08d..19921891e 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -213,6 +213,7 @@ def writeFile(filename, content, mode='w+'): fp.close() return True except Exception as e: + print(e) return False From 41000f0c5d4f1b5c9fd0f3c2aa2c2a23b9159fd1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:10:19 +0800 Subject: [PATCH 230/965] update --- plugins/webstats/install.sh | 2 +- plugins/webstats/tool_migrate.py | 7 +------ web/core/mw.py | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/plugins/webstats/install.sh b/plugins/webstats/install.sh index 771f8d290..cb14928e0 100755 --- a/plugins/webstats/install.sh +++ b/plugins/webstats/install.sh @@ -133,7 +133,7 @@ Install_App() cp -rf $serverPath/source/webstats/GeoLite2-City.mmdb $serverPath/webstats/GeoLite2-City.mmdb fi - cd $rootPath && python3 ${rootPath}/plugins/webstats/index.py start + cd $rootPath && python3 plugins/webstats/index.py start echo '网站统计安装完成' diff --git a/plugins/webstats/tool_migrate.py b/plugins/webstats/tool_migrate.py index 1c02f8438..ddc66e884 100644 --- a/plugins/webstats/tool_migrate.py +++ b/plugins/webstats/tool_migrate.py @@ -31,11 +31,6 @@ def getServerDir(): return mw.getServerDir() + '/' + getPluginName() -def getTaskConf(): - conf = getServerDir() + "/task_config.json" - return conf - - def getConf(): conf = getServerDir() + "/lua/config.json" return conf @@ -126,7 +121,7 @@ def migrateSiteHotLogs(site_name, query_date): if field is None: field = "\'\'" elif type(field) == str: - field = "\'" + field.replace("\'", "\”") + "\'" + field = "\'" + field.replace("\'", "\"") + "\'" params += str(field) insert_sql = "insert into web_logs(" + \ _columns + ") values(" + params + ")" diff --git a/web/core/mw.py b/web/core/mw.py index 19921891e..38427e4d0 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -205,7 +205,7 @@ def readFile(filename): print(e) return False -def writeFile(filename, content, mode='w+'): +def writeFile(filename, content, mode='aw+'): # 写文件内容 try: fp = open(filename, mode) From 27e1e0c73c827a36020877d9ed018e71288749a7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:11:35 +0800 Subject: [PATCH 231/965] Update mw.py --- web/core/mw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/mw.py b/web/core/mw.py index 38427e4d0..19921891e 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -205,7 +205,7 @@ def readFile(filename): print(e) return False -def writeFile(filename, content, mode='aw+'): +def writeFile(filename, content, mode='w+'): # 写文件内容 try: fp = open(filename, mode) From 3b857833a7e061f632d6416e5c1dfd2ce6e55d51 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:18:34 +0800 Subject: [PATCH 232/965] update --- plugins/webstats/install.sh | 2 +- web/core/mw.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/webstats/install.sh b/plugins/webstats/install.sh index cb14928e0..bb4395bd2 100755 --- a/plugins/webstats/install.sh +++ b/plugins/webstats/install.sh @@ -148,7 +148,7 @@ Install_App() Uninstall_App() { - cd $rootPath && python3 ${rootPath}/plugins/webstats/index.py stop + cd $rootPath && python3 plugins/webstats/index.py stop rm -rf $serverPath/webstats echo "网站统计卸载完成" } diff --git a/web/core/mw.py b/web/core/mw.py index 19921891e..7f827f0e4 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -213,7 +213,7 @@ def writeFile(filename, content, mode='w+'): fp.close() return True except Exception as e: - print(e) + print(getTracebackInfo()) return False From 62a94c5c153d17818a9b87252ebccb8dd8271b27 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:26:35 +0800 Subject: [PATCH 233/965] Update tool_task.py --- plugins/webstats/tool_task.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py index c57f517da..e1c1af25d 100644 --- a/plugins/webstats/tool_task.py +++ b/plugins/webstats/tool_task.py @@ -39,10 +39,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, "task_list": ["migrate_hot_logs"], From add966bc62785e47aa979ca90d1f0e62ae7a7fc7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:32:51 +0800 Subject: [PATCH 234/965] Update crontab.py --- web/utils/crontab.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/utils/crontab.py b/web/utils/crontab.py index f61dc41ff..e308b7a5e 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -258,9 +258,10 @@ class crontab(object): file = '' for i in cron_file: - if os.path.exists(file): + if os.path.exists(i): file = i + if file == '': return False From d57b0d0922dda7e1c173d2ddb173af4045a884cb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:36:53 +0800 Subject: [PATCH 235/965] Update mw.py --- web/core/mw.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/web/core/mw.py b/web/core/mw.py index 7f827f0e4..0a04a42b5 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -533,6 +533,33 @@ def getCpuType(): return cpuType +def getLanguage(): + path = 'data/language.pl' + if not os.path.exists(path): + return 'Simplified_Chinese' + return readFile(path).strip() + + +def getStaticJson(name="public"): + file = 'static/language/' + getLanguage() + '/' + name + '.json' + if not os.path.exists(file): + file = 'route/static/language/' + getLanguage() + '/' + name + '.json' + return file + + +def returnMsg(status, msg, args=()): + # 取通用字曲返回 + pjson = getStaticJson('public') + logMessage = json.loads(readFile(pjson)) + keys = logMessage.keys() + + if msg in keys: + msg = logMessage[msg] + for i in range(len(args)): + rep = '{' + str(i + 1) + '}' + msg = msg.replace(rep, args[i]) + return {'status': status, 'msg': msg, 'data': args} + def getInfo(msg, args=()): # 取提示消息 for i in range(len(args)): From b0392b7cf106a24fdaf83b47f7d135abae6a8738 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:40:30 +0800 Subject: [PATCH 236/965] update --- plugins/rsyncd/index.py | 9 +++++++-- plugins/rsyncd/tool_task.py | 29 +++++++++++++++-------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/plugins/rsyncd/index.py b/plugins/rsyncd/index.py index 708031edb..778e4da14 100755 --- a/plugins/rsyncd/index.py +++ b/plugins/rsyncd/index.py @@ -7,8 +7,13 @@ import json import re import sys -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/rsyncd/tool_task.py b/plugins/rsyncd/tool_task.py index 0d332ea9b..e57f9eb9e 100644 --- a/plugins/rsyncd/tool_task.py +++ b/plugins/rsyncd/tool_task.py @@ -6,8 +6,15 @@ 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 +40,9 @@ def getTaskConf(): def getConfigData(): - try: + conf = getTaskConf() + if os.path.exists(conf): return json.loads(mw.readFile(getTaskConf())) - except: - pass return [] @@ -68,8 +74,6 @@ def createBgTaskByName(name, args): if res and res["id"] == cfg["task_id"]: print("计划任务已经存在!") return True - import crontab_api - api = crontab_api.crontab_api() period = args['period'] _hour = '' @@ -108,10 +112,10 @@ logs_file=$plugin_path/send/${rname}/run.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: cfg["task_id"] = task_id cfg["name"] = name @@ -126,12 +130,9 @@ def removeBgTask(): for x in range(len(cfg_list)): cfg = cfg_list[x] if "task_id" in cfg.keys() and cfg["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (cfg["task_id"],)).find() + 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 From beb6dd874ade7f026370ab810e92ba816b87f7a0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:41:55 +0800 Subject: [PATCH 237/965] update --- plugins/php-apt/index.py | 12 +++++------- plugins/php-apt/index_php_apt.py | 12 +++++------- plugins/php-yum/index.py | 12 +++++------- plugins/php-yum/index_php_yum.py | 12 +++++------- plugins/php/index.py | 12 +++++------- plugins/php/index_php.py | 17 +++++------------ 6 files changed, 30 insertions(+), 47 deletions(-) diff --git a/plugins/php-apt/index.py b/plugins/php-apt/index.py index b3906c33d..b5abc0efb 100755 --- a/plugins/php-apt/index.py +++ b/plugins/php-apt/index.py @@ -9,14 +9,12 @@ import json import shutil -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) -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) +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/php-apt/index_php_apt.py b/plugins/php-apt/index_php_apt.py index 0be394d49..2562967a4 100755 --- a/plugins/php-apt/index_php_apt.py +++ b/plugins/php-apt/index_php_apt.py @@ -8,14 +8,12 @@ import re import json import shutil -# reload(sys) -# sys.setdefaultencoding('utf8') +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 +import core.mw as mw if mw.isAppleSystem(): cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' diff --git a/plugins/php-yum/index.py b/plugins/php-yum/index.py index 2d0c87ad3..fefbed017 100755 --- a/plugins/php-yum/index.py +++ b/plugins/php-yum/index.py @@ -9,14 +9,12 @@ import json import shutil -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) -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) +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/php-yum/index_php_yum.py b/plugins/php-yum/index_php_yum.py index 1a77fb329..11ea23240 100755 --- a/plugins/php-yum/index_php_yum.py +++ b/plugins/php-yum/index_php_yum.py @@ -8,14 +8,12 @@ import re import json import shutil -# reload(sys) -# sys.setdefaultencoding('utf8') +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 +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/php/index.py b/plugins/php/index.py index ac905c358..f08c86485 100755 --- a/plugins/php/index.py +++ b/plugins/php/index.py @@ -11,14 +11,12 @@ import shutil # reload(sys) # sys.setdefaultencoding('utf8') -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) -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) +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/php/index_php.py b/plugins/php/index_php.py index bfd9bca31..617613b1e 100755 --- a/plugins/php/index_php.py +++ b/plugins/php/index_php.py @@ -8,19 +8,12 @@ import re import json import shutil -# reload(sys) -# sys.setdefaultencoding('utf8') -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) -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) +import core.mw as mw app_debug = False if mw.isAppleSystem(): From 141c56fbc2ddcd0c21a9edd7bd547ec7a2555521 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:46:05 +0800 Subject: [PATCH 238/965] update --- plugins/op_waf/index.py | 8 ++++++-- plugins/op_waf/tool_task.py | 22 ++++++++++++---------- plugins/pgadmin/index.py | 11 +++++++---- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/plugins/op_waf/index.py b/plugins/op_waf/index.py index 0f14caaf8..f6db7a141 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 diff --git a/plugins/op_waf/tool_task.py b/plugins/op_waf/tool_task.py index 1ee26c173..88416b9ef 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 [] @@ -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/pgadmin/index.py b/plugins/pgadmin/index.py index 178d42804..526f5f145 100755 --- a/plugins/pgadmin/index.py +++ b/plugins/pgadmin/index.py @@ -7,9 +7,12 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw -import site_api +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(): @@ -90,7 +93,7 @@ def contentReplace(content): content = content.replace('{$APP_PATH}', service_path+'/'+getPluginName()+'/data') port = cfg["port"] - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' content = re.sub(rep, "listen " + port + ';', content) return content From 941285d168a4b330cd435ad0836465bc9126c336 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 05:48:35 +0800 Subject: [PATCH 239/965] update --- plugins/clean/index.py | 8 ++++++-- plugins/clean/tool_task.py | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 11 deletions(-) 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/tool_task.py b/plugins/clean/tool_task.py index bf9654e2d..a90862a06 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", @@ -97,7 +101,7 @@ logs_file=$plugin_path/${rname}.log 'urladdress': '', } - 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 +114,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)) From c9f03c0f691d2e78aa36e1b9d456a495c71a2fe5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:02:41 +0800 Subject: [PATCH 240/965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E6=96=B9=E6=B3=95=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/acme_pandominassl_apply/index.py | 8 +++- plugins/alist/index.py | 8 +++- plugins/backup_ftp/index.py | 13 +++--- plugins/cloudreve/index.py | 8 +++- plugins/data_query/index.py | 8 +++- plugins/data_query/nosql_memcached.py | 11 ++--- plugins/data_query/nosql_mongodb.py | 11 ++--- plugins/data_query/nosql_redis.py | 10 ++--- plugins/data_query/sql_mysql.py | 11 ++--- plugins/docker/index.py | 8 +++- plugins/dynamic-tracking/index.py | 9 ++++- plugins/fail2ban/index.py | 8 +++- plugins/gdrive/index.py | 10 +++-- plugins/gitea/index.py | 13 +++--- plugins/gogs/index.py | 20 +++++---- plugins/gorse/index.py | 8 +++- plugins/haproxy/index.py | 8 +++- plugins/imail/index.py | 8 +++- plugins/keepalived/index.py | 8 +++- plugins/lvs/index.py | 8 +++- plugins/mariadb/index.py | 9 +++-- plugins/mariadb/index_mariadb.py | 8 +++- plugins/memcached/index.py | 8 +++- plugins/migration_api/index.py | 8 +++- plugins/mongodb/index.py | 8 +++- plugins/mosquitto/index.py | 8 +++- plugins/msonedrive/index.py | 10 +++-- plugins/mysql-apt/index.py | 9 +++-- plugins/mysql-apt/index_mysql_apt.py | 7 +++- plugins/mysql-yum/index.py | 10 ++--- plugins/mysql-yum/index_mysql_yum.py | 7 +++- plugins/mysql/index.py | 9 +++-- plugins/mysql/index_mysql.py | 8 +++- plugins/nezha/index.py | 8 +++- plugins/op_load_balance/index.py | 8 +++- plugins/phpmyadmin/index.py | 14 ++++--- plugins/postgresql/index.py | 8 +++- plugins/pureftp/index.py | 8 +++- plugins/redis/index.py | 8 +++- plugins/simpleping/index.py | 8 +++- plugins/sphinx/index.py | 8 +++- plugins/sphinx/tool_cron.py | 47 +++++++++------------- plugins/supervisor/index.py | 8 +++- plugins/swap/index.py | 8 +++- plugins/sys-opt/index.py | 8 +++- plugins/system_safe/system_safe.py | 9 ++++- plugins/tamper_proof_py/index.py | 8 +++- plugins/task_manager/index.py | 8 +++- plugins/task_manager/task_manager_index.py | 11 ++--- plugins/tgbot/index.py | 8 +++- plugins/tgclient/index.py | 8 +++- plugins/varnish/index.py | 8 +++- plugins/webhook/index.py | 8 +++- plugins/webhook/webhook_index.py | 8 +++- plugins/webssh/index.py | 8 +++- plugins/xhprof/index.py | 12 ++++-- plugins/zabbix/index.py | 8 +++- plugins/zabbix_agent/index.py | 8 +++- 58 files changed, 368 insertions(+), 190 deletions(-) diff --git a/plugins/acme_pandominassl_apply/index.py b/plugins/acme_pandominassl_apply/index.py index e7ba431a2..6e31800bc 100755 --- a/plugins/acme_pandominassl_apply/index.py +++ b/plugins/acme_pandominassl_apply/index.py @@ -8,8 +8,12 @@ import re import requests 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 app_debug = False if mw.isAppleSystem(): diff --git a/plugins/alist/index.py b/plugins/alist/index.py index 3f14b33af..7399dcf7e 100755 --- a/plugins/alist/index.py +++ b/plugins/alist/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/backup_ftp/index.py b/plugins/backup_ftp/index.py index 34dd3f959..1f1403890 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) @@ -251,8 +255,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/cloudreve/index.py b/plugins/cloudreve/index.py index a312a2028..c89d411a4 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(): 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/nosql_memcached.py b/plugins/data_query/nosql_memcached.py index 4b392aad8..18b844edf 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): 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..8a3e10f4a 100755 --- a/plugins/data_query/nosql_redis.py +++ b/plugins/data_query/nosql_redis.py @@ -7,12 +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 +import core.mw as mw # def getPluginName(): diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index ee8c83fbf..6b9dc35e4 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -8,11 +8,12 @@ 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 +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/docker/index.py b/plugins/docker/index.py index 59488cead..128498d9a 100755 --- a/plugins/docker/index.py +++ b/plugins/docker/index.py @@ -7,8 +7,12 @@ import time 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 try: import docker diff --git a/plugins/dynamic-tracking/index.py b/plugins/dynamic-tracking/index.py index 12a4d4a93..a54965cdc 100755 --- a/plugins/dynamic-tracking/index.py +++ b/plugins/dynamic-tracking/index.py @@ -6,8 +6,13 @@ 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/fail2ban/index.py b/plugins/fail2ban/index.py index f2917dcdb..a1aa4e003 100755 --- a/plugins/fail2ban/index.py +++ b/plugins/fail2ban/index.py @@ -7,8 +7,12 @@ import time 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 app_debug = False if mw.isAppleSystem(): diff --git a/plugins/gdrive/index.py b/plugins/gdrive/index.py index edf1ed57b..bf940d4c6 100644 --- a/plugins/gdrive/index.py +++ b/plugins/gdrive/index.py @@ -13,9 +13,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) diff --git a/plugins/gitea/index.py b/plugins/gitea/index.py index 8aff8694e..40305aa35 100755 --- a/plugins/gitea/index.py +++ b/plugins/gitea/index.py @@ -7,15 +7,12 @@ import sys import re import subprocess -sys.path.append(os.getcwd() + "/class/core") -import mw - - -# 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(): diff --git a/plugins/gogs/index.py b/plugins/gogs/index.py index 5311feefc..20c7bc69e 100755 --- a/plugins/gogs/index.py +++ b/plugins/gogs/index.py @@ -5,19 +5,17 @@ import time import os import sys import re - - -sys.path.append(os.getcwd() + "/class/core") -import mw - - -# 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) - import psutil +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/gorse/index.py b/plugins/gorse/index.py index 35855b5f8..21465ef60 100755 --- a/plugins/gorse/index.py +++ b/plugins/gorse/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/haproxy/index.py b/plugins/haproxy/index.py index 452fa05e9..675ba84d0 100755 --- a/plugins/haproxy/index.py +++ b/plugins/haproxy/index.py @@ -8,8 +8,12 @@ import re import string import subprocess -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/imail/index.py b/plugins/imail/index.py index a79b8d576..a61ac5f3f 100755 --- a/plugins/imail/index.py +++ b/plugins/imail/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(): diff --git a/plugins/keepalived/index.py b/plugins/keepalived/index.py index 12455677b..ecd5e8a80 100755 --- a/plugins/keepalived/index.py +++ b/plugins/keepalived/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/lvs/index.py b/plugins/lvs/index.py index 9b7ca8c40..82a9f21a3 100755 --- a/plugins/lvs/index.py +++ b/plugins/lvs/index.py @@ -8,8 +8,12 @@ import re import string import subprocess -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/mariadb/index.py b/plugins/mariadb/index.py index 8e9c75f7a..ea61704bb 100755 --- a/plugins/mariadb/index.py +++ b/plugins/mariadb/index.py @@ -9,11 +9,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(): diff --git a/plugins/mariadb/index_mariadb.py b/plugins/mariadb/index_mariadb.py index 2eed99a8e..644f32576 100644 --- a/plugins/mariadb/index_mariadb.py +++ b/plugins/mariadb/index_mariadb.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(): diff --git a/plugins/memcached/index.py b/plugins/memcached/index.py index b6b47d343..617f33a2e 100755 --- a/plugins/memcached/index.py +++ b/plugins/memcached/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/migration_api/index.py b/plugins/migration_api/index.py index 2d8fd706e..425806cc5 100755 --- a/plugins/migration_api/index.py +++ b/plugins/migration_api/index.py @@ -9,8 +9,12 @@ import hashlib import json import subprocess -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/mongodb/index.py b/plugins/mongodb/index.py index cfb06584e..c288d66d3 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -9,8 +9,12 @@ import json import datetime import yaml -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/mosquitto/index.py b/plugins/mosquitto/index.py index 5ef4472f7..a1ffaf440 100755 --- a/plugins/mosquitto/index.py +++ b/plugins/mosquitto/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/msonedrive/index.py b/plugins/msonedrive/index.py index 2a451df2c..854ec3aec 100644 --- a/plugins/msonedrive/index.py +++ b/plugins/msonedrive/index.py @@ -13,9 +13,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) diff --git a/plugins/mysql-apt/index.py b/plugins/mysql-apt/index.py index 456d9aa11..09e79f3fc 100755 --- a/plugins/mysql-apt/index.py +++ b/plugins/mysql-apt/index.py @@ -9,11 +9,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 app_debug = False if mw.isAppleSystem(): diff --git a/plugins/mysql-apt/index_mysql_apt.py b/plugins/mysql-apt/index_mysql_apt.py index a61c2a015..323d051a9 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}\'' diff --git a/plugins/mysql-yum/index.py b/plugins/mysql-yum/index.py index 2ce17b7b3..aa61fefb5 100755 --- a/plugins/mysql-yum/index.py +++ b/plugins/mysql-yum/index.py @@ -9,12 +9,12 @@ import re import json -# reload(sys) -# sys.setdefaultencoding('utf-8') - -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}\'' diff --git a/plugins/mysql-yum/index_mysql_yum.py b/plugins/mysql-yum/index_mysql_yum.py index 1f7cc3a71..7252b633e 100644 --- a/plugins/mysql-yum/index_mysql_yum.py +++ b/plugins/mysql-yum/index_mysql_yum.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}\'' diff --git a/plugins/mysql/index.py b/plugins/mysql/index.py index 885536eb3..84e12e7cd 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(): diff --git a/plugins/mysql/index_mysql.py b/plugins/mysql/index_mysql.py index 791d9038f..89d987490 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(): diff --git a/plugins/nezha/index.py b/plugins/nezha/index.py index 7f4dc3db4..0fa578c8c 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(): diff --git a/plugins/op_load_balance/index.py b/plugins/op_load_balance/index.py index 59a646fb2..622c3025b 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 diff --git a/plugins/phpmyadmin/index.py b/plugins/phpmyadmin/index.py index eac12be55..5e8df07eb 100755 --- a/plugins/phpmyadmin/index.py +++ b/plugins/phpmyadmin/index.py @@ -7,9 +7,13 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw -import site_api +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.site import sites as MwSites app_debug = False if mw.isAppleSystem(): @@ -85,7 +89,7 @@ def getHomePage(): def getPhpVer(expect=55): - v = site_api.site_api().getPhpVersion() + v = MwSites.instance().getPhpVersion() is_find = False for i in range(len(v)): t = str(v[i]['version']) @@ -142,7 +146,7 @@ def contentReplace(content): content = content.replace('{$PMA_PATH}', cfg['path']) port = cfg["port"] - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' content = re.sub(rep, "listen " + port + ';', content) return content diff --git a/plugins/postgresql/index.py b/plugins/postgresql/index.py index c7217219d..22dfc7fff 100755 --- a/plugins/postgresql/index.py +++ b/plugins/postgresql/index.py @@ -17,8 +17,12 @@ import json # ps -ef | grep -v grep| grep run_info | awk '{print $2}' | xargs kill -9 # vi /etc/sysconfig/network-scripts/ifcfg-eth0 -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(): diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index 6bb40ff74..909f71ab9 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -6,8 +6,12 @@ import os import time import shutil -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/redis/index.py b/plugins/redis/index.py index 74c117897..3d451c02e 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/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/simpleping/index.py b/plugins/simpleping/index.py index 6565cf2f4..176536c90 100755 --- a/plugins/simpleping/index.py +++ b/plugins/simpleping/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/sphinx/index.py b/plugins/sphinx/index.py index ade4fca3d..9a04a5d22 100755 --- a/plugins/sphinx/index.py +++ b/plugins/sphinx/index.py @@ -8,8 +8,12 @@ import re import string import subprocess -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/sphinx/tool_cron.py b/plugins/sphinx/tool_cron.py index 063573d37..19a1ec6a0 100644 --- a/plugins/sphinx/tool_cron.py +++ b/plugins/sphinx/tool_cron.py @@ -6,8 +6,14 @@ 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 @@ -35,12 +41,10 @@ def getTaskDeltaConf(): conf = getServerDir() + "/cron_delta_config.json" return conf - 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", @@ -50,10 +54,9 @@ def getConfigData(): } def getConfigDeltaData(): - try: + conf = getTaskDeltaConf() + if os.path.exists(conf): return json.loads(mw.readFile(getTaskDeltaConf())) - except: - pass return { "task_id": -1, "period": "minute-n", @@ -80,15 +83,12 @@ def createBgTaskByName(name): return True if "task_id" in args and args["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (args["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (args["task_id"],)).find() 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 @@ -118,7 +118,7 @@ logs_file=$plugin_path/${rname}.log 'urladdress': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: args["task_id"] = task_id args["name"] = name @@ -132,15 +132,12 @@ def createBgTaskDeltaByName(name): return True if "task_id" in args and args["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (args["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (args["task_id"],)).find() 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 @@ -170,7 +167,7 @@ logs_file=$plugin_path/${rname}.log 'urladdress': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: args["task_id"] = task_id args["name"] = name @@ -183,9 +180,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)) @@ -198,9 +193,7 @@ def removeDeltaBgTask(): 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(getTaskDeltaConf(), json.dumps(cfg)) diff --git a/plugins/supervisor/index.py b/plugins/supervisor/index.py index efc9c6293..0fe4780b8 100755 --- a/plugins/supervisor/index.py +++ b/plugins/supervisor/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/swap/index.py b/plugins/swap/index.py index dddc1aac8..3fb07947c 100755 --- a/plugins/swap/index.py +++ b/plugins/swap/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/sys-opt/index.py b/plugins/sys-opt/index.py index 0b97057be..26a7a7bb0 100755 --- a/plugins/sys-opt/index.py +++ b/plugins/sys-opt/index.py @@ -7,8 +7,12 @@ import json import re import sys -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 def getPluginName(): diff --git a/plugins/system_safe/system_safe.py b/plugins/system_safe/system_safe.py index b454a8685..d0f0352d1 100755 --- a/plugins/system_safe/system_safe.py +++ b/plugins/system_safe/system_safe.py @@ -10,8 +10,13 @@ import psutil from datetime import datetime sys.dont_write_bytecode = True -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/tamper_proof_py/index.py b/plugins/tamper_proof_py/index.py index 446dee0c3..82c06070b 100755 --- a/plugins/tamper_proof_py/index.py +++ b/plugins/tamper_proof_py/index.py @@ -10,8 +10,12 @@ import psutil from datetime import datetime sys.dont_write_bytecode = True -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/task_manager/index.py b/plugins/task_manager/index.py index f2f22f90e..90173fb97 100755 --- a/plugins/task_manager/index.py +++ b/plugins/task_manager/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/task_manager/task_manager_index.py b/plugins/task_manager/task_manager_index.py index 9790b19b9..22a44fb91 100755 --- a/plugins/task_manager/task_manager_index.py +++ b/plugins/task_manager/task_manager_index.py @@ -11,11 +11,12 @@ import json from typing import List, Dict -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 app_debug = False diff --git a/plugins/tgbot/index.py b/plugins/tgbot/index.py index 95c1cbf19..0be333a8e 100755 --- a/plugins/tgbot/index.py +++ b/plugins/tgbot/index.py @@ -8,8 +8,12 @@ import re import json import base64 -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/tgclient/index.py b/plugins/tgclient/index.py index a10030ab9..ec2eb7238 100755 --- a/plugins/tgclient/index.py +++ b/plugins/tgclient/index.py @@ -8,8 +8,12 @@ import re import json import base64 -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/varnish/index.py b/plugins/varnish/index.py index 9b761e997..050e842a0 100755 --- a/plugins/varnish/index.py +++ b/plugins/varnish/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/webhook/index.py b/plugins/webhook/index.py index 4fb14d030..15189f727 100755 --- a/plugins/webhook/index.py +++ b/plugins/webhook/index.py @@ -7,8 +7,12 @@ import time 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 app_debug = False if mw.isAppleSystem(): diff --git a/plugins/webhook/webhook_index.py b/plugins/webhook/webhook_index.py index 5b2777da6..dc60912d3 100755 --- a/plugins/webhook/webhook_index.py +++ b/plugins/webhook/webhook_index.py @@ -7,8 +7,12 @@ import time 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 app_debug = False if mw.isAppleSystem(): diff --git a/plugins/webssh/index.py b/plugins/webssh/index.py index ad624bc32..4aacad870 100755 --- a/plugins/webssh/index.py +++ b/plugins/webssh/index.py @@ -7,8 +7,12 @@ import json import re import sys -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 class App(): diff --git a/plugins/xhprof/index.py b/plugins/xhprof/index.py index cbe06fd73..7bd54ee70 100755 --- a/plugins/xhprof/index.py +++ b/plugins/xhprof/index.py @@ -6,9 +6,13 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw -import site_api +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.site import sites as MwSites app_debug = False if mw.isAppleSystem(): @@ -70,7 +74,7 @@ def getHomePage(): def getPhpVer(expect=56): import json - v = site_api.site_api().getPhpVersion() + v = MwSites.instance().getPhpVersion() for i in range(len(v)): t = int(v[i]['version']) if (t >= expect): diff --git a/plugins/zabbix/index.py b/plugins/zabbix/index.py index e7901729b..06ea20e96 100755 --- a/plugins/zabbix/index.py +++ b/plugins/zabbix/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/zabbix_agent/index.py b/plugins/zabbix_agent/index.py index 2b48cd444..797a1f6b2 100755 --- a/plugins/zabbix_agent/index.py +++ b/plugins/zabbix_agent/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(): From c731077fba9bc81fe2ea16435d93b42118a35db9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:04:37 +0800 Subject: [PATCH 241/965] Update tool_task.py --- plugins/clean/tool_task.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/clean/tool_task.py b/plugins/clean/tool_task.py index a90862a06..ad4880660 100644 --- a/plugins/clean/tool_task.py +++ b/plugins/clean/tool_task.py @@ -68,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 From df0efaa7b1e84301be958f54c032dcb2ee23b717 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:05:18 +0800 Subject: [PATCH 242/965] update --- plugins/clean/tool_task.py | 2 +- plugins/sphinx/tool_cron.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/clean/tool_task.py b/plugins/clean/tool_task.py index ad4880660..cc762f7c9 100644 --- a/plugins/clean/tool_task.py +++ b/plugins/clean/tool_task.py @@ -96,7 +96,7 @@ logs_file=$plugin_path/${rname}.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } task_id = MwCrontab.instance().add(params) diff --git a/plugins/sphinx/tool_cron.py b/plugins/sphinx/tool_cron.py index 19a1ec6a0..e9859be0a 100644 --- a/plugins/sphinx/tool_cron.py +++ b/plugins/sphinx/tool_cron.py @@ -164,7 +164,7 @@ logs_file=$plugin_path/${rname}.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } task_id = MwCrontab.instance().add(params) From 315a3759c34fdac5e2f174be6c93f37f9969bbb6 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:10:07 +0800 Subject: [PATCH 243/965] Update tool_cron.py --- plugins/sphinx/tool_cron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sphinx/tool_cron.py b/plugins/sphinx/tool_cron.py index e9859be0a..84c6b11a6 100644 --- a/plugins/sphinx/tool_cron.py +++ b/plugins/sphinx/tool_cron.py @@ -115,7 +115,7 @@ logs_file=$plugin_path/${rname}.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } task_id = MwCrontab.instance().add(params) From b83e632328a112800390666c93058464ffbbbe6e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:12:24 +0800 Subject: [PATCH 244/965] Update tool_task.py --- plugins/rsyncd/tool_task.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/rsyncd/tool_task.py b/plugins/rsyncd/tool_task.py index e57f9eb9e..e38a29626 100644 --- a/plugins/rsyncd/tool_task.py +++ b/plugins/rsyncd/tool_task.py @@ -42,7 +42,7 @@ def getTaskConf(): def getConfigData(): conf = getTaskConf() if os.path.exists(conf): - return json.loads(mw.readFile(getTaskConf())) + return json.loads(mw.readFile(conf)) return [] @@ -69,8 +69,7 @@ def createBgTaskByName(name, args): return True if "task_id" in cfg.keys() and cfg["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (cfg["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: print("计划任务已经存在!") return True From 8d6e0f049c5877ecb0ada66042ed4f3742e0240b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:16:14 +0800 Subject: [PATCH 245/965] Update install.sh --- plugins/supervisor/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/supervisor/install.sh b/plugins/supervisor/install.sh index eeafc2985..2695ae7e6 100755 --- a/plugins/supervisor/install.sh +++ b/plugins/supervisor/install.sh @@ -42,8 +42,8 @@ Install_app() echo "${VERSION}" > $serverPath/supervisor/version.pl - cd ${rootPath} && python3 ${rootPath}/plugins/supervisor/index.py start - cd ${rootPath} && python3 ${rootPath}/plugins/supervisor/index.py initd_install + cd ${rootPath} && python3 plugins/supervisor/index.py start + cd ${rootPath} && python3 plugins/supervisor/index.py initd_install echo '安装完成[supervisor]' } From e9efc4cc3512bd0ce1ab6c8dc528af8a04dc6021 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:18:15 +0800 Subject: [PATCH 246/965] Update index.py --- plugins/supervisor/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/supervisor/index.py b/plugins/supervisor/index.py index 0fe4780b8..b1d4fbbb7 100755 --- a/plugins/supervisor/index.py +++ b/plugins/supervisor/index.py @@ -107,7 +107,7 @@ def initDreplace(): mw.writeFile(conf, conf_content) if os.path.exists(systemDir) and not os.path.exists(systemService): - activate_file = mw.getRunDir() + '/bin/activate' + activate_file = mw.getPanelDir() + '/bin/activate' if os.path.exists(activate_file): supervisord_bin = mw.execShell( 'source ' + activate_file + '&& which supervisord')[0].strip() From e76a0b12880b595fe6134a87aef26248873236e4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 06:25:28 +0800 Subject: [PATCH 247/965] Update index.py --- plugins/supervisor/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/supervisor/index.py b/plugins/supervisor/index.py index b1d4fbbb7..485992fce 100755 --- a/plugins/supervisor/index.py +++ b/plugins/supervisor/index.py @@ -89,7 +89,7 @@ def initDreplace(): systemService = systemDir + '/supervisor.service' systemServiceTpl = getPluginDir() + '/init.d/supervisor.service' - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() if not os.path.exists(confD): os.mkdir(confD) From 06f8a3c01a4529a104841f8eefcf8bf4ef0f5ffc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 16:38:08 +0800 Subject: [PATCH 248/965] Update site.py --- web/utils/site.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index 345cc5b1f..1e9265b11 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1755,7 +1755,7 @@ location ^~ {from} {\n\ def createAcmeFile(self, site_name, domains, email, force, renew): - print(site_name, domains,force,renew, email) + print(site_name, domains,force, renew, email) file = self.getHostConf(site_name) @@ -1778,15 +1778,14 @@ location ^~ {from} {\n\ if os.path.exists(proxy_dir_file): return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') - siteInfo = thisdb.getSitesByName(site_name) + site_info = thisdb.getSitesByName(site_name) path = self.getSitePath(site_name) if path == '': return mw.returnData(False, '【'+site_name+'】配置文件,异常!') - srcPath = siteInfo['path'] - - + srcPath = site_info['path'] + acme_dir = mw.getAcmeDir() if force == 'true': force_bool = True @@ -1819,10 +1818,8 @@ location ^~ {from} {\n\ self.writeAcmeLog('开始ACME申请...') log_file = self.acmeLogFile() + cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + execStr + ' >> ' + log_file - print(domains) - print(cmd) - return mw.returnData(False, '测试中!') result = mw.execShell(cmd) src_path = mw.getAcmeDomainDir(domains[0]) @@ -1863,7 +1860,7 @@ location ^~ {from} {\n\ # 写入配置文件 result = self.setSslConf(site_name) if not result['status']: - return mw.getJson(result) + return result result['csr'] = mw.readFile(src_cert) result['key'] = mw.readFile(src_key) From 80dd014be5cc23550fdf6d501a7d3295873e1ee3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 16:48:42 +0800 Subject: [PATCH 249/965] update --- web/admin/site/ssl.py | 9 +++++++++ web/utils/site.py | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 15b6a129d..16378177e 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -32,6 +32,15 @@ def get_ssl(): ssl_type = request.form.get('ssl_type', '') return MwSites.instance().getSsl(site_name, ssl_type) +# 删除证书 +@blueprint.route('/delete_ssl', endpoint='delete_ssl', methods=['POST']) +@panel_login_required +def delete_ssl(): + site_name = request.form.get('site_name', '') + ssl_type = request.form.get('ssl_type', '') + return MwSites.instance().deleteSsl(site_name, ssl_type) + + # 获取证书列表 @blueprint.route('/get_cert_list', endpoint='get_cert_list', methods=['GET','POST']) @panel_login_required diff --git a/web/utils/site.py b/web/utils/site.py index 1e9265b11..d6a683ffb 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1750,8 +1750,39 @@ location ^~ {from} {\n\ mw.writeFile(log_file, msg+"\n", "wb+") return True - def createAcmeMultiDomin(self): - pass + def deleteSsl(self,site_name,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.returnData(False, '使用中,先关闭再删除') + if os.path.exists(path): + mw.execShell('rm -rf ' + path) + else: + return mw.returnData(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.returnData(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.returnData(False, '使用中,先关闭再删除') + mw.execShell('rm -rf ' + ssl_acme_dir) + + mw.restartWeb() + return mw.returnData(True, '删除成功') def createAcmeFile(self, site_name, domains, email, force, renew): From 7956b6c8f4fb1201a73b6dc16088e5bf3410a2d7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 17:18:57 +0800 Subject: [PATCH 250/965] update --- web/admin/site/ssl.py | 9 ++++ web/utils/site.py | 105 +++++++++++++++++++++++++++++++----------- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 16378177e..5d7a5b048 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -32,6 +32,15 @@ def get_ssl(): ssl_type = request.form.get('ssl_type', '') return MwSites.instance().getSsl(site_name, ssl_type) +# 删除证书 +@blueprint.route('/close_ssl_conf', endpoint='close_ssl_conf', methods=['POST']) +@panel_login_required +def close_ssl_conf(): + site_name = request.form.get('siteName', '') + ssl_type = request.form.get('updateOf', '') + return MwSites.instance().closeSslConf(site_name) + + # 删除证书 @blueprint.route('/delete_ssl', endpoint='delete_ssl', methods=['POST']) @panel_login_required diff --git a/web/utils/site.py b/web/utils/site.py index d6a683ffb..c5cffa4da 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1750,6 +1750,56 @@ location ^~ {from} {\n\ mw.writeFile(log_file, msg+"\n", "wb+") return True + def closeSslConf(self, site_name): + file = self.getHostConf(site_name) + 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成功!', (site_name,)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, 'SSL已关闭!') + def deleteSsl(self,site_name,ssl_type): path = self.sslDir + '/' + site_name csr_path = path + '/fullchain.pem' @@ -1785,45 +1835,46 @@ location ^~ {from} {\n\ return mw.returnData(True, '删除成功') def createAcmeFile(self, site_name, domains, email, force, renew): - print(site_name, domains,force, renew, email) - file = self.getHostConf(site_name) - if os.path.exists(file): - siteConf = mw.readFile(file) - if siteConf.find('301-END') != -1: - return mw.returnData(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') + if not os.path.exists(file): + return mw.returnData(False, '配置异常!') - # 检测存在反向代理 - data_path = self.getProxyDataPath(site_name) - 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, site_name) - proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' - if os.path.exists(proxy_dir_file): - return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') + content = mw.readFile(file) + if content.find('301-END') != -1: + return mw.returnData(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') + + # 检测存在反向代理 + data_path = self.getProxyDataPath(site_name) + + 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, site_name) + proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' + if os.path.exists(proxy_dir_file): + return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') site_info = thisdb.getSitesByName(site_name) path = self.getSitePath(site_name) if path == '': return mw.returnData(False, '【'+site_name+'】配置文件,异常!') - srcPath = site_info['path'] - + src_path = site_info['path'] acme_dir = mw.getAcmeDir() + if force == 'true': force_bool = True if renew == 'true': - execStr = acme_dir + "/acme.sh --renew --yes-I-know-dns-manual-mode-enough-go-ahead-please" + cmd = acme_dir + "/acme.sh --renew --yes-I-know-dns-manual-mode-enough-go-ahead-please" else: - execStr = acme_dir + "/acme.sh --issue --force" + cmd = acme_dir + "/acme.sh --issue --force" # 确定主域名顺序 domainsTmp = [] @@ -1841,8 +1892,8 @@ location ^~ {from} {\n\ continue if domain.find('*.') != -1: return mw.returnData(False, '泛域名不能使用【文件验证】的方式申请证书!') - execStr += ' -w ' + path - execStr += ' -d ' + domain + cmd += ' -w ' + path + cmd += ' -d ' + domain domainCount += 1 if domainCount == 0: return mw.returnData(False, '请选择域名(不包括IP地址与泛域名)!') @@ -1850,7 +1901,7 @@ location ^~ {from} {\n\ self.writeAcmeLog('开始ACME申请...') log_file = self.acmeLogFile() - cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + execStr + ' >> ' + log_file + cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + cmd + ' >> ' + log_file result = mw.execShell(cmd) src_path = mw.getAcmeDomainDir(domains[0]) @@ -1875,7 +1926,7 @@ location ^~ {from} {\n\ if data['result']['status'] == 429: data['msg'] = msg data['status'] = False - return mw.getJson(data) + return data dst_path = self.sslDir + '/' + site_name dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 From c5500c57f16aaa6a624190a87bfea5dd32d8db6c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 17:30:45 +0800 Subject: [PATCH 251/965] Update site.py --- web/utils/site.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index c5cffa4da..729bceff4 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1847,18 +1847,15 @@ location ^~ {from} {\n\ # 检测存在反向代理 data_path = self.getProxyDataPath(site_name) - - data_content = mw.readFile(data_path) - if data_content != False: - try: - data = json.loads(data_content) - except: - pass + if os.path.exists(data_path): + data_content = mw.readFile(data_path) + data = json.loads(data_content) for proxy in data: proxy_dir = "{}/{}".format(self.proxyPath, site_name) proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' if os.path.exists(proxy_dir_file): return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') + site_info = thisdb.getSitesByName(site_name) path = self.getSitePath(site_name) From 7836622cd6519ec7257a2e147af6c4aa8868ba6f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 17:32:09 +0800 Subject: [PATCH 252/965] Update site.py --- web/utils/site.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index 729bceff4..9efb8d2fb 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1846,15 +1846,15 @@ location ^~ {from} {\n\ return mw.returnData(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') # 检测存在反向代理 - data_path = self.getProxyDataPath(site_name) - if os.path.exists(data_path): - data_content = mw.readFile(data_path) - data = json.loads(data_content) - for proxy in data: - proxy_dir = "{}/{}".format(self.proxyPath, site_name) - proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' - if os.path.exists(proxy_dir_file): - return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') + # data_path = self.getProxyDataPath(site_name) + # if os.path.exists(data_path): + # data_content = mw.readFile(data_path) + # data = json.loads(data_content) + # for proxy in data: + # proxy_dir = "{}/{}".format(self.proxyPath, site_name) + # proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' + # if os.path.exists(proxy_dir_file): + # return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') site_info = thisdb.getSitesByName(site_name) From d1f8cf9a8e68e4dd2767a7d6708b0f6ce3ff1fb3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 17:42:40 +0800 Subject: [PATCH 253/965] update --- web/admin/site/proxy.py | 10 ++++++++++ web/utils/site.py | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/web/admin/site/proxy.py b/web/admin/site/proxy.py index 55e267693..b4de66b52 100644 --- a/web/admin/site/proxy.py +++ b/web/admin/site/proxy.py @@ -47,6 +47,16 @@ def set_proxy(): pid = request.form.get('id', '') return MwSites.instance().setProxy(site_name,site_from,to,host,name,open_proxy, open_cache,cache_time, pid) +# 获取代理状态 +@blueprint.route('/set_proxy_status', endpoint='set_proxy_status', methods=['POST']) +@panel_login_required +def set_proxy_status(): + site_name = request.form.get("siteName", '') + status = request.form.get("status", '') + proxy_id = request.form.get("id", '') + return MwSites.instance().setProxyStatus(site_name,proxy_id,status) + + # 获取代理配置 @blueprint.route('/get_proxy_conf', endpoint='get_proxy_conf', methods=['POST']) @panel_login_required diff --git a/web/utils/site.py b/web/utils/site.py index 9efb8d2fb..e7c0f8e36 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1291,7 +1291,7 @@ class sites(object): mw.writeFile(vhost_file, content) # 设置 网站 反向代理列表 - def setProxy(self, site_name, site_from, to, host, name, open_proxy, open_cache, cache_time, pid): + def setProxy(self, site_name, site_from, to, host, name, open_proxy, open_cache, cache_time, site_id): from urllib.parse import urlparse if site_name == "" or site_from == "" or to == "" or host == "" or name == "": return mw.returnData(False, "必填项不能为空") @@ -1433,6 +1433,21 @@ location ^~ {from} {\n\ mw.restartWeb() return mw.returnData(True, "ok", {"hash": pid}) + def setProxyStatus(self, site_name, proxy_id, status): + if status == '' or site_name == '' or proxy_id == '': + return mw.returnData(False, "必填项不能为空!") + + conf_file = "{}/{}/{}.conf".format(self.proxyPath, site_name, proxy_id) + conf_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) + + if _status == '1': + mw.execShell('mv ' + conf_txt + ' ' + conf_file) + else: + mw.execShell('mv ' + conf_file + ' ' + conf_txt) + + mw.restartWeb() + return mw.returnData(True, "OK") + def getProxyConf(self, site_name, pid): if pid == '' or site_name == '': return mw.returnData(False, "必填项不能为空!") From dc9d114ca47aee24183fad8bf9f550abbbddd63e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 17:46:23 +0800 Subject: [PATCH 254/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index e7c0f8e36..f40739d2c 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1440,7 +1440,7 @@ location ^~ {from} {\n\ conf_file = "{}/{}/{}.conf".format(self.proxyPath, site_name, proxy_id) conf_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) - if _status == '1': + if status == '1': mw.execShell('mv ' + conf_txt + ' ' + conf_file) else: mw.execShell('mv ' + conf_file + ' ' + conf_txt) From 196ab188bb2c9d5c9851f3ef8a16a93760d61a74 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 17:53:07 +0800 Subject: [PATCH 255/965] Update site.py --- web/utils/site.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index f40739d2c..bb5ef8669 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1448,13 +1448,13 @@ location ^~ {from} {\n\ mw.restartWeb() return mw.returnData(True, "OK") - def getProxyConf(self, site_name, pid): + def getProxyConf(self, site_name, proxy_id): if pid == '' or site_name == '': return mw.returnData(False, "必填项不能为空!") - conf_file = "{}/{}/{}.conf".format(self.proxyPath, site_name, pid) + conf_file = "{}/{}/{}.conf".format(self.proxyPath, site_name, proxy_id) if not os.path.exists(conf_file): - conf_file = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, pid) + conf_file = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) if not os.path.exists(conf_file): return mw.returnData(False, "获取失败!") From df46b40c0fb6705d6fccc79ab0d52daa6e638b59 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 17:53:34 +0800 Subject: [PATCH 256/965] Update site.py --- web/utils/site.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index bb5ef8669..c934d4e99 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1462,8 +1462,8 @@ location ^~ {from} {\n\ data = mw.readFile(conf_file) return mw.returnData(True, "ok", {"result": data}) - def delProxy(self, site_name, pid): - if pid == '' or site_name == '': + def delProxy(self, site_name, proxy_id): + if proxy_id == '' or site_name == '': return mw.returnData(False, "必填项不能为空!") try: @@ -1471,7 +1471,7 @@ location ^~ {from} {\n\ 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"] == pid: + if item["id"] == proxy_id: data.remove(item) break # write database @@ -1481,7 +1481,7 @@ location ^~ {from} {\n\ if len(data) == 0: self.operateProxyConf(site_name, 'stop') # remove conf file - cmd = "rm -rf {}/{}.conf*".format(self.getProxyPath(site_name), pid) + cmd = "rm -rf {}/{}.conf*".format(self.getProxyPath(site_name), proxy_id) mw.execShell(cmd) except: return mw.returnData(False, "删除反代失败!") From 116140f5df7574a9390e87a5f153de274c0d526b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:20:57 +0800 Subject: [PATCH 257/965] Update site.py --- web/utils/site.py | 88 +++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index c934d4e99..c7ff44153 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -156,8 +156,8 @@ class sites(object): return path[0:-1] return path - def getSitePath(self, siteName): - file = self.getHostConf(siteName) + def getSitePath(self, site_name): + file = self.getHostConf(site_name) if os.path.exists(file): conf = mw.readFile(file) rep = r'\s*root\s*(.+);' @@ -1448,6 +1448,34 @@ location ^~ {from} {\n\ mw.restartWeb() return mw.returnData(True, "OK") + + def closeProxyAll(self, site_name): + self.close_proxy = [] + proxy_path = self.getProxyDataPath(site_name) + if os.path.exists(proxy_path): + content = mw.readFile(proxy_path) + data = json.loads(content) + for proxy in data: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + proxy_conf = proxy_dir + '/' + proxy['id'] + '.conf' + proxy_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy['id']) + if os.path.exists(proxy_conf): + self.close_proxy.append(proxy['id']) + mw.execShell('mv ' + proxy_conf + ' ' + proxy_txt) + return True + + def openProxyByOpen(self, site_name): + for proxy_id in self.close_proxy: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + proxy_conf = proxy_dir + '/' + proxy_id + '.conf' + proxy_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) + if os.path.exists(proxy_conf): + mw.execShell('mv ' + proxy_txt + ' ' + proxy_conf) + self.close_proxy = [] + return True + + + def getProxyConf(self, site_name, proxy_id): if pid == '' or site_name == '': return mw.returnData(False, "必填项不能为空!") @@ -1489,8 +1517,6 @@ location ^~ {from} {\n\ mw.restartWeb() return mw.returnData(True, "删除反代成功!") - - # 是否跳转到https def isToHttps(self, site_name): file = self.getHostConf(site_name) @@ -1754,7 +1780,7 @@ location ^~ {from} {\n\ def writeAcmeLog(self,msg): log_file = self.acmeLogFile() - mw.writeFile(log_file, msg+"\n", "wb+") + mw.writeFile(log_file, msg+"\n", 'w+') return True def letLogFile(self): @@ -1852,25 +1878,16 @@ location ^~ {from} {\n\ def createAcmeFile(self, site_name, domains, email, force, renew): print(site_name, domains,force, renew, email) - file = self.getHostConf(site_name) - if not os.path.exists(file): + site_conf = self.getHostConf(site_name) + if not os.path.exists(site_conf): return mw.returnData(False, '配置异常!') - content = mw.readFile(file) + content = mw.readFile(site_conf) if content.find('301-END') != -1: return mw.returnData(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') - # 检测存在反向代理 - # data_path = self.getProxyDataPath(site_name) - # if os.path.exists(data_path): - # data_content = mw.readFile(data_path) - # data = json.loads(data_content) - # for proxy in data: - # proxy_dir = "{}/{}".format(self.proxyPath, site_name) - # proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' - # if os.path.exists(proxy_dir_file): - # return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') - + # 关闭反向代理 + self.closeProxyAll(site_name) site_info = thisdb.getSitesByName(site_name) path = self.getSitePath(site_name) @@ -1889,25 +1906,25 @@ location ^~ {from} {\n\ cmd = acme_dir + "/acme.sh --issue --force" # 确定主域名顺序 - domainsTmp = [] + t = [] if site_name in domains: - domainsTmp.append(site_name) - for domainTmp in domains: - if domainTmp == site_name: + t.append(site_name) + for dd in domains: + if dd == site_name: continue - domainsTmp.append(domainTmp) - domains = domainsTmp + t.append(dd) + domains = t - domainCount = 0 - for domain in domains: - if mw.checkIp(domain): + domain_nums = 0 + for d in domains: + if mw.checkIp(d): continue - if domain.find('*.') != -1: + if d.find('*.') != -1: return mw.returnData(False, '泛域名不能使用【文件验证】的方式申请证书!') cmd += ' -w ' + path - cmd += ' -d ' + domain - domainCount += 1 - if domainCount == 0: + cmd += ' -d ' + d + domain_nums += 1 + if domain_nums == 0: return mw.returnData(False, '请选择域名(不包括IP地址与泛域名)!') self.writeAcmeLog('开始ACME申请...') @@ -1916,6 +1933,9 @@ location ^~ {from} {\n\ cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + cmd + ' >> ' + log_file result = mw.execShell(cmd) + # 开启代理 + self.openProxyByOpen(site_name) + src_path = mw.getAcmeDomainDir(domains[0]) src_cert = src_path + '/fullchain.cer' src_key = src_path + '/' + domains[0] + '.key' @@ -1933,8 +1953,7 @@ location ^~ {from} {\n\ data['msg'] = msg data['result'] = {} if result[1].find('new-authz error:') != -1: - data['result'] = json.loads( - re.search("{.+}", result[1]).group()) + data['result'] = json.loads(re.search("{.+}", result[1]).group()) if data['result']['status'] == 429: data['msg'] = msg data['status'] = False @@ -2073,6 +2092,7 @@ export PATH return mw.returnData(False, '正在申请或更新SSL中...') if apply_type == 'file': + print('apply_type',apply_type) return self.createAcmeFile(site_name, domains, email,force,renew) elif apply_type == 'dns': return self.createAcmeDns(site_name, domains, dnspai, wildcard_domain,force, renew) From f2dac6e1e6f3722db14d9637651d4a4c7c565d69 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:28:30 +0800 Subject: [PATCH 258/965] Update site.py --- web/utils/site.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/utils/site.py b/web/utils/site.py index c7ff44153..e9a370dbc 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1462,6 +1462,7 @@ location ^~ {from} {\n\ if os.path.exists(proxy_conf): self.close_proxy.append(proxy['id']) mw.execShell('mv ' + proxy_conf + ' ' + proxy_txt) + mw.restartWeb() return True def openProxyByOpen(self, site_name): From 06bdad4ab09de98f724a2294d8cdd939cdbe9791 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:31:04 +0800 Subject: [PATCH 259/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index e9a370dbc..e7d70651d 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1462,7 +1462,7 @@ location ^~ {from} {\n\ if os.path.exists(proxy_conf): self.close_proxy.append(proxy['id']) mw.execShell('mv ' + proxy_conf + ' ' + proxy_txt) - mw.restartWeb() + mw.restartWeb() return True def openProxyByOpen(self, site_name): From 3f0f21989649ccc1daa43e29f8bada634abd0fc3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:31:44 +0800 Subject: [PATCH 260/965] Update site.py --- web/utils/site.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index e7d70651d..82c725998 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1472,7 +1472,11 @@ location ^~ {from} {\n\ proxy_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) if os.path.exists(proxy_conf): mw.execShell('mv ' + proxy_txt + ' ' + proxy_conf) - self.close_proxy = [] + + + if len(self.close_proxy) > 0: + mw.restartWeb() + self.close_proxy = [] return True From d0742244477871922384003e6834d0b3ce615b34 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:43:03 +0800 Subject: [PATCH 261/965] Update site.py --- web/utils/site.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index 82c725998..611a428d2 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1470,10 +1470,9 @@ location ^~ {from} {\n\ proxy_dir = "{}/{}".format(self.proxyPath, site_name) proxy_conf = proxy_dir + '/' + proxy_id + '.conf' proxy_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) - if os.path.exists(proxy_conf): + if os.path.exists(proxy_txt): mw.execShell('mv ' + proxy_txt + ' ' + proxy_conf) - if len(self.close_proxy) > 0: mw.restartWeb() self.close_proxy = [] From 1ab7c2191011080ebb11777962c3fab3598d6b69 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:50:08 +0800 Subject: [PATCH 262/965] update --- web/admin/site/proxy.py | 4 ++-- web/utils/site.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/web/admin/site/proxy.py b/web/admin/site/proxy.py index b4de66b52..5c333845a 100644 --- a/web/admin/site/proxy.py +++ b/web/admin/site/proxy.py @@ -44,8 +44,8 @@ def set_proxy(): open_proxy = request.form.get('open_proxy', '') open_cache = request.form.get('open_cache', '') cache_time = request.form.get('cache_time', '') - pid = request.form.get('id', '') - return MwSites.instance().setProxy(site_name,site_from,to,host,name,open_proxy, open_cache,cache_time, pid) + proxy_id = request.form.get('id', '') + return MwSites.instance().setProxy(site_name,site_from,to,host,name,open_proxy, open_cache,cache_time, proxy_id) # 获取代理状态 @blueprint.route('/set_proxy_status', endpoint='set_proxy_status', methods=['POST']) diff --git a/web/utils/site.py b/web/utils/site.py index 611a428d2..cb4dd2eee 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1291,7 +1291,7 @@ class sites(object): mw.writeFile(vhost_file, content) # 设置 网站 反向代理列表 - def setProxy(self, site_name, site_from, to, host, name, open_proxy, open_cache, cache_time, site_id): + def setProxy(self, site_name, site_from, to, host, name, open_proxy, open_cache, cache_time, proxy_id): from urllib.parse import urlparse if site_name == "" or site_from == "" or to == "" or host == "" or name == "": return mw.returnData(False, "必填项不能为空") @@ -1313,8 +1313,8 @@ class sites(object): data = json.loads(data_content) if data_content != "" else [] proxy_action = 'add' - if pid == "": - pid = mw.md5("{}".format(name)) + if proxy_id == "": + proxy_id = mw.md5("{}".format(name)) else: proxy_action = 'edit' @@ -1382,8 +1382,8 @@ location ^~ {from} {\n\ tpl = tpl.replace("{proxy_cache}", tpl_proxy_nocache, 999) - conf_proxy = "{}/{}.conf".format(self.getProxyPath(site_name), pid) - conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(site_name), pid) + conf_proxy = "{}/{}.conf".format(self.getProxyPath(site_name), proxy_id) + conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(site_name), proxy_id) mw.writeFile(conf_proxy, tpl) rule_test = mw.checkWebConfig() @@ -1393,7 +1393,7 @@ location ^~ {from} {\n\ if proxy_action == "add": # 添加代理 - pid = mw.md5("{}".format(name)) + proxy_id = mw.md5("{}".format(name)) for item in data: if item["name"] == name: return mw.returnData(False, "名称重复!") @@ -1413,7 +1413,7 @@ location ^~ {from} {\n\ # 修改代理 dindex = -1 for x in range(len(data)): - if data[x]["id"] == pid: + if data[x]["id"] == proxy_id: dindex = x break if dindex < 0: @@ -1431,7 +1431,7 @@ location ^~ {from} {\n\ mw.writeFile(proxy_site_path, json.dumps(data)) self.operateProxyConf(site_name, 'start') mw.restartWeb() - return mw.returnData(True, "ok", {"hash": pid}) + return mw.returnData(True, "ok", {"hash": proxy_id}) def setProxyStatus(self, site_name, proxy_id, status): if status == '' or site_name == '' or proxy_id == '': From 3da3453503a2fa71dfd9ed1aa4a19caee50cb434 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:53:38 +0800 Subject: [PATCH 263/965] Update cli.sh --- cli.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli.sh b/cli.sh index d4a3f95dc..77cc72203 100755 --- a/cli.sh +++ b/cli.sh @@ -82,14 +82,14 @@ mw_start_bgtask(){ mw_stop() { PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` - for i in $PLIST + for i in ($PLIST) do kill -9 $i > /dev/null 2>&1 done - pids=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'` - arr=($pids) - for p in ${arr[@]} + PIDS=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'` + ARR=($PIDS) + for p in ${ARR[@]} do kill -9 $p > /dev/null 2>&1 done From cd069b24c62d7263b2bfc0366d3b8292724b1997 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:54:00 +0800 Subject: [PATCH 264/965] Update cli.sh --- cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli.sh b/cli.sh index 77cc72203..c5cdc97db 100755 --- a/cli.sh +++ b/cli.sh @@ -82,7 +82,7 @@ mw_start_bgtask(){ mw_stop() { PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` - for i in ($PLIST) + for i in ${($PLIST[@])} do kill -9 $i > /dev/null 2>&1 done From 6329ce3fd68e7cb58ec39552aa0314bb7fe09e71 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:54:28 +0800 Subject: [PATCH 265/965] Update cli.sh --- cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli.sh b/cli.sh index c5cdc97db..b451de912 100755 --- a/cli.sh +++ b/cli.sh @@ -82,7 +82,7 @@ mw_start_bgtask(){ mw_stop() { PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` - for i in ${($PLIST[@])} + for i in ${($PLIST)[@]} do kill -9 $i > /dev/null 2>&1 done From 5ea712b9439df68f1f5081b453cf775274bf6613 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 21:56:23 +0800 Subject: [PATCH 266/965] Update cli.sh --- cli.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/cli.sh b/cli.sh index b451de912..a2f173b8f 100755 --- a/cli.sh +++ b/cli.sh @@ -84,6 +84,7 @@ mw_stop() PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` for i in ${($PLIST)[@]} do + echo "kill -9 $i > /dev/null 2>&1" kill -9 $i > /dev/null 2>&1 done From 590a246fc01ee1ee09e9b12d54747c2e6912eb50 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 22:03:51 +0800 Subject: [PATCH 267/965] Update mw.tpl --- scripts/init.d/mw.tpl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 4ba5b9339..3d8364a67 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -135,6 +135,12 @@ mw_stop_panel() do kill -9 $p > /dev/null 2>&1 done + + PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` + for i in ${($PLIST)[@]} + do + kill -9 $i > /dev/null 2>&1 + done echo -e "\033[32mdone\033[0m" } From c560d3fced875633dfb6c16a9fc2b6e914a29456 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 22:04:07 +0800 Subject: [PATCH 268/965] Update mw.tpl --- scripts/init.d/mw.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 3d8364a67..e879c98ca 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -130,8 +130,8 @@ mw_stop_panel() rm -f $pidfile fi - arr=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'` - for p in ${arr[@]} + ARR=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'` + for p in ${ARR[@]} do kill -9 $p > /dev/null 2>&1 done From 729ea2df8f51f90afea52ce5061af288d1ade49b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 22:04:31 +0800 Subject: [PATCH 269/965] Update cli.sh --- cli.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/cli.sh b/cli.sh index a2f173b8f..b451de912 100755 --- a/cli.sh +++ b/cli.sh @@ -84,7 +84,6 @@ mw_stop() PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` for i in ${($PLIST)[@]} do - echo "kill -9 $i > /dev/null 2>&1" kill -9 $i > /dev/null 2>&1 done From 7d83f29db7a24cb5198b8e9a239a79a105c6b589 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 24 Nov 2024 22:07:47 +0800 Subject: [PATCH 270/965] Update mw.tpl --- scripts/init.d/mw.tpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index e879c98ca..05df11e28 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -137,7 +137,8 @@ mw_stop_panel() done PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` - for i in ${($PLIST)[@]} + ARR=($PLIST) + for i in ${ARR[@]} do kill -9 $i > /dev/null 2>&1 done From 9c130746fb1dddf5fe015d2a2789cbdacda0d181 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:29:25 +0800 Subject: [PATCH 271/965] update --- cli.sh | 5 +- web/admin/dashboard/dashboard.py | 8 +- web/admin/site/redirect.py | 18 ++- web/static/app/site.js | 220 ++++++++++++++++++------------- web/utils/site.py | 95 +++++++++---- 5 files changed, 223 insertions(+), 123 deletions(-) diff --git a/cli.sh b/cli.sh index b451de912..084f81840 100755 --- a/cli.sh +++ b/cli.sh @@ -82,14 +82,13 @@ mw_start_bgtask(){ mw_stop() { PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` - for i in ${($PLIST)[@]} + for i in $PLIST do kill -9 $i > /dev/null 2>&1 done PIDS=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'` - ARR=($PIDS) - for p in ${ARR[@]} + for p in $PIDS do kill -9 $p > /dev/null 2>&1 done diff --git a/web/admin/dashboard/dashboard.py b/web/admin/dashboard/dashboard.py index eb5b3d4fc..0fd7b28d8 100644 --- a/web/admin/dashboard/dashboard.py +++ b/web/admin/dashboard/dashboard.py @@ -37,16 +37,16 @@ def index(): @blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) def admin_safe_path(path): db_path = thisdb.getOption('admin_path') + name = thisdb.getOption('template', default='default') if isLogined(): return redirect('/') - - print(db_path,path) + # print(db_path,path) if db_path == path: - return render_template('default/login.html') + return render_template('%s/login.html' % name) unauthorized_status = thisdb.getOption('unauthorized_status') if unauthorized_status == '0': - return render_template('default/path.html') + return render_template('%s/path.html' % name) return Response(status=int(unauthorized_status)) # 仅针对webhook插件 diff --git a/web/admin/site/redirect.py b/web/admin/site/redirect.py index b1b2e4b72..b8e641cd5 100644 --- a/web/admin/site/redirect.py +++ b/web/admin/site/redirect.py @@ -43,21 +43,31 @@ def set_redirect(): keep_path = request.form.get("keep_path", '') # keep path return MwSites.instance().setRedirect(site_name, site_from, to, type, r_type, keep_path) + +# 设置重定向状态 +@blueprint.route('/set_redirect_status', endpoint='set_redirect_status', methods=['POST']) +@panel_login_required +def set_redirect_status(): + site_name = request.form.get("siteName", '') + status = request.form.get("status") + redirect_id = request.form.get("id", '') + return MwSites.instance().setRedirectStatus(site_name, redirect_id, status) + # 获取重定向配置 @blueprint.route('/get_redirect_conf', endpoint='get_redirect_conf', methods=['POST']) @panel_login_required def get_redirect_conf(): site_name = request.form.get("siteName", '') - rid = request.form.get("id", '') - return MwSites.instance().getRedirectConf(site_name, rid) + redirect_id = request.form.get("id", '') + return MwSites.instance().getRedirectConf(site_name, redirect_id) # 删除重定向配置 @blueprint.route('/del_redirect', endpoint='del_redirect', methods=['POST']) @panel_login_required def del_redirect(): site_name = request.form.get("siteName", '') - rid = request.form.get("id", '') - return MwSites.instance().delRedirect(site_name, rid) + redirect_id = request.form.get("id", '') + return MwSites.instance().delRedirect(site_name, redirect_id) diff --git a/web/static/app/site.js b/web/static/app/site.js index cd19d997f..ce2fcd4ec 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1373,70 +1373,75 @@ function to301(siteName, type, obj){ // 设置 页面展示 if(type == 1) { - obj = { + var obj = { + type: 1, + keep_path: 1, to: 'http://', from: '', r_type: '', + type: 'path' + }; + + var keep_path_ht = obj.keep_path == 1 ? 'checked="checked"' : ''; + var redirect_title = type == 1 ? '创建重定向' : '修改重定向[' + obj.redirectname + ']'; + layer.open({ type: 1, - type: 'path', - keep_path: 1 - } - var redirect_form = layer.open({ - type: 1, - skin: 'demo-class', - area: '650px', - title: type == 1 ? '创建重定向' : '修改重定向[' + obj.redirectname + ']', + area: ['650px','270px'], + title: redirect_title, closeBtn: 1, shift: 5, + btn: ['提交','关闭'], shadeClose: false, - content: "
                    " + - "
                    " + - "
                    " + - "保留URI参数" + - "" + - "
                    " + - "
                    " + - "
                    " + - "重定向类型" + - "
                    " + - "" + - "重定向方式" + - "
                    " + - "
                    " + - "
                    " + - "重定向源" + - "
                    " + - "" + - "目标URL" + - "" + - "
                    " + - "
                    " + - "" + - "
                    " + - "
                    " - }); - setTimeout(function() { - $('.btn-colse-prosy').click(function() { - layer.close(redirect_form); - }); - - $('.btn-submit-redirect').click(function() { + content: "
                    \ +
                    \ +
                    \ + 保留URI参数\ + \ + \ +
                    \ +
                    \ +
                    重定向类型\ +
                    \ + \ + 重定向方式\ + \ +
                    \ +
                    \ +
                    \ + 重定向源\ +
                    \ + \ + 目标URL\ + \ +
                    \ +
                    \ +
                    ", + success:function(index,layero){ + }, + yes:function(index,index1){ var keep_path = $('[name="keep_path"]').prop('checked') ? 1 : 0; var r_type = $('[name="r_type"]').val(); var type = $('[name="type"]').val(); var from = $('[name="from"]').val(); var to = $('[name="to"]').val(); - $.post('/site/set_redirect', {siteName: siteName,type: type,r_type: r_type,from: from,to: to,keep_path: keep_path}, function(data) { + var pdata = {siteName: siteName, type: type,r_type: r_type,from: from,to: to,keep_path: keep_path}; + $.post('/site/set_redirect', pdata, function(data) { if (data.status) { - layer.close(redirect_form); + layer.close(index); to301(siteName); } else { layer.msg(data.msg, {icon: 2}); } },'json'); - }); - }, 100); + } + }); } if (type == 2) { @@ -1516,40 +1521,53 @@ function to301(siteName, type, obj){ } var body = '
                    \ -
                    \ - \ -
                    \ -
                    \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                    重定向类型重定向方式保留URL参数操作
                    \ -
                    \ -
                    '; +
                    \ + \ +
                    \ +
                    \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    重定向类型重定向方式保留URL参数状态操作
                    \ +
                    \ + '; $("#webedit-con").html(body); var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_redirect','siteName='+siteName, function(res) { layer.close(loadT); $("#md-301-loading").remove(); - if (res.status === true) { - let data = res.data.result; + if (res.status) { + var data = res.data.result; data.forEach(function(item){ - lan_r_type = item.r_type == 0 ? "永久重定向" : "临时重定向" - keep_path = item.keep_path == 0 ? "不保留" : "保留" + var lan_r_type = item.r_type == 0 ? "永久重定向" : "临时重定向"; + var keep_path = item.keep_path == 0 ? "不保留" : "保留"; + + var switchProxy = ''; + if (!item['status']){ + switchProxy = ''; + } + let tmp = '\ '+item.r_from+'\ '+lan_r_type+'\ - '+keep_path+'\ - 详细 | 删除\ + '+keep_path+'\ + '+switchProxy+'\ + \ + 详细 | \ + 删除\ + \ '; $("#md-301-body").append(tmp); }) @@ -1560,6 +1578,28 @@ function to301(siteName, type, obj){ } +function toRedirect(siteName, redirect_id, type){ + if (type == 10 || type == 11) { + //[11]启动 或 停止[10] + var status = type == 10 ? '0' : '1'; + var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); + + var pdata = {siteName: siteName, 'status':status,'id':redirect_id }; + $.post('/site/set_redirect_status', pdata, function(rdata) { + layer.close(loading); + if (!rdata.status){ + layer.msg(res.msg, {time: 3000,icon: 2}); + return; + } + + showMsg("设置成功",function(){ + to301(siteName); + },{icon: 1,time:2000}); + },'json'); + return; + } +} + //反向代理 function toProxy(siteName, type, obj) { // 设置 页面展示 @@ -1828,27 +1868,27 @@ function toProxy(siteName, type, obj) { } var body = '
                    \ -
                    \ - \ -
                    \ -
                    \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                    名称代理目录目标地址缓存状态操作
                    \ -
                    \ -
                    '; +
                    \ + \ +
                    \ +
                    \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    名称代理目录目标地址缓存状态操作
                    \ +
                    \ + '; $("#webedit-con").html(body); var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); diff --git a/web/utils/site.py b/web/utils/site.py index cb4dd2eee..ae99a4489 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -128,20 +128,20 @@ class sites(object): mw.removeBackFile(path) return rdata - def getRewriteConf(self, siteName): - return self.rewritePath + '/' + siteName + '.conf' + def getRewriteConf(self, site_name): + return self.rewritePath + '/' + site_name + '.conf' - def getRedirectDataPath(self, siteName): - return "{}/{}/data.json".format(self.redirectPath, siteName) + def getRedirectDataPath(self, site_name): + return "{}/{}/data.json".format(self.redirectPath, site_name) - def getRedirectPath(self, siteName): - return "{}/{}".format(self.redirectPath, siteName) + def getRedirectPath(self, site_name): + return "{}/{}".format(self.redirectPath, site_name) - def getProxyDataPath(self, siteName): - return "{}/{}/data.json".format(self.proxyPath, siteName) + def getProxyDataPath(self, site_name): + return "{}/{}/data.json".format(self.proxyPath, site_name) - def getProxyPath(self, siteName): - return "{}/{}".format(self.proxyPath, siteName) + def getProxyPath(self, site_name): + return "{}/{}".format(self.proxyPath, site_name) def getDirBindRewrite(self, site_name, dir_name): return self.rewritePath + '/' + site_name + '_' + dir_name + '.conf' @@ -1084,8 +1084,10 @@ class sites(object): 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) + 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() @@ -1116,17 +1118,39 @@ class sites(object): # 获取重定向配置 - def getRedirect(self, siteName): - redirect_file = self.getRedirectDataPath(siteName) + def getRedirect(self, site_name): + redirect_file = self.getRedirectDataPath(site_name) if not os.path.exists(redirect_file): - mw.execShell("mkdir {}/{}".format(self.redirectPath, siteName)) + mw.execShell("mkdir " + self.getRedirectPath(site_name)) return mw.returnData(True, "no exists!", {"result": [], "count": 0}) content = mw.readFile(redirect_file) data = json.loads(content) - # 处理301信息 + + for i in range(len(data)): + redirect_dir = self.getRedirectPath(site_name) + redirect_file = redirect_dir + '/' + data[i]['id'] + '.conf' + if os.path.exists(redirect_file): + data[i]['status'] = True + else: + data[i]['status'] = False return mw.returnData(True, "ok", {"result": data, "count": len(data)}) + def setRedirectStatus(self, site_name, redirect_id, status): + if status == '' or site_name == '' or redirect_id == '': + return mw.returnData(False, "必填项不能为空!") + + conf_file = "{}/{}/{}.conf".format(self.redirectPath, site_name, redirect_id) + conf_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_id) + + if status == '1': + mw.execShell('mv ' + conf_txt + ' ' + conf_file) + else: + mw.execShell('mv ' + conf_file + ' ' + conf_txt) + + mw.restartWeb() + return mw.returnData(True, "OK") + # 操作 重定向配置 def operateRedirectConf(self, siteName, method='start'): vhost_file = self.getHostConf(siteName) @@ -1134,7 +1158,7 @@ class sites(object): cnf_301 = '''#301-START include %s/*.conf; - #301-END''' % (self.getRedirectPath( siteName)) + #301-END''' % (self.getRedirectPath(siteName,)) cnf_301_source = '#301-START' # print('operateRedirectConf', content.find('#301-END')) @@ -1213,12 +1237,12 @@ class sites(object): mw.restartWeb() return mw.returnData(True, "设置成功") - def getRedirectConf(self, siteName, rid): - if rid == '' or siteName == '': + def getRedirectConf(self, site_name, redirect_id): + if redirect_id == '' or site_name == '': return mw.returnData(False, "必填项不能为空!") - path = self.getRedirectPath(siteName) - conf = "{}/{}.conf".format(path, rid) + path = self.getRedirectPath(site_name) + conf = "{}/{}.conf".format(path, redirect_id) data = mw.readFile(conf) if data == False: return mw.returnData(False, "获取失败!") @@ -1407,7 +1431,7 @@ location ^~ {from} {\n\ "open_cache": open_cache, "open_proxy": open_proxy, "cache_time": cache_time, - "id": pid, + "id": proxy_id, }) else: # 修改代理 @@ -1478,6 +1502,33 @@ location ^~ {from} {\n\ self.close_proxy = [] return True + def closeRedirectAll(self, site_name): + self.close_redirect = [] + redirect_path = self.getRedirectDataPath(site_name) + if os.path.exists(redirect_path): + content = mw.readFile(redirect_path) + data = json.loads(content) + for r in data: + redirect_dir = "{}/{}".format(self.redirectPath, site_name) + redirect_conf = redirect_dir + '/' + r['id'] + '.conf' + redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, r['id']) + if os.path.exists(proxy_conf): + self.close_redirect.append(r['id']) + mw.execShell('mv ' + proxy_conf + ' ' + proxy_txt) + mw.restartWeb() + + def openProxyByOpen(self, site_name): + for redirect_id in self.close_redirect: + redirect_dir = "{}/{}".format(self.redirectPath, site_name) + redirect_conf = redirect_dir + '/' + redirect_id + '.conf' + redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_id) + if os.path.exists(proxy_txt): + mw.execShell('mv ' + redirect_txt + ' ' + redirect_conf) + + if len(self.close_redirect) > 0: + mw.restartWeb() + self.close_redirect = [] + return True def getProxyConf(self, site_name, proxy_id): From 5ebdbc74ade94e8e952997e55f344e79921255c9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:30:10 +0800 Subject: [PATCH 272/965] Update version.py --- web/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/version.py b/web/version.py index ffe400f04..31e7a2e8f 100644 --- a/web/version.py +++ b/web/version.py @@ -13,7 +13,7 @@ # 应用程序版本号组件 APP_RELEASE = 0 APP_REVISION = 18 -APP_SMALL_VERSION = 0.1 +APP_SMALL_VERSION = 0.2 # 应用程序版本后缀,例如“beta1”、“dev”。通常为空字符串GA发布。 APP_SUFFIX = '' From 1fa57de2c0950e3e9078d0cf7a8ef4bee8b25fd1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:35:54 +0800 Subject: [PATCH 273/965] Update site.py --- web/utils/site.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index ae99a4489..08c7d4ec3 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1517,7 +1517,7 @@ location ^~ {from} {\n\ mw.execShell('mv ' + proxy_conf + ' ' + proxy_txt) mw.restartWeb() - def openProxyByOpen(self, site_name): + def openRedirectByOpen(self, site_name): for redirect_id in self.close_redirect: redirect_dir = "{}/{}".format(self.redirectPath, site_name) redirect_conf = redirect_dir + '/' + redirect_id + '.conf' @@ -1931,18 +1931,14 @@ location ^~ {from} {\n\ return mw.returnData(True, '删除成功') def createAcmeFile(self, site_name, domains, email, force, renew): - print(site_name, domains,force, renew, email) - site_conf = self.getHostConf(site_name) if not os.path.exists(site_conf): return mw.returnData(False, '配置异常!') - content = mw.readFile(site_conf) - if content.find('301-END') != -1: - return mw.returnData(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') - # 关闭反向代理 self.closeProxyAll(site_name) + # 关闭重定向 + self.closeRedirectAll(site_name) site_info = thisdb.getSitesByName(site_name) path = self.getSitePath(site_name) @@ -1990,6 +1986,8 @@ location ^~ {from} {\n\ # 开启代理 self.openProxyByOpen(site_name) + # 开启重定向 + self.openRedirectByOpen(site_name) src_path = mw.getAcmeDomainDir(domains[0]) src_cert = src_path + '/fullchain.cer' @@ -2147,7 +2145,6 @@ export PATH return mw.returnData(False, '正在申请或更新SSL中...') if apply_type == 'file': - print('apply_type',apply_type) return self.createAcmeFile(site_name, domains, email,force,renew) elif apply_type == 'dns': return self.createAcmeDns(site_name, domains, dnspai, wildcard_domain,force, renew) From 6cd613a1722102cd4338c620619ec71b266f8ff2 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:38:50 +0800 Subject: [PATCH 274/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index 08c7d4ec3..fbf793b03 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1514,7 +1514,7 @@ location ^~ {from} {\n\ redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, r['id']) if os.path.exists(proxy_conf): self.close_redirect.append(r['id']) - mw.execShell('mv ' + proxy_conf + ' ' + proxy_txt) + mw.execShell('mv ' + redirect_conf + ' ' + redirect_txt) mw.restartWeb() def openRedirectByOpen(self, site_name): From 58950d581c73a8d5c9ca871fa061dbbf43b70d58 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:40:18 +0800 Subject: [PATCH 275/965] Update site.py --- web/utils/site.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index fbf793b03..e49850566 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1512,7 +1512,7 @@ location ^~ {from} {\n\ redirect_dir = "{}/{}".format(self.redirectPath, site_name) redirect_conf = redirect_dir + '/' + r['id'] + '.conf' redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, r['id']) - if os.path.exists(proxy_conf): + if os.path.exists(redirect_conf): self.close_redirect.append(r['id']) mw.execShell('mv ' + redirect_conf + ' ' + redirect_txt) mw.restartWeb() @@ -1522,7 +1522,7 @@ location ^~ {from} {\n\ redirect_dir = "{}/{}".format(self.redirectPath, site_name) redirect_conf = redirect_dir + '/' + redirect_id + '.conf' redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_id) - if os.path.exists(proxy_txt): + if os.path.exists(redirect_txt): mw.execShell('mv ' + redirect_txt + ' ' + redirect_conf) if len(self.close_redirect) > 0: From 6433d6fc509834bad0b5d2d42385f8dd06add2e4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:40:41 +0800 Subject: [PATCH 276/965] Update site.py --- web/utils/site.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index e49850566..2ea31e939 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1508,10 +1508,10 @@ location ^~ {from} {\n\ if os.path.exists(redirect_path): content = mw.readFile(redirect_path) data = json.loads(content) - for r in data: + for redirect_data in data: redirect_dir = "{}/{}".format(self.redirectPath, site_name) - redirect_conf = redirect_dir + '/' + r['id'] + '.conf' - redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, r['id']) + redirect_conf = redirect_dir + '/' + redirect_data['id'] + '.conf' + redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_data['id']) if os.path.exists(redirect_conf): self.close_redirect.append(r['id']) mw.execShell('mv ' + redirect_conf + ' ' + redirect_txt) From d56de6d827ef98e68c4f8e8737bcfe8ae7453a9a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:41:58 +0800 Subject: [PATCH 277/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index 2ea31e939..fbb3a83dc 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -1513,7 +1513,7 @@ location ^~ {from} {\n\ redirect_conf = redirect_dir + '/' + redirect_data['id'] + '.conf' redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_data['id']) if os.path.exists(redirect_conf): - self.close_redirect.append(r['id']) + self.close_redirect.append(redirect_data['id']) mw.execShell('mv ' + redirect_conf + ' ' + redirect_txt) mw.restartWeb() From a156c14248cfe4e53609b6e76cafd643d72d1c40 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:52:48 +0800 Subject: [PATCH 278/965] Update index.py --- plugins/mysql/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mysql/index.py b/plugins/mysql/index.py index 84e12e7cd..d40ad5c45 100755 --- a/plugins/mysql/index.py +++ b/plugins/mysql/index.py @@ -954,7 +954,7 @@ def __createUser(dbname, username, password, address): def getDbBackupListFunc(dbname=''): - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getFatherDir() + '/backup/database' blist = os.listdir(bkDir) r = [] From 741e1aedf6b4b4810b814a981cc7279ffa7f441a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 00:56:59 +0800 Subject: [PATCH 279/965] Update mw.py --- web/core/mw.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 0a04a42b5..118eb7d46 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -106,11 +106,19 @@ def getPanelTaskLog(): return getMWLogs() + '/panel_task.log' def getWwwDir(): - file = getPanelDir() + '/data/site.pl' - if os.path.exists(file): - return readFile(file).strip() - return getFatherDir() + '/wwwroot' + import thisdb + site_path = thisdb.getOption('site_path') + return site_path +def getBackupDir(): + import thisdb + backup_path = thisdb.getOption('backup_path') + return backup_path + +def setBackupDir(bdir): + import thisdb + thisdb.setOption('backup_path', bdir) + return True def getPanelPort(): port_file = getPanelDir()+'/data/port.pl' From 3340112c110d05cbca80a34edfff3faaf9423690 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:01:20 +0800 Subject: [PATCH 280/965] update --- plugins/mysql-apt/index_mysql_apt.py | 6 ++-- plugins/mysql-yum/index.py | 41 +++++++++++++--------------- plugins/mysql-yum/index_mysql_yum.py | 6 ++-- plugins/mysql/index.py | 2 +- plugins/mysql/index_mysql.py | 6 ++-- 5 files changed, 29 insertions(+), 32 deletions(-) diff --git a/plugins/mysql-apt/index_mysql_apt.py b/plugins/mysql-apt/index_mysql_apt.py index 323d051a9..d252c1cea 100644 --- a/plugins/mysql-apt/index_mysql_apt.py +++ b/plugins/mysql-apt/index_mysql_apt.py @@ -51,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() @@ -59,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() @@ -67,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-yum/index.py b/plugins/mysql-yum/index.py index aa61fefb5..470dfdaeb 100755 --- a/plugins/mysql-yum/index.py +++ b/plugins/mysql-yum/index.py @@ -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() @@ -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) @@ -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'] diff --git a/plugins/mysql-yum/index_mysql_yum.py b/plugins/mysql-yum/index_mysql_yum.py index 7252b633e..79e7bbf83 100644 --- a/plugins/mysql-yum/index_mysql_yum.py +++ b/plugins/mysql-yum/index_mysql_yum.py @@ -51,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() @@ -59,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() @@ -67,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/index.py b/plugins/mysql/index.py index d40ad5c45..4181c93cc 100755 --- a/plugins/mysql/index.py +++ b/plugins/mysql/index.py @@ -954,7 +954,7 @@ def __createUser(dbname, username, password, address): def getDbBackupListFunc(dbname=''): - bkDir = mw.getFatherDir() + '/backup/database' + bkDir = mw.getBackupDir() + '/database' blist = os.listdir(bkDir) r = [] diff --git a/plugins/mysql/index_mysql.py b/plugins/mysql/index_mysql.py index 89d987490..d06228d4b 100644 --- a/plugins/mysql/index_mysql.py +++ b/plugins/mysql/index_mysql.py @@ -52,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() @@ -60,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() @@ -68,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() From 6603a49d7bb2dfff067583c1482ad376a723e53c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:01:55 +0800 Subject: [PATCH 281/965] Update index.py --- plugins/msonedrive/index.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/msonedrive/index.py b/plugins/msonedrive/index.py index 854ec3aec..6a34658ed 100644 --- a/plugins/msonedrive/index.py +++ b/plugins/msonedrive/index.py @@ -285,8 +285,7 @@ def backupAllFunc(stype): bk_name = stype find_path = backup_dir + '/' + bk_name + '/' + bk_prefix + '_' + 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}'" # print(find_new_file) From 00c0c592fce0fab8762e41120758f37da1da1254 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:04:38 +0800 Subject: [PATCH 282/965] Update index.py --- plugins/mongodb/index.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/mongodb/index.py b/plugins/mongodb/index.py index c288d66d3..62c1d4ff5 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -1387,15 +1387,15 @@ def importDbBackup(): port = getConfPort() - file_tgz = mw.getRootDir() + '/backup/database/' + file - file_dir = mw.getRootDir() + '/backup/database/' + file.replace('.tar.gz','') + file_tgz = mw.getBackupDir() + '/database/' + file + file_dir = mw.getBackupDir() + '/database/' + file.replace('.tar.gz','') if not os.path.exists(file_dir): mw.execShell("mkdir -p "+file_dir) # print(os.path.exists(file_tgz)) if os.path.exists(file_tgz): - cmd = 'cd ' + mw.getRootDir() + '/backup/database && tar -xzvf ' + file + " -C "+file_dir + cmd = 'cd ' + mw.getBackupDir() + '/database && tar -xzvf ' + file + " -C "+file_dir # print(cmd) mw.execShell(cmd) @@ -1549,8 +1549,8 @@ def installPreInspection(version): def uninstallPreInspection(version): stop() - import plugins_api - plugins_api.plugins_api().removeIndex(getPluginName(), version) + from utils.plugin import plugin as MwPlugin + MwPlugin.instance().removeIndex(getPluginName(), version) return "请手动删除MongoDB[{}]
                    rm -rf {}".format(version, getServerDir()) From d7fe140ff2a9e4a819cbb32975370eb117524ff4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:13:47 +0800 Subject: [PATCH 283/965] update --- plugins/acme_pandominassl_apply/index.py | 2 +- plugins/alist/index.py | 2 +- plugins/cloudreve/index.py | 2 +- plugins/docker/index.py | 8 ++++---- plugins/fail2ban/index.py | 2 +- plugins/gitea/index.py | 4 ++-- plugins/gogs/index.py | 4 ++-- plugins/gorse/index.py | 2 +- plugins/haproxy/index.py | 2 +- plugins/imail/index.py | 2 +- plugins/mariadb/index.py | 4 ++-- plugins/mariadb/scripts/backup.py | 4 ++-- plugins/migration_api/index.py | 4 ++-- plugins/mongodb/index.py | 15 +++++++-------- plugins/mongodb/scripts/backup.py | 2 +- plugins/mysql-apt/index.py | 4 ++-- plugins/mysql-apt/scripts/backup.py | 4 ++-- plugins/mysql-yum/index.py | 4 ++-- plugins/mysql-yum/scripts/backup.py | 4 ++-- plugins/mysql/index.py | 24 ++++++++++++------------ plugins/nezha/index.py | 2 +- plugins/op_load_balance/index.py | 2 +- plugins/op_waf/index.py | 2 +- plugins/pgadmin/index.py | 2 +- plugins/php-apt/index.py | 4 ++-- plugins/php-apt/index_php_apt.py | 2 +- plugins/php-yum/index.py | 10 +++++----- plugins/php-yum/index_php_yum.py | 2 +- plugins/php/index.py | 4 ++-- plugins/php/index_php.py | 2 +- plugins/phpmyadmin/index.py | 2 +- plugins/postgresql/index.py | 5 ++--- plugins/pureftp/index.py | 2 +- plugins/redis/index.py | 2 +- plugins/simpleping/index.py | 2 +- plugins/sphinx/index.py | 2 +- plugins/sys-opt/index.py | 2 +- plugins/varnish/index.py | 2 +- plugins/xhprof/index.py | 4 ++-- plugins/zabbix/index.py | 2 +- plugins/zabbix_agent/index.py | 2 +- 41 files changed, 78 insertions(+), 80 deletions(-) diff --git a/plugins/acme_pandominassl_apply/index.py b/plugins/acme_pandominassl_apply/index.py index 6e31800bc..1679e0bf9 100755 --- a/plugins/acme_pandominassl_apply/index.py +++ b/plugins/acme_pandominassl_apply/index.py @@ -131,7 +131,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 + '/redis') content = content.replace('{$REDIS_PASS}', mw.getRandomString(10)) diff --git a/plugins/alist/index.py b/plugins/alist/index.py index 7399dcf7e..1d0ded778 100755 --- a/plugins/alist/index.py +++ b/plugins/alist/index.py @@ -107,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 diff --git a/plugins/cloudreve/index.py b/plugins/cloudreve/index.py index c89d411a4..8f26a8e82 100755 --- a/plugins/cloudreve/index.py +++ b/plugins/cloudreve/index.py @@ -107,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 diff --git a/plugins/docker/index.py b/plugins/docker/index.py index 128498d9a..252cd678b 100755 --- a/plugins/docker/index.py +++ b/plugins/docker/index.py @@ -438,7 +438,7 @@ def dockerRemoveImage(): def getImageListFunc(dbname=''): - bkDir = mw.getRootDir() + '/backup/docker' + bkDir = mw.getFatherDir() + '/backup/docker' blist = os.listdir(bkDir) r = [] @@ -452,13 +452,13 @@ def getImageListFunc(dbname=''): def dockerImagePickDir(): - bkDir = mw.getRootDir() + '/backup/docker' + bkDir = mw.getFatherDir() + '/backup/docker' return mw.returnJson(True, 'ok', bkDir) def dockerImagePickList(): - bkDir = mw.getRootDir() + '/backup/docker' + bkDir = mw.getFatherDir() + '/backup/docker' if not os.path.exists(bkDir): os.mkdir(bkDir) @@ -490,7 +490,7 @@ def dockerImagePickSave(): if not data[0]: return data[1] - bkDir = mw.getRootDir() + '/backup/docker/' + bkDir = mw.getFatherDir() + '/backup/docker/' images = args['images'] try: file_name = bkDir + \ diff --git a/plugins/fail2ban/index.py b/plugins/fail2ban/index.py index a1aa4e003..4d478ab10 100755 --- a/plugins/fail2ban/index.py +++ b/plugins/fail2ban/index.py @@ -123,7 +123,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) return content diff --git a/plugins/gitea/index.py b/plugins/gitea/index.py index 40305aa35..b263398ac 100755 --- a/plugins/gitea/index.py +++ b/plugins/gitea/index.py @@ -119,7 +119,7 @@ export HOME=%s && ''' % ( getRunUser(), getHomeDir()) 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('{$RUN_USER}', getRunUser()) content = content.replace('{$HOME_DIR}', getHomeDir()) @@ -686,7 +686,7 @@ def projectScriptLoad(): commit_tpl = getPluginDir() + '/hook/commit.tpl' commit = path + '/custom_hooks/commit' - codeDir = mw.getRootDir() + '/git' + codeDir = mw.getFatherDir() + '/git' cc_content = mw.readFile(commit_tpl) diff --git a/plugins/gogs/index.py b/plugins/gogs/index.py index 20c7bc69e..e4643edb8 100755 --- a/plugins/gogs/index.py +++ b/plugins/gogs/index.py @@ -119,7 +119,7 @@ export HOME=%s && ''' % ( getRunUser(), getHomeDir()) 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('{$RUN_USER}', getRunUser()) content = content.replace('{$HOME_DIR}', getHomeDir()) @@ -688,7 +688,7 @@ def projectScriptLoad(): commit_tpl = getPluginDir() + '/hook/commit.tpl' commit = path + '/custom_hooks/commit' - codeDir = mw.getRootDir() + '/git' + codeDir = mw.getFatherDir() + '/git' cc_content = mw.readFile(commit_tpl) diff --git a/plugins/gorse/index.py b/plugins/gorse/index.py index 21465ef60..090c48576 100755 --- a/plugins/gorse/index.py +++ b/plugins/gorse/index.py @@ -140,7 +140,7 @@ def initRedisConf(): 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('{$CONFIG_ADMIN}', mw.getRandomString(6)) content = content.replace('{$CONFIG_PASS}', mw.getRandomString(10)) diff --git a/plugins/haproxy/index.py b/plugins/haproxy/index.py index 675ba84d0..843ac5090 100755 --- a/plugins/haproxy/index.py +++ b/plugins/haproxy/index.py @@ -99,7 +99,7 @@ def readConfigTpl(): 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('{$HA_USER}', mw.getRandomString(8)) content = content.replace('{$HA_PWD}', mw.getRandomString(10)) diff --git a/plugins/imail/index.py b/plugins/imail/index.py index a61ac5f3f..6fa0c4758 100755 --- a/plugins/imail/index.py +++ b/plugins/imail/index.py @@ -104,7 +104,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/mariadb/index.py b/plugins/mariadb/index.py index ea61704bb..01f895af1 100755 --- a/plugins/mariadb/index.py +++ b/plugins/mariadb/index.py @@ -139,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 + '/mariadb') server_id = int(time.time()) @@ -890,7 +890,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): diff --git a/plugins/mariadb/scripts/backup.py b/plugins/mariadb/scripts/backup.py index a70445a28..a6177cc38 100755 --- a/plugins/mariadb/scripts/backup.py +++ b/plugins/mariadb/scripts/backup.py @@ -40,7 +40,7 @@ class backupTools: "----------------------------------------------------------------------------") return - backup_path = mw.getRootDir() + '/backup/database/mariadb' + backup_path = mw.getFatherDir() + '/backup/database/mariadb' if not os.path.exists(backup_path): mw.execShell("mkdir -p " + backup_path) @@ -52,7 +52,7 @@ class backupTools: my_conf_path = db_path + '/etc/my.cnf' content = 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" content = content.replace(sea, subStr) diff --git a/plugins/migration_api/index.py b/plugins/migration_api/index.py index 425806cc5..061deca92 100755 --- a/plugins/migration_api/index.py +++ b/plugins/migration_api/index.py @@ -379,7 +379,7 @@ class classApi: # 创建目录 self.send('/files/create_dir', {"path": dpath}) - backup_path = mw.getRootDir() + '/backup' + backup_path = mw.getFatherDir() + '/backup' if not os.path.exists(backup_path): os.makedirs(backup_path, 384) @@ -701,7 +701,7 @@ class classApi: root = self.getDbConn('mysql', 'config').where( 'id=?', (1,)).getField('mysql_root') - backup_path = mw.getRootDir() + '/backup' + backup_path = mw.getFatherDir() + '/backup' if not os.path.exists(backup_path): os.makedirs(backup_path, 384) diff --git a/plugins/mongodb/index.py b/plugins/mongodb/index.py index 62c1d4ff5..07a1b5a3c 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -1204,7 +1204,7 @@ def replClose(): return mw.returnJson(True, '关闭副本同步成功!') def getDbBackupListFunc(dbname=''): - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getBackupDir() + '/database' blist = os.listdir(bkDir) r = [] @@ -1223,7 +1223,7 @@ def getDbBackupList(): return data[1] r = getDbBackupListFunc(args['name']) - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getBackupDir() + '/database' rr = [] for x in range(0, len(r)): p = bkDir + '/' + r[x] @@ -1245,7 +1245,7 @@ def getDbBackupList(): def getDbBackupImportList(): - bkImportDir = mw.getRootDir() + '/backup/mongodb_import' + bkImportDir = mw.getBackupDir() + '/mongodb_import' if not os.path.exists(bkImportDir): os.mkdir(bkImportDir) @@ -1283,7 +1283,7 @@ def deleteDbBackup(): path = args['path'] full_file = "" - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getBackupDir() + '/database' full_file = bkDir + '/' + args['filename'] if path != "": full_file = path + "/" + args['filename'] @@ -1303,7 +1303,7 @@ def setDbBackup(): def getListBson(dbname=''): - bkDir = mw.getRootDir() + '/backup/mongodb_import/'+dbname + bkDir = mw.getBackupDir() + '/mongodb_import/'+dbname blist = os.listdir(bkDir) r = [] @@ -1327,7 +1327,7 @@ def importDbExternal(): file = args['file'] name = args['name'] - import_dir = mw.getRootDir() + '/backup/mongodb_import/' + import_dir = mw.getBackupDir() + '/mongodb_import/' mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root') port = getConfPort() @@ -1535,8 +1535,7 @@ def installPreInspection(version): if sys[1] != '': return '暂时不支持该系统' - sys_id = mw.execShell( - "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + sys_id = mw.execShell("cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") sysName = sys[0].strip().lower() sysId = sys_id[0].strip() diff --git a/plugins/mongodb/scripts/backup.py b/plugins/mongodb/scripts/backup.py index 77a9ed0d6..12a887353 100644 --- a/plugins/mongodb/scripts/backup.py +++ b/plugins/mongodb/scripts/backup.py @@ -131,7 +131,7 @@ def mongdbClient(): class backupTools: def getDbBackupList(self,dbname=''): - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getFatherDir() + '/backup/database' blist = os.listdir(bkDir) r = [] diff --git a/plugins/mysql-apt/index.py b/plugins/mysql-apt/index.py index 09e79f3fc..73a46e8a5 100755 --- a/plugins/mysql-apt/index.py +++ b/plugins/mysql-apt/index.py @@ -135,7 +135,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()) @@ -932,7 +932,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): diff --git a/plugins/mysql-apt/scripts/backup.py b/plugins/mysql-apt/scripts/backup.py index 992219655..55603de19 100755 --- a/plugins/mysql-apt/scripts/backup.py +++ b/plugins/mysql-apt/scripts/backup.py @@ -44,7 +44,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 +56,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) diff --git a/plugins/mysql-yum/index.py b/plugins/mysql-yum/index.py index 470dfdaeb..7c85d1afb 100755 --- a/plugins/mysql-yum/index.py +++ b/plugins/mysql-yum/index.py @@ -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()) @@ -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): diff --git a/plugins/mysql-yum/scripts/backup.py b/plugins/mysql-yum/scripts/backup.py index 1c4dd3cc0..e958f182b 100755 --- a/plugins/mysql-yum/scripts/backup.py +++ b/plugins/mysql-yum/scripts/backup.py @@ -44,7 +44,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 +58,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) diff --git a/plugins/mysql/index.py b/plugins/mysql/index.py index 4181c93cc..b53a42ecc 100755 --- a/plugins/mysql/index.py +++ b/plugins/mysql/index.py @@ -139,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') @@ -1011,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): @@ -1096,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): @@ -1159,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() @@ -1210,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() @@ -1241,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'] @@ -1256,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] @@ -1279,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) diff --git a/plugins/nezha/index.py b/plugins/nezha/index.py index 0fa578c8c..d79ad4bc2 100755 --- a/plugins/nezha/index.py +++ b/plugins/nezha/index.py @@ -119,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/op_load_balance/index.py b/plugins/op_load_balance/index.py index 622c3025b..262a20b04 100755 --- a/plugins/op_load_balance/index.py +++ b/plugins/op_load_balance/index.py @@ -77,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_waf/index.py b/plugins/op_waf/index.py index f6db7a141..92698b3e9 100755 --- a/plugins/op_waf/index.py +++ b/plugins/op_waf/index.py @@ -249,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/pgadmin/index.py b/plugins/pgadmin/index.py index 526f5f145..85984f5d6 100755 --- a/plugins/pgadmin/index.py +++ b/plugins/pgadmin/index.py @@ -88,7 +88,7 @@ def contentReplace(content): cfg = getCfg() 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}', service_path+'/'+getPluginName()+'/data') diff --git a/plugins/php-apt/index.py b/plugins/php-apt/index.py index b5abc0efb..d6146bfac 100755 --- a/plugins/php-apt/index.py +++ b/plugins/php-apt/index.py @@ -85,7 +85,7 @@ def status(version): def contentReplace(content, version): 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('{$PHP_VERSION}', version) content = content.replace('{$LOCAL_IP}', mw.getLocalIp()) @@ -739,7 +739,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php-apt/index_php_apt.py b/plugins/php-apt/index_php_apt.py index 2562967a4..c6b4301b0 100755 --- a/plugins/php-apt/index_php_apt.py +++ b/plugins/php-apt/index_php_apt.py @@ -118,7 +118,7 @@ def getPhpinfo(version): sock_file = getFpmAddress(version) # print(sock_file) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php-yum/index.py b/plugins/php-yum/index.py index fefbed017..153addc64 100755 --- a/plugins/php-yum/index.py +++ b/plugins/php-yum/index.py @@ -77,7 +77,7 @@ def status(version): def contentReplace(content, version): 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('{$PHP_VERSION}', version) content = content.replace('{$LOCAL_IP}', mw.getLocalIp()) @@ -88,15 +88,15 @@ def contentReplace(content, version): content = content.replace('{$PHP_USER}', 'nobody') content = content.replace('{$PHP_GROUP}', 'nobody') - rep = 'listen.owner\s*=\s*(.+)\r?\n' + rep = r'listen.owner\s*=\s*(.+)\r?\n' val = ';listen.owner = nobody\n' content = re.sub(rep, val, content) - rep = 'listen.group\s*=\s*(.+)\r?\n' + rep = r'listen.group\s*=\s*(.+)\r?\n' val = ';listen.group = nobody\n' content = re.sub(rep, val, content) - rep = 'user\s*=\s*(.+)\r?\n' + rep = r'user\s*=\s*(.+)\r?\n' val = ';user = nobody\n' content = re.sub(rep, val, content) @@ -742,7 +742,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php-yum/index_php_yum.py b/plugins/php-yum/index_php_yum.py index 11ea23240..17bae712b 100755 --- a/plugins/php-yum/index_php_yum.py +++ b/plugins/php-yum/index_php_yum.py @@ -97,7 +97,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php/index.py b/plugins/php/index.py index f08c86485..761a33902 100755 --- a/plugins/php/index.py +++ b/plugins/php/index.py @@ -119,7 +119,7 @@ def status(version): def contentReplace(content, version): 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('{$PHP_VERSION}', version) content = content.replace('{$LOCAL_IP}', mw.getLocalIp()) @@ -913,7 +913,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php/index_php.py b/plugins/php/index_php.py index 617613b1e..06a90a9c4 100755 --- a/plugins/php/index_php.py +++ b/plugins/php/index_php.py @@ -111,7 +111,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/phpmyadmin/index.py b/plugins/phpmyadmin/index.py index 5e8df07eb..884ef2f34 100755 --- a/plugins/phpmyadmin/index.py +++ b/plugins/phpmyadmin/index.py @@ -122,7 +122,7 @@ def contentReplace(content): blowfish_secret = tmp[0].strip() # print php_ver php_conf_dir = mw.getServerDir() + '/web_conf/php/conf' - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_CONF_PATH}', php_conf_dir) content = content.replace('{$PHP_VER}', php_ver) diff --git a/plugins/postgresql/index.py b/plugins/postgresql/index.py index 22dfc7fff..6ce89e219 100755 --- a/plugins/postgresql/index.py +++ b/plugins/postgresql/index.py @@ -147,7 +147,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('{$APP_PATH}', service_path + '/postgresql') return content @@ -721,8 +721,7 @@ def importDbBackup(): pwd = pSqliteDb('config').where('id=?', (1,)).getField('pg_root') - mysql_cmd = mw.getRootDir() + '/server/mysql/bin/mysql -uroot -p' + pwd + \ - ' ' + name + ' < ' + file_path_sql + mysql_cmd = mw.getFatherDir() + '/server/mysql/bin/mysql -uroot -p' + pwd + ' ' + name + ' < ' + file_path_sql # print(mysql_cmd) os.system(mysql_cmd) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index 909f71ab9..15d6535ed 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -80,7 +80,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) return content diff --git a/plugins/redis/index.py b/plugins/redis/index.py index 3d451c02e..494064594 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/index.py @@ -122,7 +122,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()) content = content.replace('{$REDIS_PASS}', mw.getRandomString(10)) diff --git a/plugins/simpleping/index.py b/plugins/simpleping/index.py index 176536c90..dcc6853f0 100755 --- a/plugins/simpleping/index.py +++ b/plugins/simpleping/index.py @@ -135,7 +135,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 + '/simpleping') return content diff --git a/plugins/sphinx/index.py b/plugins/sphinx/index.py index 9a04a5d22..515d94a0a 100755 --- a/plugins/sphinx/index.py +++ b/plugins/sphinx/index.py @@ -98,7 +98,7 @@ def readConfigTpl(): 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 + '/sphinx') return content diff --git a/plugins/sys-opt/index.py b/plugins/sys-opt/index.py index 26a7a7bb0..a9a66c583 100755 --- a/plugins/sys-opt/index.py +++ b/plugins/sys-opt/index.py @@ -52,7 +52,7 @@ def checkArgs(data, ck=[]): 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 + '/sys-opt') return content diff --git a/plugins/varnish/index.py b/plugins/varnish/index.py index 050e842a0..5336dc01a 100755 --- a/plugins/varnish/index.py +++ b/plugins/varnish/index.py @@ -50,7 +50,7 @@ def getInitDTpl(): 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 + '/varnish') return content diff --git a/plugins/xhprof/index.py b/plugins/xhprof/index.py index 7bd54ee70..a2861b17a 100755 --- a/plugins/xhprof/index.py +++ b/plugins/xhprof/index.py @@ -97,7 +97,7 @@ def contentReplace(content): service_path = mw.getServerDir() php_ver = getCachePhpVer() # print php_ver - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_VER}', php_ver) return content @@ -106,7 +106,7 @@ def contentReplace(content): def contentReplacePHP(content, version): service_path = mw.getServerDir() # print php_ver - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_VER}', version) return content diff --git a/plugins/zabbix/index.py b/plugins/zabbix/index.py index 06ea20e96..167c85669 100755 --- a/plugins/zabbix/index.py +++ b/plugins/zabbix/index.py @@ -97,7 +97,7 @@ def getInstallVerion(): 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('{$ZABBIX_ROOT}', '/usr/share/zabbix') content = content.replace('{$ZABBIX_PORT}', '18888') diff --git a/plugins/zabbix_agent/index.py b/plugins/zabbix_agent/index.py index 797a1f6b2..c015c1ecd 100755 --- a/plugins/zabbix_agent/index.py +++ b/plugins/zabbix_agent/index.py @@ -83,7 +83,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) return content From e027700d506e19147d7d09da668b123828ef6243 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:33:59 +0800 Subject: [PATCH 284/965] update --- web/admin/site/ssl.py | 10 ++++++++++ web/static/app/site.js | 5 +++-- web/utils/site.py | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 5d7a5b048..a7cdd4bc4 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -32,6 +32,16 @@ def get_ssl(): ssl_type = request.form.get('ssl_type', '') return MwSites.instance().getSsl(site_name, ssl_type) +# 获取证书信息 +@blueprint.route('/set_ssl', endpoint='set_ssl', methods=['POST']) +@panel_login_required +def set_ssl(): + site_name = request.form.get('siteName', '') + key = request.form.get('key', '') + csr = request.form.get('csr', '') + return MwSites.instance().setSsl(site_name, key, csr) + + # 删除证书 @blueprint.route('/close_ssl_conf', endpoint='close_ssl_conf', methods=['POST']) @panel_login_required diff --git a/web/static/app/site.js b/web/static/app/site.js index ce2fcd4ec..8bac91dad 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1025,8 +1025,10 @@ function setIndexList(id){ } -/*站点修改*/ +/* 站点修改 */ function webEdit(id,website,endTime,addtime){ + // 暂时关闭 - 子目录绑定 + //

                    子目录绑定

                    \ layer.open({ type: 1, area: '700px', @@ -1036,7 +1038,6 @@ function webEdit(id,website,endTime,addtime){ content: "
                    \
                    \

                    域名管理

                    \ -

                    子目录绑定

                    \

                    网站目录

                    \

                    流量限制

                    \

                    伪静态

                    \ diff --git a/web/utils/site.py b/web/utils/site.py index fbb3a83dc..ae0aecf81 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -413,6 +413,46 @@ class sites(object): mw.writeLog('网站管理', '设置成功,站点【{1}】到期【{2}】后将自动停止!', (info['name'], end_date,)) return mw.returnData(True, '设置成功,站点到期后将自动停止!') + def setSsl(self, site_name, key, csr): + path = self.sslDir + '/' + site_name + 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.returnData(False, '证书错误,请粘贴正确的PEM格式证书!') + + mw.backFile(keypath) + mw.backFile(csrpath) + + mw.writeFile(keypath, key) + mw.writeFile(csrpath, csr) + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return result + + isError = mw.checkWebConfig() + if(type(isError) == str): + mw.restoreFile(keypath) + mw.restoreFile(csrpath) + + msg = 'ERROR:
                    ' + isError.replace("\n", '
                    ') + '
                    ' + return mw.returnData(False, msg) + + mw.writeLog('网站管理', '证书已保存!') + mw.restartWeb() + return mw.returnData(True, '证书已保存!') + # ssl相关方法 start def setSslConf(self, site_name): file = self.getHostConf(site_name) From cf74b0a941e8a406b980f46a8df963fe07d62443 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:37:38 +0800 Subject: [PATCH 285/965] Update mw.py --- web/core/mw.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/web/core/mw.py b/web/core/mw.py index 118eb7d46..bcf565d6b 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -180,6 +180,105 @@ def getCommonFile(): } return data +def checkCert(certPath='ssl/certificate.pem'): + # 验证证书 + openssl = '/usr/bin/openssl' + if not os.path.exists(openssl): + openssl = '/usr/local/openssl/bin/openssl' + if not os.path.exists(openssl): + openssl = 'openssl' + certPem = readFile(certPath) + s = "\n-----BEGIN CERTIFICATE-----" + tmp = certPem.strip().split(s) + for tmp1 in tmp: + if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: + tmp1 = s + tmp1 + writeFile(certPath, tmp1) + result = execShell(openssl + " x509 -in " + + certPath + " -noout -subject") + if result[1].find('-bash:') != -1: + return True + if len(result[1]) > 2: + return False + if result[0].find('error:') != -1: + return False + return True + +def sortFileList(path, ftype = 'mtime', sort = 'desc'): + flist = os.listdir(path) + if ftype == 'mtime': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=False) + + if ftype == 'size': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=False) + + if ftype == 'fname': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=False) + return flist + + +def sortAllFileList(path, ftype = 'mtime', sort = 'desc', search = '',limit = 3000): + count = 0 + flist = [] + for d_list in os.walk(path): + if count >= limit: + break + + for d in d_list[1]: + if count >= limit: + break + if d.lower().find(search) != -1: + filename = d_list[0] + '/' + d + if not os.path.exists(filename): + continue + count += 1 + flist.append(filename) + + for f in d_list[2]: + if count >= limit: + break + + if f.lower().find(search) != -1: + filename = d_list[0] + '/' + f + if not os.path.exists(filename): + continue + count += 1 + flist.append(filename) + + if ftype == 'mtime': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=False) + + if ftype == 'size': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=False) + return flist + +def getPathSize(path): + # 取文件或目录大小 + if not os.path.exists(path): + return 0 + if not os.path.isdir(path): + return os.path.getsize(path) + size_total = 0 + for nf in os.walk(path): + for f in nf[2]: + filename = nf[0] + '/' + f + size_total += os.path.getsize(filename) + return size_total def toSize(size, middle='') -> str: """ From 72917bdc5b4842b58604c210cfd63da2f2a1d3ec Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:39:17 +0800 Subject: [PATCH 286/965] Update site.py --- web/utils/site.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index ae0aecf81..5d46eadbb 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -426,10 +426,13 @@ class sites(object): if(csr.find('CERTIFICATE') == -1): return mw.returnJson(False, '证书错误,请检查!') - mw.writeFile('/tmp/cert.pl', csr) - if not mw.checkCert('/tmp/cert.pl'): + tmp_cert = '/tmp/cert.pl' + mw.writeFile(tmp_cert, csr) + if not mw.checkCert(tmp_cert): + os.remove(tmp_cert) return mw.returnData(False, '证书错误,请粘贴正确的PEM格式证书!') - + os.remove(tmp_cert) + mw.backFile(keypath) mw.backFile(csrpath) From e013eb81375d09dcff93c439ad99c7e02f6b1115 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 01:59:22 +0800 Subject: [PATCH 287/965] update --- web/static/app/site.js | 37 +++++++++++++++++++------------------ web/version.py | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/web/static/app/site.js b/web/static/app/site.js index 8bac91dad..f958a1250 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -98,16 +98,16 @@ $('#site_search').click(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){ + elem: '#site_'+ id, + min:getDate(1), + max:'9999-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'; } @@ -1031,7 +1031,7 @@ function webEdit(id,website,endTime,addtime){ //

                    子目录绑定

                    \ layer.open({ type: 1, - area: '700px', + area: ['700px','580px'], title: '站点修改['+website+'] -- 添加时间['+addtime+']', closeBtn: 1, shift: 0, @@ -1082,7 +1082,7 @@ function webEdit(id,website,endTime,addtime){ }); } -//取网站日志 +//取网站日志pluginLogs function getSiteLogs(siteName){ var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_logs',{siteName:siteName},function(logs){ @@ -1091,9 +1091,10 @@ function getSiteLogs(siteName){ logs.msg = ''; } if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); - var ob = document.getElementById('error_log'); + var h = parseInt($('.bt-w-menu').css('height')) - 50; + var con = ''; + $("#webedit-con").html(con); + var ob = document.getElementById('site_log'); ob.scrollTop = ob.scrollHeight; },'json'); } @@ -1102,14 +1103,14 @@ function getSiteLogs(siteName){ function getSiteErrorLogs(siteName){ var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_error_logs',{siteName:siteName},function(logs){ - // console.log(logs); layer.close(loadT); if(logs.status !== true){ logs.msg = ''; } if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); + var h = parseInt($('.bt-w-menu').css('height')) - 50; + var con = ''; + $("#webedit-con").html(con); var ob = document.getElementById('error_log'); ob.scrollTop = ob.scrollHeight; },'json'); diff --git a/web/version.py b/web/version.py index 31e7a2e8f..3ade5933b 100644 --- a/web/version.py +++ b/web/version.py @@ -13,7 +13,7 @@ # 应用程序版本号组件 APP_RELEASE = 0 APP_REVISION = 18 -APP_SMALL_VERSION = 0.2 +APP_SMALL_VERSION = 0.3 # 应用程序版本后缀,例如“beta1”、“dev”。通常为空字符串GA发布。 APP_SUFFIX = '' From d8a34e7a0b5d0f768762b8d9c56981c4b05273b9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:04:47 +0800 Subject: [PATCH 288/965] Update site.js --- web/static/app/site.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/static/app/site.js b/web/static/app/site.js index f958a1250..8bd7f51fa 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1031,12 +1031,12 @@ function webEdit(id,website,endTime,addtime){ //

                    子目录绑定

                    \ layer.open({ type: 1, - area: ['700px','580px'], + area: ['700px','570px'], title: '站点修改['+website+'] -- 添加时间['+addtime+']', closeBtn: 1, shift: 0, content: "
                    \ -
                    \ +
                    \

                    域名管理

                    \

                    网站目录

                    \

                    流量限制

                    \ @@ -1051,7 +1051,7 @@ function webEdit(id,website,endTime,addtime){

                    响应日志

                    \

                    错误日志

                    \
                    \ -
                    \ +
                    \
                    ", success:function(){ //域名输入提示 @@ -1091,7 +1091,7 @@ function getSiteLogs(siteName){ logs.msg = ''; } if (logs.msg == '') logs.msg = '当前没有日志.'; - var h = parseInt($('.bt-w-menu').css('height')) - 50; + var h = parseInt($('.bt-w-menu').css('height')) - 35; var con = ''; $("#webedit-con").html(con); var ob = document.getElementById('site_log'); @@ -1108,7 +1108,7 @@ function getSiteErrorLogs(siteName){ logs.msg = ''; } if (logs.msg == '') logs.msg = '当前没有日志.'; - var h = parseInt($('.bt-w-menu').css('height')) - 50; + var h = parseInt($('.bt-w-menu').css('height')) - 35; var con = ''; $("#webedit-con").html(con); var ob = document.getElementById('error_log'); From e0384dd6cb9b1c0126333d7419b1f284671e5510 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:10:44 +0800 Subject: [PATCH 289/965] Update msodclient.py --- plugins/msonedrive/class/msodclient.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/msonedrive/class/msodclient.py b/plugins/msonedrive/class/msodclient.py index 2181caf66..f21a13aac 100644 --- a/plugins/msonedrive/class/msodclient.py +++ b/plugins/msonedrive/class/msodclient.py @@ -8,14 +8,19 @@ import re import json import io -sys.path.append(os.getcwd() + "/class/core") -import mw - import oauthlib import requests import datetime from requests_oauthlib import OAuth2Session +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 = False From ae4f3851e5d48fc745a4a74b1efca84d7a7f275a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:20:47 +0800 Subject: [PATCH 290/965] Update index.py --- plugins/pureftp/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index 15d6535ed..d84534d2d 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -88,8 +88,8 @@ def contentReplace(content): def ftp_release_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'pure-ftpd', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'pure-ftpd', 'port') return port except Exception as e: return "Release failed {}".format(e) From df7a43919fdf3df76159065e2d2eb3c4f5bad251 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:21:07 +0800 Subject: [PATCH 291/965] Update index.py --- plugins/pureftp/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index d84534d2d..3c96e2a26 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -104,7 +104,7 @@ def openFtpPort(): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getFatherDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): From a3af4ac4058952aa24cb6b5614060dcc31f878fb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:22:03 +0800 Subject: [PATCH 292/965] Update index.py --- plugins/pureftp/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index 3c96e2a26..2e9436bfe 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -398,7 +398,7 @@ def modFtpPort(): return '端口范围不正确!' file = file = getServerDir() + '/etc/pure-ftpd.conf' conf = mw.readFile(file) - rep = u"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)" + rep = r"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)" # preg_match(rep,conf,tmp) conf = re.sub( rep, "\nBind 0.0.0.0," + port, conf) From 60a1bfe67980c56169543f0e6fa9befb9a29fee4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:22:13 +0800 Subject: [PATCH 293/965] Update index.py --- plugins/pureftp/index.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index 2e9436bfe..c7a88c4f7 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -400,8 +400,7 @@ def modFtpPort(): conf = mw.readFile(file) rep = r"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)" # preg_match(rep,conf,tmp) - conf = re.sub( - rep, "\nBind 0.0.0.0," + port, conf) + conf = re.sub(rep, "\nBind 0.0.0.0," + port, conf) mw.writeFile(file, conf) restart() return 'ok' From 15314974f6218030ec9a979c1528cd510ccf5aa3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:27:21 +0800 Subject: [PATCH 294/965] Update index.py --- plugins/pureftp/index.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index c7a88c4f7..cf5e118ab 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -104,7 +104,7 @@ def openFtpPort(): def initDreplace(): file_tpl = getInitDTpl() - service_path = mw.getFatherDir() + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -145,8 +145,8 @@ def initDreplace(): if os.path.exists(systemDir): # and not os.path.exists(systemService) - se_content = mw.readFile(systemServiceTpl) - se_content = se_content.replace('{$SERVER_PATH}', service_path) + content = mw.readFile(systemServiceTpl) + content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, se_content) mw.execShell('systemctl daemon-reload') From c6d900c4e01270cebe0e722231a45bf83056694f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:31:12 +0800 Subject: [PATCH 295/965] Update index.py --- plugins/pureftp/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index cf5e118ab..11508e7fa 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -147,7 +147,7 @@ def initDreplace(): # and not os.path.exists(systemService) content = mw.readFile(systemServiceTpl) content = content.replace('{$SERVER_PATH}', service_path) - mw.writeFile(systemService, se_content) + mw.writeFile(systemService, content) mw.execShell('systemctl daemon-reload') return file_bin From 2d8528ca8de85a0792a6a8e6d9ec3c03bd284a24 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:32:11 +0800 Subject: [PATCH 296/965] Update index.py --- plugins/pureftp/index.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index 11508e7fa..0dc6a00e3 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -144,7 +144,6 @@ def initDreplace(): systemServiceTpl = getPluginDir() + '/init.d/pureftp.service.tpl' if os.path.exists(systemDir): - # and not os.path.exists(systemService) content = mw.readFile(systemServiceTpl) content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, content) From d00101f6eb6a0005bf508c3f9012de1c71030e5e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:40:09 +0800 Subject: [PATCH 297/965] update --- setting.py | 95 -------------------------------------------------- web/setting.py | 13 +++++++ 2 files changed, 13 insertions(+), 95 deletions(-) delete mode 100755 setting.py diff --git a/setting.py b/setting.py deleted file mode 100755 index eac374df0..000000000 --- a/setting.py +++ /dev/null @@ -1,95 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 配置文件 -# --------------------------------------------------------------------------------- - - -import time -import sys -import random -import os - -pwd = os.getcwd() -sys.path.append(pwd + '/class/core') - -import mw - -# 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" -# p_debain = "/usr/local/lib/" + info[0].strip() + "/dist-packages" - -# sys.path.append(p) -# sys.path.append(p_debain) - -import system_api -cpu_info = system_api.system_api().getCpuInfo() -workers = cpu_info[1] - - -log_dir = os.getcwd() + '/logs' -if not os.path.exists(log_dir): - os.mkdir(log_dir) - -# default port -mw_port = "7200" -if os.path.exists("data/port.pl"): - mw_port = mw.readFile('data/port.pl') - mw_port.strip() -else: - import firewall_api - import common - common.initDB() - mw_port = str(random.randint(10000, 65530)) - firewall_api.firewall_api().addAcceptPortArgs(mw_port, 'WEB面板', 'port') - mw.writeFile('data/port.pl', mw_port) - -bind = [] -if os.path.exists('data/ipv6.pl'): - bind.append('[0:0:0:0:0:0:0:0]:%s' % mw_port) -else: - bind.append('0.0.0.0:%s' % mw_port) - -ssl_choose_file = 'ssl/choose.pl' -if os.path.exists(ssl_choose_file): - ssl_choose = mw.readFile(ssl_choose_file).strip() - if mw.inArray(['local','nginx'],ssl_choose): - tmp_cert = 'ssl/'+ssl_choose+'/cert.pem' - tmp_private = 'ssl/'+ssl_choose+'/private.pem' - if os.path.exists(tmp_cert) and os.path.exists(tmp_private): - certfile = tmp_cert - keyfile = tmp_private - ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3' - ssl_version = 2 - -# 初始安装时,自动生成安全路径 -if not os.path.exists('data/admin_path.pl'): - admin_path = mw.getRandomString(8) - mw.writeFile('data/admin_path.pl', '/' + admin_path.lower()) - -if workers > 2: - workers = 1 - -threads = workers * 1 -backlog = 512 -reload = False -daemon = True -worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -timeout = 7200 -keepalive = 60 -preload_app = True -capture_output = True -access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' -loglevel = 'info' -errorlog = log_dir + '/error.log' -accesslog = log_dir + '/access.log' -pidfile = log_dir + '/mw.pid' diff --git a/web/setting.py b/web/setting.py index 46672561d..8defa4abe 100755 --- a/web/setting.py +++ b/web/setting.py @@ -50,6 +50,19 @@ if os.path.exists(default_ipv6_file): else: bind.append('0.0.0.0:%s' % panel_port) + +ssl_choose_file = panel_dir+'/ssl/choose.pl' +if os.path.exists(ssl_choose_file): + ssl_choose = mw.readFile(ssl_choose_file).strip() + if mw.inArray(['local','nginx'],ssl_choose): + tmp_cert = panel_dir+'/ssl/'+ssl_choose+'/cert.pem' + tmp_private = panel_dir+'/ssl/'+ssl_choose+'/private.pem' + if os.path.exists(tmp_cert) and os.path.exists(tmp_private): + certfile = tmp_cert + keyfile = tmp_private + ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3' + ssl_version = 2 + if workers > 2: workers = 1 From 000b825ef08040e0db8f17cdf069216b563c8434 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:40:26 +0800 Subject: [PATCH 298/965] Delete app.py --- app.py | 52 ---------------------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 app.py 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 -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 入口文件 -# --------------------------------------------------------------------------------- - - -# pip install profiler_online -# 性能测试 -# from profiler_online import run_profiler -# run_profiler() -import sys -import io -import os - -sys.dont_write_bytecode = True -from route import app, socketio - - -from gevent.pywsgi import WSGIServer -from geventwebsocket.handler import WebSocketHandler - - -from gevent import monkey -monkey.patch_all() - -try: - if __name__ == "__main__": - - PORT = 7200 - if os.path.exists('data/port.pl'): - f = open('data/port.pl') - PORT = int(f.read()) - f.close() - - # HOST = '0.0.0.0' - # app.run(host=HOST, port=PORT) - - http_server = WSGIServer( - (HOST, PORT), app, handler_class=WebSocketHandler) - http_server.serve_forever() - socketio.run(app, host=HOST, port=PORT) -except Exception as ex: - print(ex) From 9ab361604ff68ba7f6194830680d6bd826ceeec9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:53:00 +0800 Subject: [PATCH 299/965] Update site.py --- web/utils/site.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index 5d46eadbb..2ed4a2226 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -503,8 +503,8 @@ class sites(object): 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;" + http_ssl = "\n\tlisten 443 ssl;" + http_ssl = http_ssl + "\n\tlisten [::]:443 ssl;" conf = conf.replace(listen, listen + http_ssl) From 43384f8ca41621b3f1bfae844b115ea879940133 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:53:55 +0800 Subject: [PATCH 300/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index 2ed4a2226..4d3c3269b 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -496,7 +496,7 @@ class sites(object): if not mw.inArray(tmp, '443'): listen = re.search(rep, conf).group() - if version.startswith('1.25'): + if version.startswith('1.25') or version.startswith('1.27'): http_ssl = "\n\tlisten 443 ssl;" http_ssl = http_ssl + "\n\tlisten [::]:443 ssl;" http_ssl = http_ssl + "\n\tlisten 443 quic;" From ce3e37b6f87b783d50a1bb54c7b2b371ca39cfc9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 02:54:41 +0800 Subject: [PATCH 301/965] Update site.py --- web/utils/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index 4d3c3269b..60166b1b9 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -473,7 +473,7 @@ class sites(object): add_header Strict-Transport-Security "max-age=63072000"; add_header Alt-Svc 'h3=":443";ma=86400'; """ - if not version.startswith('1.25'): + if not version.startswith('1.25') or version.startswith('1.27'): http3Header = ''; sslStr = """#error_page 404/404.html; From b3370f2937c6d4531d03f7c2e24880da872c2b3f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:04:04 +0800 Subject: [PATCH 302/965] Update firewall.py --- web/utils/firewall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 7a1906a75..1cdf02df1 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -72,7 +72,7 @@ class Firewall(object): if not is_same: t = {} - t['port'] = info[0] + t['port'] = info[0].replace('-',':') t['protocol'] = info[1] ports_all.append(t) From 360d94d573f28bf718a70b09800aa03d2a567ff3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:07:09 +0800 Subject: [PATCH 303/965] Update firewall.py --- web/utils/firewall.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 1cdf02df1..4545d8f18 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -313,8 +313,10 @@ class Firewall(object): ): panel_port = mw.getPanelPort() - if(int(port) == int(panel_port)): - return mw.returnData(False, '失败,不能删除当前面板端口!') + if port.find(':') > 0: + else: + if(int(port) == int(panel_port)): + return mw.returnData(False, '失败,不能删除当前面板端口!') try: self.delAcceptPortCmd(port, protocol) mw.M('firewall').where("id=?", (firewall_id,)).delete() From cfd7030d001dca3800ea81b60af6e177fd31f113 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:08:18 +0800 Subject: [PATCH 304/965] Update firewall.py --- web/utils/firewall.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 4545d8f18..27320b06e 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -314,6 +314,7 @@ class Firewall(object): panel_port = mw.getPanelPort() if port.find(':') > 0: + pass else: if(int(port) == int(panel_port)): return mw.returnData(False, '失败,不能删除当前面板端口!') From d5678d3f34db6ed582cc2f6641e01598b5ea6f96 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:08:33 +0800 Subject: [PATCH 305/965] Update firewall.py --- web/utils/firewall.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 27320b06e..3a5d1d7ac 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -315,6 +315,8 @@ class Firewall(object): if port.find(':') > 0: pass + elif port.find('-') > 0: + pass else: if(int(port) == int(panel_port)): return mw.returnData(False, '失败,不能删除当前面板端口!') From 8e0a4ec34bb446928e55e0dd9404bf82caebaaa4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:15:08 +0800 Subject: [PATCH 306/965] Update install.sh --- plugins/pureftp/install.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/pureftp/install.sh b/plugins/pureftp/install.sh index 961fab27a..874c46668 100755 --- a/plugins/pureftp/install.sh +++ b/plugins/pureftp/install.sh @@ -7,8 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate @@ -103,12 +101,12 @@ Install_pureftp() if [ -d ${serverPath}/pureftp ];then echo "${1}" > ${serverPath}/pureftp/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/pureftp/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/pureftp/index.py initd_install else - echo '安装失败' > $install_tmp + echo '安装失败' fi } @@ -128,7 +126,7 @@ Uninstall_pureftp() rm -rf ${serverPath}/pureftp userdel ftp groupdel ftp - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 From 4e6f867ba479708962f8db930ec63e3fff5f5dae Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:16:46 +0800 Subject: [PATCH 307/965] update --- plugins/php-apt/install.sh | 5 ++--- plugins/php-apt/versions/56/install.sh | 1 - plugins/php-apt/versions/70/install.sh | 2 -- plugins/php-apt/versions/71/install.sh | 1 - plugins/php-apt/versions/72/install.sh | 1 - plugins/php-apt/versions/73/install.sh | 1 - plugins/php-apt/versions/74/install.sh | 2 -- plugins/php-apt/versions/80/install.sh | 2 -- plugins/php-apt/versions/81/install.sh | 2 -- plugins/php-apt/versions/82/install.sh | 2 -- plugins/php-apt/versions/83/install.sh | 2 -- plugins/php-apt/versions/84/install.sh | 1 - 12 files changed, 2 insertions(+), 20 deletions(-) diff --git a/plugins/php-apt/install.sh b/plugins/php-apt/install.sh index 071fe921e..910d4b708 100755 --- a/plugins/php-apt/install.sh +++ b/plugins/php-apt/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl if id www &> /dev/null ;then echo "www uid is `id -u www`" @@ -34,12 +33,12 @@ type=$2 apt_ver=${type:0:1}.${type:1: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/php-apt/versions/56/install.sh b/plugins/php-apt/versions/56/install.sh index 7fc6788dc..ba3973dc5 100755 --- a/plugins/php-apt/versions/56/install.sh +++ b/plugins/php-apt/versions/56/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/70/install.sh b/plugins/php-apt/versions/70/install.sh index ba1457a76..3631acf80 100755 --- a/plugins/php-apt/versions/70/install.sh +++ b/plugins/php-apt/versions/70/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/71/install.sh b/plugins/php-apt/versions/71/install.sh index 0af724652..c8955a33a 100755 --- a/plugins/php-apt/versions/71/install.sh +++ b/plugins/php-apt/versions/71/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/72/install.sh b/plugins/php-apt/versions/72/install.sh index 66bb3a74b..816f7f4db 100755 --- a/plugins/php-apt/versions/72/install.sh +++ b/plugins/php-apt/versions/72/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/73/install.sh b/plugins/php-apt/versions/73/install.sh index e19cb1bb2..475825e55 100755 --- a/plugins/php-apt/versions/73/install.sh +++ b/plugins/php-apt/versions/73/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/74/install.sh b/plugins/php-apt/versions/74/install.sh index f7852231f..956a864ff 100755 --- a/plugins/php-apt/versions/74/install.sh +++ b/plugins/php-apt/versions/74/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/80/install.sh b/plugins/php-apt/versions/80/install.sh index c8102196d..f30fba31c 100755 --- a/plugins/php-apt/versions/80/install.sh +++ b/plugins/php-apt/versions/80/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/81/install.sh b/plugins/php-apt/versions/81/install.sh index cdd5b943d..124c765ad 100755 --- a/plugins/php-apt/versions/81/install.sh +++ b/plugins/php-apt/versions/81/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/82/install.sh b/plugins/php-apt/versions/82/install.sh index bab78a3d1..b23415303 100755 --- a/plugins/php-apt/versions/82/install.sh +++ b/plugins/php-apt/versions/82/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/83/install.sh b/plugins/php-apt/versions/83/install.sh index d1ee9485a..e0ace4c94 100755 --- a/plugins/php-apt/versions/83/install.sh +++ b/plugins/php-apt/versions/83/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/84/install.sh b/plugins/php-apt/versions/84/install.sh index 17027048b..33f2925ab 100755 --- a/plugins/php-apt/versions/84/install.sh +++ b/plugins/php-apt/versions/84/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 From 82a5af071a233d9f9e9826125a85fe3e0bf63d41 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:17:52 +0800 Subject: [PATCH 308/965] update --- plugins/php-yum/install.sh | 2 -- plugins/php-yum/versions/74/install.sh | 3 +-- plugins/php-yum/versions/80/install.sh | 4 +--- plugins/php-yum/versions/81/install.sh | 4 +--- plugins/php-yum/versions/82/install.sh | 4 +--- plugins/php-yum/versions/83/install.sh | 4 +--- plugins/php-yum/versions/84/install.sh | 4 +--- 7 files changed, 6 insertions(+), 19 deletions(-) diff --git a/plugins/php-yum/install.sh b/plugins/php-yum/install.sh index 65581a900..49cc293d8 100755 --- a/plugins/php-yum/install.sh +++ b/plugins/php-yum/install.sh @@ -7,8 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - if id www &> /dev/null ;then echo "www uid is `id -u www`" echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `" diff --git a/plugins/php-yum/versions/74/install.sh b/plugins/php-yum/versions/74/install.sh index 8b3eae5fd..b2098e1e5 100755 --- a/plugins/php-yum/versions/74/install.sh +++ b/plugins/php-yum/versions/74/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl version=7.4.x PHP_VER=74 @@ -30,7 +29,7 @@ Uninstall_php() { yum remove -y php74 php74-php-fpm php74-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/80/install.sh b/plugins/php-yum/versions/80/install.sh index c3e0b37f0..070c2544c 100755 --- a/plugins/php-yum/versions/80/install.sh +++ b/plugins/php-yum/versions/80/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.0.x PHP_VER=80 @@ -32,7 +30,7 @@ Uninstall_php() { # $serverPath/php-ya/init.d/php${PHP_VER} stop rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/81/install.sh b/plugins/php-yum/versions/81/install.sh index c0b4bde9e..5877e21df 100755 --- a/plugins/php-yum/versions/81/install.sh +++ b/plugins/php-yum/versions/81/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.1.x PHP_VER=81 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php81 php81-php-fpm php81-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/82/install.sh b/plugins/php-yum/versions/82/install.sh index 8aa04e5f6..6440c2966 100755 --- a/plugins/php-yum/versions/82/install.sh +++ b/plugins/php-yum/versions/82/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.2.x PHP_VER=82 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php82 php82-php-fpm php82-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/83/install.sh b/plugins/php-yum/versions/83/install.sh index 59deca624..1a195b8c9 100755 --- a/plugins/php-yum/versions/83/install.sh +++ b/plugins/php-yum/versions/83/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.3.x PHP_VER=83 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php83 php83-php-fpm php83-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/84/install.sh b/plugins/php-yum/versions/84/install.sh index fcad71440..105495362 100755 --- a/plugins/php-yum/versions/84/install.sh +++ b/plugins/php-yum/versions/84/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.4.x PHP_VER=84 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php84 php84-php-fpm php84-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} From 6a6166c8acb7872b4226e5031a6d3313dcbd318f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 03:20:54 +0800 Subject: [PATCH 309/965] update --- plugins/php/install.sh | 5 ++--- plugins/php/versions/52/install.sh | 5 ++--- plugins/php/versions/53/install.sh | 5 ++--- plugins/php/versions/54/install.sh | 6 ++---- plugins/php/versions/55/install.sh | 6 ++---- plugins/php/versions/56/install.sh | 6 ++---- plugins/php/versions/70/install.sh | 3 +-- plugins/php/versions/71/install.sh | 6 ++---- plugins/php/versions/72/install.sh | 2 -- plugins/php/versions/73/install.sh | 1 - plugins/php/versions/74/install.sh | 1 - plugins/php/versions/80/install.sh | 3 +-- plugins/php/versions/81/install.sh | 3 +-- plugins/php/versions/82/install.sh | 3 +-- plugins/php/versions/83/install.sh | 3 +-- plugins/php/versions/84/install.sh | 3 +-- 16 files changed, 20 insertions(+), 41 deletions(-) diff --git a/plugins/php/install.sh b/plugins/php/install.sh index 7ce3a2a01..1460c6cc0 100755 --- a/plugins/php/install.sh +++ b/plugins/php/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl if id www &> /dev/null ;then echo "www uid is `id -u www`" @@ -22,12 +21,12 @@ 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/php/versions/52/install.sh b/plugins/php/versions/52/install.sh index a4d80eea5..28ef0b968 100755 --- a/plugins/php/versions/52/install.sh +++ b/plugins/php/versions/52/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` version=5.2.17 @@ -16,7 +15,7 @@ PHP_VER=52 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -163,7 +162,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "uninstall php-${version} ..." > $install_tmp + echo "uninstall php-${version} ..." } action=${1} diff --git a/plugins/php/versions/53/install.sh b/plugins/php/versions/53/install.sh index 48713d8d6..5bd5a99e9 100755 --- a/plugins/php/versions/53/install.sh +++ b/plugins/php/versions/53/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` version=5.3.29 @@ -16,7 +15,7 @@ PHP_VER=53 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-5.3.29 ..." > $install_tmp +echo "安装php-5.3.29 ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -167,7 +166,7 @@ Uninstall_php() { $serverPath/php/init.d/php53 stop rm -rf $serverPath/php/53 - echo "uninstall php-5.3.29 ..." > $install_tmp + echo "uninstall php-5.3.29 ..." } action=${1} diff --git a/plugins/php/versions/54/install.sh b/plugins/php/versions/54/install.sh index a1ebad15c..149acba46 100755 --- a/plugins/php/versions/54/install.sh +++ b/plugins/php/versions/54/install.sh @@ -10,14 +10,12 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=5.4.45 PHP_VER=54 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -182,7 +180,7 @@ Uninstall_php() { $serverPath/php/init.d/php54 stop rm -rf $serverPath/php/54 - echo "卸载php-5.4.45 ..." > $install_tmp + echo "卸载php-5.4.45 ..." } action=${1} diff --git a/plugins/php/versions/55/install.sh b/plugins/php/versions/55/install.sh index 7b8cdef88..01e26ecb6 100755 --- a/plugins/php/versions/55/install.sh +++ b/plugins/php/versions/55/install.sh @@ -10,14 +10,12 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=5.5.38 PHP_VER=55 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-5.5.38 ..." > $install_tmp +echo "安装php-5.5.38 ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -151,7 +149,7 @@ Uninstall_php() { $serverPath/php/init.d/php55 stop rm -rf $serverPath/php/55 - echo "卸载php-5.5.38 ..." > $install_tmp + echo "卸载php-5.5.38 ..." } action=${1} diff --git a/plugins/php/versions/56/install.sh b/plugins/php/versions/56/install.sh index 8224972af..be2fef3da 100755 --- a/plugins/php/versions/56/install.sh +++ b/plugins/php/versions/56/install.sh @@ -12,15 +12,13 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=5.6.40 PHP_VER=56 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." > mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -161,7 +159,7 @@ Uninstall_php() { $serverPath/php/init.d/php56 stop rm -rf $serverPath/php/56 - echo "卸载php-${version} ..." > $install_tmp + echo "卸载php-${version} ..." } action=${1} diff --git a/plugins/php/versions/70/install.sh b/plugins/php/versions/70/install.sh index 6f262eadb..f2792f258 100755 --- a/plugins/php/versions/70/install.sh +++ b/plugins/php/versions/70/install.sh @@ -9,14 +9,13 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl version=7.0.33 PHP_VER=70 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php diff --git a/plugins/php/versions/71/install.sh b/plugins/php/versions/71/install.sh index 73f7818e8..542db0efa 100755 --- a/plugins/php/versions/71/install.sh +++ b/plugins/php/versions/71/install.sh @@ -10,14 +10,12 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=7.1.33 PHP_VER=71 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -144,7 +142,7 @@ Uninstall_php() { $serverPath/php/init.d/php71 stop rm -rf $serverPath/php/71 - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/72/install.sh b/plugins/php/versions/72/install.sh index b61f972fa..01f63f99e 100755 --- a/plugins/php/versions/72/install.sh +++ b/plugins/php/versions/72/install.sh @@ -9,8 +9,6 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=7.2.31 PHP_VER=72 diff --git a/plugins/php/versions/73/install.sh b/plugins/php/versions/73/install.sh index 71834229e..2a538c05f 100755 --- a/plugins/php/versions/73/install.sh +++ b/plugins/php/versions/73/install.sh @@ -9,7 +9,6 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } diff --git a/plugins/php/versions/74/install.sh b/plugins/php/versions/74/install.sh index 8d77742b7..226affc6f 100755 --- a/plugins/php/versions/74/install.sh +++ b/plugins/php/versions/74/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } diff --git a/plugins/php/versions/80/install.sh b/plugins/php/versions/80/install.sh index dd67abcf4..57ae1c0aa 100755 --- a/plugins/php/versions/80/install.sh +++ b/plugins/php/versions/80/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } @@ -21,7 +20,7 @@ PHP_VER=80 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php diff --git a/plugins/php/versions/81/install.sh b/plugins/php/versions/81/install.sh index e03bc9824..784bc27cf 100755 --- a/plugins/php/versions/81/install.sh +++ b/plugins/php/versions/81/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } @@ -21,7 +20,7 @@ PHP_VER=81 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh index a4cb42167..d30d81356 100755 --- a/plugins/php/versions/82/install.sh +++ b/plugins/php/versions/82/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } @@ -21,7 +20,7 @@ PHP_VER=82 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index 218b88925..d3d35f33a 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } @@ -20,7 +19,7 @@ PHP_VER=83 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh index d34ec9f11..7024f0b2d 100755 --- a/plugins/php/versions/84/install.sh +++ b/plugins/php/versions/84/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } @@ -20,7 +19,7 @@ PHP_VER=84 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php From bd5753eb277622d14b5151fe8e62d58d294c9fd0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 16:26:18 +0800 Subject: [PATCH 310/965] Update panel_task.py --- panel_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index 97c64e853..ba3fe4482 100755 --- a/panel_task.py +++ b/panel_task.py @@ -148,7 +148,7 @@ def siteEdate(): oldEdate = mEdate mw.writeFile('data/edate.pl', mEdate) except Exception as e: - print(str(e)) + print(mw.getTracebackInfo()) def systemTask(): From 8af22d14c865283593c4286f01437171571a4a7d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 16:52:18 +0800 Subject: [PATCH 311/965] update --- web/admin/site/site.py | 16 ++++++++++++++ web/thisdb/sites.py | 4 ++++ web/utils/site.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/web/admin/site/site.py b/web/admin/site/site.py index 0650bda16..1eeb82f27 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -53,6 +53,22 @@ def add(): port = request.form.get('port', '') return MwSites.instance().add(webinfo, port, ps, path, version) +# 站点停止 +@blueprint.route('/stop', endpoint='stop',methods=['POST']) +@panel_login_required +def stop(): + site_id = request.form.get('id', '') + site_name = request.form.get('name', '') + return MwSites.instance().stop(site_id, site_name) + +# 站点开启 +@blueprint.route('/start', endpoint='start',methods=['POST']) +@panel_login_required +def start(): + site_id = request.form.get('id', '') + site_name = request.form.get('name', '') + return MwSites.instance().start(site_id, site_name) + # 添加站点 - 域名 @blueprint.route('/add_domain', endpoint='add_domain',methods=['POST']) @panel_login_required diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index 20c316012..95a426526 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -106,6 +106,7 @@ def setSitesData(site_id, edate: str | None = None, ps: str | None = None, path: str | None = None, + status: str | None = None, ): update_data = {} if edate is not None: @@ -116,5 +117,8 @@ def setSitesData(site_id, if path is not None: update_data['path'] = path + if status is not None: + update_data['status'] = status + return mw.M('sites').where('id=?',(site_id,)).update(update_data) diff --git a/web/utils/site.py b/web/utils/site.py index 60166b1b9..055df1e12 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -211,6 +211,53 @@ class sites(object): mw.restartWeb() return mw.returnData(True, '添加成功') + def stop(self, site_id, site_name): + path = self.setupPath + '/stop' + if not os.path.exists(path): + os.makedirs(path) + default_text = 'The website has been closed!!!' + mw.writeFile(path + '/index.html', default_text) + + binding = thisdb.getBindingListBySiteId(site_id) + for b in binding: + bpath = path + '/' + b['path'] + if not os.path.exists(bpath): + mw.execShell('mkdir -p ' + bpath) + mw.execShell('ln -sf ' + path +'/index.html ' + bpath + '/index.html') + + site_info = thisdb.getSitesById(site_id) + + # nginx + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if conf: + conf = conf.replace(site_info['path'], path) + mw.writeFile(file, conf) + + thisdb.setSitesData(site_id, status='0') + msg = mw.getInfo('网站[{1}]已被停用!', (site_name,)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, '站点已停用!') + + def start(self, site_id, site_name): + path = self.setupPath + '/stop' + + site_info = thisdb.getSitesById(site_id) + # nginx + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if conf: + conf = conf.replace(path, site_info['path']) + mw.writeFile(file, conf) + + thisdb.setSitesData(site_id, status='1') + + msg = mw.getInfo('网站[{1}]已被启用!', (site_name,)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, '站点已启用!') + def nginxAddDomain(self, site_name, domain, port): file = self.getHostConf(site_name) conf = mw.readFile(file) From 5796e311a9578698e5033734954e521b65a682b2 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 22:13:45 +0800 Subject: [PATCH 312/965] update --- panel_task.py | 31 +++++++++++++------------------ web/admin/site/site.py | 6 ++---- web/static/app/site.js | 25 ++++++++++++------------- web/thisdb/sites.py | 4 ++++ web/utils/site.py | 19 ++++++++++--------- 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/panel_task.py b/panel_task.py index ba3fe4482..f896256f1 100755 --- a/panel_task.py +++ b/panel_task.py @@ -114,10 +114,10 @@ def runPanelTask(): thisdb.setTaskData(run_task['id'], end=end) thisdb.setTaskStatus(run_task['id'], 1) except Exception as e: - print(mw.getTracebackInfo()) + pass # 站点过期检查 - # siteEdate() + siteEdateCheck() # 任务队列 def startPanelTask(): @@ -130,25 +130,20 @@ def startPanelTask(): startPanelTask() # 网站到期处理 -def siteEdate(): - global oldEdate +def siteEdateCheck(): try: - if not oldEdate: - oldEdate = mw.readFile('data/edate.pl') - if not oldEdate: - oldEdate = '0000-00-00' - mEdate = time.strftime('%Y-%m-%d', time.localtime()) - if oldEdate == mEdate: + from utils.site import sites as MwSites + website_edate = thisdb.getOption('website_edate', default='0000-00-00') + now_time_ymd = time.strftime('%Y-%m-%d', time.localtime()) + + if website_edate == now_time_ymd: return False - edateSites = mw.M('sites').where('edate>? AND edate" $("#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), + min:getDate(-1), max:'9999-12-31', vlue:getDate(365), type:'date', @@ -111,7 +111,7 @@ $('#site_search').click(function(){ if(_this.html() == '永久'){ dates = '0000-00-00'; } - var loadT = layer.msg(lan.site.saving_txt, { icon: 16, time: 0, shade: [0.3, "#000"]}); + var loadT = layer.msg('正在保存...', { 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}); @@ -123,7 +123,6 @@ $('#site_search').click(function(){ } if(body.length < 10){ body = "当前没有站点数据"; - // $(".dataTables_paginate").hide(); $("#webBody").html(body); } //输出数据列表 diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index 95a426526..e02daa300 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -66,6 +66,10 @@ def isSitesExist(name): return True return False +def getSitesEdateList(edate): + elist = mw.M('sites').field(__FIELD).where('edate>? AND edate Date: Mon, 25 Nov 2024 22:16:19 +0800 Subject: [PATCH 313/965] Update panel_task.py --- panel_task.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/panel_task.py b/panel_task.py index f896256f1..1c26b6fb0 100755 --- a/panel_task.py +++ b/panel_task.py @@ -31,8 +31,6 @@ from admin import setup setup.init() g_log_file = mw.getPanelTaskLog() -isTask = mw.getMWLogs() + '/panelTask.pl' - if not os.path.exists(g_log_file): os.system("touch " + g_log_file) @@ -95,6 +93,9 @@ def downloadHook(count, blockSize, totalSize): writeLogs(json.dumps(speed)) def runPanelTask(): + # 站点过期检查 + siteEdateCheck() + try: bash_list = thisdb.getTaskList(status=-1) for task in bash_list: @@ -116,19 +117,6 @@ def runPanelTask(): except Exception as e: pass - # 站点过期检查 - siteEdateCheck() - -# 任务队列 -def startPanelTask(): - try: - while True: - runPanelTask() - time.sleep(5) - except Exception as e: - time.sleep(30) - startPanelTask() - # 网站到期处理 def siteEdateCheck(): try: @@ -145,6 +133,15 @@ def siteEdateCheck(): except Exception as e: pass +# 任务队列 +def startPanelTask(): + try: + while True: + runPanelTask() + time.sleep(5) + except Exception as e: + time.sleep(30) + startPanelTask() def systemTask(): # 系统监控任务 @@ -157,7 +154,6 @@ def systemTask(): time.sleep(30) systemTask() - # -------------------------------------- PHP监控 start --------------------------------------------- # # 502错误检查线程 def check502Task(): @@ -239,7 +235,7 @@ def startPHPVersion(version): if os.path.exists(cgi): return True except Exception as e: - print(mw.getTracebackInfo()) + # print(mw.getTracebackInfo()) mw.writeLog('PHP守护程序', '自动修复异常:'+str(e)) return True @@ -331,13 +327,11 @@ def run(): oraos = setDaemon(oraos) oraos.start() - # OpenResty Auto Restart Start oar = threading.Thread(target=openrestyAutoRestart) oar = setDaemon(oar) oar.start() - # Panel Restart Start rps = threading.Thread(target=restartPanelService) rps = setDaemon(rps) From ffcceb16cf85a2f1456e202e785d614d271ce96b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Mon, 25 Nov 2024 22:38:16 +0800 Subject: [PATCH 314/965] update --- web/admin/site/site.py | 3 +- web/static/app/site.js | 55 ++++++++++++++++----------------- web/templates/default/site.html | 12 ++++--- web/thisdb/sites.py | 9 ++++-- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/web/admin/site/site.py b/web/admin/site/site.py index b0e925977..26ffbd81b 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -34,8 +34,9 @@ def list(): limit = request.form.get('limit', '10') type_id = request.form.get('type_id', '0').strip() search = request.form.get('search', '').strip() + order = request.form.get('order', '').strip() - info = thisdb.getSitesList(page=int(p),size=int(limit),type_id=int(type_id), search=search) + info = thisdb.getSitesList(page=int(p),size=int(limit),type_id=int(type_id), search=search,order=order) data = {} data['data'] = info['list'] diff --git a/web/static/app/site.js b/web/static/app/site.js index 2993a5b22..ff10cea86 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -45,56 +45,56 @@ function getDate(a) { type = '&type_id='+type_id; } - var sUrl = '/site/list'; var pdata = 'limit=10&p=' + page + '&search=' + search + order + type; var loadT = layer.load(); //取回数据 - $.post(sUrl, pdata, function(data) { + $.post('/site/list', pdata, function(data) { layer.close(loadT); //构造数据列表 var body = ''; $("#webBody").html(body); - for (var i = 0; i < data.data.length; i++) { + var list = data.data; + for (var i = 0; i < list.length; i++) { //当前站点状态 - if (data.data[i].status == '正在运行' || data.data[i].status == '1') { - var status = "运行中"; + if (list[i].status == '正在运行' || list[i].status == '1') { + var status = "运行中"; } else { - var status = "已停止"; + var status = "已停止"; } //是否有备份 - if (data.data[i].backup_count > 0) { - var backup = "有备份"; + if (list[i].backup_count > 0) { + var backup = "有备份"; } else { - var backup = "无备份"; + var backup = "无备份"; } //是否设置有效期 - var web_end_time = (data.data[i].edate == "0000-00-00") ? '永久': data.data[i].edate; + var web_end_time = (list[i].edate == "0000-00-00") ? '永久': list[i].edate; //表格主体 - var shortwebname = data.data[i].name; - var shortpath = data.data[i].path; - if(data.data[i].name.length > 30) { - shortwebname = data.data[i].name.substring(0, 30) + "..."; + var shortwebname = list[i].name; + var shortpath = list[i].path; + if(list[i].name.length > 30) { + shortwebname = list[i].name.substring(0, 30) + "..."; } - if(data.data[i].path.length > 30){ - shortpath = data.data[i].path.substring(0, 30) + "..."; + if(list[i].path.length > 30){ + shortpath = list[i].path.substring(0, 30) + "..."; } - var idname = data.data[i].name.replace(/\./g,'_'); + var idname = list[i].name.replace(/\./g,'_'); - body = "\ - " + shortwebname + "\ + body = "\ + " + shortwebname + "\ " + status + "\ " + backup + "\ - " + shortpath + "\ - " + web_end_time + "\ - " + data.data[i].ps + "\ + " + shortpath + "\ + " + web_end_time + "\ + " + list[i].ps + "\ \ - 设置\ - | 删除\ + 设置\ + | 删除\ " $("#webBody").append(body); - $('#webBody').on('click','#site_'+ data.data[i].id,function(){ + $('#webBody').on('click','#site_'+ list[i].id,function(){ var _this = $(this); var id = $(this).attr('data-ids'); laydate.render({ @@ -134,7 +134,6 @@ function getDate(a) { //输出分页 $("#webPage").html(data.page); - // $("#webPage").html('
                    站点分类:
                    '); $(".btlinkbed").click(function(){ var dataid = $(this).attr("data-id"); @@ -336,9 +335,7 @@ function webAddPage(type) { if (len > 20) { str = str.substring(0, 20); $(this).val(str); - layer.msg('不能超出20个字符!', { - icon: 0 - }); + layer.msg('不能超出20个字符!', {icon: 0}); } }) //获取当前时间时间戳,截取后6位 diff --git a/web/templates/default/site.html b/web/templates/default/site.html index aa91828b4..29e0628c9 100755 --- a/web/templates/default/site.html +++ b/web/templates/default/site.html @@ -37,10 +37,12 @@ 域名 - 网站状态 + 网站状态 备份 网站目录 - 到期日期 + + 到期日期 + 备注 操作 @@ -55,8 +57,10 @@
                    站点分类:
                    diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index e02daa300..fb2f0a3c4 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -74,7 +74,8 @@ def getSitesList( page:int | None = 1, size:int | None = 10, type_id:int | None = 0, - search: str | None = '' + search: str | None = '', + order:str | None = None, ): sql_where = '' if search != '' : @@ -94,7 +95,11 @@ def getSitesList( start = (int(page) - 1) * (int(size)) limit = str(start) + ',' +str(size) - site_list = dbM.limit(limit).order('id desc').select() + + if order is not None: + site_list = dbM.limit(limit).order(order).select() + else: + site_list = dbM.limit(limit).order('id desc').select() data = {} From 0b37a34528fc652a5eb51e2bac2374fc007c2f5c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 00:37:55 +0800 Subject: [PATCH 315/965] up --- panel_task.py | 47 ++++++++++++++++++++------------------ web/admin/files/files.py | 20 +++++++++++++++- web/admin/task/__init__.py | 10 ++++---- web/core/mw.py | 7 ++++-- web/static/app/public.js | 2 +- web/thisdb/tasks.py | 30 +++++++++++++++++++++++- 6 files changed, 84 insertions(+), 32 deletions(-) diff --git a/panel_task.py b/panel_task.py index 1c26b6fb0..39e436064 100755 --- a/panel_task.py +++ b/panel_task.py @@ -77,43 +77,46 @@ def downloadFile(url, filename): if not mw.isAppleSystem(): os.system('chown www.www ' + filename) - writeLogs('done') + writeLogs(filename + ' download success!') except Exception as e: writeLogs(str(e)) return True def downloadHook(count, blockSize, totalSize): # 下载文件进度回调 - global pre used = count * blockSize - pre1 = int((100.0 * used / totalSize)) - if pre == (100 - pre1): - return - speed = {'total': totalSize, 'used': used, 'pre': pre1} + pre = int((100.0 * used / totalSize)) + speed = {'total': totalSize, 'used': used, 'pre': pre} writeLogs(json.dumps(speed)) def runPanelTask(): # 站点过期检查 siteEdateCheck() + lock_file = mw.getTriggerTaskLockFile() + # mw.writeFile(lock_file,'True') try: - bash_list = thisdb.getTaskList(status=-1) - for task in bash_list: - thisdb.setTaskStatus(task['id'], 0) + if os.path.exists(lock_file): + bash_list = thisdb.getTaskList(status=-1) + for task in bash_list: + thisdb.setTaskStatus(task['id'], 0) - run_list = thisdb.getTaskList(status=0) - for run_task in run_list: - start = int(time.time()) - thisdb.setTaskData(run_task['id'], start=start) - thisdb.setTaskStatus(run_task['id'], -1) - if run_task['type'] == 'download': - argv = run_task['cmd'].split('|mw|') - downloadFile(argv[0], argv[1]) - elif run_task['type'] == 'execshell': - execShell(run_task['cmd']) - end = int(time.time()) - thisdb.setTaskData(run_task['id'], end=end) - thisdb.setTaskStatus(run_task['id'], 1) + run_list = thisdb.getTaskList(status=0) + for run_task in run_list: + start = int(time.time()) + thisdb.setTaskData(run_task['id'], start=start) + thisdb.setTaskStatus(run_task['id'], -1) + + if run_task['type'] == 'download': + argv = run_task['cmd'].split('|mw|') + downloadFile(argv[0], argv[1]) + elif run_task['type'] == 'execshell': + execShell(run_task['cmd']) + + end = int(time.time()) + thisdb.setTaskData(run_task['id'], end=end) + thisdb.setTaskStatus(run_task['id'], 1) + os.remove(lock_file) except Exception as e: pass diff --git a/web/admin/files/files.py b/web/admin/files/files.py index 326ff7e9c..dcca06a1b 100644 --- a/web/admin/files/files.py +++ b/web/admin/files/files.py @@ -9,6 +9,7 @@ # --------------------------------------------------------------------------------- import os +import time from flask import Blueprint, render_template from flask import request @@ -185,7 +186,7 @@ def delete_dir(): path = request.form.get('path', '') return file.dirDelete(path) -# 删除文件 +# 下载文件 @blueprint.route('/download', endpoint='download', methods=['GET']) @panel_login_required def download(): @@ -199,6 +200,23 @@ def download(): response = make_response(send_from_directory(os.path.dirname(filename), os.path.basename(filename), as_attachment=is_attachment)) return response +# 远程下载 +@blueprint.route('/download_file', endpoint='download_file', methods=['POST']) +@panel_login_required +def download_file(): + url = request.form.get('url', '') + path = request.form.get('path', '') + filename = request.form.get('filename', '') + + execstr = url + '|mw|' + path + '/' + filename + execstr = execstr.strip() + + title = '下载文件[' + filename + ']' + thisdb.addTaskByDownload(name=title, cmd=execstr) + # self.setFileAccept(path + '/' + filename) + mw.triggerTask() + return mw.returnData(True, '已将下载任务添加到队列!') + # 日志清空 @blueprint.route('/close_logs', endpoint='close_logs', methods=['POST']) @panel_login_required diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index 68598414c..92465b109 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -8,6 +8,8 @@ # Author: midoks # --------------------------------------------------------------------------------- +import json +import time from flask import Blueprint, render_template from flask import request @@ -62,14 +64,12 @@ def get_task_speed(): data['cmd'] = row['cmd'] if row['type'] == 'download': - readLine = "" + readLine = '' for i in range(3): try: readLine = mw.readFile(task_logfile) - if len(readLine) > 10: - data['msg'] = json.loads(readLine) - data['isDownload'] = True - break + data['msg'] = json.loads(readLine) + data['isDownload'] = True except Exception as e: if i == 2: thisdb.setTaskStatus(row['id'],0) diff --git a/web/core/mw.py b/web/core/mw.py index bcf565d6b..d9bccef99 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -1273,9 +1273,12 @@ def fileNameCheck(filename): return False return True +def getTriggerTaskLockFile(): + return getPanelDir() + '/logs/panel_task.lock' + def triggerTask(): - isTask = getPanelDir() + '/logs/panel_task.lock' - writeFile(isTask, 'True') + lock_file = getTriggerTaskLockFile() + writeFile(lock_file, 'True') def restartTask(): initd = getPanelDir() + '/scripts/init.d/mw' diff --git a/web/static/app/public.js b/web/static/app/public.js index 6188975cf..d0df5768d 100755 --- a/web/static/app/public.js +++ b/web/static/app/public.js @@ -1493,7 +1493,7 @@ function getReloads() {
                    \ " + h.task[g].name + "" + (toSize(h.msg.used) + "/" + toSize(h.msg.total)) + "\ " + h.msg.pre + "%\ - "+lan.bt.task_downloading+" | "+lan.public.close+"\ + 下载中 | "+lan.public.close+"\ " } } else { diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 44c4c1722..317e5e065 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -22,6 +22,34 @@ def getTaskCount( def getTaskUnexecutedCount() -> int: return mw.M('tasks').where('status!=?',(1,)).count() +def addTaskByDownload( + name: str | None = '下载文件', + cmd: str | None = None, + type: str | None = 'download', + status: int | None = 0, +): + ''' + 添加后台任务 + :name -> str 类型 + :cmd -> str 日志内容 (必填) + :type -> str 用户ID + ''' + if cmd is None: + return False + + add_time = mw.formatDate() + insert_data = { + 'name':name, + 'type':type, + 'cmd':cmd, + 'start':0, + 'end':0, + 'status':status, + 'add_time':add_time, + } + mw.M('tasks').insert(insert_data) + return True + def addTask( name: str | None = '常用任务', cmd: str | None = None, @@ -77,7 +105,7 @@ def getTaskPage( return rdata def getTaskFirstByRun() -> None: - data = mw.M('tasks').where('status=?', (-1,)).field(__FIELD).order('id asc').find() + data = mw.M('tasks').where('status=? or status=0', (-1,)).field(__FIELD).order('id asc').find() if data is None: return None return data From 39a1c1e6e06c486d5c220d1d5c20bbbf136dd5f4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 01:56:33 +0800 Subject: [PATCH 316/965] update --- plugins/php/versions/82/install.sh | 27 ++++++++++++++++++++++++++- plugins/php/versions/83/install.sh | 6 +++++- plugins/php/versions/84/install.sh | 5 ++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh index d30d81356..3a2bdd117 100755 --- a/plugins/php/versions/82/install.sh +++ b/plugins/php/versions/82/install.sh @@ -73,7 +73,10 @@ cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` + OPTIONS="${OPTIONS} --with-curl=$LIB_DEPEND_DIR" else OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-readline" @@ -150,6 +153,28 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --disable-fileinfo \ $OPTIONS \ --enable-fpm + echo "./configure \ + --prefix=$serverPath/php/${PHP_VER} \ + --exec-prefix=$serverPath/php/${PHP_VER} \ + --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ + --enable-mysqlnd \ + --with-mysqli=mysqlnd \ + --with-pdo-mysql=mysqlnd \ + --with-zlib-dir=$serverPath/lib/zlib \ + $ZIP_OPTION \ + --enable-mbstring \ + --enable-ftp \ + --enable-sockets \ + --enable-simplexml \ + --enable-soap \ + --enable-posix \ + --enable-sysvmsg \ + --enable-sysvsem \ + --enable-sysvshm \ + --disable-intl \ + --disable-fileinfo \ + $OPTIONS \ + --enable-fpm" make clean && make -j${cpuCore} && make install && make clean # rm -rf $sourcePath/php/php${PHP_VER} diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index d3d35f33a..406040830 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -72,7 +72,11 @@ cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" + # BREW_DIR=`which brew` + # BREW_DIR=${BREW_DIR/\/bin\/brew/} + # LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` + # OPTIONS="${OPTIONS} --with-curl=$LIB_DEPEND_DIR" + echo "curl" else OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-readline" diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh index 7024f0b2d..67ad8147e 100755 --- a/plugins/php/versions/84/install.sh +++ b/plugins/php/versions/84/install.sh @@ -14,7 +14,7 @@ function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } -version=8.4.0RC2 +version=8.4.1 PHP_VER=84 Install_php() { @@ -36,8 +36,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then - # wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz - wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://downloads.php.net/~calvinb/php-${version}.tar.xz + wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz fi #检测文件是否损坏. From add4d4bb5e8eb4c099f4713d8b7735add6ba867c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 02:01:07 +0800 Subject: [PATCH 317/965] Update panel_task.py --- panel_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index 39e436064..ce12b9641 100755 --- a/panel_task.py +++ b/panel_task.py @@ -94,7 +94,7 @@ def runPanelTask(): siteEdateCheck() lock_file = mw.getTriggerTaskLockFile() - # mw.writeFile(lock_file,'True') + mw.writeFile(lock_file,'True') try: if os.path.exists(lock_file): bash_list = thisdb.getTaskList(status=-1) From 7da0fa43798fecd28f92c188eba29ddc56ba2365 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 03:41:53 +0800 Subject: [PATCH 318/965] Update index.py --- plugins/phpmyadmin/index.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/phpmyadmin/index.py b/plugins/phpmyadmin/index.py index 884ef2f34..123c99dab 100755 --- a/plugins/phpmyadmin/index.py +++ b/plugins/phpmyadmin/index.py @@ -67,7 +67,7 @@ def getConfInc(): def getPort(): file = getConf() content = mw.readFile(file) - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -196,8 +196,8 @@ def status(): def __release_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'phpMyAdmin默认端口', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'phpMyAdmin默认端口', 'port') return port except Exception as e: return "Release failed {}".format(e) @@ -206,8 +206,8 @@ def __release_port(port): def __delete_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().delAcceptPortArgs(port, 'tcp') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().delAcceptPortCmd(port, 'tcp') return port except Exception as e: return "Release failed {}".format(e) From 88b86b2b2bdb372c65aba5060390f4fd72702602 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 03:46:00 +0800 Subject: [PATCH 319/965] Update install.sh --- plugins/php/install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/php/install.sh b/plugins/php/install.sh index 1460c6cc0..d6ba610a0 100755 --- a/plugins/php/install.sh +++ b/plugins/php/install.sh @@ -73,6 +73,9 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then cd ${rootPath}/plugins/php/versions/common && bash gettext.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash redis.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash memcached.sh install ${type} + cd ${rootPath}/plugins/php/versions/common && bash pcntl.sh install ${type} + + if [ "${type}" -gt "73" ];then cd ${rootPath}/plugins/php/versions/common && bash zlib.sh install ${type} From 5ca5265b92483585972d307a595bc449017054c3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 03:49:36 +0800 Subject: [PATCH 320/965] Update index.py --- plugins/php/index.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/plugins/php/index.py b/plugins/php/index.py index 761a33902..0487f9108 100755 --- a/plugins/php/index.py +++ b/plugins/php/index.py @@ -978,14 +978,10 @@ def installLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + \ - version + ' install ' + name - - rettime = time.strftime('%Y-%m-%d %H:%M:%S') - insert_info = (None, '安装[' + name + '-' + version + ']', - 'execshell', '0', rettime, execstr) - mw.M('tasks').add('id,name,type,status,addtime,execstr', insert_info) - + cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name + install_name = '安装[' + name + '-' + version + ']' + import thisdb + thisdb.addTask(name=install_name,cmd=cmd) mw.triggerTask() return mw.returnJson(True, '已将下载任务添加到队列!') From 453639bf92e7d90d4b6d283d4cf15408527356aa Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 03:50:08 +0800 Subject: [PATCH 321/965] Update index.py --- plugins/php/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/php/index.py b/plugins/php/index.py index 0487f9108..1a6e8d6c9 100755 --- a/plugins/php/index.py +++ b/plugins/php/index.py @@ -982,6 +982,7 @@ def installLib(version): install_name = '安装[' + name + '-' + version + ']' import thisdb thisdb.addTask(name=install_name,cmd=cmd) + mw.triggerTask() return mw.returnJson(True, '已将下载任务添加到队列!') @@ -993,8 +994,7 @@ def uninstallLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + \ - version + ' uninstall ' + name + execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' uninstall ' + name data = mw.execShell(execstr) # data[0] == '' and From f5089eeb16e1bcc6894e5296571fd149d0b64197 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 03:53:21 +0800 Subject: [PATCH 322/965] update --- plugins/php-apt/index.py | 11 ++++------- plugins/php-yum/index.py | 11 ++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/plugins/php-apt/index.py b/plugins/php-apt/index.py index d6146bfac..33ed29553 100755 --- a/plugins/php-apt/index.py +++ b/plugins/php-apt/index.py @@ -798,13 +798,10 @@ def installLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + \ - version + ' install ' + name - - rettime = time.strftime('%Y-%m-%d %H:%M:%S') - insert_info = (None, '安装PHPAPT[' + name + '-' + version + ']', - 'execshell', '0', rettime, execstr) - mw.M('tasks').add('id,name,type,status,addtime,execstr', insert_info) + cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name + install_name = '安装PHPAPT[' + name + '-' + version + ']' + import thisdb + thisdb.addTask(name=install_name,cmd=cmd) mw.triggerTask() return mw.returnJson(True, '已将下载任务添加到队列!') diff --git a/plugins/php-yum/index.py b/plugins/php-yum/index.py index 153addc64..00d3c1f4d 100755 --- a/plugins/php-yum/index.py +++ b/plugins/php-yum/index.py @@ -799,13 +799,10 @@ def installLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + '/versions/' + \ - " && /bin/bash common.sh " + version + ' install ' + name - - rettime = time.strftime('%Y-%m-%d %H:%M:%S') - insert_info = (None, '安装PHPYUM[' + name + '-' + version + ']', - 'execshell', '0', rettime, execstr) - mw.M('tasks').add('id,name,type,status,addtime,execstr', insert_info) + cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name + install_name = '安装PHPYUM[' + name + '-' + version + ']' + import thisdb + thisdb.addTask(name=install_name,cmd=cmd) mw.triggerTask() return mw.returnJson(True, '已将下载任务添加到队列!') From 79160854b9ee16845f7c409eddb95d7955e901ca Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 03:53:33 +0800 Subject: [PATCH 323/965] Update index.py --- plugins/php-yum/index.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/php-yum/index.py b/plugins/php-yum/index.py index 00d3c1f4d..edae3a4b8 100755 --- a/plugins/php-yum/index.py +++ b/plugins/php-yum/index.py @@ -815,8 +815,7 @@ def uninstallLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + '/versions/' + \ - " && /bin/bash common.sh " + version + ' uninstall ' + name + execstr = "cd " + getPluginDir() + '/versions/' + " && /bin/bash common.sh " + version + ' uninstall ' + name data = mw.execShell(execstr) # data[0] == '' and From 6e48e0c3396d9e603b8ec228663ffc578afc9ac4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 03:55:43 +0800 Subject: [PATCH 324/965] Update mongodb.sh --- plugins/php/versions/common/mongodb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php/versions/common/mongodb.sh b/plugins/php/versions/common/mongodb.sh index 3f60b9f79..96228c86b 100755 --- a/plugins/php/versions/common/mongodb.sh +++ b/plugins/php/versions/common/mongodb.sh @@ -18,7 +18,7 @@ actionType=$1 version=$2 if [ "$version" -ge '74' ];then - LIBV=1.19.0 + LIBV=1.20.0 fi if [ "$version" == '71' ];then From b124aabb7b86b70afba96d0905742252bc3abba9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 04:38:55 +0800 Subject: [PATCH 325/965] update --- web/admin/setting/__init__.py | 2 + web/admin/setting/panel_ssl.py | 52 +++++++ web/core/mw.py | 11 +- web/static/app/config.js | 6 +- web/utils/setting.py | 259 +++++++++++++++++++++++++++++++++ 5 files changed, 322 insertions(+), 8 deletions(-) create mode 100644 web/admin/setting/panel_ssl.py create mode 100644 web/utils/setting.py diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py index 1627e3b8f..f6183dbaf 100644 --- a/web/admin/setting/__init__.py +++ b/web/admin/setting/__init__.py @@ -18,3 +18,5 @@ from .notify_email import * from .notify_tgbot import * from .app import * +from .panel_ssl import * + diff --git a/web/admin/setting/panel_ssl.py b/web/admin/setting/panel_ssl.py new file mode 100644 index 000000000..bf903a495 --- /dev/null +++ b/web/admin/setting/panel_ssl.py @@ -0,0 +1,52 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from utils.setting import setting as MwSetting + +from .setting import blueprint +import thisdb + +# 获取面板证书信息 +@blueprint.route('/get_panel_ssl', endpoint='get_panel_ssl', methods=['POST']) +@panel_login_required +def get_panel_ssl(): + return MwSetting.instance().getPanelSsl() + + +# 获取面板证书信息 +@blueprint.route('/save_panel_ssl', endpoint='save_panel_ssl', methods=['POST']) +@panel_login_required +def save_panel_ssl(): + choose = request.form.get('choose', '').strip() + certPem = request.form.get('certPem', '').strip() + privateKey = request.form.get('privateKey', '').strip() + return MwSetting.instance().savePanelSsl(choose,certPem,privateKey) + +# 获取面板证书信息 +@blueprint.route('/del_panel_ssl', endpoint='del_panel_ssl', methods=['POST']) +@panel_login_required +def del_panel_ssl(): + choose = request.form.get('choose', '').strip() + return MwSetting.instance().delPanelSsl(choose) + diff --git a/web/core/mw.py b/web/core/mw.py index d9bccef99..4564d6825 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -1548,9 +1548,10 @@ def getCertName(certPath): return None def createLocalSSL(): - if not os.path.exists('ssl/local'): - execShell('mkdir -p ssl/local') - + pdir = getPanelDir() + local_dir = pdir+'/ssl/local' + if not os.path.exists(local_dir): + execShell('mkdir -p ' + local_dir) # 自签证书 # if os.path.exists('ssl/local/input.pl'): @@ -1577,8 +1578,8 @@ def createLocalSSL(): cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) if len(cert_ca) > 100 and len(private_key) > 100: - writeFile('ssl/local/cert.pem', cert_ca, 'wb+') - writeFile('ssl/local/private.pem', private_key, 'wb+') + writeFile(local_dir+'/cert.pem', cert_ca, 'wb+') + writeFile(local_dir+'/private.pem', private_key, 'wb+') return True return False diff --git a/web/static/app/config.js b/web/static/app/config.js index a56a25a10..aad1c776e 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -905,7 +905,7 @@ function getPanelSSL(){ btn3:function(){ data['choose'] = 'nginx'; var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ + $.post('/setting/save_panel_ssl',data,function(rdata){ layer.close(loadT); if(rdata.status){ layer.closeAll(); @@ -917,7 +917,7 @@ function getPanelSSL(){ function(index) { data['choose'] = 'local'; var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ + $.post('/setting/save_panel_ssl',data,function(rdata){ layer.close(loadT); if(rdata.status){ layer.closeAll(); @@ -942,7 +942,7 @@ function getPanelSSL(){ var data = {}; data['choose'] = 'nginx'; var loadT = layer.msg('正在删除面板SSL【nginx】...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/del_panel_ssl',data,function(rdata){ + $.post('/setting/del_panel_ssl',data,function(rdata){ layer.close(loadT); if(rdata.status){ layer.closeAll(); diff --git a/web/utils/setting.py b/web/utils/setting.py new file mode 100644 index 000000000..bd9cd4a29 --- /dev/null +++ b/web/utils/setting.py @@ -0,0 +1,259 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import re +import threading +import re +import time + +import core.mw as mw +import thisdb + +class setting(object): + + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(setting, "_instance"): + with setting._instance_lock: + if not hasattr(setting, "_instance"): + setting._instance = setting(*args, **kwargs) + return setting._instance + + def __init__(self): + pass + + + # 保存面板证书 + def savePanelSsl(self, choose, cert_pem, private_key): + if not mw.inArray(['local','nginx'], choose): + return mw.returnData(True, '保存错误面板SSL类型!') + + pdir = mw.getPanelDir() + keyPath = pdir+'/ssl/'+choose+'/private.pem' + certPath = pdir+'/ssl/'+choose+'/cert.pem' + check_cert_pl = '/tmp/cert.pl' + + if not os.path.exists(keyPath): + return mw.returnData(False, '【'+choose+'】SSL类型不存在,先申请!') + + if(private_key.find('KEY') == -1): + return mw.returnData(False, '秘钥错误,请检查!') + if(cert_pem.find('CERTIFICATE') == -1): + return mw.returnData(False, '证书错误,请检查!') + + mw.writeFile(check_cert_pl, cert_pem) + if private_key: + mw.writeFile(keyPath, private_key) + if cert_pem: + mw.writeFile(certPath, cert_pem) + if not mw.checkCert(check_cert_pl): + os.remove(check_cert_pl) + return mw.returnData(False, '证书错误,请检查!') + os.remove(check_cert_pl) + return mw.returnData(True, '证书已保存!') + + + def getPanelSsl(self): + rdata = {} + rdata['choose'] = 'local' + + + pdir = mw.getPanelDir() + + keyPath = pdir+'/ssl/local/private.pem' + certPath = pdir+'/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 = pdir+'/ssl/nginx/private.pem' + certPath = pdir+'/ssl/nginx/cert.pem' + + cert = {} + cert['privateKey'] = '' + cert['certPem'] = '' + cert['info'] = {} + if os.path.exists(keyPath): + cert['privateKey'] = mw.readFile(keyPath) + + if os.path.exists(keyPath): + cert['certPem'] = mw.readFile(certPath) + cert['info'] = mw.getCertName(certPath) + + rdata['nginx'] = cert + + return rdata + + # 删除面板证书 + def delPanelSsl(self, choose): + ip = mw.getLocalIp() + if mw.isAppleSystem(): + ip = '127.0.0.1' + + + if not mw.inArray(['local','nginx'], choose): + return mw.returnData(True, '删除错误面板SSL类型!') + + port_pl = mw.getPanelDir()+'/data/port.pl' + port = mw.readFile(port_pl).strip() + + to_panel_url = 'http://'+ip+":"+port+'/config' + + if choose == 'local': + dst_path = mw.getPanelDir() + '/ssl/local' + if os.path.exists(dst_path): + mw.execShell('rm -rf ' + dst_path) + mw.restartMw(); + return mw.returnData(True, '删除本地面板SSL成功!',to_panel_url) + else: + return mw.returnData(True, '已经删除本地面板SSL!',to_panel_url) + + if choose == 'nginx': + + bind_domain = self.__file['bind_domain'] + if not os.path.exists(bind_domain): + return mw.returnData(False, '未绑定域名!') + + siteName = mw.readFile(bind_domain).strip() + + src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName + + dst_path = mw.getPanelDir() + '/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.returnData(True, '删除面板SSL成功!') + + mw.restartNginx() + mw.restartMw() + return mw.returnData(False, '已经删除面板SSL!') + return mw.returnData(False, '未知类型!') + + # 面板本地SSL设置 + def setPanelLocalSsl(self): + pdir = mw.getPanelDir() + + cert = {} + keyPath = pdir+'/ssl/local/private.pem' + certPath = pdir+'/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, '设置成功') + + # 申请面板let证书 + # def applyPanelAcmeSsl(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 + '

                    响应状态:' + str(emsg['status']) + '

                    错误类型:' + emsg[ + # 'type'] + '

                    错误代码:' + emsg['detail'] + '

                    ' + # return mw.returnJson(data['status'], msg, data['msg']) + # else: + # is_already_apply = True + + # mw.buildSoftLink(src_csrpath, dst_csrpath, True) + # mw.buildSoftLink(src_keypath, dst_keypath, True) + # mw.execShell('echo "acme" > "' + dst_path + '/README"') + + # tmp_well_know = auth_to + '/.well-known' + # if os.path.exists(tmp_well_know): + # mw.execShell('rm -rf ' + tmp_well_know) + + # if os.path.exists(dst_path): + # choose_file = self.__file['ssl'] + # mw.writeFile(choose_file, 'nginx') + + # data = self.getPanelSslData() + + # if is_already_apply: + # return mw.returnJson(True, '重复申请!', data) + # return mw.returnJson(True, '申请成功!', data) \ No newline at end of file From f46c28b39d647990d74c86578ec404af342aed09 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 04:43:57 +0800 Subject: [PATCH 326/965] update --- data/json/type.json | 42 ----- data/sql/default.sql | 146 ------------------ data/sql/system.sql | 41 ----- data/vip/readme.md | 1 - web/misc/nginx/rewrite/EmpireCMS.conf | 10 ++ web/misc/nginx/rewrite/cloudfare_real_ip.conf | 43 ++++++ web/misc/nginx/rewrite/dedecms.conf | 12 ++ web/misc/nginx/rewrite/discuzx.conf | 18 +++ web/misc/nginx/rewrite/discuzx2.conf | 14 ++ web/misc/nginx/rewrite/discuzx3.conf | 15 ++ web/misc/nginx/rewrite/drupal.conf | 5 + web/misc/nginx/rewrite/ecshop.conf | 33 ++++ web/misc/nginx/rewrite/emlog.conf | 7 + web/misc/nginx/rewrite/fastdfs.conf | 5 + web/misc/nginx/rewrite/gunicorn.conf | 9 ++ web/misc/nginx/rewrite/header_cors.conf | 15 ++ web/misc/nginx/rewrite/laravel5.conf | 3 + web/misc/nginx/rewrite/mvc.conf | 6 + web/misc/nginx/rewrite/nezha.conf | 15 ++ web/misc/nginx/rewrite/phpcms.conf | 9 ++ web/misc/nginx/rewrite/phpwind.conf | 4 + web/misc/nginx/rewrite/proxy.conf | 7 + web/misc/nginx/rewrite/proxydg.conf | 8 + web/misc/nginx/rewrite/sablog.conf | 16 ++ web/misc/nginx/rewrite/seacms.conf | 11 ++ web/misc/nginx/rewrite/shopex.conf | 5 + web/misc/nginx/rewrite/sub.conf.tpl | 16 ++ web/misc/nginx/rewrite/thinkphp.conf | 5 + web/misc/nginx/rewrite/typecho.conf | 13 ++ web/misc/nginx/rewrite/v2ray.conf | 15 ++ web/misc/nginx/rewrite/walle.conf | 34 ++++ web/misc/nginx/rewrite/whmcs.conf | 16 ++ web/misc/nginx/rewrite/wmcms.conf | 16 ++ web/misc/nginx/rewrite/wordpress.conf | 14 ++ web/misc/nginx/rewrite/zblog.conf | 11 ++ web/misc/nginx/tpl/nginx.conf | 53 +++++++ web/misc/nginx/tpl/nginx_dirbind.conf | 48 ++++++ web/misc/nginx/tpl/nginx_panel.conf | 23 +++ 38 files changed, 534 insertions(+), 230 deletions(-) delete mode 100755 data/json/type.json delete mode 100755 data/sql/default.sql delete mode 100755 data/sql/system.sql delete mode 100644 data/vip/readme.md create mode 100755 web/misc/nginx/rewrite/EmpireCMS.conf create mode 100644 web/misc/nginx/rewrite/cloudfare_real_ip.conf create mode 100755 web/misc/nginx/rewrite/dedecms.conf create mode 100755 web/misc/nginx/rewrite/discuzx.conf create mode 100755 web/misc/nginx/rewrite/discuzx2.conf create mode 100755 web/misc/nginx/rewrite/discuzx3.conf create mode 100755 web/misc/nginx/rewrite/drupal.conf create mode 100755 web/misc/nginx/rewrite/ecshop.conf create mode 100755 web/misc/nginx/rewrite/emlog.conf create mode 100644 web/misc/nginx/rewrite/fastdfs.conf create mode 100755 web/misc/nginx/rewrite/gunicorn.conf create mode 100755 web/misc/nginx/rewrite/header_cors.conf create mode 100755 web/misc/nginx/rewrite/laravel5.conf create mode 100755 web/misc/nginx/rewrite/mvc.conf create mode 100644 web/misc/nginx/rewrite/nezha.conf create mode 100755 web/misc/nginx/rewrite/phpcms.conf create mode 100755 web/misc/nginx/rewrite/phpwind.conf create mode 100644 web/misc/nginx/rewrite/proxy.conf create mode 100644 web/misc/nginx/rewrite/proxydg.conf create mode 100755 web/misc/nginx/rewrite/sablog.conf create mode 100755 web/misc/nginx/rewrite/seacms.conf create mode 100755 web/misc/nginx/rewrite/shopex.conf create mode 100644 web/misc/nginx/rewrite/sub.conf.tpl create mode 100755 web/misc/nginx/rewrite/thinkphp.conf create mode 100755 web/misc/nginx/rewrite/typecho.conf create mode 100644 web/misc/nginx/rewrite/v2ray.conf create mode 100755 web/misc/nginx/rewrite/walle.conf create mode 100755 web/misc/nginx/rewrite/whmcs.conf create mode 100755 web/misc/nginx/rewrite/wmcms.conf create mode 100755 web/misc/nginx/rewrite/wordpress.conf create mode 100755 web/misc/nginx/rewrite/zblog.conf create mode 100755 web/misc/nginx/tpl/nginx.conf create mode 100755 web/misc/nginx/tpl/nginx_dirbind.conf create mode 100755 web/misc/nginx/tpl/nginx_panel.conf diff --git a/data/json/type.json b/data/json/type.json deleted file mode 100755 index 2943516b5..000000000 --- a/data/json/type.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "title":"全部", - "type":0, - "ps":"" - }, - { - "title":"已安装", - "type":-1, - "ps":"" - }, - { - "title":"运行环境", - "type":1, - "ps":"" - }, - { - "title":"数据软件", - "type":2, - "ps":"" - }, - { - "title":"代码管理", - "type":3, - "ps":"" - }, - { - "title":"系统工具", - "type":4, - "ps":"" - }, - { - "title":"其他插件", - "type":5, - "ps":"" - }, - { - "title":"辅助插件", - "type":6, - "ps":"" - } -] \ No newline at end of file diff --git a/data/sql/default.sql b/data/sql/default.sql deleted file mode 100755 index ec3bc47d0..000000000 --- a/data/sql/default.sql +++ /dev/null @@ -1,146 +0,0 @@ - -CREATE TABLE IF NOT EXISTS `backup` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `type` INTEGER, - `name` TEXT, - `pid` INTEGER, - `filename` TEXT, - `size` INTEGER, - `add_time` TEXT -); - -CREATE TABLE IF NOT EXISTS `binding` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pid` INTEGER, - `domain` TEXT, - `path` TEXT, - `port` INTEGER, - `add_time` TEXT -); - - -CREATE TABLE IF NOT EXISTS `crontab` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `type` TEXT, - `where1` TEXT, - `where_hour` INTEGER, - `where_minute` INTEGER, - `echo` TEXT, - `status` INTEGER DEFAULT '1', - `save` INTEGER DEFAULT '3', - `backup_to` TEXT DEFAULT 'off', - `sname` TEXT, - `sbody` TEXT, - 'stype' TEXT, - `url_address` TEXT, - `add_time` TEXT, - `update_time` TEXT -); - -CREATE TABLE IF NOT EXISTS `firewall` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `port` TEXT, - `protocol` TEXT DEFAULT 'tcp', - `ps` TEXT, - `add_time` TEXT, - `update_time` TEXT -); - -ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp'; - -INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`) VALUES -(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), -(2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00','0000-00-00 00:00:00'); - - - -CREATE TABLE IF NOT EXISTS `logs` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `type` TEXT, - `log` TEXT, - `uid` INTEGER DEFAULT '1', - `add_time` TEXT -); - -CREATE TABLE IF NOT EXISTS `sites` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `path` TEXT, - `status` TEXT, - `index` TEXT, - `type_id` INTEGER, - `ps` TEXT, - `edate` TEXT, - `ssl_effective_date` TEXT, - `ssl_expiration_date` TEXT, - `add_time` TEXT, - `update_time` TEXT -); - -CREATE TABLE IF NOT EXISTS `site_types` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT -); - -CREATE TABLE IF NOT EXISTS `domain` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pid` INTEGER, - `name` TEXT, - `port` INTEGER, - `add_time` TEXT -); - -CREATE TABLE IF NOT EXISTS `users` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `password` TEXT, - `login_ip` TEXT, - `login_time` TEXT, - `phone` TEXT, - `email` TEXT, - `add_time` INTEGER, - `update_time` INTEGER -); - -CREATE TABLE IF NOT EXISTS `tasks` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `type` TEXT, - `start` INTEGER, - `end` INTEGER, - `cmd` TEXT, - `status` INTEGER, - `add_time` INTEGER -); - -CREATE TABLE IF NOT EXISTS `temp_login` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `token` REAL, - `salt` REAL, - `state` INTEGER, - `login_time` INTEGER, - `login_addr` REAL, - `logout_time` INTEGER, - `expire` INTEGER, - `add_time` INTEGER -); - -CREATE TABLE IF NOT EXISTS `panel` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `title` TEXT, - `url` TEXT, - `username` TEXT, - `password` TEXT, - `click` INTEGER, - `add_time` INTEGER -); - -CREATE TABLE IF NOT EXISTS `option` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `type` TEXT, - `value` TEXT -); - -CREATE UNIQUE INDEX name_idx ON option(name); diff --git a/data/sql/system.sql b/data/sql/system.sql deleted file mode 100755 index 75fe9557f..000000000 --- a/data/sql/system.sql +++ /dev/null @@ -1,41 +0,0 @@ -CREATE TABLE IF NOT EXISTS `network` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `up` TEXT, - `down` TEXT, - `total_up` INTEGER, - `total_down` INTEGER, - `down_packets` INTEGER, - `up_packets` INTEGER, - `addtime` INTEGER -); -CREATE INDEX addtime_idx ON network(`addtime`); - -CREATE TABLE IF NOT EXISTS `cpuio` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pro` TEXT, - `mem` TEXT, - `addtime` INTEGER -); -CREATE INDEX addtime_idx ON cpuio(`addtime`); - -CREATE TABLE IF NOT EXISTS `diskio` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `read_count` INTEGER, - `write_count` INTEGER, - `read_bytes` INTEGER, - `write_bytes` INTEGER, - `read_time` INTEGER, - `write_time` INTEGER, - `addtime` INTEGER -); -CREATE INDEX addtime_idx ON diskio(`addtime`); - -CREATE TABLE IF NOT EXISTS `load_average` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pro` REAL, - `one` REAL, - `five` REAL, - `fifteen` REAL, - `addtime` INTEGER -); -CREATE INDEX addtime_idx ON load_average(`addtime`); \ No newline at end of file diff --git a/data/vip/readme.md b/data/vip/readme.md deleted file mode 100644 index cf9aac38d..000000000 --- a/data/vip/readme.md +++ /dev/null @@ -1 +0,0 @@ -存放VIP关键数据 \ No newline at end of file diff --git a/web/misc/nginx/rewrite/EmpireCMS.conf b/web/misc/nginx/rewrite/EmpireCMS.conf new file mode 100755 index 000000000..33cdf5791 --- /dev/null +++ b/web/misc/nginx/rewrite/EmpireCMS.conf @@ -0,0 +1,10 @@ +location / { + rewrite ^([^\.]*)/listinfo-(.+?)-(.+?)\.html$ $1/e/action/ListInfo/index.php?classid=$2&page=$3 last; + rewrite ^([^\.]*)/showinfo-(.+?)-(.+?)-(.+?)\.html$ $1/e/action/ShowInfo.php?classid=$2&id=$3&page=$4 last; + rewrite ^([^\.]*)/infotype-(.+?)-(.+?)\.html$ $1/e/action/InfoType/index.php?ttid=$2&page=$3 last; + rewrite ^([^\.]*)/tags-(.+?)-(.+?)\.html$ $1/e/tags/index.php?tagname=$2&page=$3 last; + rewrite ^([^\.]*)/comment-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)\.html$ $1/e/pl/index\.php\?doaction=$2&classid=$3&id=$4&page=$5&myorder=$6&tempid=$7 last; + if (!-e $request_filename) { + return 404; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/cloudfare_real_ip.conf b/web/misc/nginx/rewrite/cloudfare_real_ip.conf new file mode 100644 index 000000000..cd19beb0e --- /dev/null +++ b/web/misc/nginx/rewrite/cloudfare_real_ip.conf @@ -0,0 +1,43 @@ +location / { + + # https://www.cloudflare.com/zh-cn/ips/ + set_real_ip_from 173.245.48.0/20; + set_real_ip_from 103.21.244.0/22; + set_real_ip_from 103.22.200.0/22; + set_real_ip_from 103.31.4.0/22; + set_real_ip_from 141.101.64.0/18; + set_real_ip_from 108.162.192.0/18; + set_real_ip_from 190.93.240.0/20; + set_real_ip_from 188.114.96.0/20; + set_real_ip_from 197.234.240.0/22; + set_real_ip_from 198.41.128.0/17; + set_real_ip_from 162.158.0.0/15; + set_real_ip_from 104.16.0.0/13; + set_real_ip_from 104.24.0.0/14; + set_real_ip_from 172.64.0.0/13; + set_real_ip_from 131.0.72.0/22; + set_real_ip_from 2400:cb00::/32; + set_real_ip_from 2606:4700::/32; + set_real_ip_from 2803:f800::/32; + set_real_ip_from 2405:b500::/32; + set_real_ip_from 2405:8100::/32; + set_real_ip_from 2a06:98c0::/29; + set_real_ip_from 2c0f:f248::/32; + # use any of the following two + real_ip_header CF-Connecting-IP; + #real_ip_header X-Forwarded-For; + + + index index.html index.php; + if (-f $request_filename/index.html){ + rewrite (.*) $1/index.html break; + } + if (-f $request_filename/index.php){ + rewrite (.*) $1/index.php; + } + if (!-f $request_filename){ + rewrite (.*) /index.php; + } +} + +rewrite /wp-admin$ $scheme://$host$uri/ permanent; diff --git a/web/misc/nginx/rewrite/dedecms.conf b/web/misc/nginx/rewrite/dedecms.conf new file mode 100755 index 000000000..9e1d20206 --- /dev/null +++ b/web/misc/nginx/rewrite/dedecms.conf @@ -0,0 +1,12 @@ +location / { + rewrite ^/list-([0-9]+)\.html$ /plus/list.php?tid=$1 last; + rewrite ^/list-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last; + rewrite ^/view-([0-9]+)-1\.html$ /plus/view.php?arcID=$1 last; + rewrite ^/view-([0-9]+)-([0-9]+)\.html$" /plus/view.php?aid=$1&pageno=$2 last; + rewrite ^/plus/list-([0-9]+)\.html$ /plus/list.php?tid=$1 last; + rewrite ^/plus/list-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last; + rewrite ^/plus/view-([0-9]+)-1\.html$ /plus/view.php?arcID=$1 last; + rewrite ^/plus/view-([0-9]+)-([0-9]+)\.html$ /plus/view.php?aid=$1&pageno=$2 last; + rewrite ^/tags.html$ /tags.php last; + rewrite ^/tag-([0-9]+)-([0-9]+)\.html$ /tags.php?/$1/$2/ last; +} diff --git a/web/misc/nginx/rewrite/discuzx.conf b/web/misc/nginx/rewrite/discuzx.conf new file mode 100755 index 000000000..be93e7de0 --- /dev/null +++ b/web/misc/nginx/rewrite/discuzx.conf @@ -0,0 +1,18 @@ +location / { + rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last; + rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last; + rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last; + rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last; + rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last; + rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last; + rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last; + rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last; + rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last; + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php last; + } + + #if (!-e $request_filename) { + # return 404; + #} +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/discuzx2.conf b/web/misc/nginx/rewrite/discuzx2.conf new file mode 100755 index 000000000..f4040718b --- /dev/null +++ b/web/misc/nginx/rewrite/discuzx2.conf @@ -0,0 +1,14 @@ +location /bbs/ { + rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last; + rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last; + rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last; + rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last; + rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last; + rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last; + rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last; + rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last; + rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last; + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php last; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/discuzx3.conf b/web/misc/nginx/rewrite/discuzx3.conf new file mode 100755 index 000000000..bfc8ef92c --- /dev/null +++ b/web/misc/nginx/rewrite/discuzx3.conf @@ -0,0 +1,15 @@ +location / { + rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last; + rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last; + rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last; + rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last; + rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last; + rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last; + rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last; + rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last; + rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last; + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php last; + } +} + diff --git a/web/misc/nginx/rewrite/drupal.conf b/web/misc/nginx/rewrite/drupal.conf new file mode 100755 index 000000000..2c4bc7b82 --- /dev/null +++ b/web/misc/nginx/rewrite/drupal.conf @@ -0,0 +1,5 @@ +location / { + if (!-e $request_filename) { + rewrite ^/(.*)$ /index.php?q=$1 last; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/ecshop.conf b/web/misc/nginx/rewrite/ecshop.conf new file mode 100755 index 000000000..109c04b6c --- /dev/null +++ b/web/misc/nginx/rewrite/ecshop.conf @@ -0,0 +1,33 @@ +location / { + if (!-e $request_filename) { + rewrite "^/index\.html" /index.php last; + rewrite "^/category$" /index.php last; + rewrite "^/feed-c([0-9]+)\.xml$" /feed.php?cat=$1 last; + rewrite "^/feed-b([0-9]+)\.xml$" /feed.php?brand=$1 last; + rewrite "^/feed\.xml$" /feed.php last; + rewrite "^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5&page=$6&sort=$7&order=$8 last; + rewrite "^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)(.*)\.html$" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5 last; + rewrite "^/category-([0-9]+)-b([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /category.php?id=$1&brand=$2&page=$3&sort=$4&order=$5 last; + rewrite "^/category-([0-9]+)-b([0-9]+)-([0-9]+)(.*)\.html$" /category.php?id=$1&brand=$2&page=$3 last; + rewrite "^/category-([0-9]+)-b([0-9]+)(.*)\.html$" /category.php?id=$1&brand=$2 last; + rewrite "^/category-([0-9]+)(.*)\.html$" /category.php?id=$1 last; + rewrite "^/goods-([0-9]+)(.*)\.html" /goods.php?id=$1 last; + rewrite "^/article_cat-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /article_cat.php?id=$1&page=$2&sort=$3&order=$4 last; + rewrite "^/article_cat-([0-9]+)-([0-9]+)(.*)\.html$" /article_cat.php?id=$1&page=$2 last; + rewrite "^/article_cat-([0-9]+)(.*)\.html$" /article_cat.php?id=$1 last; + rewrite "^/article-([0-9]+)(.*)\.html$" /article.php?id=$1 last; + rewrite "^/brand-([0-9]+)-c([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)\.html" /brand.php?id=$1&cat=$2&page=$3&sort=$4&order=$5 last; + rewrite "^/brand-([0-9]+)-c([0-9]+)-([0-9]+)(.*)\.html" /brand.php?id=$1&cat=$2&page=$3 last; + rewrite "^/brand-([0-9]+)-c([0-9]+)(.*)\.html" /brand.php?id=$1&cat=$2 last; + rewrite "^/brand-([0-9]+)(.*)\.html" /brand.php?id=$1 last; + rewrite "^/tag-(.*)\.html" /search.php?keywords=$1 last; + rewrite "^/snatch-([0-9]+)\.html$" /snatch.php?id=$1 last; + rewrite "^/group_buy-([0-9]+)\.html$" /group_buy.php?act=view&id=$1 last; + rewrite "^/auction-([0-9]+)\.html$" /auction.php?act=view&id=$1 last; + rewrite "^/exchange-id([0-9]+)(.*)\.html$" /exchange.php?id=$1&act=view last; + rewrite "^/exchange-([0-9]+)-min([0-9]+)-max([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /exchange.php?cat_id=$1&integral_min=$2&integral_max=$3&page=$4&sort=$5&order=$6 last; + rewrite ^/exchange-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /exchange.php?cat_id=$1&page=$2&sort=$3&order=$4 last; + rewrite "^/exchange-([0-9]+)-([0-9]+)(.*)\.html$" /exchange.php?cat_id=$1&page=$2 last; + rewrite "^/exchange-([0-9]+)(.*)\.html$" /exchange.php?cat_id=$1 last; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/emlog.conf b/web/misc/nginx/rewrite/emlog.conf new file mode 100755 index 000000000..77f1a17d6 --- /dev/null +++ b/web/misc/nginx/rewrite/emlog.conf @@ -0,0 +1,7 @@ +location / { + index index.php index.html; + if (!-e $request_filename) + { + rewrite ^/(.*)$ /index.php last; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/fastdfs.conf b/web/misc/nginx/rewrite/fastdfs.conf new file mode 100644 index 000000000..1b89c6e01 --- /dev/null +++ b/web/misc/nginx/rewrite/fastdfs.conf @@ -0,0 +1,5 @@ +location ~/group([0-9])/M00 { + # Need to cooperate with it + # https://github.com/mw-plugin/fastdfs + ngx_fastdfs_module; +} diff --git a/web/misc/nginx/rewrite/gunicorn.conf b/web/misc/nginx/rewrite/gunicorn.conf new file mode 100755 index 000000000..80709d083 --- /dev/null +++ b/web/misc/nginx/rewrite/gunicorn.conf @@ -0,0 +1,9 @@ + +add_header Access-Control-Allow-Origin *; +add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; + +location / { + proxy_pass http://127.0.0.1:8000; # 这里是指向 gunicorn host 的服务地址 + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/header_cors.conf b/web/misc/nginx/rewrite/header_cors.conf new file mode 100755 index 000000000..61a875c36 --- /dev/null +++ b/web/misc/nginx/rewrite/header_cors.conf @@ -0,0 +1,15 @@ + +add_header 'Access-Control-Allow-Methods' 'GET,OPTIONS,POST' always; +add_header 'Access-Control-Allow-Credentials' 'true' always; +add_header 'Access-Control-Allow-Origin' $http_origin always; +add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With, Cache-Control' always; + + + +#add_header Access-Control-Allow-Origin *; +#add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; + + +#add_header Access-Control-Allow-Origin *; +#add_header Access-Control-Allow-Methods *; +#add_header Access-Control-Allow-Header *; diff --git a/web/misc/nginx/rewrite/laravel5.conf b/web/misc/nginx/rewrite/laravel5.conf new file mode 100755 index 000000000..d550bbb09 --- /dev/null +++ b/web/misc/nginx/rewrite/laravel5.conf @@ -0,0 +1,3 @@ +location / { + try_files $uri $uri/ /index.php$is_args$query_string; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/mvc.conf b/web/misc/nginx/rewrite/mvc.conf new file mode 100755 index 000000000..44d945a14 --- /dev/null +++ b/web/misc/nginx/rewrite/mvc.conf @@ -0,0 +1,6 @@ +location / { + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php/$1 last; + break; + } +} diff --git a/web/misc/nginx/rewrite/nezha.conf b/web/misc/nginx/rewrite/nezha.conf new file mode 100644 index 000000000..fe3631d7a --- /dev/null +++ b/web/misc/nginx/rewrite/nezha.conf @@ -0,0 +1,15 @@ +#PROXY-START/ +# root /www/server/nezha/dashboard/resource; +location / { + proxy_pass http://127.0.0.1:9527; + proxy_set_header Host $http_host; + proxy_set_header Upgrade $http_upgrade; +} +location ~ ^/(ws|terminal/.+)$ { + proxy_pass http://127.0.0.1:9527; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $http_host; +} +#PROXY-END/ \ No newline at end of file diff --git a/web/misc/nginx/rewrite/phpcms.conf b/web/misc/nginx/rewrite/phpcms.conf new file mode 100755 index 000000000..a6e0df346 --- /dev/null +++ b/web/misc/nginx/rewrite/phpcms.conf @@ -0,0 +1,9 @@ +location / { + ###以下为PHPCMS 伪静态化rewrite法则 + rewrite ^(.*)show-([0-9]+)-([0-9]+)\.html$ $1/show.php?itemid=$2&page=$3; + rewrite ^(.*)list-([0-9]+)-([0-9]+)\.html$ $1/list.php?catid=$2&page=$3; + rewrite ^(.*)show-([0-9]+)\.html$ $1/show.php?specialid=$2; + ####以下为PHPWind 伪静态化rewrite法则 + rewrite ^(.*)-htm-(.*)$ $1.php?$2 last; + rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/phpwind.conf b/web/misc/nginx/rewrite/phpwind.conf new file mode 100755 index 000000000..6dba1e34e --- /dev/null +++ b/web/misc/nginx/rewrite/phpwind.conf @@ -0,0 +1,4 @@ +location / { + rewrite ^(.*)-htm-(.*)$ $1.php?$2 last; + rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/proxy.conf b/web/misc/nginx/rewrite/proxy.conf new file mode 100644 index 000000000..8c34c461c --- /dev/null +++ b/web/misc/nginx/rewrite/proxy.conf @@ -0,0 +1,7 @@ + +location / { + proxy_pass http://localhost:11334/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +} diff --git a/web/misc/nginx/rewrite/proxydg.conf b/web/misc/nginx/rewrite/proxydg.conf new file mode 100644 index 000000000..1a2c1e319 --- /dev/null +++ b/web/misc/nginx/rewrite/proxydg.conf @@ -0,0 +1,8 @@ +location / { + proxy_redirect off; + proxy_pass http://127.0.0.1:12345; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/sablog.conf b/web/misc/nginx/rewrite/sablog.conf new file mode 100755 index 000000000..8009724ef --- /dev/null +++ b/web/misc/nginx/rewrite/sablog.conf @@ -0,0 +1,16 @@ +location / { + rewrite "^/date/([0-9]{6})/?([0-9]+)?/?$" /index.php?action=article&setdate=$1&page=$2 last; + rewrite ^/page/([0-9]+)?/?$ /index.php?action=article&page=$1 last; + rewrite ^/category/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&cid=$1&page=$2 last; + rewrite ^/category/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&curl=$1&page=$2 last; + rewrite ^/(archives|search|article|links)/?$ /index.php?action=$1 last; + rewrite ^/(comments|tagslist|trackbacks|article)/?([0-9]+)?/?$ /index.php?action=$1&page=$2 last; + rewrite ^/tag/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&item=$1&page=$2 last; + rewrite ^/archives/([0-9]+)/?([0-9]+)?/?$ /index.php?action=show&id=$1&page=$2 last; + rewrite ^/rss/([0-9]+)?/?$ /rss.php?cid=$1 last; + rewrite ^/rss/([^/]+)/?$ /rss.php?url=$1 last; + rewrite ^/uid/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&uid=$1&page=$2 last; + rewrite ^/user/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&user=$1&page=$2 last; + rewrite sitemap.xml sitemap.php last; + rewrite ^(.*)/([0-9a-zA-Z\-\_]+)/?([0-9]+)?/?$ $1/index.php?action=show&alias=$2&page=$3 last; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/seacms.conf b/web/misc/nginx/rewrite/seacms.conf new file mode 100755 index 000000000..0dc6f3612 --- /dev/null +++ b/web/misc/nginx/rewrite/seacms.conf @@ -0,0 +1,11 @@ +location / { + rewrite ^/frim/index(.+?)\.html$ /list/index.php?$1 last; + rewrite ^/movie/index(.+?)\.html$ /detail/index.php?$1 last; + rewrite ^/play/([0-9]+)-([0-9]+)-([0-9]+)\.html$ /video/index.php?$1-$2-$3 last; + rewrite ^/topic/index(.+?)\.html$ /topic/index.php?$1 last; + rewrite ^/topiclist/index(.+?).html$ /topiclist/index.php?$1 last; + rewrite ^/index\.html$ index.php permanent; + rewrite ^/news\.html$ news/ permanent; + rewrite ^/part/index(.+?)\.html$ /articlelist/index.php?$1 last; + rewrite ^/article/index(.+?)\.html$ /article/index.php?$1 last; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/shopex.conf b/web/misc/nginx/rewrite/shopex.conf new file mode 100755 index 000000000..afc87532d --- /dev/null +++ b/web/misc/nginx/rewrite/shopex.conf @@ -0,0 +1,5 @@ +location / { + if (!-e $request_filename) { + rewrite ^/(.+\.(html|xml|json|htm|php|jsp|asp|shtml))$ /index.php?$1 last; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/sub.conf.tpl b/web/misc/nginx/rewrite/sub.conf.tpl new file mode 100644 index 000000000..5996e01d4 --- /dev/null +++ b/web/misc/nginx/rewrite/sub.conf.tpl @@ -0,0 +1,16 @@ +location / { + set $replace 'tmplate_replace'; + set $spider_request '0'; + + if ($http_user_agent ~* '(baiduspider|360sipder|Sogou Orion spider|Sogou News Spider|Sogou blog|Sogou spider2|Sogou inst spider|Sogou web spider|Sogou spider|trendiction|Yahoo|semrush|Toutiao|Google|qihoobot|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|MSNBot|ia_archiver|Tomato Bot)') { + set $spider_request '1'; + } + + if ($spider_request = '0' ) { + set $replace 'tmplate_replace'; + } + + sub_filter 'tmplate_replace' $replace; + sub_filter_once on; + sub_filter_types *; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/thinkphp.conf b/web/misc/nginx/rewrite/thinkphp.conf new file mode 100755 index 000000000..b8c527532 --- /dev/null +++ b/web/misc/nginx/rewrite/thinkphp.conf @@ -0,0 +1,5 @@ +location / { + if (!-e $request_filename){ + rewrite ^(.*)$ /index.php?s=$1 last; break; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/typecho.conf b/web/misc/nginx/rewrite/typecho.conf new file mode 100755 index 000000000..dcc7c3f1a --- /dev/null +++ b/web/misc/nginx/rewrite/typecho.conf @@ -0,0 +1,13 @@ +location / { + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php$1 last; + } +} + +# 不需要,可以删除 +location /typecho/ { + if (!-e $request_filename) { + rewrite ^(.*)$ /typecho/index.php$1 last; + } +} + diff --git a/web/misc/nginx/rewrite/v2ray.conf b/web/misc/nginx/rewrite/v2ray.conf new file mode 100644 index 000000000..641313279 --- /dev/null +++ b/web/misc/nginx/rewrite/v2ray.conf @@ -0,0 +1,15 @@ +location /{ + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php/$1 last; + break; + } +} + +location /ws { + proxy_redirect off; + proxy_pass http://127.0.0.1:34861; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/walle.conf b/web/misc/nginx/rewrite/walle.conf new file mode 100755 index 000000000..c88ba3d7c --- /dev/null +++ b/web/misc/nginx/rewrite/walle.conf @@ -0,0 +1,34 @@ + +#upstream webservers { +# server 0.0.0.0:5000 weight=1; +#} + +location / { + try_files $uri $uri/ /index.html; + add_header access-control-allow-origin *; +} + +location ^~ /api/ { + add_header access-control-allow-origin *; + proxy_pass http://webservers; + proxy_set_header X-Forwarded-Host $host:$server_port; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Origin $host:$server_port; + proxy_set_header Referer $host:$server_port; +} + +location ^~ /socket.io/ { + add_header access-control-allow-origin *; + proxy_pass http://webservers; + proxy_set_header X-Forwarded-Host $host:$server_port; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Origin $host:$server_port; + proxy_set_header Referer $host:$server_port; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + # WebScoket Support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/whmcs.conf b/web/misc/nginx/rewrite/whmcs.conf new file mode 100755 index 000000000..2cdd0e710 --- /dev/null +++ b/web/misc/nginx/rewrite/whmcs.conf @@ -0,0 +1,16 @@ +location / { + + if (!-e $request_filename) { + rewrite ^/contact/$ /./contact.php last; + rewrite ^/status$ /./pages.php?cate=$1&page=status last; + rewrite ^/act$ /./pages.php?cate=$1&page=actindex last; + rewrite ^/(\w+)/(\w+)/$ /./pages.php?cate=$1&page=$2 last; + rewrite ^/cart/(\w+)$ /./cart.php?a=$1 last; + rewrite ^/console$ /./clientarea.php last; + rewrite ^/sitemap.xml$ /./sitemap.php last; + rewrite ^/console/(\w+)$ /./clientarea.php?action=services&group=$1 last; + rewrite ^/console/ec/([0-9]+)$ /./clientarea.php?action=productdetails&id=$1 last; + rewrite ^/console/ec/(\w+)$ /./clientarea.php?action=services&group=ec&page=$1 last; + rewrite ^ /index.php last; + } +} diff --git a/web/misc/nginx/rewrite/wmcms.conf b/web/misc/nginx/rewrite/wmcms.conf new file mode 100755 index 000000000..386b75085 --- /dev/null +++ b/web/misc/nginx/rewrite/wmcms.conf @@ -0,0 +1,16 @@ +location / { + if (!-e $request_filename) { + rewrite ^/index.html$ /index.php last; + rewrite ^/top/index.html$ /module/novel/topindex.php last; + rewrite ^/(\d*)/list/(\d*).html$ /module/novel/type.php?tid=$1&page=$2 last; + rewrite ^/(.*?)/(.*?)/info.html$ /module/novel/info.php?tid=$1&nid=$2 last; + rewrite ^/(.*?)/(.*?)/read/(\d*).html$ /module/novel/read.php?tid=$1&nid=$2&cid=$3 last; + rewrite ^/(.*?)/(.*?)/menu/(\d*).html$ /module/novel/menu.php?tid=$1&nid=$2&page=$3 last; + rewrite ^/search/(\d*)/(.*?)/(\d*).html$ /module/novel/search.php?type=$1&key=$2&page=$3 last; + + rewrite ^/(\d*)/list/(\d*)_(\d*)_(\d*)_(\d*)_(\d*)_(\d*)_(\d*)_(\d*).html$ /module/novel/type.php?tid=$1&page=$9&process=$2&word=$3&chapter=$4©=$5&cost=$6&letter=$7&order=$8 last; + + rewrite ^/top/list/(\d*)/(\d*)/(\d*).html$ /module/novel/toplist.php?tid=$1&dtype=$2&page=$3 last; + break; + } +} \ No newline at end of file diff --git a/web/misc/nginx/rewrite/wordpress.conf b/web/misc/nginx/rewrite/wordpress.conf new file mode 100755 index 000000000..dfa6381d4 --- /dev/null +++ b/web/misc/nginx/rewrite/wordpress.conf @@ -0,0 +1,14 @@ +location / { + index index.html index.php; + if (-f $request_filename/index.html){ + rewrite (.*) $1/index.html break; + } + if (-f $request_filename/index.php){ + rewrite (.*) $1/index.php; + } + if (!-f $request_filename){ + rewrite (.*) /index.php; + } +} + +rewrite /wp-admin$ $scheme://$host$uri/ permanent; \ No newline at end of file diff --git a/web/misc/nginx/rewrite/zblog.conf b/web/misc/nginx/rewrite/zblog.conf new file mode 100755 index 000000000..37d410519 --- /dev/null +++ b/web/misc/nginx/rewrite/zblog.conf @@ -0,0 +1,11 @@ +location / { + if (-f $request_filename/index.html){ + rewrite (.*) $1/index.html break; + } + if (-f $request_filename/index.php){ + rewrite (.*) $1/index.php; + } + if (!-f $request_filename){ + rewrite (.*) /index.php; + } +} \ No newline at end of file diff --git a/web/misc/nginx/tpl/nginx.conf b/web/misc/nginx/tpl/nginx.conf new file mode 100755 index 000000000..5da306f9d --- /dev/null +++ b/web/misc/nginx/tpl/nginx.conf @@ -0,0 +1,53 @@ +server +{ + # reuseport 只能在一个server出现一次 + listen {$PORT}; + listen [::]:{$PORT}; + server_name {$SERVER_NAME}; + index index.php index.html index.htm default.php default.htm default.html; + root {$ROOT_DIR}; + + #SSL-START + #error_page 404/404.html; + #SSL-END + + #301-START + + #PROXY-START + + #ERROR-PAGE-START + #error_page 404 /404.html; + #error_page 502 /502.html; + #ERROR-PAGE-END + + #PHP-INFO-START + include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf; + #PHP-INFO-END + + #REWRITE-START + include {$OR_REWRITE}/{$SERVER_NAME}.conf; + #REWRITE-END + + #禁止访问的文件或目录 + location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) + { + return 404; + } + + #一键申请SSL证书验证目录相关设置 + location ~ \.well-known{ + allow all; + } + + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2)$ + { + expires 30d; + access_log /dev/null; + if ($invalid_referer){ + return 404; + } + } + + access_log {$LOGPATH}/{$SERVER_NAME}.log main; + error_log {$LOGPATH}/{$SERVER_NAME}.error.log; +} \ No newline at end of file diff --git a/web/misc/nginx/tpl/nginx_dirbind.conf b/web/misc/nginx/tpl/nginx_dirbind.conf new file mode 100755 index 000000000..129a7ddd1 --- /dev/null +++ b/web/misc/nginx/tpl/nginx_dirbind.conf @@ -0,0 +1,48 @@ + +#BINDING-{$DIRBIND}-START +server +{ + listen {$PORT}; + server_name {$DIRBIND}; + index index.php index.html index.htm default.php default.htm default.html; + root {$ROOT_DIR}; + + #SSL-START + #error_page 404/404.html; + #SSL-END + + #ERROR-PAGE-START + #error_page 404 /404.html; + #error_page 502 /502.html; + #ERROR-PAGE-END + + #PHP-INFO-START + include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf; + #PHP-INFO-END + + #REWRITE-START + include {$OR_REWRITE}/{$SERVER_MAIN}.conf; + #REWRITE-END + + #禁止访问的文件或目录 + location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) + { + return 404; + } + + #一键申请SSL证书验证目录相关设置 + location ~ \.well-known{ + allow all; + } + + location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ + { + error_log /dev/null; + access_log /dev/null; + expires 30d; + } + + access_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.log; + error_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.error.log; +} +#BINDING-{$DIRBIND}-END \ No newline at end of file diff --git a/web/misc/nginx/tpl/nginx_panel.conf b/web/misc/nginx/tpl/nginx_panel.conf new file mode 100755 index 000000000..c487094fd --- /dev/null +++ b/web/misc/nginx/tpl/nginx_panel.conf @@ -0,0 +1,23 @@ +server +{ + listen {$PORT}; + listen [::]:{$PORT}; + + server_name {$SERVER_NAME}; + index index.php index.html index.htm default.php default.htm default.html; + + #SSL-START + #error_page 404/404.html; + #SSL-END + + #PROXY-START + location ^~ / { + proxy_pass http://0.0.0.0:{$PANAL_PORT}/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + } + #PROXY-END + + error_log {$LOGPATH}/{$SERVER_NAME}.error.log; +} \ No newline at end of file From 4ed5c48650a75f0016b0dc8039e5e05b5db9de30 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 04:45:38 +0800 Subject: [PATCH 327/965] update --- {version => web/misc/version}/api/mw_api.php | 0 {version => web/misc/version}/api/mw_api.py | 0 {version => web/misc/version}/info.json | 0 {version => web/misc/version}/r3.6.txt | 0 {version => web/misc/version}/r3.7.txt | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {version => web/misc/version}/api/mw_api.php (100%) rename {version => web/misc/version}/api/mw_api.py (100%) rename {version => web/misc/version}/info.json (100%) rename {version => web/misc/version}/r3.6.txt (100%) rename {version => web/misc/version}/r3.7.txt (100%) diff --git a/version/api/mw_api.php b/web/misc/version/api/mw_api.php similarity index 100% rename from version/api/mw_api.php rename to web/misc/version/api/mw_api.php diff --git a/version/api/mw_api.py b/web/misc/version/api/mw_api.py similarity index 100% rename from version/api/mw_api.py rename to web/misc/version/api/mw_api.py diff --git a/version/info.json b/web/misc/version/info.json similarity index 100% rename from version/info.json rename to web/misc/version/info.json diff --git a/version/r3.6.txt b/web/misc/version/r3.6.txt similarity index 100% rename from version/r3.6.txt rename to web/misc/version/r3.6.txt diff --git a/version/r3.7.txt b/web/misc/version/r3.7.txt similarity index 100% rename from version/r3.7.txt rename to web/misc/version/r3.7.txt From 4e3e7b7301afb27b1e0a24d5750242ffd503bc03 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 04:53:03 +0800 Subject: [PATCH 328/965] update --- {rewrite => rewrites}/nginx/EmpireCMS.conf | 0 .../nginx/cloudfare_real_ip.conf | 0 {rewrite => rewrites}/nginx/dedecms.conf | 0 {rewrite => rewrites}/nginx/discuzx.conf | 0 {rewrite => rewrites}/nginx/discuzx2.conf | 0 {rewrite => rewrites}/nginx/discuzx3.conf | 0 {rewrite => rewrites}/nginx/drupal.conf | 0 {rewrite => rewrites}/nginx/ecshop.conf | 0 {rewrite => rewrites}/nginx/emlog.conf | 0 {rewrite => rewrites}/nginx/fastdfs.conf | 0 {rewrite => rewrites}/nginx/gunicorn.conf | 0 {rewrite => rewrites}/nginx/header_cors.conf | 0 {rewrite => rewrites}/nginx/laravel5.conf | 0 {rewrite => rewrites}/nginx/mvc.conf | 0 {rewrite => rewrites}/nginx/nezha.conf | 0 {rewrite => rewrites}/nginx/phpcms.conf | 0 {rewrite => rewrites}/nginx/phpwind.conf | 0 {rewrite => rewrites}/nginx/proxy.conf | 0 {rewrite => rewrites}/nginx/proxydg.conf | 0 {rewrite => rewrites}/nginx/sablog.conf | 0 {rewrite => rewrites}/nginx/seacms.conf | 0 {rewrite => rewrites}/nginx/shopex.conf | 0 {rewrite => rewrites}/nginx/sub.conf.tpl | 0 {rewrite => rewrites}/nginx/thinkphp.conf | 0 {rewrite => rewrites}/nginx/typecho.conf | 0 {rewrite => rewrites}/nginx/v2ray.conf | 0 {rewrite => rewrites}/nginx/walle.conf | 0 {rewrite => rewrites}/nginx/whmcs.conf | 0 {rewrite => rewrites}/nginx/wmcms.conf | 0 {rewrite => rewrites}/nginx/wordpress.conf | 0 {rewrite => rewrites}/nginx/zblog.conf | 0 web/admin/site/site.py | 10 +++++++++- web/utils/site.py | 17 +++++++++++++++-- 33 files changed, 24 insertions(+), 3 deletions(-) rename {rewrite => rewrites}/nginx/EmpireCMS.conf (100%) rename {rewrite => rewrites}/nginx/cloudfare_real_ip.conf (100%) rename {rewrite => rewrites}/nginx/dedecms.conf (100%) rename {rewrite => rewrites}/nginx/discuzx.conf (100%) rename {rewrite => rewrites}/nginx/discuzx2.conf (100%) rename {rewrite => rewrites}/nginx/discuzx3.conf (100%) rename {rewrite => rewrites}/nginx/drupal.conf (100%) rename {rewrite => rewrites}/nginx/ecshop.conf (100%) rename {rewrite => rewrites}/nginx/emlog.conf (100%) rename {rewrite => rewrites}/nginx/fastdfs.conf (100%) rename {rewrite => rewrites}/nginx/gunicorn.conf (100%) rename {rewrite => rewrites}/nginx/header_cors.conf (100%) rename {rewrite => rewrites}/nginx/laravel5.conf (100%) rename {rewrite => rewrites}/nginx/mvc.conf (100%) rename {rewrite => rewrites}/nginx/nezha.conf (100%) rename {rewrite => rewrites}/nginx/phpcms.conf (100%) rename {rewrite => rewrites}/nginx/phpwind.conf (100%) rename {rewrite => rewrites}/nginx/proxy.conf (100%) rename {rewrite => rewrites}/nginx/proxydg.conf (100%) rename {rewrite => rewrites}/nginx/sablog.conf (100%) rename {rewrite => rewrites}/nginx/seacms.conf (100%) rename {rewrite => rewrites}/nginx/shopex.conf (100%) rename {rewrite => rewrites}/nginx/sub.conf.tpl (100%) rename {rewrite => rewrites}/nginx/thinkphp.conf (100%) rename {rewrite => rewrites}/nginx/typecho.conf (100%) rename {rewrite => rewrites}/nginx/v2ray.conf (100%) rename {rewrite => rewrites}/nginx/walle.conf (100%) rename {rewrite => rewrites}/nginx/whmcs.conf (100%) rename {rewrite => rewrites}/nginx/wmcms.conf (100%) rename {rewrite => rewrites}/nginx/wordpress.conf (100%) rename {rewrite => rewrites}/nginx/zblog.conf (100%) diff --git a/rewrite/nginx/EmpireCMS.conf b/rewrites/nginx/EmpireCMS.conf similarity index 100% rename from rewrite/nginx/EmpireCMS.conf rename to rewrites/nginx/EmpireCMS.conf diff --git a/rewrite/nginx/cloudfare_real_ip.conf b/rewrites/nginx/cloudfare_real_ip.conf similarity index 100% rename from rewrite/nginx/cloudfare_real_ip.conf rename to rewrites/nginx/cloudfare_real_ip.conf diff --git a/rewrite/nginx/dedecms.conf b/rewrites/nginx/dedecms.conf similarity index 100% rename from rewrite/nginx/dedecms.conf rename to rewrites/nginx/dedecms.conf diff --git a/rewrite/nginx/discuzx.conf b/rewrites/nginx/discuzx.conf similarity index 100% rename from rewrite/nginx/discuzx.conf rename to rewrites/nginx/discuzx.conf diff --git a/rewrite/nginx/discuzx2.conf b/rewrites/nginx/discuzx2.conf similarity index 100% rename from rewrite/nginx/discuzx2.conf rename to rewrites/nginx/discuzx2.conf diff --git a/rewrite/nginx/discuzx3.conf b/rewrites/nginx/discuzx3.conf similarity index 100% rename from rewrite/nginx/discuzx3.conf rename to rewrites/nginx/discuzx3.conf diff --git a/rewrite/nginx/drupal.conf b/rewrites/nginx/drupal.conf similarity index 100% rename from rewrite/nginx/drupal.conf rename to rewrites/nginx/drupal.conf diff --git a/rewrite/nginx/ecshop.conf b/rewrites/nginx/ecshop.conf similarity index 100% rename from rewrite/nginx/ecshop.conf rename to rewrites/nginx/ecshop.conf diff --git a/rewrite/nginx/emlog.conf b/rewrites/nginx/emlog.conf similarity index 100% rename from rewrite/nginx/emlog.conf rename to rewrites/nginx/emlog.conf diff --git a/rewrite/nginx/fastdfs.conf b/rewrites/nginx/fastdfs.conf similarity index 100% rename from rewrite/nginx/fastdfs.conf rename to rewrites/nginx/fastdfs.conf diff --git a/rewrite/nginx/gunicorn.conf b/rewrites/nginx/gunicorn.conf similarity index 100% rename from rewrite/nginx/gunicorn.conf rename to rewrites/nginx/gunicorn.conf diff --git a/rewrite/nginx/header_cors.conf b/rewrites/nginx/header_cors.conf similarity index 100% rename from rewrite/nginx/header_cors.conf rename to rewrites/nginx/header_cors.conf diff --git a/rewrite/nginx/laravel5.conf b/rewrites/nginx/laravel5.conf similarity index 100% rename from rewrite/nginx/laravel5.conf rename to rewrites/nginx/laravel5.conf diff --git a/rewrite/nginx/mvc.conf b/rewrites/nginx/mvc.conf similarity index 100% rename from rewrite/nginx/mvc.conf rename to rewrites/nginx/mvc.conf diff --git a/rewrite/nginx/nezha.conf b/rewrites/nginx/nezha.conf similarity index 100% rename from rewrite/nginx/nezha.conf rename to rewrites/nginx/nezha.conf diff --git a/rewrite/nginx/phpcms.conf b/rewrites/nginx/phpcms.conf similarity index 100% rename from rewrite/nginx/phpcms.conf rename to rewrites/nginx/phpcms.conf diff --git a/rewrite/nginx/phpwind.conf b/rewrites/nginx/phpwind.conf similarity index 100% rename from rewrite/nginx/phpwind.conf rename to rewrites/nginx/phpwind.conf diff --git a/rewrite/nginx/proxy.conf b/rewrites/nginx/proxy.conf similarity index 100% rename from rewrite/nginx/proxy.conf rename to rewrites/nginx/proxy.conf diff --git a/rewrite/nginx/proxydg.conf b/rewrites/nginx/proxydg.conf similarity index 100% rename from rewrite/nginx/proxydg.conf rename to rewrites/nginx/proxydg.conf diff --git a/rewrite/nginx/sablog.conf b/rewrites/nginx/sablog.conf similarity index 100% rename from rewrite/nginx/sablog.conf rename to rewrites/nginx/sablog.conf diff --git a/rewrite/nginx/seacms.conf b/rewrites/nginx/seacms.conf similarity index 100% rename from rewrite/nginx/seacms.conf rename to rewrites/nginx/seacms.conf diff --git a/rewrite/nginx/shopex.conf b/rewrites/nginx/shopex.conf similarity index 100% rename from rewrite/nginx/shopex.conf rename to rewrites/nginx/shopex.conf diff --git a/rewrite/nginx/sub.conf.tpl b/rewrites/nginx/sub.conf.tpl similarity index 100% rename from rewrite/nginx/sub.conf.tpl rename to rewrites/nginx/sub.conf.tpl diff --git a/rewrite/nginx/thinkphp.conf b/rewrites/nginx/thinkphp.conf similarity index 100% rename from rewrite/nginx/thinkphp.conf rename to rewrites/nginx/thinkphp.conf diff --git a/rewrite/nginx/typecho.conf b/rewrites/nginx/typecho.conf similarity index 100% rename from rewrite/nginx/typecho.conf rename to rewrites/nginx/typecho.conf diff --git a/rewrite/nginx/v2ray.conf b/rewrites/nginx/v2ray.conf similarity index 100% rename from rewrite/nginx/v2ray.conf rename to rewrites/nginx/v2ray.conf diff --git a/rewrite/nginx/walle.conf b/rewrites/nginx/walle.conf similarity index 100% rename from rewrite/nginx/walle.conf rename to rewrites/nginx/walle.conf diff --git a/rewrite/nginx/whmcs.conf b/rewrites/nginx/whmcs.conf similarity index 100% rename from rewrite/nginx/whmcs.conf rename to rewrites/nginx/whmcs.conf diff --git a/rewrite/nginx/wmcms.conf b/rewrites/nginx/wmcms.conf similarity index 100% rename from rewrite/nginx/wmcms.conf rename to rewrites/nginx/wmcms.conf diff --git a/rewrite/nginx/wordpress.conf b/rewrites/nginx/wordpress.conf similarity index 100% rename from rewrite/nginx/wordpress.conf rename to rewrites/nginx/wordpress.conf diff --git a/rewrite/nginx/zblog.conf b/rewrites/nginx/zblog.conf similarity index 100% rename from rewrite/nginx/zblog.conf rename to rewrites/nginx/zblog.conf diff --git a/web/admin/site/site.py b/web/admin/site/site.py index 26ffbd81b..b8e1a1bcc 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -248,10 +248,18 @@ def get_rewrite_conf(): # 获取Rewrite模版名 @blueprint.route('/get_rewrite_tpl', endpoint='get_rewrite_tpl',methods=['POST']) @panel_login_required -def get_php_version(): +def get_rewrite_tpl(): tplname = request.form.get('tplname', '') return MwSites.instance().getRewriteTpl(tplname) +# 设置Rewrite模版名 +@blueprint.route('/set_rewrite_tpl', endpoint='set_rewrite_tpl',methods=['POST']) +@panel_login_required +def set_rewrite_tpl(): + name = request.form.get('name', '') + data = request.form.get('data', '') + return MwSites.instance().setRewriteTpl(name,data) + # 网站日志开关 @blueprint.route('/logs_open', endpoint='logs_open',methods=['POST']) @panel_login_required diff --git a/web/utils/site.py b/web/utils/site.py index 5d8a460dd..e9c9a3329 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -606,16 +606,29 @@ class sites(object): # 获取模版名内容 def getRewriteTpl(self, tplname): - file = mw.getPanelDir() + '/rewrite/nginx/' + tplname + '.conf' + file = mw.getPanelDir() + '/web/misc/nginx/rewrite/' + tplname + '.conf' if not os.path.exists(file): return mw.returnData(False, '模版不存在!') return mw.returnData(True, 'OK', file) + def setRewriteTpl(self,name,data): + path = mw.getPanelDir() + '/web/misc/nginx/rewrite/' + name + ".conf" + if os.path.exists(path): + return mw.returnData(False, '模版已经存在!') + + if data == "": + return mw.returnData(False, '模版内容不能为空!') + ok = mw.writeFile(path, data) + if not ok: + return mw.returnData(False, '模版保持失败!') + + return mw.returnData(True, '设置模板成功!') + def getRewriteList(self): rewriteList = {} rewriteList['rewrite'] = [] rewriteList['rewrite'].append('0.当前') - rewrite_nginx_dir = mw.getPanelDir() + '/rewrite/nginx' + rewrite_nginx_dir = mw.getPanelDir() + '/web/misc/nginx/rewrite' for ds in os.listdir(rewrite_nginx_dir): rewriteList['rewrite'].append(ds[0:len(ds) - 5]) rewriteList['rewrite'] = sorted(rewriteList['rewrite']) From 9933035caefcdd7cd619c24a7a907c755efc84ae Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 04:53:43 +0800 Subject: [PATCH 329/965] update --- rewrites/nginx/EmpireCMS.conf | 10 ------- rewrites/nginx/cloudfare_real_ip.conf | 43 --------------------------- rewrites/nginx/dedecms.conf | 12 -------- rewrites/nginx/discuzx.conf | 18 ----------- rewrites/nginx/discuzx2.conf | 14 --------- rewrites/nginx/discuzx3.conf | 15 ---------- rewrites/nginx/drupal.conf | 5 ---- rewrites/nginx/ecshop.conf | 33 -------------------- rewrites/nginx/emlog.conf | 7 ----- rewrites/nginx/fastdfs.conf | 5 ---- rewrites/nginx/gunicorn.conf | 9 ------ rewrites/nginx/header_cors.conf | 15 ---------- rewrites/nginx/laravel5.conf | 3 -- rewrites/nginx/mvc.conf | 6 ---- rewrites/nginx/nezha.conf | 15 ---------- rewrites/nginx/phpcms.conf | 9 ------ rewrites/nginx/phpwind.conf | 4 --- rewrites/nginx/proxy.conf | 7 ----- rewrites/nginx/proxydg.conf | 8 ----- rewrites/nginx/sablog.conf | 16 ---------- rewrites/nginx/seacms.conf | 11 ------- rewrites/nginx/shopex.conf | 5 ---- rewrites/nginx/sub.conf.tpl | 16 ---------- rewrites/nginx/thinkphp.conf | 5 ---- rewrites/nginx/typecho.conf | 13 -------- rewrites/nginx/v2ray.conf | 15 ---------- rewrites/nginx/walle.conf | 34 --------------------- rewrites/nginx/whmcs.conf | 16 ---------- rewrites/nginx/wmcms.conf | 16 ---------- rewrites/nginx/wordpress.conf | 14 --------- rewrites/nginx/zblog.conf | 11 ------- 31 files changed, 410 deletions(-) delete mode 100755 rewrites/nginx/EmpireCMS.conf delete mode 100644 rewrites/nginx/cloudfare_real_ip.conf delete mode 100755 rewrites/nginx/dedecms.conf delete mode 100755 rewrites/nginx/discuzx.conf delete mode 100755 rewrites/nginx/discuzx2.conf delete mode 100755 rewrites/nginx/discuzx3.conf delete mode 100755 rewrites/nginx/drupal.conf delete mode 100755 rewrites/nginx/ecshop.conf delete mode 100755 rewrites/nginx/emlog.conf delete mode 100644 rewrites/nginx/fastdfs.conf delete mode 100755 rewrites/nginx/gunicorn.conf delete mode 100755 rewrites/nginx/header_cors.conf delete mode 100755 rewrites/nginx/laravel5.conf delete mode 100755 rewrites/nginx/mvc.conf delete mode 100644 rewrites/nginx/nezha.conf delete mode 100755 rewrites/nginx/phpcms.conf delete mode 100755 rewrites/nginx/phpwind.conf delete mode 100644 rewrites/nginx/proxy.conf delete mode 100644 rewrites/nginx/proxydg.conf delete mode 100755 rewrites/nginx/sablog.conf delete mode 100755 rewrites/nginx/seacms.conf delete mode 100755 rewrites/nginx/shopex.conf delete mode 100644 rewrites/nginx/sub.conf.tpl delete mode 100755 rewrites/nginx/thinkphp.conf delete mode 100755 rewrites/nginx/typecho.conf delete mode 100644 rewrites/nginx/v2ray.conf delete mode 100755 rewrites/nginx/walle.conf delete mode 100755 rewrites/nginx/whmcs.conf delete mode 100755 rewrites/nginx/wmcms.conf delete mode 100755 rewrites/nginx/wordpress.conf delete mode 100755 rewrites/nginx/zblog.conf diff --git a/rewrites/nginx/EmpireCMS.conf b/rewrites/nginx/EmpireCMS.conf deleted file mode 100755 index 33cdf5791..000000000 --- a/rewrites/nginx/EmpireCMS.conf +++ /dev/null @@ -1,10 +0,0 @@ -location / { - rewrite ^([^\.]*)/listinfo-(.+?)-(.+?)\.html$ $1/e/action/ListInfo/index.php?classid=$2&page=$3 last; - rewrite ^([^\.]*)/showinfo-(.+?)-(.+?)-(.+?)\.html$ $1/e/action/ShowInfo.php?classid=$2&id=$3&page=$4 last; - rewrite ^([^\.]*)/infotype-(.+?)-(.+?)\.html$ $1/e/action/InfoType/index.php?ttid=$2&page=$3 last; - rewrite ^([^\.]*)/tags-(.+?)-(.+?)\.html$ $1/e/tags/index.php?tagname=$2&page=$3 last; - rewrite ^([^\.]*)/comment-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)\.html$ $1/e/pl/index\.php\?doaction=$2&classid=$3&id=$4&page=$5&myorder=$6&tempid=$7 last; - if (!-e $request_filename) { - return 404; - } -} \ No newline at end of file diff --git a/rewrites/nginx/cloudfare_real_ip.conf b/rewrites/nginx/cloudfare_real_ip.conf deleted file mode 100644 index cd19beb0e..000000000 --- a/rewrites/nginx/cloudfare_real_ip.conf +++ /dev/null @@ -1,43 +0,0 @@ -location / { - - # https://www.cloudflare.com/zh-cn/ips/ - set_real_ip_from 173.245.48.0/20; - set_real_ip_from 103.21.244.0/22; - set_real_ip_from 103.22.200.0/22; - set_real_ip_from 103.31.4.0/22; - set_real_ip_from 141.101.64.0/18; - set_real_ip_from 108.162.192.0/18; - set_real_ip_from 190.93.240.0/20; - set_real_ip_from 188.114.96.0/20; - set_real_ip_from 197.234.240.0/22; - set_real_ip_from 198.41.128.0/17; - set_real_ip_from 162.158.0.0/15; - set_real_ip_from 104.16.0.0/13; - set_real_ip_from 104.24.0.0/14; - set_real_ip_from 172.64.0.0/13; - set_real_ip_from 131.0.72.0/22; - set_real_ip_from 2400:cb00::/32; - set_real_ip_from 2606:4700::/32; - set_real_ip_from 2803:f800::/32; - set_real_ip_from 2405:b500::/32; - set_real_ip_from 2405:8100::/32; - set_real_ip_from 2a06:98c0::/29; - set_real_ip_from 2c0f:f248::/32; - # use any of the following two - real_ip_header CF-Connecting-IP; - #real_ip_header X-Forwarded-For; - - - index index.html index.php; - if (-f $request_filename/index.html){ - rewrite (.*) $1/index.html break; - } - if (-f $request_filename/index.php){ - rewrite (.*) $1/index.php; - } - if (!-f $request_filename){ - rewrite (.*) /index.php; - } -} - -rewrite /wp-admin$ $scheme://$host$uri/ permanent; diff --git a/rewrites/nginx/dedecms.conf b/rewrites/nginx/dedecms.conf deleted file mode 100755 index 9e1d20206..000000000 --- a/rewrites/nginx/dedecms.conf +++ /dev/null @@ -1,12 +0,0 @@ -location / { - rewrite ^/list-([0-9]+)\.html$ /plus/list.php?tid=$1 last; - rewrite ^/list-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last; - rewrite ^/view-([0-9]+)-1\.html$ /plus/view.php?arcID=$1 last; - rewrite ^/view-([0-9]+)-([0-9]+)\.html$" /plus/view.php?aid=$1&pageno=$2 last; - rewrite ^/plus/list-([0-9]+)\.html$ /plus/list.php?tid=$1 last; - rewrite ^/plus/list-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last; - rewrite ^/plus/view-([0-9]+)-1\.html$ /plus/view.php?arcID=$1 last; - rewrite ^/plus/view-([0-9]+)-([0-9]+)\.html$ /plus/view.php?aid=$1&pageno=$2 last; - rewrite ^/tags.html$ /tags.php last; - rewrite ^/tag-([0-9]+)-([0-9]+)\.html$ /tags.php?/$1/$2/ last; -} diff --git a/rewrites/nginx/discuzx.conf b/rewrites/nginx/discuzx.conf deleted file mode 100755 index be93e7de0..000000000 --- a/rewrites/nginx/discuzx.conf +++ /dev/null @@ -1,18 +0,0 @@ -location / { - rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last; - rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last; - rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last; - rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last; - rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last; - rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last; - rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last; - rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last; - rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last; - if (!-e $request_filename) { - rewrite ^(.*)$ /index.php last; - } - - #if (!-e $request_filename) { - # return 404; - #} -} \ No newline at end of file diff --git a/rewrites/nginx/discuzx2.conf b/rewrites/nginx/discuzx2.conf deleted file mode 100755 index f4040718b..000000000 --- a/rewrites/nginx/discuzx2.conf +++ /dev/null @@ -1,14 +0,0 @@ -location /bbs/ { - rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last; - rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last; - rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last; - rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last; - rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last; - rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last; - rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last; - rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last; - rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last; - if (!-e $request_filename) { - rewrite ^(.*)$ /index.php last; - } -} \ No newline at end of file diff --git a/rewrites/nginx/discuzx3.conf b/rewrites/nginx/discuzx3.conf deleted file mode 100755 index bfc8ef92c..000000000 --- a/rewrites/nginx/discuzx3.conf +++ /dev/null @@ -1,15 +0,0 @@ -location / { - rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last; - rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last; - rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last; - rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last; - rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last; - rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last; - rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last; - rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last; - rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last; - if (!-e $request_filename) { - rewrite ^(.*)$ /index.php last; - } -} - diff --git a/rewrites/nginx/drupal.conf b/rewrites/nginx/drupal.conf deleted file mode 100755 index 2c4bc7b82..000000000 --- a/rewrites/nginx/drupal.conf +++ /dev/null @@ -1,5 +0,0 @@ -location / { - if (!-e $request_filename) { - rewrite ^/(.*)$ /index.php?q=$1 last; - } -} \ No newline at end of file diff --git a/rewrites/nginx/ecshop.conf b/rewrites/nginx/ecshop.conf deleted file mode 100755 index 109c04b6c..000000000 --- a/rewrites/nginx/ecshop.conf +++ /dev/null @@ -1,33 +0,0 @@ -location / { - if (!-e $request_filename) { - rewrite "^/index\.html" /index.php last; - rewrite "^/category$" /index.php last; - rewrite "^/feed-c([0-9]+)\.xml$" /feed.php?cat=$1 last; - rewrite "^/feed-b([0-9]+)\.xml$" /feed.php?brand=$1 last; - rewrite "^/feed\.xml$" /feed.php last; - rewrite "^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5&page=$6&sort=$7&order=$8 last; - rewrite "^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)(.*)\.html$" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5 last; - rewrite "^/category-([0-9]+)-b([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /category.php?id=$1&brand=$2&page=$3&sort=$4&order=$5 last; - rewrite "^/category-([0-9]+)-b([0-9]+)-([0-9]+)(.*)\.html$" /category.php?id=$1&brand=$2&page=$3 last; - rewrite "^/category-([0-9]+)-b([0-9]+)(.*)\.html$" /category.php?id=$1&brand=$2 last; - rewrite "^/category-([0-9]+)(.*)\.html$" /category.php?id=$1 last; - rewrite "^/goods-([0-9]+)(.*)\.html" /goods.php?id=$1 last; - rewrite "^/article_cat-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /article_cat.php?id=$1&page=$2&sort=$3&order=$4 last; - rewrite "^/article_cat-([0-9]+)-([0-9]+)(.*)\.html$" /article_cat.php?id=$1&page=$2 last; - rewrite "^/article_cat-([0-9]+)(.*)\.html$" /article_cat.php?id=$1 last; - rewrite "^/article-([0-9]+)(.*)\.html$" /article.php?id=$1 last; - rewrite "^/brand-([0-9]+)-c([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)\.html" /brand.php?id=$1&cat=$2&page=$3&sort=$4&order=$5 last; - rewrite "^/brand-([0-9]+)-c([0-9]+)-([0-9]+)(.*)\.html" /brand.php?id=$1&cat=$2&page=$3 last; - rewrite "^/brand-([0-9]+)-c([0-9]+)(.*)\.html" /brand.php?id=$1&cat=$2 last; - rewrite "^/brand-([0-9]+)(.*)\.html" /brand.php?id=$1 last; - rewrite "^/tag-(.*)\.html" /search.php?keywords=$1 last; - rewrite "^/snatch-([0-9]+)\.html$" /snatch.php?id=$1 last; - rewrite "^/group_buy-([0-9]+)\.html$" /group_buy.php?act=view&id=$1 last; - rewrite "^/auction-([0-9]+)\.html$" /auction.php?act=view&id=$1 last; - rewrite "^/exchange-id([0-9]+)(.*)\.html$" /exchange.php?id=$1&act=view last; - rewrite "^/exchange-([0-9]+)-min([0-9]+)-max([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /exchange.php?cat_id=$1&integral_min=$2&integral_max=$3&page=$4&sort=$5&order=$6 last; - rewrite ^/exchange-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /exchange.php?cat_id=$1&page=$2&sort=$3&order=$4 last; - rewrite "^/exchange-([0-9]+)-([0-9]+)(.*)\.html$" /exchange.php?cat_id=$1&page=$2 last; - rewrite "^/exchange-([0-9]+)(.*)\.html$" /exchange.php?cat_id=$1 last; - } -} \ No newline at end of file diff --git a/rewrites/nginx/emlog.conf b/rewrites/nginx/emlog.conf deleted file mode 100755 index 77f1a17d6..000000000 --- a/rewrites/nginx/emlog.conf +++ /dev/null @@ -1,7 +0,0 @@ -location / { - index index.php index.html; - if (!-e $request_filename) - { - rewrite ^/(.*)$ /index.php last; - } -} \ No newline at end of file diff --git a/rewrites/nginx/fastdfs.conf b/rewrites/nginx/fastdfs.conf deleted file mode 100644 index 1b89c6e01..000000000 --- a/rewrites/nginx/fastdfs.conf +++ /dev/null @@ -1,5 +0,0 @@ -location ~/group([0-9])/M00 { - # Need to cooperate with it - # https://github.com/mw-plugin/fastdfs - ngx_fastdfs_module; -} diff --git a/rewrites/nginx/gunicorn.conf b/rewrites/nginx/gunicorn.conf deleted file mode 100755 index 80709d083..000000000 --- a/rewrites/nginx/gunicorn.conf +++ /dev/null @@ -1,9 +0,0 @@ - -add_header Access-Control-Allow-Origin *; -add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; - -location / { - proxy_pass http://127.0.0.1:8000; # 这里是指向 gunicorn host 的服务地址 - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -} \ No newline at end of file diff --git a/rewrites/nginx/header_cors.conf b/rewrites/nginx/header_cors.conf deleted file mode 100755 index 61a875c36..000000000 --- a/rewrites/nginx/header_cors.conf +++ /dev/null @@ -1,15 +0,0 @@ - -add_header 'Access-Control-Allow-Methods' 'GET,OPTIONS,POST' always; -add_header 'Access-Control-Allow-Credentials' 'true' always; -add_header 'Access-Control-Allow-Origin' $http_origin always; -add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With, Cache-Control' always; - - - -#add_header Access-Control-Allow-Origin *; -#add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; - - -#add_header Access-Control-Allow-Origin *; -#add_header Access-Control-Allow-Methods *; -#add_header Access-Control-Allow-Header *; diff --git a/rewrites/nginx/laravel5.conf b/rewrites/nginx/laravel5.conf deleted file mode 100755 index d550bbb09..000000000 --- a/rewrites/nginx/laravel5.conf +++ /dev/null @@ -1,3 +0,0 @@ -location / { - try_files $uri $uri/ /index.php$is_args$query_string; -} \ No newline at end of file diff --git a/rewrites/nginx/mvc.conf b/rewrites/nginx/mvc.conf deleted file mode 100755 index 44d945a14..000000000 --- a/rewrites/nginx/mvc.conf +++ /dev/null @@ -1,6 +0,0 @@ -location / { - if (!-e $request_filename) { - rewrite ^(.*)$ /index.php/$1 last; - break; - } -} diff --git a/rewrites/nginx/nezha.conf b/rewrites/nginx/nezha.conf deleted file mode 100644 index fe3631d7a..000000000 --- a/rewrites/nginx/nezha.conf +++ /dev/null @@ -1,15 +0,0 @@ -#PROXY-START/ -# root /www/server/nezha/dashboard/resource; -location / { - proxy_pass http://127.0.0.1:9527; - proxy_set_header Host $http_host; - proxy_set_header Upgrade $http_upgrade; -} -location ~ ^/(ws|terminal/.+)$ { - proxy_pass http://127.0.0.1:9527; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $http_host; -} -#PROXY-END/ \ No newline at end of file diff --git a/rewrites/nginx/phpcms.conf b/rewrites/nginx/phpcms.conf deleted file mode 100755 index a6e0df346..000000000 --- a/rewrites/nginx/phpcms.conf +++ /dev/null @@ -1,9 +0,0 @@ -location / { - ###以下为PHPCMS 伪静态化rewrite法则 - rewrite ^(.*)show-([0-9]+)-([0-9]+)\.html$ $1/show.php?itemid=$2&page=$3; - rewrite ^(.*)list-([0-9]+)-([0-9]+)\.html$ $1/list.php?catid=$2&page=$3; - rewrite ^(.*)show-([0-9]+)\.html$ $1/show.php?specialid=$2; - ####以下为PHPWind 伪静态化rewrite法则 - rewrite ^(.*)-htm-(.*)$ $1.php?$2 last; - rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last; -} \ No newline at end of file diff --git a/rewrites/nginx/phpwind.conf b/rewrites/nginx/phpwind.conf deleted file mode 100755 index 6dba1e34e..000000000 --- a/rewrites/nginx/phpwind.conf +++ /dev/null @@ -1,4 +0,0 @@ -location / { - rewrite ^(.*)-htm-(.*)$ $1.php?$2 last; - rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last; -} \ No newline at end of file diff --git a/rewrites/nginx/proxy.conf b/rewrites/nginx/proxy.conf deleted file mode 100644 index 8c34c461c..000000000 --- a/rewrites/nginx/proxy.conf +++ /dev/null @@ -1,7 +0,0 @@ - -location / { - proxy_pass http://localhost:11334/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -} diff --git a/rewrites/nginx/proxydg.conf b/rewrites/nginx/proxydg.conf deleted file mode 100644 index 1a2c1e319..000000000 --- a/rewrites/nginx/proxydg.conf +++ /dev/null @@ -1,8 +0,0 @@ -location / { - proxy_redirect off; - proxy_pass http://127.0.0.1:12345; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; -} \ No newline at end of file diff --git a/rewrites/nginx/sablog.conf b/rewrites/nginx/sablog.conf deleted file mode 100755 index 8009724ef..000000000 --- a/rewrites/nginx/sablog.conf +++ /dev/null @@ -1,16 +0,0 @@ -location / { - rewrite "^/date/([0-9]{6})/?([0-9]+)?/?$" /index.php?action=article&setdate=$1&page=$2 last; - rewrite ^/page/([0-9]+)?/?$ /index.php?action=article&page=$1 last; - rewrite ^/category/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&cid=$1&page=$2 last; - rewrite ^/category/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&curl=$1&page=$2 last; - rewrite ^/(archives|search|article|links)/?$ /index.php?action=$1 last; - rewrite ^/(comments|tagslist|trackbacks|article)/?([0-9]+)?/?$ /index.php?action=$1&page=$2 last; - rewrite ^/tag/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&item=$1&page=$2 last; - rewrite ^/archives/([0-9]+)/?([0-9]+)?/?$ /index.php?action=show&id=$1&page=$2 last; - rewrite ^/rss/([0-9]+)?/?$ /rss.php?cid=$1 last; - rewrite ^/rss/([^/]+)/?$ /rss.php?url=$1 last; - rewrite ^/uid/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&uid=$1&page=$2 last; - rewrite ^/user/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&user=$1&page=$2 last; - rewrite sitemap.xml sitemap.php last; - rewrite ^(.*)/([0-9a-zA-Z\-\_]+)/?([0-9]+)?/?$ $1/index.php?action=show&alias=$2&page=$3 last; -} \ No newline at end of file diff --git a/rewrites/nginx/seacms.conf b/rewrites/nginx/seacms.conf deleted file mode 100755 index 0dc6f3612..000000000 --- a/rewrites/nginx/seacms.conf +++ /dev/null @@ -1,11 +0,0 @@ -location / { - rewrite ^/frim/index(.+?)\.html$ /list/index.php?$1 last; - rewrite ^/movie/index(.+?)\.html$ /detail/index.php?$1 last; - rewrite ^/play/([0-9]+)-([0-9]+)-([0-9]+)\.html$ /video/index.php?$1-$2-$3 last; - rewrite ^/topic/index(.+?)\.html$ /topic/index.php?$1 last; - rewrite ^/topiclist/index(.+?).html$ /topiclist/index.php?$1 last; - rewrite ^/index\.html$ index.php permanent; - rewrite ^/news\.html$ news/ permanent; - rewrite ^/part/index(.+?)\.html$ /articlelist/index.php?$1 last; - rewrite ^/article/index(.+?)\.html$ /article/index.php?$1 last; -} \ No newline at end of file diff --git a/rewrites/nginx/shopex.conf b/rewrites/nginx/shopex.conf deleted file mode 100755 index afc87532d..000000000 --- a/rewrites/nginx/shopex.conf +++ /dev/null @@ -1,5 +0,0 @@ -location / { - if (!-e $request_filename) { - rewrite ^/(.+\.(html|xml|json|htm|php|jsp|asp|shtml))$ /index.php?$1 last; - } -} \ No newline at end of file diff --git a/rewrites/nginx/sub.conf.tpl b/rewrites/nginx/sub.conf.tpl deleted file mode 100644 index 5996e01d4..000000000 --- a/rewrites/nginx/sub.conf.tpl +++ /dev/null @@ -1,16 +0,0 @@ -location / { - set $replace 'tmplate_replace'; - set $spider_request '0'; - - if ($http_user_agent ~* '(baiduspider|360sipder|Sogou Orion spider|Sogou News Spider|Sogou blog|Sogou spider2|Sogou inst spider|Sogou web spider|Sogou spider|trendiction|Yahoo|semrush|Toutiao|Google|qihoobot|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|MSNBot|ia_archiver|Tomato Bot)') { - set $spider_request '1'; - } - - if ($spider_request = '0' ) { - set $replace 'tmplate_replace'; - } - - sub_filter 'tmplate_replace' $replace; - sub_filter_once on; - sub_filter_types *; -} \ No newline at end of file diff --git a/rewrites/nginx/thinkphp.conf b/rewrites/nginx/thinkphp.conf deleted file mode 100755 index b8c527532..000000000 --- a/rewrites/nginx/thinkphp.conf +++ /dev/null @@ -1,5 +0,0 @@ -location / { - if (!-e $request_filename){ - rewrite ^(.*)$ /index.php?s=$1 last; break; - } -} \ No newline at end of file diff --git a/rewrites/nginx/typecho.conf b/rewrites/nginx/typecho.conf deleted file mode 100755 index dcc7c3f1a..000000000 --- a/rewrites/nginx/typecho.conf +++ /dev/null @@ -1,13 +0,0 @@ -location / { - if (!-e $request_filename) { - rewrite ^(.*)$ /index.php$1 last; - } -} - -# 不需要,可以删除 -location /typecho/ { - if (!-e $request_filename) { - rewrite ^(.*)$ /typecho/index.php$1 last; - } -} - diff --git a/rewrites/nginx/v2ray.conf b/rewrites/nginx/v2ray.conf deleted file mode 100644 index 641313279..000000000 --- a/rewrites/nginx/v2ray.conf +++ /dev/null @@ -1,15 +0,0 @@ -location /{ - if (!-e $request_filename) { - rewrite ^(.*)$ /index.php/$1 last; - break; - } -} - -location /ws { - proxy_redirect off; - proxy_pass http://127.0.0.1:34861; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; -} \ No newline at end of file diff --git a/rewrites/nginx/walle.conf b/rewrites/nginx/walle.conf deleted file mode 100755 index c88ba3d7c..000000000 --- a/rewrites/nginx/walle.conf +++ /dev/null @@ -1,34 +0,0 @@ - -#upstream webservers { -# server 0.0.0.0:5000 weight=1; -#} - -location / { - try_files $uri $uri/ /index.html; - add_header access-control-allow-origin *; -} - -location ^~ /api/ { - add_header access-control-allow-origin *; - proxy_pass http://webservers; - proxy_set_header X-Forwarded-Host $host:$server_port; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Origin $host:$server_port; - proxy_set_header Referer $host:$server_port; -} - -location ^~ /socket.io/ { - add_header access-control-allow-origin *; - proxy_pass http://webservers; - proxy_set_header X-Forwarded-Host $host:$server_port; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Origin $host:$server_port; - proxy_set_header Referer $host:$server_port; - proxy_set_header Host $http_host; - proxy_set_header X-NginX-Proxy true; - - # WebScoket Support - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; -} \ No newline at end of file diff --git a/rewrites/nginx/whmcs.conf b/rewrites/nginx/whmcs.conf deleted file mode 100755 index 2cdd0e710..000000000 --- a/rewrites/nginx/whmcs.conf +++ /dev/null @@ -1,16 +0,0 @@ -location / { - - if (!-e $request_filename) { - rewrite ^/contact/$ /./contact.php last; - rewrite ^/status$ /./pages.php?cate=$1&page=status last; - rewrite ^/act$ /./pages.php?cate=$1&page=actindex last; - rewrite ^/(\w+)/(\w+)/$ /./pages.php?cate=$1&page=$2 last; - rewrite ^/cart/(\w+)$ /./cart.php?a=$1 last; - rewrite ^/console$ /./clientarea.php last; - rewrite ^/sitemap.xml$ /./sitemap.php last; - rewrite ^/console/(\w+)$ /./clientarea.php?action=services&group=$1 last; - rewrite ^/console/ec/([0-9]+)$ /./clientarea.php?action=productdetails&id=$1 last; - rewrite ^/console/ec/(\w+)$ /./clientarea.php?action=services&group=ec&page=$1 last; - rewrite ^ /index.php last; - } -} diff --git a/rewrites/nginx/wmcms.conf b/rewrites/nginx/wmcms.conf deleted file mode 100755 index 386b75085..000000000 --- a/rewrites/nginx/wmcms.conf +++ /dev/null @@ -1,16 +0,0 @@ -location / { - if (!-e $request_filename) { - rewrite ^/index.html$ /index.php last; - rewrite ^/top/index.html$ /module/novel/topindex.php last; - rewrite ^/(\d*)/list/(\d*).html$ /module/novel/type.php?tid=$1&page=$2 last; - rewrite ^/(.*?)/(.*?)/info.html$ /module/novel/info.php?tid=$1&nid=$2 last; - rewrite ^/(.*?)/(.*?)/read/(\d*).html$ /module/novel/read.php?tid=$1&nid=$2&cid=$3 last; - rewrite ^/(.*?)/(.*?)/menu/(\d*).html$ /module/novel/menu.php?tid=$1&nid=$2&page=$3 last; - rewrite ^/search/(\d*)/(.*?)/(\d*).html$ /module/novel/search.php?type=$1&key=$2&page=$3 last; - - rewrite ^/(\d*)/list/(\d*)_(\d*)_(\d*)_(\d*)_(\d*)_(\d*)_(\d*)_(\d*).html$ /module/novel/type.php?tid=$1&page=$9&process=$2&word=$3&chapter=$4©=$5&cost=$6&letter=$7&order=$8 last; - - rewrite ^/top/list/(\d*)/(\d*)/(\d*).html$ /module/novel/toplist.php?tid=$1&dtype=$2&page=$3 last; - break; - } -} \ No newline at end of file diff --git a/rewrites/nginx/wordpress.conf b/rewrites/nginx/wordpress.conf deleted file mode 100755 index dfa6381d4..000000000 --- a/rewrites/nginx/wordpress.conf +++ /dev/null @@ -1,14 +0,0 @@ -location / { - index index.html index.php; - if (-f $request_filename/index.html){ - rewrite (.*) $1/index.html break; - } - if (-f $request_filename/index.php){ - rewrite (.*) $1/index.php; - } - if (!-f $request_filename){ - rewrite (.*) /index.php; - } -} - -rewrite /wp-admin$ $scheme://$host$uri/ permanent; \ No newline at end of file diff --git a/rewrites/nginx/zblog.conf b/rewrites/nginx/zblog.conf deleted file mode 100755 index 37d410519..000000000 --- a/rewrites/nginx/zblog.conf +++ /dev/null @@ -1,11 +0,0 @@ -location / { - if (-f $request_filename/index.html){ - rewrite (.*) $1/index.html break; - } - if (-f $request_filename/index.php){ - rewrite (.*) $1/index.php; - } - if (!-f $request_filename){ - rewrite (.*) /index.php; - } -} \ No newline at end of file From cbdf826951b30a45fa843a392f51c882d28e6139 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 04:56:25 +0800 Subject: [PATCH 330/965] Update site.py --- web/utils/site.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/utils/site.py b/web/utils/site.py index e9c9a3329..b359f419a 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -630,7 +630,10 @@ class sites(object): rewriteList['rewrite'].append('0.当前') rewrite_nginx_dir = mw.getPanelDir() + '/web/misc/nginx/rewrite' for ds in os.listdir(rewrite_nginx_dir): - rewriteList['rewrite'].append(ds[0:len(ds) - 5]) + if ds.startswith('.'): + continue + if ds.endswith('conf'): + rewriteList['rewrite'].append(ds[0:len(ds) - 5]) rewriteList['rewrite'] = sorted(rewriteList['rewrite']) return rewriteList From 570f775c664ac634c56d2a12601e22b604bbad6d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 04:58:11 +0800 Subject: [PATCH 331/965] Update site.py --- web/utils/site.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/web/utils/site.py b/web/utils/site.py index b359f419a..a61d0e932 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -604,15 +604,18 @@ class sites(object): return mw.returnData(False, '日志为空') return mw.returnData(True, mw.getLastLine(logPath, 100)) + def getNgxRewriteDir(self): + return mw.getPanelDir() + '/web/misc/nginx/rewrite/' + # 获取模版名内容 def getRewriteTpl(self, tplname): - file = mw.getPanelDir() + '/web/misc/nginx/rewrite/' + tplname + '.conf' + file = self.getNgxRewriteDir() + tplname + '.conf' if not os.path.exists(file): return mw.returnData(False, '模版不存在!') return mw.returnData(True, 'OK', file) def setRewriteTpl(self,name,data): - path = mw.getPanelDir() + '/web/misc/nginx/rewrite/' + name + ".conf" + path = self.getNgxRewriteDir() + name + ".conf" if os.path.exists(path): return mw.returnData(False, '模版已经存在!') @@ -628,7 +631,7 @@ class sites(object): rewriteList = {} rewriteList['rewrite'] = [] rewriteList['rewrite'].append('0.当前') - rewrite_nginx_dir = mw.getPanelDir() + '/web/misc/nginx/rewrite' + rewrite_nginx_dir = self.getNgxRewriteDir() for ds in os.listdir(rewrite_nginx_dir): if ds.startswith('.'): continue From 5f144924e48f0e3ae25a50d4018c656793834b55 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 05:02:47 +0800 Subject: [PATCH 332/965] update --- data/tpl/nginx.conf | 53 ------------------------------------- data/tpl/nginx_dirbind.conf | 48 --------------------------------- data/tpl/nginx_panel.conf | 23 ---------------- web/utils/site.py | 16 ++++++----- 4 files changed, 10 insertions(+), 130 deletions(-) delete mode 100755 data/tpl/nginx.conf delete mode 100755 data/tpl/nginx_dirbind.conf delete mode 100755 data/tpl/nginx_panel.conf diff --git a/data/tpl/nginx.conf b/data/tpl/nginx.conf deleted file mode 100755 index 5da306f9d..000000000 --- a/data/tpl/nginx.conf +++ /dev/null @@ -1,53 +0,0 @@ -server -{ - # reuseport 只能在一个server出现一次 - listen {$PORT}; - listen [::]:{$PORT}; - server_name {$SERVER_NAME}; - index index.php index.html index.htm default.php default.htm default.html; - root {$ROOT_DIR}; - - #SSL-START - #error_page 404/404.html; - #SSL-END - - #301-START - - #PROXY-START - - #ERROR-PAGE-START - #error_page 404 /404.html; - #error_page 502 /502.html; - #ERROR-PAGE-END - - #PHP-INFO-START - include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf; - #PHP-INFO-END - - #REWRITE-START - include {$OR_REWRITE}/{$SERVER_NAME}.conf; - #REWRITE-END - - #禁止访问的文件或目录 - location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) - { - return 404; - } - - #一键申请SSL证书验证目录相关设置 - location ~ \.well-known{ - allow all; - } - - location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2)$ - { - expires 30d; - access_log /dev/null; - if ($invalid_referer){ - return 404; - } - } - - access_log {$LOGPATH}/{$SERVER_NAME}.log main; - error_log {$LOGPATH}/{$SERVER_NAME}.error.log; -} \ No newline at end of file diff --git a/data/tpl/nginx_dirbind.conf b/data/tpl/nginx_dirbind.conf deleted file mode 100755 index 129a7ddd1..000000000 --- a/data/tpl/nginx_dirbind.conf +++ /dev/null @@ -1,48 +0,0 @@ - -#BINDING-{$DIRBIND}-START -server -{ - listen {$PORT}; - server_name {$DIRBIND}; - index index.php index.html index.htm default.php default.htm default.html; - root {$ROOT_DIR}; - - #SSL-START - #error_page 404/404.html; - #SSL-END - - #ERROR-PAGE-START - #error_page 404 /404.html; - #error_page 502 /502.html; - #ERROR-PAGE-END - - #PHP-INFO-START - include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf; - #PHP-INFO-END - - #REWRITE-START - include {$OR_REWRITE}/{$SERVER_MAIN}.conf; - #REWRITE-END - - #禁止访问的文件或目录 - location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) - { - return 404; - } - - #一键申请SSL证书验证目录相关设置 - location ~ \.well-known{ - allow all; - } - - location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ - { - error_log /dev/null; - access_log /dev/null; - expires 30d; - } - - access_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.log; - error_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.error.log; -} -#BINDING-{$DIRBIND}-END \ No newline at end of file diff --git a/data/tpl/nginx_panel.conf b/data/tpl/nginx_panel.conf deleted file mode 100755 index c487094fd..000000000 --- a/data/tpl/nginx_panel.conf +++ /dev/null @@ -1,23 +0,0 @@ -server -{ - listen {$PORT}; - listen [::]:{$PORT}; - - server_name {$SERVER_NAME}; - index index.php index.html index.htm default.php default.htm default.html; - - #SSL-START - #error_page 404/404.html; - #SSL-END - - #PROXY-START - location ^~ / { - proxy_pass http://0.0.0.0:{$PANAL_PORT}/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - } - #PROXY-END - - error_log {$LOGPATH}/{$SERVER_NAME}.error.log; -} \ No newline at end of file diff --git a/web/utils/site.py b/web/utils/site.py index a61d0e932..b2e0df56c 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -421,7 +421,8 @@ class sites(object): return mw.returnData(True, '站点【%s】删除成功!' % webname) def nginxAddConf(self): - source_tpl = mw.getPanelDir() + '/data/tpl/nginx.conf' + + source_tpl = self.getNgxTplDir() + '/nginx.conf' vhost_file = self.getHostConf(self.siteName) content = mw.readFile(source_tpl) @@ -605,17 +606,20 @@ class sites(object): return mw.returnData(True, mw.getLastLine(logPath, 100)) def getNgxRewriteDir(self): - return mw.getPanelDir() + '/web/misc/nginx/rewrite/' + return mw.getPanelDir() + '/web/misc/nginx/rewrite' + + def getNgxTplDir(self): + return mw.getPanelDir() + '/web/misc/nginx/tpl' # 获取模版名内容 - def getRewriteTpl(self, tplname): - file = self.getNgxRewriteDir() + tplname + '.conf' + def getRewriteTpl(self, name): + path = self.getNgxRewriteDir() +'/'+ name + ".conf" if not os.path.exists(file): return mw.returnData(False, '模版不存在!') return mw.returnData(True, 'OK', file) def setRewriteTpl(self,name,data): - path = self.getNgxRewriteDir() + name + ".conf" + path = self.getNgxRewriteDir() +'/'+ name + ".conf" if os.path.exists(path): return mw.returnData(False, '模版已经存在!') @@ -820,7 +824,7 @@ class sites(object): domain_split = re.search(rep, conf).groups() version = domain_split[0] - source_dirbind_tpl = mw.getPanelDir() + '/data/tpl/nginx_dirbind.conf' + source_dirbind_tpl = self.getNgxTplDir() + '/nginx_dirbind.conf' content = mw.readFile(source_dirbind_tpl) content = content.replace('{$PORT}', port) content = content.replace('{$PHPVER}', version) From e3070b618fcfd16ecc93014ca1b13949d3200b89 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 05:14:26 +0800 Subject: [PATCH 333/965] Update plugin.py --- web/utils/plugin.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index a9b4a9a55..19375d7a1 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -78,8 +78,6 @@ class plugin(object): ] __plugin_dir = 'plugins' - __index = 'data/json/index.json' - __index_data = None # lock _instance_lock = threading.Lock() @@ -95,15 +93,8 @@ class plugin(object): """插件类初始化""" def __init__(self): self.__plugin_dir = mw.getPluginDir() - self.__index = mw.getPanelDataDir() + '/json/index.json' - self.initIndexData() - def initIndexData(self): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - self.__index_data = json.loads(mw.readFile(self.__index)) - def getIndexList(self): indexList = thisdb.getOptionByJson('display_index') plist = [] @@ -307,9 +298,9 @@ class plugin(object): return False def getVersion(self, path): - version_t = path + '/version.pl' - if os.path.exists(version_t): - return mw.readFile(version_t).strip() + version_pl = path + '/version.pl' + if os.path.exists(version_pl): + return mw.readFile(version_pl).strip() return '' def checkIndexList(self, name, version): @@ -321,7 +312,7 @@ class plugin(object): return False def checkDisplayIndex(self, name, version, coexist): - indexList = self.__index_data + indexList = thisdb.getOptionByJson('display_index',default=[]) if coexist: if type(version) == list: for index in range(len(version)): From 3894eebfb5a546d5ec28a69cb830af8e433b756d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 15:47:22 +0800 Subject: [PATCH 334/965] update --- cli.sh | 2 +- panel_task.py | 20 ++++---------------- web/admin/setting/panel_ssl.py | 17 +++++++++++++++++ web/core/mw.py | 2 +- web/setting.py | 20 ++++++++++---------- web/templates/default/setting.html | 2 +- web/thisdb/option.py | 3 +-- web/utils/config.py | 2 ++ web/utils/setting.py | 28 +++++++++++++++++++++------- 9 files changed, 58 insertions(+), 38 deletions(-) diff --git a/cli.sh b/cli.sh index 084f81840..6c0fbf97c 100755 --- a/cli.sh +++ b/cli.sh @@ -110,7 +110,7 @@ case "$1" in mw_start_panel ;; 'task') - mw_stop + # mw_stop mw_start_bgtask ;; esac \ No newline at end of file diff --git a/panel_task.py b/panel_task.py index ce12b9641..859beeebb 100755 --- a/panel_task.py +++ b/panel_task.py @@ -47,19 +47,6 @@ def writeLogs(data): except: pass -def mw_async(f): - def wrapper(*args, **kwargs): - thr = threading.Thread(target=f, args=args, kwargs=kwargs) - thr.start() - return wrapper - -@mw_async -def restartMw(): - time.sleep(1) - cmd = mw.getPanelDir() + '/scripts/init.d/mw reload &' - mw.execShell(cmd) - - def downloadFile(url, filename): # 下载文件 try: @@ -298,10 +285,11 @@ def openrestyRestartAtOnce(): # --------------------------------------Panel Restart Start --------------------------------------------- # def restartPanelService(): - restartTip = mw.getPanelDir()+'/data/restart.pl' + restart_tip = mw.getPanelDir()+'/data/restart.pl' while True: - if os.path.exists(restartTip): - os.remove(restartTip) + if os.path.exists(restart_tip): + print("restart panel") + os.remove(restart_tip) mw.panelCmd('restart_panel') time.sleep(1) # --------------------------------------Panel Restart End --------------------------------------------- # diff --git a/web/admin/setting/panel_ssl.py b/web/admin/setting/panel_ssl.py index bf903a495..d6823c0f7 100644 --- a/web/admin/setting/panel_ssl.py +++ b/web/admin/setting/panel_ssl.py @@ -50,3 +50,20 @@ def del_panel_ssl(): choose = request.form.get('choose', '').strip() return MwSetting.instance().delPanelSsl(choose) +# 开启面板证书 +@blueprint.route('/set_panel_local_ssl', endpoint='set_panel_local_ssl', methods=['POST']) +@panel_login_required +def set_panel_local_ssl(): + cert_type = request.form.get('cert_type', '').strip() + return MwSetting.instance().setPanelLocalSsl(cert_type) + +@blueprint.route('/close_panel_ssl', endpoint='close_panel_ssl', methods=['POST']) +@panel_login_required +def close_panel_ssl(): + return MwSetting.instance().closePanelSsl() + + + + + + diff --git a/web/core/mw.py b/web/core/mw.py index 4564d6825..55c527e44 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -1298,7 +1298,7 @@ def panelCmd(method): execShell(cmd + ' ' + method) return - cmd = mw.getPanelDir() + '/scripts/init.d/mw' + cmd = getPanelDir() + '/scripts/init.d/mw' if os.path.exists(cmd): data = execShell(cmd + ' ' + method) return diff --git a/web/setting.py b/web/setting.py index 8defa4abe..57e9d86a7 100755 --- a/web/setting.py +++ b/web/setting.py @@ -21,6 +21,7 @@ import os import core.mw as mw import utils.system as system +import thisdb cpu_info = system.getCpuInfo() workers = cpu_info[1] @@ -50,16 +51,15 @@ if os.path.exists(default_ipv6_file): else: bind.append('0.0.0.0:%s' % panel_port) - -ssl_choose_file = panel_dir+'/ssl/choose.pl' -if os.path.exists(ssl_choose_file): - ssl_choose = mw.readFile(ssl_choose_file).strip() - if mw.inArray(['local','nginx'],ssl_choose): - tmp_cert = panel_dir+'/ssl/'+ssl_choose+'/cert.pem' - tmp_private = panel_dir+'/ssl/'+ssl_choose+'/private.pem' - if os.path.exists(tmp_cert) and os.path.exists(tmp_private): - certfile = tmp_cert - keyfile = tmp_private +panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) +if panel_ssl_data['open']: + choose = panel_ssl_data['choose'] + if mw.inArray(['local','nginx'],choose): + panel_cert = panel_dir+'/ssl/'+choose+'/cert.pem' + panel_private = panel_dir+'/ssl/'+choose+'/private.pem' + if os.path.exists(panel_cert) and os.path.exists(panel_private): + certfile = panel_cert + keyfile = panel_private ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3' ssl_version = 2 diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index eb8bd401c..3d1d068fc 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -113,7 +113,7 @@

                    面板SSL - + 为面板设置https协议访问,提升面板访问安全性 diff --git a/web/thisdb/option.py b/web/thisdb/option.py index 767078425..eaf373dcb 100644 --- a/web/thisdb/option.py +++ b/web/thisdb/option.py @@ -63,5 +63,4 @@ def setOption(name, value, 'type':type, 'value':value } - mw.M('option').insert(add_option) - return True \ No newline at end of file + return mw.M('option').insert(add_option) \ No newline at end of file diff --git a/web/utils/config.py b/web/utils/config.py index bc8307893..b6112ddc7 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -83,5 +83,7 @@ def getGlobalVar(): # 邮件通知设置 data['notify_email'] = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') data['notify_tgbot'] = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + + data['panel_ssl'] = thisdb.getOptionByJson('panel_ssl', default={'open':False}) return data \ No newline at end of file diff --git a/web/utils/setting.py b/web/utils/setting.py index bd9cd4a29..5ce5f1fcf 100644 --- a/web/utils/setting.py +++ b/web/utils/setting.py @@ -13,6 +13,7 @@ import re import threading import re import time +import json import core.mw as mw import thisdb @@ -164,21 +165,34 @@ class setting(object): return mw.returnData(False, '未知类型!') # 面板本地SSL设置 - def setPanelLocalSsl(self): - pdir = mw.getPanelDir() + def setPanelLocalSsl(self, cert_type): + panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + if not panel_ssl_data['open']: + panel_ssl_data['open'] = True + + pdir = mw.getPanelDir() cert = {} keyPath = pdir+'/ssl/local/private.pem' certPath = pdir+'/ssl/local/cert.pem' - if not os.path.exists(certPath): mw.createLocalSSL() - choose_file = self.__file['ssl'] - mw.writeFile(choose_file, 'local') - + panel_ssl_data['choose'] = 'local' + thisdb.setOption('panel_ssl', json.dumps(panel_ssl_data)) mw.restartMw() - return mw.returnJson(True, '设置成功') + return mw.returnData(True, '设置成功') + + def closePanelSsl(self): + panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + + if panel_ssl_data['open']: + panel_ssl_data['open'] = False + + thisdb.setOption('panel_ssl', json.dumps(panel_ssl_data)) + mw.restartMw() + return mw.returnData(True, '设置成功') + # 申请面板let证书 # def applyPanelAcmeSsl(self): From 0f1b98721b48ba27e6cb216a904a049819eadbc9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 23:50:17 +0800 Subject: [PATCH 335/965] up --- web/admin/__init__.py | 4 +++ web/admin/setting/panel_ssl.py | 11 +++++++ web/core/mw.py | 52 ++++++++++++++++++++++++++++++ web/templates/default/setting.html | 2 +- web/utils/config.py | 2 ++ web/utils/setting.py | 20 +++++++++++- 6 files changed, 89 insertions(+), 2 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 6bb30f1a0..738fe5cfe 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -117,6 +117,10 @@ def requestCheck(): basic_pwd = mw.md5(auth.password.strip() + salt) if basic_user != basic_auth['basic_user'] or basic_pwd != basic_auth['basic_pwd']: return sendAuthenticated() + + domain_check = mw.checkDomainPanel() + if domain_check: + return domain_check diff --git a/web/admin/setting/panel_ssl.py b/web/admin/setting/panel_ssl.py index d6823c0f7..19f904dcd 100644 --- a/web/admin/setting/panel_ssl.py +++ b/web/admin/setting/panel_ssl.py @@ -57,11 +57,22 @@ def set_panel_local_ssl(): cert_type = request.form.get('cert_type', '').strip() return MwSetting.instance().setPanelLocalSsl(cert_type) +# 关闭面板证书 @blueprint.route('/close_panel_ssl', endpoint='close_panel_ssl', methods=['POST']) @panel_login_required def close_panel_ssl(): return MwSetting.instance().closePanelSsl() +# 设置面板域名 +@blueprint.route('/set_panel_domain', endpoint='set_panel_domain', methods=['POST']) +@panel_login_required +def set_panel_domain(): + domain = request.form.get('domain', '').strip() + return MwSetting.instance().setPanelDomain(domain) + + + + diff --git a/web/core/mw.py b/web/core/mw.py index 55c527e44..89db9b6dc 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -420,10 +420,62 @@ def getFileMd5(filename): return myhash.hexdigest() +def getHost(port=False): + from flask import request + host_tmp = request.headers.get('host') + if not host_tmp: + if request.url_root: + tmp = re.findall(r"(https|http)://([\w:\.-]+)", request.url_root) + if tmp: + host_tmp = tmp[0][1] + if not host_tmp: + host_tmp = getLocalIp() + ':' + readFile('data/port.pl').strip() + try: + if host_tmp.find(':') == -1: + host_tmp += ':80' + except: + host_tmp = "127.0.0.1:8888" + h = host_tmp.split(':') + if port: + return h[-1] + return ':'.join(h[0:-1]) + def getClientIp(): from flask import request return request.remote_addr.replace('::ffff:', '') +def checkDomainPanel(): + import thisdb + from flask import Flask, redirect, request, url_for + current_host = getHost() + + domain = thisdb.getOption('panel_domain', default='') + port = getPanelPort() + scheme = 'http' + + panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + if panel_ssl_data['open']: + choose = panel_ssl_data['choose'] + if not inArray(['local','nginx'], choose): + return False + scheme = 'https' + + client_ip = getClientIp() + if client_ip in ['127.0.0.1', 'localhost', '::1']: + return False + + ip = getHostAddr() + if domain == '': + if current_host.strip().lower() != ip.strip().lower(): + to = scheme + "://" + ip + ":" + str(port) + return redirect(to, code=302) + return False + else: + if current_host.strip().lower() != domain.strip().lower(): + to = scheme + "://" + domain + ":" + str(port) + return redirect(to, code=302) + return False + def getLocalIp(): filename = getPanelDir()+'/data/iplist.txt' try: diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index 3d1d068fc..1c5edea6c 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -106,7 +106,7 @@

                    绑定域名 - + 为面板绑定一个访问域名,注意:一旦绑定域名,只能通过域名访问面板

                    diff --git a/web/utils/config.py b/web/utils/config.py index b6112ddc7..cbc0ecc90 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -85,5 +85,7 @@ def getGlobalVar(): data['notify_tgbot'] = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') data['panel_ssl'] = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + data['panel_domain'] = thisdb.getOption('panel_domain', default='') + return data \ No newline at end of file diff --git a/web/utils/setting.py b/web/utils/setting.py index 5ce5f1fcf..cdeb67e0c 100644 --- a/web/utils/setting.py +++ b/web/utils/setting.py @@ -270,4 +270,22 @@ class setting(object): # if is_already_apply: # return mw.returnJson(True, '重复申请!', data) - # return mw.returnJson(True, '申请成功!', data) \ No newline at end of file + # return mw.returnJson(True, '申请成功!', data) + + def setPanelDomain(self, domain): + port = mw.getPanelPort() + ip = mw.getLocalIp() + + panel_domain = thisdb.getOption('panel_domain', default='') + if domain == '': + to_panel_url = 'http://'+ip+":"+str(port)+'/setting/index' + thisdb.setOption('panel_domain', '') + mw.restartMw() + return mw.returnData(True, '清空域名成功!', to_panel_url) + + thisdb.setOption('panel_domain', domain) + to_panel_url = 'http://'+domain+":"+str(port)+'/setting/index' + mw.restartMw() + return mw.returnData(True, '设置域名成功!',to_panel_url) + + From 5564b27ff2f3f4ac6e5ddd677a960a8ba2cfe42b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 26 Nov 2024 23:50:47 +0800 Subject: [PATCH 336/965] Update mw.py --- web/core/mw.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 89db9b6dc..4df3def3c 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -447,16 +447,15 @@ def getClientIp(): def checkDomainPanel(): import thisdb from flask import Flask, redirect, request, url_for + current_host = getHost() - domain = thisdb.getOption('panel_domain', default='') port = getPanelPort() scheme = 'http' panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) if panel_ssl_data['open']: - choose = panel_ssl_data['choose'] - if not inArray(['local','nginx'], choose): + if not inArray(['local','nginx'], panel_ssl_data['choose']): return False scheme = 'https' From 4adaa8565c9ce52dae66159c6864208fe4bef9ce Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 00:01:00 +0800 Subject: [PATCH 337/965] Update setting.py --- web/utils/setting.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/web/utils/setting.py b/web/utils/setting.py index cdeb67e0c..19b16ccfc 100644 --- a/web/utils/setting.py +++ b/web/utils/setting.py @@ -274,10 +274,14 @@ class setting(object): def setPanelDomain(self, domain): port = mw.getPanelPort() - ip = mw.getLocalIp() - + panel_domain = thisdb.getOption('panel_domain', default='') if domain == '': + ip = mw.getLocalIp() + client_ip = mw.getClientIp() + if client_ip in ['127.0.0.1', 'localhost', '::1']: + ip = client_ip + to_panel_url = 'http://'+ip+":"+str(port)+'/setting/index' thisdb.setOption('panel_domain', '') mw.restartMw() From 3305e51fda5c49bccb38637d4b5b7b46f6602f6b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 00:13:34 +0800 Subject: [PATCH 338/965] Update install.sh --- plugins/php-apt/versions/84/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php-apt/versions/84/install.sh b/plugins/php-apt/versions/84/install.sh index 33f2925ab..585954ca6 100755 --- a/plugins/php-apt/versions/84/install.sh +++ b/plugins/php-apt/versions/84/install.sh @@ -9,7 +9,6 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh bash ${rootPath}/scripts/getos.sh @@ -22,6 +21,7 @@ PHP_VER=84 Install_php() { +apt update -y #------------------------ install start ------------------------------------# apt -y install php${version} php${version}-fpm php${version}-dev if [ "$?" == "0" ];then From b8161b86d2811853a198bd50d8a42d6ddd212089 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 00:17:57 +0800 Subject: [PATCH 339/965] update --- plugins/php-apt/install.sh | 2 ++ plugins/php-apt/versions/84/install.sh | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/php-apt/install.sh b/plugins/php-apt/install.sh index 910d4b708..e0f9bed05 100755 --- a/plugins/php-apt/install.sh +++ b/plugins/php-apt/install.sh @@ -86,6 +86,8 @@ fi cd ${curPath} && sh -x $curPath/versions/$2/install.sh $1 if [ "${action}" == "install" ] && [ -d ${serverPath}/php-apt/${type} ];then + apt update -y + #初始化 cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py start ${type} cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py restart ${type} diff --git a/plugins/php-apt/versions/84/install.sh b/plugins/php-apt/versions/84/install.sh index 585954ca6..435102c7a 100755 --- a/plugins/php-apt/versions/84/install.sh +++ b/plugins/php-apt/versions/84/install.sh @@ -21,7 +21,6 @@ PHP_VER=84 Install_php() { -apt update -y #------------------------ install start ------------------------------------# apt -y install php${version} php${version}-fpm php${version}-dev if [ "$?" == "0" ];then From 296f54127d453c6fe34a04e8332551afdf064ead Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 00:22:01 +0800 Subject: [PATCH 340/965] Update panel_task.py --- panel_task.py | 1 - 1 file changed, 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index 859beeebb..441572d4c 100755 --- a/panel_task.py +++ b/panel_task.py @@ -81,7 +81,6 @@ def runPanelTask(): siteEdateCheck() lock_file = mw.getTriggerTaskLockFile() - mw.writeFile(lock_file,'True') try: if os.path.exists(lock_file): bash_list = thisdb.getTaskList(status=-1) From f7c9d61e8d3c4a55546e9eb87bd71b239ffd1492 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 00:22:33 +0800 Subject: [PATCH 341/965] Update plugin.py --- web/utils/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index 19375d7a1..c24d085a4 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -250,7 +250,7 @@ class plugin(object): self.hookInstall(info_data) title = '{0}[{1}-{2}]'.format(msg_head,name,version) thisdb.addTask(name=title,cmd=exec_bash, status=0) - + mw.triggerTask() # 调式日志 mw.debugLog(exec_bash) return mw.returnData(True, '已将安装任务添加到队列!') From 2a3bad681b2e41d45866801c91236e00c69bedfe Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 00:56:23 +0800 Subject: [PATCH 342/965] Update install_new.sh --- scripts/install_new.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_new.sh b/scripts/install_new.sh index 71ca2fb51..608a2d556 100755 --- a/scripts/install_new.sh +++ b/scripts/install_new.sh @@ -147,7 +147,7 @@ if [ "${OSNAME}" == "macos" ];then exit 0 fi -cd /www/server/mdserver-web && bash cli_new.sh start +cd /www/server/mdserver-web && bash cli.sh start isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'` n=0 while [ ! -f /etc/rc.d/init.d/mw ]; From 80e49a6ff872eb2838fffe3aa58b8ca9ee3bf816 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:19:40 +0800 Subject: [PATCH 343/965] Update setting.py --- web/setting.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/setting.py b/web/setting.py index 57e9d86a7..d0c09281e 100755 --- a/web/setting.py +++ b/web/setting.py @@ -32,6 +32,10 @@ log_dir = mw.getMWLogs() if not os.path.exists(log_dir): os.mkdir(log_dir) +data_dir = panel_dir+'/data' +if not os.path.exists(data_dir): + os.mkdir(data_dir) + # default port panel_port = '7200' default_port_file = panel_dir+'/data/port.pl' From 8df979956461798007e8dc6358685a6262e6fa0e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:23:59 +0800 Subject: [PATCH 344/965] Update mw.py --- web/core/mw.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 4df3def3c..32f7f9fe0 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -546,9 +546,8 @@ def getFileStatsDesc( filename: str | None = None, path: str | None = None, ): - # print(filename,path) - filename = filename.replace('//', '/') try: + filename = filename.replace('//', '/') stat = os.stat(filename) accept = str(oct(stat.st_mode)[-3:]) mtime = str(int(stat.st_mtime)) From f9f074ef01045caffbb46c397f24ef2c6226e6e5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:27:32 +0800 Subject: [PATCH 345/965] update --- web/core/mw.py | 2 ++ web/utils/file.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 32f7f9fe0..253aca131 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -546,6 +546,8 @@ def getFileStatsDesc( filename: str | None = None, path: str | None = None, ): + if path is None or filename is None: + return ';;;;;' try: filename = filename.replace('//', '/') stat = os.stat(filename) diff --git a/web/utils/file.py b/web/utils/file.py index a9adf31ea..948a8379c 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -295,7 +295,7 @@ def getAllDirList(path, page=1, size=10, order = '', search=None): for dst_file in plist: if not os.path.exists(dst_file): continue - stat = getFileStatsDesc(dst_file, path) + stat = getFileStatsDesc(filename=dst_file, path=path) if os.path.isdir(dst_file): dirnames.append(stat) else: @@ -343,7 +343,7 @@ def getDirList(path, page=1, size=10, order = '', search=None): if not os.path.exists(abs_file): continue - stats = getFileStatsDesc(abs_file, path) + stats = getFileStatsDesc(filename=abs_file, path=path) if os.path.isdir(abs_file): dirnames.append(stats) else: From 4f1001e487d78b8b44ecd68a9f90dbe2d491ae0e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:30:38 +0800 Subject: [PATCH 346/965] Update file.py --- web/utils/file.py | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/web/utils/file.py b/web/utils/file.py index 948a8379c..aa3874079 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -176,37 +176,6 @@ def saveBody(path, data, encoding): except Exception as ex: return mw.returnData(False, '文件保存错误:' + str(ex)) -# 获取文件权限描述 -def getFileStatsDesc( - filename: str | None = None, - path: str | None = None, -): - if path is None or filename is None: - return ';;;;;' - filename = filename.replace('//', '/') - try: - stat = os.stat(filename) - accept = str(oct(stat.st_mode)[-3:]) - mtime = str(int(stat.st_mtime)) - user = '' - try: - user = str(pwd.getpwuid(stat.st_uid).pw_name) - except: - user = str(stat.st_uid) - size = str(stat.st_size) - link = '' - if os.path.islink(filename): - link = ' -> ' + os.readlink(filename) - - if path: - path_t = (path + '/').replace('//', '/') - filename = filename.replace(path_t, '', 1) - - return filename + ';' + size + ';' + mtime + ';' + accept + ';' + user + ';' + link - except Exception as e: - print(str(e)) - return ';;;;;' - def sortFileList(path, ftype = 'mtime', sort = 'desc'): flist = os.listdir(path) @@ -295,7 +264,7 @@ def getAllDirList(path, page=1, size=10, order = '', search=None): for dst_file in plist: if not os.path.exists(dst_file): continue - stat = getFileStatsDesc(filename=dst_file, path=path) + stat = mw.getFileStatsDesc(filename=dst_file, path=path) if os.path.isdir(dst_file): dirnames.append(stat) else: @@ -343,7 +312,7 @@ def getDirList(path, page=1, size=10, order = '', search=None): if not os.path.exists(abs_file): continue - stats = getFileStatsDesc(filename=abs_file, path=path) + stats = mw.getFileStatsDesc(filename=abs_file, path=path) if os.path.isdir(abs_file): dirnames.append(stats) else: From 37c8da89ae7a5aee3cb39f3181a31a23ceb73cfd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:32:57 +0800 Subject: [PATCH 347/965] Update mw.py --- web/core/mw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 253aca131..3584d4dd2 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -543,10 +543,10 @@ def getOsID(): # 获取文件权限描述 def getFileStatsDesc( - filename: str | None = None, - path: str | None = None, + filename: str | None = '', + path: str | None = '', ): - if path is None or filename is None: + if path == '' or filename == '': return ';;;;;' try: filename = filename.replace('//', '/') From 053cd9bc011a277ded4673c5dd38b8ab473fb740 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:33:15 +0800 Subject: [PATCH 348/965] Update mw.py --- web/core/mw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/mw.py b/web/core/mw.py index 3584d4dd2..86db6ab62 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -563,7 +563,7 @@ def getFileStatsDesc( if os.path.islink(filename): link = ' -> ' + os.readlink(filename) - if path: + if path != '': tmp_path = (path + '/').replace('//', '/') filename = filename.replace(tmp_path, '', 1) From b0070109122cce42324934efef3057cf0287605c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:34:38 +0800 Subject: [PATCH 349/965] Update mw.py --- web/core/mw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/mw.py b/web/core/mw.py index 86db6ab62..7d87f970d 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -544,7 +544,7 @@ def getOsID(): # 获取文件权限描述 def getFileStatsDesc( filename: str | None = '', - path: str | None = '', + path: str | None = '' ): if path == '' or filename == '': return ';;;;;' From 1b007460de5bc29350415debb826f32fc3bcddbd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:35:37 +0800 Subject: [PATCH 350/965] update --- web/core/mw.py | 5 +---- web/utils/file.py | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 7d87f970d..cf60c8222 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -542,10 +542,7 @@ def getOsID(): return data[0].strip() # 获取文件权限描述 -def getFileStatsDesc( - filename: str | None = '', - path: str | None = '' -): +def getFileStatsDesc(filename,path): if path == '' or filename == '': return ';;;;;' try: diff --git a/web/utils/file.py b/web/utils/file.py index aa3874079..cf8d04e5d 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -264,7 +264,7 @@ def getAllDirList(path, page=1, size=10, order = '', search=None): for dst_file in plist: if not os.path.exists(dst_file): continue - stat = mw.getFileStatsDesc(filename=dst_file, path=path) + stat = mw.getFileStatsDesc(dst_file, path) if os.path.isdir(dst_file): dirnames.append(stat) else: @@ -312,7 +312,7 @@ def getDirList(path, page=1, size=10, order = '', search=None): if not os.path.exists(abs_file): continue - stats = mw.getFileStatsDesc(filename=abs_file, path=path) + stats = mw.getFileStatsDesc(abs_file, path) if os.path.isdir(abs_file): dirnames.append(stats) else: From 26bc566aedc52ead21cb3c410ea23eaf82ac256f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:41:17 +0800 Subject: [PATCH 351/965] update --- web/thisdb/app.py | 11 ++--------- web/thisdb/option.py | 12 +++++------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/web/thisdb/app.py b/web/thisdb/app.py index f75b2faf7..a3c38e9d2 100644 --- a/web/thisdb/app.py +++ b/web/thisdb/app.py @@ -24,10 +24,7 @@ def addApp(app_id,app_secret,white_list): } return mw.M('app').insert(add_data) -def getAppList( - page:int | None = 1, - size:int | None = 10, -): +def getAppList(page=1,size=10): m = mw.M('app').field(__FIELD) start = (int(page) - 1) * (int(size)) @@ -53,11 +50,7 @@ def toggleAppStatus(aid): return mw.M('app').where('id=?',(aid,)).update({'status':1}) -def setAppData(aid, - status: str | None = None, - app_id: str | None = None, - app_secret: str | None = None, -): +def setAppData(aid, status=None, app_id=None, app_secret=None): up_data = {} if status is not None: up_data['status'] = status diff --git a/web/thisdb/option.py b/web/thisdb/option.py index eaf373dcb..b57fe1f35 100644 --- a/web/thisdb/option.py +++ b/web/thisdb/option.py @@ -13,8 +13,8 @@ import json import core.mw as mw def getOption(name, - type: str | None = 'common', - default : str | None = None + type='common', + default=None ) -> str: ''' 获取配置的值 @@ -29,8 +29,8 @@ def getOption(name, def getOptionByJson(name, - type: str | None = 'common', - default : object | None = None + type='common', + default=None ) -> object: ''' 获取配置的值,返回对象类型 @@ -44,9 +44,7 @@ def getOptionByJson(name, if data is not None: return json.loads(data) -def setOption(name, value, - type: str | None = 'common' -) -> bool: +def setOption(name, value, type = 'common') -> bool: ''' 设置配置的值 :name -> str 名称 (必填) From 9265bd57e48e9ea4935181b12aa2076aef59ea70 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:46:54 +0800 Subject: [PATCH 352/965] update --- web/thisdb/firewall.py | 10 ++-------- web/thisdb/logs.py | 10 ++-------- web/thisdb/option.py | 10 ++-------- web/thisdb/sites.py | 7 +------ web/thisdb/tasks.py | 30 ++++++------------------------ web/thisdb/temp_login.py | 10 ++-------- web/thisdb/user.py | 11 +++-------- 7 files changed, 18 insertions(+), 70 deletions(-) diff --git a/web/thisdb/firewall.py b/web/thisdb/firewall.py index 88c7b0c39..bff32e6f2 100644 --- a/web/thisdb/firewall.py +++ b/web/thisdb/firewall.py @@ -12,10 +12,7 @@ import core.mw as mw __FIELD = 'id,port,protocol,ps,add_time,update_time' -def getFirewallList( - page:int | None = 1, - size:int | None = 10, -): +def getFirewallList(page=1,size=10): start = (int(page) - 1) * (int(size)) limit = str(start) + ',' +str(size) @@ -27,10 +24,7 @@ def getFirewallList( data['list'] = firewall_list return data -def addFirewall(port, - protocol: str | None = 'tcp', - ps: str | None = '备注' -) -> bool: +def addFirewall(port, protocol='tcp',ps='备注') -> bool: ''' 设置配置的值 :port -> str 端口 (必填) diff --git a/web/thisdb/logs.py b/web/thisdb/logs.py index d23d59719..f08b7888c 100644 --- a/web/thisdb/logs.py +++ b/web/thisdb/logs.py @@ -17,9 +17,7 @@ def clearLog(): mw.M('logs').where('id>?', (0,)).delete() return True -def addLog(type, log, - uid: int | None = 1 -) -> str: +def addLog(type, log, uid = 1) -> str: ''' 添加日志 :type -> str 类型 (必填) @@ -35,11 +33,7 @@ def addLog(type, log, mw.M('logs').insert(insert_data) return True -def getLogsList( - page:int | None = 1, - size:int | None = 10, - search: str | None = '' -): +def getLogsList(page = 1,size = 10,search = ''): sql_where = '' if search != '' : sql_where = " type like '%" + search + "%' or log like '%" + search + "%' " diff --git a/web/thisdb/option.py b/web/thisdb/option.py index b57fe1f35..35a32f5cc 100644 --- a/web/thisdb/option.py +++ b/web/thisdb/option.py @@ -12,10 +12,7 @@ import os import json import core.mw as mw -def getOption(name, - type='common', - default=None -) -> str: +def getOption(name,type='common',default=None) -> str: ''' 获取配置的值 :name -> str 名称 (必填) @@ -28,10 +25,7 @@ def getOption(name, return data -def getOptionByJson(name, - type='common', - default=None -) -> object: +def getOptionByJson(name,type='common',default=None) -> object: ''' 获取配置的值,返回对象类型 :name -> str 名称 (必填) diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index fb2f0a3c4..4ba679e4e 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -111,12 +111,7 @@ def getSitesList( def deleteSitesById(site_id): return mw.M('sites').where("id=?", (site_id,)).delete() -def setSitesData(site_id, - edate: str | None = None, - ps: str | None = None, - path: str | None = None, - status: str | None = None, -): +def setSitesData(site_id, edate = None, ps = None, path = None,status = None): update_data = {} if edate is not None: update_data['edate'] = edate diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 317e5e065..bf839cca8 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -12,9 +12,7 @@ import core.mw as mw __FIELD = 'id,name,type,start,end,cmd,status,add_time' -def getTaskCount( - status: int | None = -1 - ) -> int: +def getTaskCount(status = -1) -> int: return mw.M('tasks').where('status=?',(1,)).count() @@ -22,12 +20,7 @@ def getTaskCount( def getTaskUnexecutedCount() -> int: return mw.M('tasks').where('status!=?',(1,)).count() -def addTaskByDownload( - name: str | None = '下载文件', - cmd: str | None = None, - type: str | None = 'download', - status: int | None = 0, -): +def addTaskByDownload(name = '下载文件',cmd = None,type = 'download',status = 0): ''' 添加后台任务 :name -> str 类型 @@ -110,19 +103,13 @@ def getTaskFirstByRun() -> None: return None return data -def getTaskRunList( - page: int | None = 1, - size: int | None = 10, -): +def getTaskRunList(page = 1,size = 10): start = (page - 1) * size limit = str(start) + ',' + str(size) task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id asc').select() return task_list -def getTaskRunPage( - page: int | None = 1, - size: int | None = 10, -): +def getTaskRunPage(page = 1, size = 10): start = (page - 1) * size limit = str(start) + ',' + str(size) @@ -135,16 +122,11 @@ def getTaskRunPage( return rdata -def setTaskStatus(task_id, - status: int | None = 0 -): +def setTaskStatus(task_id,status = 0): mw.M('tasks').where('id=?',(task_id,)).update({'status':status}) return True -def setTaskData(id, - start: int | None = None, - end: int | None = None, -): +def setTaskData(id, start = None,end = None): if start is not None: mw.M('tasks').where('id=?',(id,)).update({'start':start}) if end is not None: diff --git a/web/thisdb/temp_login.py b/web/thisdb/temp_login.py index b6ac4e840..558f2526f 100644 --- a/web/thisdb/temp_login.py +++ b/web/thisdb/temp_login.py @@ -14,10 +14,7 @@ import core.mw as mw __FIELD = 'id,token,salt,state,login_time,login_addr,logout_time,expire,add_time' -def addTempLogin( - token:str | None = None, - expire:int | None = None -): +def addTempLogin(token = None,expire = None): if expire is None: start_time = int(time.time()) expire=start_time+3600 @@ -40,10 +37,7 @@ def addTempLogin( return mw.M('temp_login').insert(insert_data) -def getTempLoginPage( - page: int | None = 1, - size: int | None = 10, -): +def getTempLoginPage(page = 1,size = 10): start = (page - 1) * size limit = str(start) + ',' + str(size) tl_list = mw.M('temp_login').where('', ()).field(__FIELD).limit(limit).order('id desc').select() diff --git a/web/thisdb/user.py b/web/thisdb/user.py index ebf74532a..20fa5ff09 100644 --- a/web/thisdb/user.py +++ b/web/thisdb/user.py @@ -36,8 +36,7 @@ def initAdminUser(): return True -def getUserByName(name, -) -> None: +def getUserByName(name) -> None: ''' 获取用户信息通过用户名 ''' @@ -56,8 +55,7 @@ def getUserByName(name, row['update_time'] = data['update_time'] return row -def getUserById(id, -) -> None: +def getUserById(id) -> None: ''' 获取用户信息通过用户名 ''' @@ -95,10 +93,7 @@ def setUserPwdByName(name, password): pwd = mw.md5(password) return mw.M('users').where("name=?", (name,)).setField('password', pwd) -def setUserByRoot( - name: str | None = None, - password: str | None = None, -) -> bool: +def setUserByRoot(name = None,password = None) -> bool: ''' 设置配置的值 :name -> str 名称 (必填) From be55e79dcfe6e423be0d5200666ad38f55999340 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:47:53 +0800 Subject: [PATCH 353/965] Update sites.py --- web/thisdb/sites.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index 4ba679e4e..3b46f98f7 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -71,11 +71,11 @@ def getSitesEdateList(edate): return elist def getSitesList( - page:int | None = 1, - size:int | None = 10, - type_id:int | None = 0, - search: str | None = '', - order:str | None = None, + page = 1, + size = 10, + type_id = 0, + search = '', + order = None, ): sql_where = '' if search != '' : From 9b0a41c3d53ced2d11387a0ed7ab4523f688da4d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:49:20 +0800 Subject: [PATCH 354/965] update --- web/thisdb/crontab.py | 4 ++-- web/thisdb/tasks.py | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/web/thisdb/crontab.py b/web/thisdb/crontab.py index 32f755598..85d14ccd5 100644 --- a/web/thisdb/crontab.py +++ b/web/thisdb/crontab.py @@ -36,8 +36,8 @@ def deleteCronById(id): return True def getCrontabList( - page:int | None = 1, - size:int | None = 10, + page = 1, + size = 10, ): start = (int(page) - 1) * size limit = str(start) + ',' + str(size) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index bf839cca8..656bc4313 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -44,10 +44,10 @@ def addTaskByDownload(name = '下载文件',cmd = None,type = 'download',status return True def addTask( - name: str | None = '常用任务', - cmd: str | None = None, - type: str | None = 'execshell', - status: int | None = 0, + name = '常用任务', + cmd = None, + type = 'execshell', + status = 0, ): ''' 添加后台任务 @@ -73,9 +73,9 @@ def addTask( def getTaskList( - status: int | None = 1, - page: int | None = 1, - size: int | None = 10, + status = 1, + page = 1, + size = 10, ): start = (page - 1) * size limit = str(start) + ',' + str(size) @@ -83,9 +83,9 @@ def getTaskList( return task_list def getTaskPage( - status: int | None = 1, - page: int | None = 1, - size: int | None = 10, + status = 1, + page = 1, + size = 10, ): start = (page - 1) * size limit = str(start) + ',' + str(size) From 2c5ab012377ae86504e513eb1b6f25aa9e12d80f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:50:32 +0800 Subject: [PATCH 355/965] Update firewall.py --- web/utils/firewall.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 3a5d1d7ac..99844a5c5 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -245,9 +245,7 @@ class Firewall(object): pass return mw.returnData(True, '设置成功!') - def addAcceptPortCmd(self, port, - protocol:str | None ='tcp' - ): + def addAcceptPortCmd(self, port, protocol ='tcp'): if self.__isUfw: if protocol == 'tcp': mw.execShell('ufw allow ' + port + '/tcp') From ae016172ea84b22b8664c8e61561e06c40d53536 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:50:47 +0800 Subject: [PATCH 356/965] Update firewall.py --- web/utils/firewall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 99844a5c5..1dd9c5c40 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -285,7 +285,7 @@ class Firewall(object): # 添加放行端口 def addAcceptPort(self, port, ps, stype, - protocol: str | None ='tcp' + protocol='tcp' ): if not self.getFwStatus(): self.setFw(0) From 86ac000117ac51f91de5f4da0c73ae15131fc606 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:51:15 +0800 Subject: [PATCH 357/965] Update firewall.py --- web/utils/firewall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 1dd9c5c40..e2e8aff5b 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -307,7 +307,7 @@ class Firewall(object): return mw.returnData(True, msg) def delAcceptPort(self, firewall_id, port, - protocol: str | None ='tcp' + protocol='tcp' ): panel_port = mw.getPanelPort() @@ -326,7 +326,7 @@ class Firewall(object): return mw.returnData(False, '删除失败!:' + str(e)) def delAcceptPortCmd(self, port, - protocol: str | None ='tcp' + protocol ='tcp' ): if self.__isUfw: if protocol == 'tcp': From e4ad01dd42391315ef2c4180fe9ee7ebd65cec05 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 01:55:03 +0800 Subject: [PATCH 358/965] update --- web/admin/model/__init__.py | 36 ------- web/admin/model/firewall.py | 41 ------- web/admin/model/initdb.py | 194 ---------------------------------- web/admin/model/logs.py | 48 --------- web/admin/model/option.py | 74 ------------- web/admin/model/sites.py | 16 --- web/admin/model/task.py | 135 ----------------------- web/admin/model/temp_login.py | 44 -------- web/admin/model/user.py | 93 ---------------- 9 files changed, 681 deletions(-) delete mode 100644 web/admin/model/__init__.py delete mode 100644 web/admin/model/firewall.py delete mode 100644 web/admin/model/initdb.py delete mode 100644 web/admin/model/logs.py delete mode 100644 web/admin/model/option.py delete mode 100644 web/admin/model/sites.py delete mode 100644 web/admin/model/task.py delete mode 100644 web/admin/model/temp_login.py delete mode 100644 web/admin/model/user.py diff --git a/web/admin/model/__init__.py b/web/admin/model/__init__.py deleted file mode 100644 index e2bb5f82e..000000000 --- a/web/admin/model/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - - -from .initdb import * - -from .logs import addLog,clearLog - -from .option import getOption,getOptionByJson,setOption -from .option import setOption - -from .sites import getSitesCount - -from .task import addTask -from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun,getTaskRunList -from .task import setTaskStatus,setTaskData - -from .user import isLoginCheck -from .user import getUserByName,getUserById,getUserByRoot -from .user import setUserByRoot - -from .firewall import addFirewall - -from .temp_login import getTempLoginByToken,clearTempLogin - - - - - diff --git a/web/admin/model/firewall.py b/web/admin/model/firewall.py deleted file mode 100644 index db7c36939..000000000 --- a/web/admin/model/firewall.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -from admin.model import db, Firewall - -import core.mw as mw - - - -def addFirewall(port, - protocol: str | None = 'tcp', - ps: str | None = '备注' -) -> bool: - ''' - 设置配置的值 - :port -> str 端口 (必填) - :protocol -> str 协议 (可选|tcp,udp,tcp/udp) - :ps -> str 备注 (可选) - ''' - now_time = mw.formatDate() - add_data = Firewall( - port=port, - protocol=protocol, - add_time=now_time, - update_time=now_time) - db.session.add(add_data) - db.session.commit() - return True - - -def getFirewallCountByPort(port): - return Firewall.query.filter(Tasks.port==port).count() - - diff --git a/web/admin/model/initdb.py b/web/admin/model/initdb.py deleted file mode 100644 index 8923feebc..000000000 --- a/web/admin/model/initdb.py +++ /dev/null @@ -1,194 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - - -from flask_sqlalchemy import SQLAlchemy -# from flask_security import UserMixin, RoleMixin -import config - -SCHEMA_VERSION = 1 - - -db = SQLAlchemy( - engine_options={ - 'pool_size': config.CONFIG_DATABASE_CONNECTION_POOL_SIZE, - 'max_overflow': config.CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW, - } -) - - -class Version(db.Model): - """用于参考/升级的版本号""" - __tablename__ = 'version' - name = db.Column(db.String(32), primary_key=True) - value = db.Column(db.Integer(), nullable=False) - -class Role(db.Model): - """定义安全角色""" - __tablename__ = 'role' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.String(128), unique=True, nullable=False) - description = db.Column(db.String(256), nullable=False) - -class Option(db.Model): - """定义类型""" - __tablename__ = 'option' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.String(128), unique=True, nullable=False, comment="配置名称") - type = db.Column(db.String(50), unique=False, nullable=False, default='common',comment="计划类型:common/hook/web") - value = db.Column(db.TEXT, unique=False, nullable=False, comment="内容") - -class Users(db.Model): - """定义登录用户""" - __tablename__ = 'users' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.String(128), unique=True, nullable=False, comment="用户名") - password = db.Column(db.String(128), unique=False, nullable=False, comment="密码") - login_ip = db.Column(db.String(128), unique=False, nullable=True, comment="登录IP") - login_time = db.Column(db.String(128), unique=False, nullable=True, comment="登录时间") - phone = db.Column(db.String(20), unique=False, nullable=False,comment="手机") - email = db.Column(db.String(320), nullable=False, comment="邮件") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class Crontab(db.Model): - """定义计划任务""" - __tablename__ = 'crontab' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=True, nullable=False, comment="任务名称") - type = db.Column(db.String(128), unique=False, nullable=False, comment="计划类型") - where1 = db.Column(db.TEXT, unique=False, nullable=True, comment="执行条件") - where_hour = db.Column(db.TEXT, unique=False, nullable=True, comment="执行条件/小时") - where_minute = db.Column(db.TEXT, unique=False, nullable=True, comment="执行条件/分钟") - echo = db.Column(db.TEXT, unique=False, nullable=True, comment="脚本保存名称") - sname = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="名称") - sbody = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="脚本") - stype = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="脚本类型") - url_address = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="URL访问地址") - backup_to = db.Column(db.TEXT, unique=False, nullable=True, comment="备份地址") - save = db.Column(db.Integer, unique=False, nullable=True, default=3,comment="备份数量") - status = db.Column(db.Integer, unique=False, nullable=True, default=1, comment="状态") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class Logs(db.Model): - """定义日志""" - __tablename__ = 'logs' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - uid = db.Column(db.Integer(), unique=False, nullable=False, comment="用户ID") - type = db.Column(db.String(128), unique=False, nullable=False, comment="日志类型") - log = db.Column(db.TEXT, unique=False, nullable=True, comment="日志内容") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Firewall(db.Model): - """定义防火墙""" - __tablename__ = 'firewall' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") - protocol = db.Column(db.Integer(), unique=False, nullable=False, comment="协议/tcp/udp") - ps = db.Column(db.TEXT, unique=False, nullable=False, comment="备注") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - - -class Backup(db.Model): - """定义备份""" - __tablename__ = 'backup' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - pid = db.Column(db.Integer(), unique=False, nullable=False, comment="父级ID") - type = db.Column(db.String(128), unique=False, nullable=False, comment="备份类型") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="名称") - filename = db.Column(db.TEXT, unique=False, nullable=False, comment="文件绝对位置") - size = db.Column(db.Integer(), unique=False, nullable=False, comment="大小") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Sites(db.Model): - """定义计划任务""" - __tablename__ = 'sites' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="网站名称") - path = db.Column(db.TEXT, unique=False, nullable=False, comment="网站路径") - index = db.Column(db.TEXT, unique=False, nullable=False, comment="") - ps = db.Column(db.TEXT, unique=False, nullable=False, comment="备注") - edate = db.Column(db.TEXT, unique=False, nullable=False, comment="到期时间") - type_id = db.Column(db.TEXT, unique=False, nullable=False, comment="分类ID") - status = db.Column(db.Integer(), unique=False, nullable=True, default=1, comment="状态") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class SiteType(db.Model): - """定义站点类型""" - __tablename__ = 'site_type' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="类型名称") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class Domain(db.Model): - """定义站点域名""" - __tablename__ = 'domain' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - pid = db.Column(db.Integer(), unique=False, nullable=False, comment="站点ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="域名") - port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Binding(db.Model): - """定义站点绑定""" - __tablename__ = 'binding' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - pid = db.Column(db.Integer(), unique=False, nullable=False, comment="站点ID") - domain = db.Column(db.TEXT, unique=False, nullable=False, comment="域名") - path = db.Column(db.TEXT, unique=False, nullable=False, comment="路径") - port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - - -class Tasks(db.Model): - """定义任务""" - __tablename__ = 'tasks' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="任务名称") - type = db.Column(db.TEXT, unique=False, nullable=False, comment="类型") - cmd = db.Column(db.TEXT, unique=False, nullable=False, comment="执行命令") - start = db.Column(db.Integer(), unique=False, nullable=True, comment="开始执行时间") - end = db.Column(db.Integer(), unique=False, nullable=True, comment="结束执行时间") - status = db.Column(db.Integer(), unique=False, nullable=True, default=1, comment="状态") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - - -class TempLogin(db.Model): - """定义临时登录""" - __tablename__ = 'temp_login' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - token = db.Column(db.TEXT, unique=False, nullable=False, comment="token") - salt = db.Column(db.TEXT, unique=False, nullable=False, comment="salt") - state = db.Column(db.Integer(), unique=False, nullable=False, comment="状态") - login_time = db.Column(db.Integer(), unique=False, nullable=True, comment="登录时间") - login_addr = db.Column(db.Integer(), unique=False, nullable=True, comment="登录地址") - logout_time = db.Column(db.Integer(), unique=False, nullable=True, comment="登出时间") - expire = db.Column(db.Integer(), unique=False, nullable=True, comment="过期时间") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Panel(db.Model): - """定义其他面板""" - __tablename__ = 'panel' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - title = db.Column(db.TEXT, unique=False, nullable=False, comment="标题") - url = db.Column(db.TEXT, unique=False, nullable=False, comment="地址") - username = db.Column(db.Integer(), unique=False, nullable=False, comment="用户名") - password = db.Column(db.Integer(), unique=False, nullable=True, comment="密码") - click = db.Column(db.Integer(), unique=False, nullable=True, comment="点击次数") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - - - - - diff --git a/web/admin/model/logs.py b/web/admin/model/logs.py deleted file mode 100644 index 54cda769f..000000000 --- a/web/admin/model/logs.py +++ /dev/null @@ -1,48 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - - -import json -import time - -from admin.model import db, Logs -import core.mw as mw - -def clearLog(): - try: - # from sqlalchemy import text - # db.session.execute(text("DELETE FROM logs where id>1")) - db.session.query(Logs).filter(Logs.id > 0).delete(synchronize_session=False) - db.session.commit() - except Exception as e: - db.session.commit() - finally: - db.session.close() - return True - -def addLog(type, log, - uid: int | None = 1 -) -> str: - ''' - 添加日志 - :type -> str 类型 (必填) - :log -> str 日志内容 (必填) - :uid -> int 用户ID - ''' - now_time = mw.formatDate() - add_logs = Logs( - uid=uid, - log=log, - type=type, - add_time=now_time) - db.session.add(add_logs) - db.session.commit() - db.session.close() - return True \ No newline at end of file diff --git a/web/admin/model/option.py b/web/admin/model/option.py deleted file mode 100644 index cbb277be5..000000000 --- a/web/admin/model/option.py +++ /dev/null @@ -1,74 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - - -import json - -from admin.model import db, Option - -def getOption(name, - type: str | None = 'common', - default : str | None = None -) -> str: - ''' - 获取配置的值 - :name -> str 名称 (必填) - :type -> str 类型 (可选|默认common) - :default -> str 默认值 (可选) - ''' - data = Option.query.filter_by(name=name, type=type).first() - if data is not None: - return data.value - - if default is not None: - return default - return '' - -def getOptionByJson(name, - type: str | None = 'common', - default : object | None = None -) -> object: - ''' - 获取配置的值,返回对象类型 - :name -> str 名称 (必填) - :type -> str 类型 (可选|默认common) - :default -> str 默认值 (可选) - ''' - data = Option.query.filter_by(name=name, type=type).first() - if data is not None: - return json.loads(data.value) - - if default is not None: - return default - return [] - -def setOption(name, value, - type: str | None = 'common' -) -> bool: - ''' - 设置配置的值 - :name -> str 名称 (必填) - :value -> object值 (必填) - :type -> str 类型 (可选|默认common) - ''' - data = Option.query.filter_by(name=name, type=type).first() - if data is None: - add_option = Option( - name=name, - type=type, - value=value) - db.session.add(add_option) - db.session.commit() - db.session.close() - return True - - db.session.query(Option).filter_by(name=name, type=type).update({"value":value}) - db.session.commit() - return True \ No newline at end of file diff --git a/web/admin/model/sites.py b/web/admin/model/sites.py deleted file mode 100644 index dbb54064f..000000000 --- a/web/admin/model/sites.py +++ /dev/null @@ -1,16 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -from admin.model import db, Sites - - -def getSitesCount(): - return Sites.query.count() - diff --git a/web/admin/model/task.py b/web/admin/model/task.py deleted file mode 100644 index c3ba9ccc7..000000000 --- a/web/admin/model/task.py +++ /dev/null @@ -1,135 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -from admin.model import db, Tasks - -import core.mw as mw - -def getTaskCount( - status: int | None = -1 - ) -> int: - return Tasks.query.filter(Tasks.status==status).count() - -# 未执行任务总数 -def getTaskUnexecutedCount() -> int: - return Tasks.query.filter(Tasks.status!=1).count() - - -def addTask( - name: str | None = '常用任务', - cmd: str | None = None, - type: str | None = 'execshell', - status: int | None = 0, -): - ''' - 添加后台任务 - :name -> str 类型 - :cmd -> str 日志内容 (必填) - :type -> str 用户ID - ''' - if cmd is None: - return False - - add_time = mw.formatDate() - add_data = Tasks( - name=name, - cmd=cmd, - type=type, - start=0, - end=0, - status=status, - add_time=add_time) - db.session.add(add_data) - db.session.commit() - db.session.close() - return True - -def getTaskFirstByRun() -> None: - item = Tasks.query.filter(Tasks.status==-1).order_by(Tasks.id.asc()).first() - - if item is None: - return None - row = {} - row['id'] = item.id - row['name'] = item.name - row['type'] = item.type - row['cmd'] = item.cmd - row['start'] = item.start - row['end'] = item.end - row['status'] = item.status - row['add_time'] = item.add_time - return row - -def getTaskList( - status: int | None = 1, - page: int | None = 1, - size: int | None = 10, -): - pagination = Tasks.query.filter(Tasks.status==status).order_by(Tasks.id.asc()).paginate(page=int(page), per_page=int(size)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['name'] = item.name - t['type'] = item.type - t['cmd'] = item.cmd - t['start'] = item.start - t['end'] = item.end - t['status'] = item.status - t['add_time'] = item.add_time - rows.append(t) - return rows - -def getTaskRunList( - page: int | None = 1, - size: int | None = 10, -): - pagination = Tasks.query.filter(Tasks.status!=1).order_by(Tasks.id.asc()).paginate(page=int(page), per_page=int(size)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['name'] = item.name - t['type'] = item.type - t['cmd'] = item.cmd - t['start'] = item.start - t['end'] = item.end - t['status'] = item.status - t['add_time'] = item.add_time - rows.append(t) - return rows - - - - -def setTaskStatus(id, - status: int | None = 0 -): - Tasks.query.filter(Tasks.id==id).update({'status':status}) - db.session.commit() - return True - -def setTaskData(id, - start: int | None = None, - end: int | None = None, -): - update_data = {} - - if start is not None: - update_data['start'] = start - if end is not None: - update_data['end'] = end - - Tasks.query.filter(Tasks.id==id).update(update_data) - db.session.commit() - return True - diff --git a/web/admin/model/temp_login.py b/web/admin/model/temp_login.py deleted file mode 100644 index ac60f45c4..000000000 --- a/web/admin/model/temp_login.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -import time - -from admin.model import db, TempLogin - -import core.mw as mw - -def getTempLoginByToken(token, -) -> None: - ''' - 获取用户信息通过用户名 - ''' - item = TempLogin.query.filter(TempLogin.token==token).first() - if item is None: - return None - row = {} - row['id'] = item.id - row['token'] = item.token - row['salt'] = item.salt - row['state'] = item.state - row['login_time'] = item.login_time - row['login_addr'] = item.login_addr - row['logout_time'] = item.logout_time - row['expire'] = item.expire - row['add_time'] = item.add_time - return row - -def clearTempLogin()->bool: - ''' - 清空过期数据 - ''' - now_time = int(time.time()) - TempLogin.query.filter(TempLogin.expire -# --------------------------------------------------------------------------------- - -from admin.model import db, Users - -import core.mw as mw - -def getUserByName(name, -) -> None: - ''' - 获取用户信息通过用户名 - ''' - item = Users.query.filter(Users.name==name).first() - if item is None: - return None - row = {} - row['id'] = item.id - row['name'] = item.name - row['password'] = item.password - row['login_ip'] = item.login_ip - row['login_time'] = item.login_time - row['phone'] = item.phone - row['email'] = item.email - row['add_time'] = item.add_time - row['update_time'] = item.update_time - return row - -def getUserById(id, -) -> None: - ''' - 获取用户信息通过用户名 - ''' - item = Users.query.filter(Users.id==id).first() - if item is None: - return None - row = {} - row['id'] = item.id - row['name'] = item.name - row['password'] = item.password - row['login_ip'] = item.login_ip - row['login_time'] = item.login_time - row['phone'] = item.phone - row['email'] = item.email - row['add_time'] = item.add_time - row['update_time'] = item.update_time - return row - -def getUserByRoot() -> None: - ''' - 获取用户信息通过用户名 - ''' - return getUserById(1) - -def setUserByRoot( - name: str | None = None, - password: str | None = None, -) -> bool: - ''' - 设置配置的值 - :name -> str 名称 (必填) - :value -> object值 (必填) - :type -> str 类型 (可选|默认common) - ''' - data = {} - - if name is not None: - data['name'] = name - - if password is not None: - data['password'] = mw.md5(password) - - if not data: - return False - - Users.query.filter(Users.id==1).update(data) - db.session.commit() - return True - -def isLoginCheck(username, password) -> bool: - info = getUserByName(data['username']) - if info is None: - return False - - if info['password'] == mw.md5(password): - return True - return False From c9c52cf7f519756895208f76ce82abb7bd8fc01b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:07:08 +0800 Subject: [PATCH 359/965] update --- web/admin/__init__.py | 4 ++-- web/admin/common.py | 1 - web/admin/dashboard/dashboard.py | 1 - web/admin/dashboard/login.py | 4 ++-- web/admin/setting/secondary_verifiy.py | 2 -- web/admin/setting/temp_login.py | 2 -- web/admin/setting/timezone.py | 1 - web/admin/setup/option.py | 2 +- web/admin/setup/user.py | 2 -- web/admin/site/site.py | 1 - web/admin/system/upgrade.py | 2 -- web/admin/task/__init__.py | 2 -- 12 files changed, 5 insertions(+), 19 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 738fe5cfe..c11f3dd19 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -73,7 +73,7 @@ setup.init() # BASIC AUTH app.config['BASIC_AUTH_OPEN'] = False try: - basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) + basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False}) if basic_auth['open']: app.config['BASIC_AUTH_OPEN'] = True except Exception as e: @@ -101,7 +101,7 @@ def requestCheck(): config.APP_START_TIME=time.time() # 自定义basic auth认证 if app.config['BASIC_AUTH_OPEN']: - basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) + basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False}) if not basic_auth['open']: return diff --git a/web/admin/common.py b/web/admin/common.py index cc1be06db..235bbbdac 100644 --- a/web/admin/common.py +++ b/web/admin/common.py @@ -10,7 +10,6 @@ import time -# from admin import model from admin import session import thisdb diff --git a/web/admin/dashboard/dashboard.py b/web/admin/dashboard/dashboard.py index 0fd7b28d8..4fac21077 100644 --- a/web/admin/dashboard/dashboard.py +++ b/web/admin/dashboard/dashboard.py @@ -20,7 +20,6 @@ from flask import request,g from admin.common import isLogined from admin.user_login_check import panel_login_required from admin import cache,session -from admin.model import db,TempLogin,Users import core.mw as mw import thisdb diff --git a/web/admin/dashboard/login.py b/web/admin/dashboard/login.py index 746560ce2..f64837f9d 100644 --- a/web/admin/dashboard/login.py +++ b/web/admin/dashboard/login.py @@ -174,7 +174,7 @@ def do_login(): login_cache_limit = int(login_cache_limit) + 1 if login_cache_limit >= login_cache_count: - model.setOption('admin_close', 'yes') + thisdb.setOption('admin_close', 'yes') return mw.returnJson(False, '面板已经关闭!') cache.set('login_cache_limit', login_cache_limit, timeout=10000) @@ -195,7 +195,7 @@ def do_login(): login_cache_limit = int(login_cache_limit) + 1 if login_cache_limit >= login_cache_count: - model.setOption('admin_close', 'yes') + thisdb.setOption('admin_close', 'yes') return mw.returnData(False, '面板已经关闭!') cache.set('login_cache_limit', login_cache_limit, timeout=10000) diff --git a/web/admin/setting/secondary_verifiy.py b/web/admin/setting/secondary_verifiy.py index 2e170bf56..d7bf70feb 100644 --- a/web/admin/setting/secondary_verifiy.py +++ b/web/admin/setting/secondary_verifiy.py @@ -16,9 +16,7 @@ import time from flask import Blueprint, render_template from flask import request -from admin import model from admin import session -from admin.model import db,TempLogin from admin.user_login_check import panel_login_required import core.mw as mw diff --git a/web/admin/setting/temp_login.py b/web/admin/setting/temp_login.py index 95156b579..6aa7633af 100644 --- a/web/admin/setting/temp_login.py +++ b/web/admin/setting/temp_login.py @@ -16,9 +16,7 @@ import time from flask import Blueprint, render_template from flask import request -from admin import model from admin import session -from admin.model import db,TempLogin from admin.user_login_check import panel_login_required import core.mw as mw diff --git a/web/admin/setting/timezone.py b/web/admin/setting/timezone.py index 246b411c3..4b542d123 100644 --- a/web/admin/setting/timezone.py +++ b/web/admin/setting/timezone.py @@ -15,7 +15,6 @@ import os from flask import Blueprint, render_template from flask import request -from admin import model from admin.user_login_check import panel_login_required import core.mw as mw diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 93a02551a..d6c8c87a5 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -39,7 +39,7 @@ def init_option(): thisdb.setOption('two_step_verification', json.dumps({'open':False})) # 开启后台任务 - # model.setOption('run_bg_task', 'close') + # thisdb.setOption('run_bg_task', 'close') # 首页展示初始化 thisdb.setOption('display_index', '[]') diff --git a/web/admin/setup/user.py b/web/admin/setup/user.py index 7c2092ace..1065ef0c1 100644 --- a/web/admin/setup/user.py +++ b/web/admin/setup/user.py @@ -10,8 +10,6 @@ from flask import request -from admin import model -from admin.model import db, Users import core.mw as mw import thisdb diff --git a/web/admin/site/site.py b/web/admin/site/site.py index b8e1a1bcc..7fc98afe7 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -13,7 +13,6 @@ import os from flask import Blueprint, render_template from flask import request -from admin.model import Sites from admin.user_login_check import panel_login_required from utils.plugin import plugin as MwPlugin diff --git a/web/admin/system/upgrade.py b/web/admin/system/upgrade.py index 82a00684d..cd8b29d38 100644 --- a/web/admin/system/upgrade.py +++ b/web/admin/system/upgrade.py @@ -14,8 +14,6 @@ from flask import request from admin.user_login_check import panel_login_required -from admin import model - import core.mw as mw import utils.system as sys diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index 92465b109..650d4f26e 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -14,8 +14,6 @@ import time from flask import Blueprint, render_template from flask import request -from admin import model -from admin.model import db,Tasks from admin.user_login_check import panel_login_required import core.mw as mw From ab088d9355d39ff73777a07759b517b1d2419388 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:07:30 +0800 Subject: [PATCH 360/965] Update config.py --- web/utils/config.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/utils/config.py b/web/utils/config.py index cbc0ecc90..87ff77f25 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -10,8 +10,6 @@ import os -from admin import model - import core.mw as mw import thisdb From 486361f3b7ce24a835a87d8b55d6a68fdf7b4329 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:08:55 +0800 Subject: [PATCH 361/965] Update config.py --- web/utils/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/config.py b/web/utils/config.py index 87ff77f25..ac7be3a21 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -14,7 +14,7 @@ import core.mw as mw import thisdb def getUnauthStatus( - code: str | None = '0' + code= '0' ): code = str(code) data = {} From bf0269a7da6184b96a8781518e4c286ce5974666 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:11:58 +0800 Subject: [PATCH 362/965] Update plugin.py --- web/utils/plugin.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index c24d085a4..8bbc710d8 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -222,7 +222,7 @@ class plugin(object): return False def install(self, name, version, - upgrade: bool | None = None + upgrade = None ): if name.strip() == '': return mw.returnData(False, '缺少插件名称!', ()) @@ -283,7 +283,7 @@ class plugin(object): # 插件搜索匹配 def searchKey(self, info, - keyword: str | None = None, + keyword = None, ): if keyword == None: return True @@ -456,8 +456,8 @@ class plugin(object): def getPluginList(self, name, - keyword: str | None = None, - type: str | None = None, + keyword = None, + type = None, ): info = [] path = self.__plugin_dir + '/' + name @@ -513,10 +513,10 @@ class plugin(object): def getAllPluginList( self, - type: str | None = None, - keyword: str | None = None, - page: int | None = 1, - size: int | None = 10, + type = None, + keyword = None, + page = 1, + size = 10, ): info = [] @@ -539,10 +539,10 @@ class plugin(object): def getList( self, - type: str | None = None, - keyword: str | None = None, - page: int | None = 1, - size: int | None = 10, + type = None, + keyword = None, + page = 1, + size = 10, ) -> object: # print(type,keyword,page,size) rdata = {} @@ -555,9 +555,9 @@ class plugin(object): # shell/bash方式调用 def run(self, name, func, - version: str | None = '', - args: str | None = '', - script: str | None = 'index', + version = '', + args = '', + script = 'index', ): path = self.__plugin_dir + '/' + name + '/' + script + '.py' @@ -583,8 +583,8 @@ class plugin(object): # 映射包调用 def callback(self, name, func, - args: str | None = '', - script: str | None = 'index', + args = '', + script = 'index', ): package = self.__plugin_dir + '/' + name From 06db6b02d814b97d73174f6f92ee92b432f0d5e7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:13:28 +0800 Subject: [PATCH 363/965] Update plugin.py --- web/utils/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index 8bbc710d8..504977834 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -439,7 +439,7 @@ class plugin(object): # 对多版本共存进行处理 def makeCoexistList(self, data, - plugin_type: str | None = None, + plugin_type = None, ): plugins_t = [] # 返回指定类型 From db1dfb9aedb6c7477c70648b3ec8711a26abd649 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:15:08 +0800 Subject: [PATCH 364/965] Update crontab.py --- web/utils/crontab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/utils/crontab.py b/web/utils/crontab.py index e308b7a5e..68fbbb82d 100644 --- a/web/utils/crontab.py +++ b/web/utils/crontab.py @@ -274,8 +274,8 @@ class crontab(object): return True def getCrontabList(self, - page:int | None = 1, - size:int | None = 10 + page = 1, + size = 10 ): info = thisdb.getCrontabList(page=int(page),size=int(size)) From aa5ffdc3c0c518c65d40c1d116c7e7f67eb5b59a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:17:01 +0800 Subject: [PATCH 365/965] Update mw.tpl --- scripts/init.d/mw.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 05df11e28..62392e727 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -523,10 +523,10 @@ mw_default(){ port=$(cat ${PANEL_DIR}/data/port.pl) fi - if [ ! -f ${PANEL_DIR}/data/default.pl ];then - echo -e "\033[33mInstall Failed\033[0m" - exit 1 - fi + # if [ ! -f ${PANEL_DIR}/data/default.pl ];then + # echo -e "\033[33mInstall Failed\033[0m" + # exit 1 + # fi password=$(cat ${PANEL_DIR}/data/default.pl) From 3994d8f0ce15f9c88b0f767ddf3af0cf9bbfe0f1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:21:45 +0800 Subject: [PATCH 366/965] update --- data/README.md | 4 ++++ ssl/README.md | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 data/README.md diff --git a/data/README.md b/data/README.md new file mode 100644 index 000000000..bc8dc9fc5 --- /dev/null +++ b/data/README.md @@ -0,0 +1,4 @@ +# 自动生成 + +certificate.pem - 证书信息 +privateKey.pem - 私钥 \ No newline at end of file diff --git a/ssl/README.md b/ssl/README.md index bc8dc9fc5..39c380ce8 100644 --- a/ssl/README.md +++ b/ssl/README.md @@ -1,4 +1 @@ -# 自动生成 - -certificate.pem - 证书信息 -privateKey.pem - 私钥 \ No newline at end of file +# 数据目录 \ No newline at end of file From 7a1b30994b790995baf5ee64be22c47c36c9d605 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:47:23 +0800 Subject: [PATCH 367/965] update --- web/admin/setting/app.py | 13 ++++++++----- web/admin/setup/option.py | 2 +- web/setting.py | 4 ++-- web/templates/default/setting.html | 2 +- web/utils/config.py | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/web/admin/setting/app.py b/web/admin/setting/app.py index 5b025a3e4..273e19ce8 100644 --- a/web/admin/setting/app.py +++ b/web/admin/setting/app.py @@ -29,12 +29,15 @@ import thisdb @blueprint.route('/set_panel_api', endpoint='set_panel_api', methods=['POST']) @panel_login_required def set_panel_api(): - admin_api = thisdb.getOption('api', default='no') - if admin_api == 'no': - thisdb.setOption('api', 'yes') + panel_api = thisdb.getOptionByJson('panel_api', default={'open':False}) + if not panel_api['open']: + panel_api['open'] = True + thisdb.setOption('panel_api', json.dumps(panel_api)) return mw.returnData(True, '开启API成功!') - thisdb.setOption('api', 'no') - return mw.returnData(True, '开启API成功!') + else: + panel_api['open'] = False + thisdb.setOption('panel_api', json.dumps(panel_api)) + return mw.returnData(True, '关闭API成功!') # 获取APP列表 diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index d6c8c87a5..7542f3791 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -53,7 +53,7 @@ def init_option(): thisdb.setOption('admin_path', mw.getRandomString(8)) # API是否开启|默认关闭 - thisdb.setOption('api', 'no') + thisdb.setOption('panel_api', json.dumps({'open':False})) # 获取服务器IP ip = mw.getLocalIp() diff --git a/web/setting.py b/web/setting.py index d0c09281e..500849760 100755 --- a/web/setting.py +++ b/web/setting.py @@ -45,8 +45,8 @@ if os.path.exists(default_port_file): else: from utils.firewall import Firewall as MwFirewall mw_port = str(random.randint(10000, 65530)) - MwFirewall.instance().addAcceptPort(panel_port, 'PANEL端口', 'port') - mw.writeFile(default_port_file, panel_port) + MwFirewall.instance().addAcceptPort(mw_port, 'PANEL端口', 'port') + mw.writeFile(default_port_file, mw_port) bind = [] default_ipv6_file = panel_dir+'/data/ipv6.pl' diff --git a/web/templates/default/setting.html b/web/templates/default/setting.html index 1c5edea6c..9a123904a 100755 --- a/web/templates/default/setting.html +++ b/web/templates/default/setting.html @@ -129,7 +129,7 @@

                    API接口 - + 提供面板API接口访问的支持 diff --git a/web/utils/config.py b/web/utils/config.py index ac7be3a21..ee081e86b 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -81,9 +81,9 @@ def getGlobalVar(): # 邮件通知设置 data['notify_email'] = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') data['notify_tgbot'] = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') - + + data['panel_api'] = thisdb.getOptionByJson('panel_api', default={'open':False}) data['panel_ssl'] = thisdb.getOptionByJson('panel_ssl', default={'open':False}) data['panel_domain'] = thisdb.getOption('panel_domain', default='') - return data \ No newline at end of file From 7d2ca4ea744553ae08b3a7efe46dc5558a936ef0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:53:02 +0800 Subject: [PATCH 368/965] Update user.py --- web/thisdb/user.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/thisdb/user.py b/web/thisdb/user.py index 20fa5ff09..56bd24328 100644 --- a/web/thisdb/user.py +++ b/web/thisdb/user.py @@ -18,17 +18,17 @@ def initAdminUser(): if data is None: name = mw.getRandomString(8).lower() password = mw.getRandomString(8).lower() - insert_time = mw.formatDate() + now_time = mw.formatDate() login_ip = '127.0.0.1' add_user = { 'name':name, 'password':mw.md5(password), 'login_ip':login_ip, - 'login_time':insert_time, + 'login_time':now_time, 'phone':'', 'email':'', - 'add_time':insert_time, - 'update_time':insert_time + 'add_time':now_time, + 'update_time':now_time } file_pass_pl = mw.getPanelDataDir() + '/default.pl' mw.writeFile(file_pass_pl, password) From da0e599bad7736136207e028f5a1ab951ca0ab8c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 02:56:43 +0800 Subject: [PATCH 369/965] Update mw.py --- web/core/mw.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index cf60c8222..9369abed7 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -463,16 +463,16 @@ def checkDomainPanel(): if client_ip in ['127.0.0.1', 'localhost', '::1']: return False - ip = getHostAddr() - if domain == '': - if current_host.strip().lower() != ip.strip().lower(): - to = scheme + "://" + ip + ":" + str(port) - return redirect(to, code=302) - return False - else: - if current_host.strip().lower() != domain.strip().lower(): - to = scheme + "://" + domain + ":" + str(port) - return redirect(to, code=302) + # ip = getHostAddr() + # if domain == '': + # if current_host.strip().lower() != ip.strip().lower(): + # to = scheme + "://" + ip + ":" + str(port) + # return redirect(to, code=302) + # return False + # else: + # if current_host.strip().lower() != domain.strip().lower(): + # to = scheme + "://" + domain + ":" + str(port) + # return redirect(to, code=302) return False def getLocalIp(): From 0c4460282ad3c6b5f1f0f9ec6d319a9848c1cba1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 03:01:15 +0800 Subject: [PATCH 370/965] Update default.sql --- web/admin/setup/sql/default.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index a9b8cb531..a3b4216c7 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -47,10 +47,10 @@ CREATE TABLE IF NOT EXISTS `firewall` ( `add_time` TEXT, `update_time` TEXT ); -CREATE UNIQUE INDEX port_idx ON firewall(port); +-- CREATE UNIQUE INDEX port_idx ON firewall(port); INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`, `update_time`) VALUES -(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), +(1, '80', 'tcp', '网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), (2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00','0000-00-00 00:00:00'); CREATE TABLE IF NOT EXISTS `logs` ( From 2d892575f300ae826c959ac39dea69e97f1659b1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 03:17:24 +0800 Subject: [PATCH 371/965] Update default.sql --- web/admin/setup/sql/default.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index a3b4216c7..5d8bf40ed 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -47,7 +47,8 @@ CREATE TABLE IF NOT EXISTS `firewall` ( `add_time` TEXT, `update_time` TEXT ); --- CREATE UNIQUE INDEX port_idx ON firewall(port); + +CREATE UNIQUE INDEX port_idx ON firewall(port); INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`, `update_time`) VALUES (1, '80', 'tcp', '网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), From e145e2c57fef70e2246670ef5b50a4ec48d0cab0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 03:27:04 +0800 Subject: [PATCH 372/965] Update default.sql --- web/admin/setup/sql/default.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index 5d8bf40ed..2e2c60eff 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -37,7 +37,7 @@ CREATE TABLE IF NOT EXISTS `crontab` ( `add_time` TEXT, `update_time` TEXT ); -CREATE UNIQUE INDEX name_idx ON crontab(name); +CREATE UNIQUE INDEX crontab_name_idx ON crontab(name); CREATE TABLE IF NOT EXISTS `firewall` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -48,7 +48,7 @@ CREATE TABLE IF NOT EXISTS `firewall` ( `update_time` TEXT ); -CREATE UNIQUE INDEX port_idx ON firewall(port); +CREATE UNIQUE INDEX firewall_port_idx ON firewall(port); INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`, `update_time`) VALUES (1, '80', 'tcp', '网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), @@ -76,13 +76,13 @@ CREATE TABLE IF NOT EXISTS `sites` ( `add_time` TEXT, `update_time` TEXT ); -CREATE UNIQUE INDEX name_idx ON sites(name); +CREATE UNIQUE INDEX sites_name_idx ON sites(name); CREATE TABLE IF NOT EXISTS `site_types` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT ); -CREATE UNIQUE INDEX name_idx ON site_types(name); +CREATE UNIQUE INDEX site_types_name_idx ON site_types(name); CREATE TABLE IF NOT EXISTS `domain` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -147,7 +147,7 @@ CREATE TABLE IF NOT EXISTS `app` ( `add_time` TEXT, `update_time` TEXT ); -CREATE UNIQUE INDEX name_idx ON app(name); +CREATE UNIQUE INDEX app_name_idx ON app(name); CREATE UNIQUE INDEX app_id_idx ON app(app_id); CREATE TABLE IF NOT EXISTS `option` ( @@ -156,4 +156,4 @@ CREATE TABLE IF NOT EXISTS `option` ( `type` TEXT, `value` TEXT ); -CREATE UNIQUE INDEX name_idx ON option(name); +CREATE UNIQUE INDEX option_name_idx ON option(name); From e4c7a0470176eaf4ae751de5a226c3730e60493b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 03:31:14 +0800 Subject: [PATCH 373/965] Update init.py --- web/thisdb/init.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index fac16d6bf..20654721b 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -19,8 +19,10 @@ def initPanelData(): return True sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') - csql = mw.readFile(sql_file) - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) + content = mw.readFile(sql_file) + csql_data = content.split(';') + for i in range(len(csql_data)): + # print(csql_data[i]) + # print(sql.execute(csql_data[i], ())) + sql.execute(csql_data[i], ()) return True \ No newline at end of file From e8d1e6e1ebd13505c473a94b92ff209bc5cb92c7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 03:45:31 +0800 Subject: [PATCH 374/965] Update system.sql --- web/admin/setup/sql/system.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/admin/setup/sql/system.sql b/web/admin/setup/sql/system.sql index 75fe9557f..31ed453ee 100755 --- a/web/admin/setup/sql/system.sql +++ b/web/admin/setup/sql/system.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS `network` ( `up_packets` INTEGER, `addtime` INTEGER ); -CREATE INDEX addtime_idx ON network(`addtime`); +CREATE INDEX network_addtime_idx ON network(`addtime`); CREATE TABLE IF NOT EXISTS `cpuio` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS `cpuio` ( `mem` TEXT, `addtime` INTEGER ); -CREATE INDEX addtime_idx ON cpuio(`addtime`); +CREATE INDEX cpuio_addtime_idx ON cpuio(`addtime`); CREATE TABLE IF NOT EXISTS `diskio` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS `diskio` ( `write_time` INTEGER, `addtime` INTEGER ); -CREATE INDEX addtime_idx ON diskio(`addtime`); +CREATE INDEX diskio_addtime_idx ON diskio(`addtime`); CREATE TABLE IF NOT EXISTS `load_average` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -38,4 +38,4 @@ CREATE TABLE IF NOT EXISTS `load_average` ( `fifteen` REAL, `addtime` INTEGER ); -CREATE INDEX addtime_idx ON load_average(`addtime`); \ No newline at end of file +CREATE INDEX load_average_addtime_idx ON load_average(`addtime`); \ No newline at end of file From 50eb0137a4ecf4a0ccbd01a3e7ad7d12684d350f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 04:13:19 +0800 Subject: [PATCH 375/965] Update __init__.py --- web/admin/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index c11f3dd19..0da237640 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -37,6 +37,8 @@ import core.mw as mw import config import utils.config as utils_config import thisdb +# 初始化db +setup.init() # from threading import Lock # thread = None @@ -67,9 +69,6 @@ app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) # app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 SQLite 数据库 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True -# 初始化db -setup.init() - # BASIC AUTH app.config['BASIC_AUTH_OPEN'] = False try: From aef3c5d69410fc6c7c65df77ca6a99ad883e83e1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 04:15:28 +0800 Subject: [PATCH 376/965] Update init.py --- web/thisdb/init.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 20654721b..1f6050df9 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -14,9 +14,9 @@ import core.mw as mw def initPanelData(): - _dbfile = mw.getPanelDataDir() + '/panel.db' - if os.path.exists(_dbfile): - return True + # _dbfile = mw.getPanelDataDir() + '/panel.db' + # if os.path.exists(_dbfile): + # return True sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') content = mw.readFile(sql_file) From 17d46958f86002d4669d050c805cc9d63f489530 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 04:35:55 +0800 Subject: [PATCH 377/965] Update panel_tools.py --- panel_tools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panel_tools.py b/panel_tools.py index 8697b747b..6e2f536c5 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -27,6 +27,9 @@ from utils.firewall import Firewall as MwFirewall import core.mw as mw import thisdb +from admin import setup +setup.init() + INIT_DIR = "/etc/rc.d/init.d" if mw.isAppleSystem(): From c3ef396728ead4c945e6070c4c4cfc5968d05dab Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 04:40:29 +0800 Subject: [PATCH 378/965] Update panel_tools.py --- panel_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index 6e2f536c5..888886b7a 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -27,8 +27,8 @@ from utils.firewall import Firewall as MwFirewall import core.mw as mw import thisdb -from admin import setup -setup.init() +# from admin import setup +# setup.init() INIT_DIR = "/etc/rc.d/init.d" From d25993e78b2ee27bba05fe406240a2971b82fd0a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 04:48:39 +0800 Subject: [PATCH 379/965] update --- web/admin/plugins/__init__.py | 35 +++++----------------- web/thisdb/init.py | 6 ++-- web/utils/plugin.py | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py index e35a9fb9b..8d7c72fb5 100644 --- a/web/admin/plugins/__init__.py +++ b/web/admin/plugins/__init__.py @@ -34,35 +34,14 @@ def index(): @blueprint.route('/init', endpoint='init', methods=['POST']) @panel_login_required def init(): - plugin_names = { - 'openresty': '1.25.3', - 'php': '56', - 'swap': '1.1', - 'mysql': '5.7', - 'phpmyadmin': '4.4.15', - } + return MwPlugin.instance().init() - pn_dir = mw.getPluginDir() - pn_server_dir = mw.getServerDir() - pn_list = [] - for pn in plugin_names: - info = {} - pn_json = pn_dir + '/' + pn + '/info.json' - pn_server = pn_server_dir + '/' + pn - if not os.path.exists(pn_server): - - tmp = mw.readFile(pn_json) - tmp = json.loads(tmp) - - info['title'] = tmp['title'] - info['name'] = tmp['name'] - info['versions'] = tmp['versions'] - info['default_ver'] = plugin_names[pn] - pn_list.append(info) - else: - return mw.returnData(False, 'ok') - - return mw.returnData(True, 'ok', pn_list) +# 初始化安装 +@blueprint.route('/init_install', endpoint='init_install', methods=['POST']) +@panel_login_required +def init_install(): + plugin_list = request.form.get('list', '') + return MwPlugin.instance().initInstall(plugin_list) # 首页软件展示 @blueprint.route('/index_list', endpoint='index_list', methods=['GET','POST']) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 1f6050df9..20654721b 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -14,9 +14,9 @@ import core.mw as mw def initPanelData(): - # _dbfile = mw.getPanelDataDir() + '/panel.db' - # if os.path.exists(_dbfile): - # return True + _dbfile = mw.getPanelDataDir() + '/panel.db' + if os.path.exists(_dbfile): + return True sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') content = mw.readFile(sql_file) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index 504977834..ee2b89a46 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -132,6 +132,61 @@ class plugin(object): plist = self.checkStatusMThreads(plist) return plist + def init(self): + plugin_names = { + 'openresty': '1.25.3', + 'php': '56', + 'swap': '1.1', + 'mysql': '5.7', + 'phpmyadmin': '4.4.15', + } + + pn_dir = mw.getPluginDir() + pn_server_dir = mw.getServerDir() + pn_list = [] + for pn in plugin_names: + info = {} + pn_json = pn_dir + '/' + pn + '/info.json' + pn_server = pn_server_dir + '/' + pn + if not os.path.exists(pn_server): + + tmp = mw.readFile(pn_json) + tmp = json.loads(tmp) + + info['title'] = tmp['title'] + info['name'] = tmp['name'] + info['versions'] = tmp['versions'] + info['default_ver'] = plugin_names[pn] + pn_list.append(info) + else: + return mw.returnData(False, 'ok') + + return mw.returnData(True, 'ok', pn_list) + + def initInstall(self, plugin_list): + try: + pn_list = json.loads(plugin_list) + for pn in pn_list: + name = pn['name'] + version = pn['version'] + info_file = self.__plugin_dir + '/' + name + '/' + 'info.json' + pluginInfo = json.loads(mw.readFile(info_file)) + self.hookInstall(pluginInfo) + + cmd = 'cd {0} && bash {1} install {2}'.format( + mw.getPluginDir() + '/'+name, + pluginInfo['shell'], + version + ) + title = '安装[' + name + '-' + version + ']' + thisdb.addTask(name=title,cmd=cmd) + os.mkdir(mw.getServerDir() + '/php') + # 任务执行相关 + mw.triggerTask() + return mw.returnData(True, '添加成功') + except Exception as e: + return mw.returnData(False, mw.getTracebackInfo()) + def menuGetAbsPath(self, tag, path): if path[0:1] == '/': return path From 293c902a805ca57bb93bee87b00d5db86c6bb3ae Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 05:23:05 +0800 Subject: [PATCH 380/965] Update init.py --- web/thisdb/init.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 20654721b..1f6050df9 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -14,9 +14,9 @@ import core.mw as mw def initPanelData(): - _dbfile = mw.getPanelDataDir() + '/panel.db' - if os.path.exists(_dbfile): - return True + # _dbfile = mw.getPanelDataDir() + '/panel.db' + # if os.path.exists(_dbfile): + # return True sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') content = mw.readFile(sql_file) From a44873b142edfdf105f91e79aa5d547ba466ea66 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 05:48:44 +0800 Subject: [PATCH 381/965] update --- web/admin/__init__.py | 7 ++++--- web/app.py | 4 ++-- web/setting.py | 6 +++++- web/thisdb/init.py | 6 +++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 0da237640..82d7fde1b 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -30,14 +30,16 @@ from flask_migrate import Migrate from flask_caching import Cache from werkzeug.local import LocalProxy -from admin import setup + from admin.common import isLogined import core.mw as mw import config import utils.config as utils_config import thisdb + # 初始化db +from admin import setup setup.init() # from threading import Lock @@ -77,8 +79,6 @@ try: app.config['BASIC_AUTH_OPEN'] = True except Exception as e: pass - - # 加载模块 from .submodules import get_submodules @@ -178,6 +178,7 @@ def webssh(data): shell.run(data) return + # File logging logger = logging.getLogger('werkzeug') logger.setLevel(config.CONSOLE_LOG_LEVEL) diff --git a/web/app.py b/web/app.py index a6f5f7610..273bc6179 100644 --- a/web/app.py +++ b/web/app.py @@ -17,8 +17,8 @@ import config from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler -# from gevent import monkey -# monkey.patch_all() +import eventlet +eventlet.monkey_patch(socket=True, select=True) if sys.version_info < (3, 6): diff --git a/web/setting.py b/web/setting.py index 500849760..82827b9f3 100755 --- a/web/setting.py +++ b/web/setting.py @@ -23,6 +23,10 @@ import core.mw as mw import utils.system as system import thisdb +# 初始化db +from admin import setup +setup.init() + cpu_info = system.getCpuInfo() workers = cpu_info[1] @@ -73,7 +77,7 @@ if workers > 2: threads = workers * 1 backlog = 512 reload = False -daemon = True +daemon = False # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' worker_class = 'eventlet' timeout = 600 diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 1f6050df9..20654721b 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -14,9 +14,9 @@ import core.mw as mw def initPanelData(): - # _dbfile = mw.getPanelDataDir() + '/panel.db' - # if os.path.exists(_dbfile): - # return True + _dbfile = mw.getPanelDataDir() + '/panel.db' + if os.path.exists(_dbfile): + return True sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') content = mw.readFile(sql_file) From 4078f4c67bc5fc74ee73a96cfd5e80863971bf4c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 05:50:15 +0800 Subject: [PATCH 382/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index 82827b9f3..62a46f293 100755 --- a/web/setting.py +++ b/web/setting.py @@ -77,7 +77,7 @@ if workers > 2: threads = workers * 1 backlog = 512 reload = False -daemon = False +daemon = True # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' worker_class = 'eventlet' timeout = 600 From 0e6ea7cb7edd1bbd1a274440538502c857c44bed Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 06:05:54 +0800 Subject: [PATCH 383/965] Update setting.py --- web/setting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/setting.py b/web/setting.py index 62a46f293..84fb630df 100755 --- a/web/setting.py +++ b/web/setting.py @@ -48,9 +48,9 @@ if os.path.exists(default_port_file): panel_port.strip() else: from utils.firewall import Firewall as MwFirewall - mw_port = str(random.randint(10000, 65530)) - MwFirewall.instance().addAcceptPort(mw_port, 'PANEL端口', 'port') - mw.writeFile(default_port_file, mw_port) + panel_port = str(random.randint(10000, 65530)) + MwFirewall.instance().addAcceptPort(panel_port, 'PANEL端口', 'port') + mw.writeFile(default_port_file, panel_port) bind = [] default_ipv6_file = panel_dir+'/data/ipv6.pl' From 5a800ac1b22cf03cfa405f82e376c58d67f7b472 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 15:52:17 +0800 Subject: [PATCH 384/965] Update gd_old.sh --- plugins/php/versions/common/gd_old.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/php/versions/common/gd_old.sh b/plugins/php/versions/common/gd_old.sh index 6cc7ed365..7f4badb82 100755 --- a/plugins/php/versions/common/gd_old.sh +++ b/plugins/php/versions/common/gd_old.sh @@ -89,8 +89,8 @@ Install_lib() --with-png-dir \ --with-webp-dir \ --with-zlib-dir \ - --enable-gd-jis-conv - # --with-freetype-dir=${serverPath}/lib/freetype_old + --enable-gd-jis-conv \ + --with-freetype-dir=${serverPath}/lib/freetype_old # --enable-gd-native-ttf make clean && make && make install && make clean From c4c3c1659c3c09bef5d53c83100260515c38885f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 15:59:25 +0800 Subject: [PATCH 385/965] update --- plugins/php/versions/56/install.sh | 2 +- plugins/php/versions/all_test.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/php/versions/56/install.sh b/plugins/php/versions/56/install.sh index be2fef3da..d9c5fcf18 100755 --- a/plugins/php/versions/56/install.sh +++ b/plugins/php/versions/56/install.sh @@ -18,7 +18,7 @@ PHP_VER=56 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php diff --git a/plugins/php/versions/all_test.sh b/plugins/php/versions/all_test.sh index 98623465a..29f9dd8fd 100644 --- a/plugins/php/versions/all_test.sh +++ b/plugins/php/versions/all_test.sh @@ -8,7 +8,7 @@ ROOT_DIR=$(cd "$(dirname "$0")"; pwd) # cd /www/server/mdserver-web/scripts/quick && bash debug.sh # cd /www/server/mdserver-web/plugins/php/versions && /bin/bash all_test.sh -# cd /www/server/mdserver-web/plugins/php && bash install.sh install 82 +# cd /www/server/mdserver-web/plugins/php && bash install.sh install 56 # cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 73 # cd /www/server/mdserver-web/plugins/php/versions/common && bash swoole.sh install 54 @@ -28,7 +28,7 @@ ROOT_DIR=$(cd "$(dirname "$0")"; pwd) # echo "php${PHP_VER} -- end" -PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83) +PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84) # PHP_VER_LIST=(81) for PHP_VER in ${PHP_VER_LIST[@]}; do echo "php${PHP_VER} -- start" @@ -39,7 +39,7 @@ for PHP_VER in ${PHP_VER_LIST[@]}; do done cd $DIR -PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83) +PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84) # yar PHP_EXT_LIST=(ZendGuardLoader pdo mysqlnd sqlite3 openssl opcache mcrypt fileinfo \ exif gd intl pcntl memcache memcached redis imagemagick xdebug \ From 58fafd6233ec85b8b3f7d08057ae71352afbe0de Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 18:09:17 +0800 Subject: [PATCH 386/965] Update all_test.sh --- plugins/php/versions/all_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php/versions/all_test.sh b/plugins/php/versions/all_test.sh index 29f9dd8fd..259c86ee5 100644 --- a/plugins/php/versions/all_test.sh +++ b/plugins/php/versions/all_test.sh @@ -8,7 +8,7 @@ ROOT_DIR=$(cd "$(dirname "$0")"; pwd) # cd /www/server/mdserver-web/scripts/quick && bash debug.sh # cd /www/server/mdserver-web/plugins/php/versions && /bin/bash all_test.sh -# cd /www/server/mdserver-web/plugins/php && bash install.sh install 56 +# cd /www/server/mdserver-web/plugins/php && bash install.sh install 82 # cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 73 # cd /www/server/mdserver-web/plugins/php/versions/common && bash swoole.sh install 54 From eb925d004e36c803b4bbd2822c338449dfcb9146 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 20:28:10 +0800 Subject: [PATCH 387/965] Create backup.py --- web/thisdb/backup.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 web/thisdb/backup.py diff --git a/web/thisdb/backup.py b/web/thisdb/backup.py new file mode 100644 index 000000000..5b03532cc --- /dev/null +++ b/web/thisdb/backup.py @@ -0,0 +1,30 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +# 站点-子目录绑定 + +__FIELD = 'id,type,name,pid,filename,size,add_time' + +def getBackupPage( + site_id = 1, + page = 1, + size = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + bk_list = mw.M('backup').where('pid=?', (site_id,)).field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('backup').where('pid=?', (site_id,)).count() + + rdata = {} + rdata['list'] = bk_list + rdata['count'] = count + return rdata From 4edae5114ef249daeff64b08572c97aa781974b6 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 20:28:15 +0800 Subject: [PATCH 388/965] Update site.py --- web/utils/site.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/utils/site.py b/web/utils/site.py index b2e0df56c..90f9789b2 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -2344,6 +2344,16 @@ export PATH mw.restartWeb() return mw.returnData(data['status'], data['msg'], result) + def getBackup(self,site_id,page=1,size=10): + site_info = thisdb.getSitesById(site_id) + info = thisdb.getBackupPage(site_id, page, size) + + data = {} + data['data'] = info['list'] + data['site'] = site_info + data['page'] = mw.getPage({'count':info['count'],'tojs':'getBackup','p':page, 'row':limit}) + return data + def getPhpVersion(self): phpVersions = ('00', '52', '53', '54', '55', From d950501ec6d50102e860454d4d4911560e0e0454 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 20:28:19 +0800 Subject: [PATCH 389/965] Create backup.py --- web/admin/site/backup.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 web/admin/site/backup.py diff --git a/web/admin/site/backup.py b/web/admin/site/backup.py new file mode 100644 index 000000000..e543d81f4 --- /dev/null +++ b/web/admin/site/backup.py @@ -0,0 +1,38 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + +# 设置默认站 +@blueprint.route('/get_backup', endpoint='get_backup',methods=['POST']) +@panel_login_required +def get_backup(): + limit = request.form.get('limit', '') + page = request.form.get('p', '') + site_id = request.form.get('search', '') + return MwSites.instance().getBackup(site_id,page=page,size=limit) + + + + + From ccb9d678f683afc40cf29eecbfd355455c06cb33 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 20:28:23 +0800 Subject: [PATCH 390/965] Update __init__.py --- web/admin/site/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 31844ce93..d1128d871 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -12,6 +12,8 @@ from .site import * from .site_types import * from .site_default import * +from .backup import * + from .php import * from .logs import * from .dir import * From 85c62dd51afef9538b3583f3b6445e6953d85f08 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 27 Nov 2024 23:59:56 +0800 Subject: [PATCH 391/965] update --- web/admin/setup/sql/default.sql | 2 +- web/admin/site/backup.py | 18 ++++++++++++++- web/static/app/site.js | 30 ++++++++++++------------ web/thisdb/__init__.py | 2 ++ web/thisdb/backup.py | 30 +++++++++++++++++------- web/utils/site.py | 41 +++++++++++++++++++++++++++++---- 6 files changed, 93 insertions(+), 30 deletions(-) diff --git a/web/admin/setup/sql/default.sql b/web/admin/setup/sql/default.sql index 2e2c60eff..b4011cf77 100755 --- a/web/admin/setup/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -1,9 +1,9 @@ CREATE TABLE IF NOT EXISTS `backup` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `pid` INTEGER, `type` INTEGER, `name` TEXT, - `pid` INTEGER, `filename` TEXT, `size` INTEGER, `add_time` TEXT diff --git a/web/admin/site/backup.py b/web/admin/site/backup.py index e543d81f4..72b978fcf 100644 --- a/web/admin/site/backup.py +++ b/web/admin/site/backup.py @@ -23,7 +23,7 @@ import thisdb from .site import blueprint -# 设置默认站 +# 获取备份列表 @blueprint.route('/get_backup', endpoint='get_backup',methods=['POST']) @panel_login_required def get_backup(): @@ -32,6 +32,22 @@ def get_backup(): site_id = request.form.get('search', '') return MwSites.instance().getBackup(site_id,page=page,size=limit) +# 设置备份 +@blueprint.route('/to_backup', endpoint='to_backup',methods=['POST']) +@panel_login_required +def to_backup(): + site_id = request.form.get('id', '') + return MwSites.instance().toBackup(site_id) + +# 删除备份 +@blueprint.route('/del_backup', endpoint='del_backup',methods=['POST']) +@panel_login_required +def del_backup(): + site_id = request.form.get('id', '') + return MwSites.instance().delBackup(site_id) + + + diff --git a/web/static/app/site.js b/web/static/app/site.js index ff10cea86..50baefff4 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -803,7 +803,7 @@ function getBackup(id,name,page) { var ftpdown = "下载 | "; body += ""+frdata.data[i].name+"\ " + (toSize(frdata.data[i].size)) + "\ - " + frdata.data[i].addtime + "\ + " + frdata.data[i].add_time + "\ "+ ftpdown + "删除\ " } @@ -813,9 +813,9 @@ function getBackup(id,name,page) { if(name == 0){ var sBody = "\ - \ - "+body+"\ -
                    文件名称文件大小打包时间操作
                    " + 文件名称文件大小打包时间操作\ + "+body+"\ + " $("#webBackupList").html(sBody); $(".page").html(frdata.page); return; @@ -830,17 +830,17 @@ function getBackup(id,name,page) { shift: 0, shadeClose: false, content: "

                    \ - \ -
                    \ - \ - \ - \ - \ - " + body + "\ -
                    文件名称文件大小打包时间操作
                    \ -
                    " + frdata.page + "
                    \ -
                    \ -
                    " + \ +
                    \ + \ + \ + \ + \ + " + body + "\ +
                    文件名称文件大小打包时间操作
                    \ +
                    " + frdata.page + "
                    \ +
                    \ +
                    " }); },'json'); } diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py index 33f664b8b..41674e2d2 100644 --- a/web/thisdb/__init__.py +++ b/web/thisdb/__init__.py @@ -13,6 +13,8 @@ from .option import * from .sites import * from .site_types import * +from .backup import * + from .domain import * from .binding import * diff --git a/web/thisdb/backup.py b/web/thisdb/backup.py index 5b03532cc..961498b82 100644 --- a/web/thisdb/backup.py +++ b/web/thisdb/backup.py @@ -10,16 +10,27 @@ import core.mw as mw -# 站点-子目录绑定 +__FIELD = 'id,pid,type,name,filename,size,add_time' -__FIELD = 'id,type,name,pid,filename,size,add_time' +def addBackup(pid,name,filename,size,type=0): -def getBackupPage( - site_id = 1, - page = 1, - size = 10, -): - start = (page - 1) * size + add_time = mw.formatDate() + insert_data = { + 'type':type, + 'name':name, + 'pid':pid, + 'filename':filename, + 'size':size, + 'add_time':add_time, + } + mw.M('backup').insert(insert_data) + return True + +def getBackupById(bp_id): + return mw.M('backup').field(__FIELD).where("id=?", (bp_id,)).find() + +def getBackupPage(site_id,page = 1,size = 10): + start = (int(page) - 1) * int(size) limit = str(start) + ',' + str(size) bk_list = mw.M('backup').where('pid=?', (site_id,)).field(__FIELD).limit(limit).order('id desc').select() count = mw.M('backup').where('pid=?', (site_id,)).count() @@ -28,3 +39,6 @@ def getBackupPage( rdata['list'] = bk_list rdata['count'] = count return rdata + +def deleteBackupById(bp_id): + return mw.M('backup').where("id=?", (bp_id,)).delete() diff --git a/web/utils/site.py b/web/utils/site.py index 90f9789b2..5cdb56a4d 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -2351,15 +2351,46 @@ export PATH data = {} data['data'] = info['list'] data['site'] = site_info - data['page'] = mw.getPage({'count':info['count'],'tojs':'getBackup','p':page, 'row':limit}) + data['page'] = mw.getPage({'count':info['count'],'tojs':'getBackup','p':page, 'row':size}) return data + def toBackup(self, site_id): + site_info = thisdb.getSitesById(site_id) + + filename = site_info['name'] + '_' + time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.zip' + backup_path = mw.getBackupDir() + '/site' + zip_name = backup_path + '/' + filename + if not (os.path.exists(backup_path)): + os.makedirs(backup_path) + exec_log = mw.getPanelDir() + '/logs/panel_exec.log' + cmd = "cd '" + site_info['path'] + "' && zip '" + zip_name + "' -r ./* > " + exec_log + " 2>&1" + mw.execShell(cmd) + + fsize = 0 + if os.path.exists(zip_name): + fsize = os.path.getsize(zip_name) + + thisdb.addBackup(site_id,filename,zip_name,fsize) + + msg = mw.getInfo('备份网站[{1}]成功!', (site_info['name'],)) + mw.writeLog('网站管理', msg) + return mw.returnData(True, '备份成功!') + + def delBackup(self,backup_id): + info = thisdb.getBackupById(backup_id) + if os.path.exists(info['filename']): + os.remove(info['filename']) + msg = mw.getInfo('删除网站[{1}]的备份[{2}]成功!', (info['name'], info['filename'])) + mw.writeLog('网站管理', msg) + + thisdb.deleteBackupById(backup_id) + return mw.returnData(True, '站点删除成功!') + def getPhpVersion(self): - phpVersions = ('00', '52', '53', '54', '55', - '56', '70', '71', '72', '73', - '74', '80', '81', '82', '83', - '84') + phpVersions = ('00', '52', '53', '54', '55', '56', + '70', '71', '72', '73', '74', '80', + '81', '82', '83', '84') data = [] for val in phpVersions: tmp = {} From 41978d33bd0ff5ded671dd01ba32cf2a704d6b1a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 00:20:33 +0800 Subject: [PATCH 392/965] Update app.py --- web/app.py | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/web/app.py b/web/app.py index 273bc6179..78a70bbcc 100644 --- a/web/app.py +++ b/web/app.py @@ -8,17 +8,18 @@ # Author: midoks # --------------------------------------------------------------------------------- +# import eventlet +# eventlet.monkey_patch() + import sys import os +# from admin import create_app from admin import app, socketio import config -from gevent.pywsgi import WSGIServer -from geventwebsocket.handler import WebSocketHandler - -import eventlet -eventlet.monkey_patch(socket=True, select=True) +# from gevent.pywsgi import WSGIServer +# from geventwebsocket.handler import WebSocketHandler if sys.version_info < (3, 6): @@ -28,21 +29,27 @@ if sys.version_info < (3, 6): if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) + # print(config.DEFAULT_SERVER,config.DEFAULT_SERVER_PORT) # app = create_app() + def main(): - # http_server = WSGIServer( - # (HOST, PORT), app, handler_class=WebSocketHandler) - # http_server.serve_forever() - # socketio.run(app, host=HOST, port=PORT) - - socketio.run( - app, - debug=config.DEBUG, - allow_unsafe_werkzeug=True, - host=config.DEFAULT_SERVER, - port=7201, - ) + + # try: + # # http_server = WSGIServer( + # # (HOST, PORT), app, handler_class=WebSocketHandler) + # # http_server.serve_forever() + # # socketio.run(app, host=HOST, port=PORT) + # socketio.run( + # app, + # debug=config.DEBUG, + # host=config.DEFAULT_SERVER, + # port=7201, + # ) + # except Exception as e: + # print(str(e)) + + app.run(debug=True) if __name__ == '__main__': - app.run(debug=True) + main() From d6b68f45e580c9ec5f37ad7c559e98999e9035a6 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 00:24:35 +0800 Subject: [PATCH 393/965] Update __init__.py --- web/admin/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 82d7fde1b..8291c21de 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -153,7 +153,9 @@ def inject_global_variables(): return dict(config=g_config, data=data) # webssh -socketio = SocketIO() +socketio = SocketIO(manage_session=False, async_mode='threading', + logger=False, engineio_logger=False, debug=False, + ping_interval=25, ping_timeout=120) socketio.init_app(app) @socketio.on('webssh_websocketio') From 0ccf70be5691737c8200d0f1bf01ebbb9ab6f72e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 00:33:08 +0800 Subject: [PATCH 394/965] update --- cli.sh | 8 ++++---- web/app.py | 7 +++++-- web/setting.py | 5 ++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cli.sh b/cli.sh index 6c0fbf97c..83d1ab515 100755 --- a/cli.sh +++ b/cli.sh @@ -59,8 +59,8 @@ mw_start_debug(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app - # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } mw_start_panel(){ @@ -68,8 +68,8 @@ mw_start_panel(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app - # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } diff --git a/web/app.py b/web/app.py index 78a70bbcc..1f7a8a868 100644 --- a/web/app.py +++ b/web/app.py @@ -8,8 +8,6 @@ # Author: midoks # --------------------------------------------------------------------------------- -# import eventlet -# eventlet.monkey_patch() import sys import os @@ -21,6 +19,11 @@ import config # from gevent.pywsgi import WSGIServer # from geventwebsocket.handler import WebSocketHandler +from gevent import monkey +monkey.patch_all() + +from requests.packages.urllib3.util.ssl_ import create_urllib3_context +create_urllib3_context() if sys.version_info < (3, 6): raise RuntimeError('This application must be run under Python 3.6 or later.') diff --git a/web/setting.py b/web/setting.py index 84fb630df..fa464ed05 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,7 +12,6 @@ # 配置文件 # --------------------------------------------------------------------------------- - import time import sys import random @@ -78,8 +77,8 @@ threads = workers * 1 backlog = 512 reload = False daemon = True -# worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -worker_class = 'eventlet' +worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' +# worker_class = 'eventlet' timeout = 600 keepalive = 60 preload_app = True From 1bb99bb90e905a4f3acc8ce56c48400e42a98584 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 00:33:31 +0800 Subject: [PATCH 395/965] Update app.py --- web/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app.py b/web/app.py index 1f7a8a868..2ef8b0186 100644 --- a/web/app.py +++ b/web/app.py @@ -22,8 +22,8 @@ import config from gevent import monkey monkey.patch_all() -from requests.packages.urllib3.util.ssl_ import create_urllib3_context -create_urllib3_context() +# from requests.packages.urllib3.util.ssl_ import create_urllib3_context +# create_urllib3_context() if sys.version_info < (3, 6): raise RuntimeError('This application must be run under Python 3.6 or later.') From f4f4e32612ba1b44df4c5e02ce2961d4528c6ddb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 00:45:50 +0800 Subject: [PATCH 396/965] update --- web/app.py | 28 +++++++++++----------------- web/config.py | 2 +- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/web/app.py b/web/app.py index 2ef8b0186..d6df771e1 100644 --- a/web/app.py +++ b/web/app.py @@ -12,16 +12,16 @@ import sys import os -# from admin import create_app from admin import app, socketio import config -# from gevent.pywsgi import WSGIServer -# from geventwebsocket.handler import WebSocketHandler +from gevent.pywsgi import WSGIServer +from geventwebsocket.handler import WebSocketHandler from gevent import monkey monkey.patch_all() + # from requests.packages.urllib3.util.ssl_ import create_urllib3_context # create_urllib3_context() @@ -38,21 +38,15 @@ if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): def main(): - # try: - # # http_server = WSGIServer( - # # (HOST, PORT), app, handler_class=WebSocketHandler) - # # http_server.serve_forever() - # # socketio.run(app, host=HOST, port=PORT) - # socketio.run( - # app, - # debug=config.DEBUG, - # host=config.DEFAULT_SERVER, - # port=7201, - # ) - # except Exception as e: - # print(str(e)) + try: + http_server = WSGIServer( + (HOST, PORT), app, handler_class=WebSocketHandler) + http_server.serve_forever() + socketio.run(app, host=config.DEFAULT_SERVER,debug=config.DEBUG) + except Exception as e: + print(str(e)) - app.run(debug=True) + # app.run(debug=True) if __name__ == '__main__': main() diff --git a/web/config.py b/web/config.py index 60d4b98f2..286d0c517 100644 --- a/web/config.py +++ b/web/config.py @@ -60,7 +60,7 @@ LOG_ROTATION_MAX_LOG_FILES = 5 # 要保留的最大备份数 SQLITE_PATH = os.path.join(DATA_DIR, APP_SQLITE_NAME + '.db') -DEFAULT_SERVER = '127.0.0.1' +DEFAULT_SERVER = '0.0.0.0' DEFAULT_SERVER_PORT = 7201 # APP启动时间 From 7ce3160adee7008150bca75e2e55cfef4efd6be8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 00:52:16 +0800 Subject: [PATCH 397/965] Update __init__.py --- web/admin/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 8291c21de..4b95c6533 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -153,9 +153,10 @@ def inject_global_variables(): return dict(config=g_config, data=data) # webssh -socketio = SocketIO(manage_session=False, async_mode='threading', - logger=False, engineio_logger=False, debug=False, - ping_interval=25, ping_timeout=120) +# socketio = SocketIO(manage_session=False, async_mode='threading', +# logger=False, engineio_logger=False, debug=False, +# ping_interval=25, ping_timeout=120) +socketio = SocketIO() socketio.init_app(app) @socketio.on('webssh_websocketio') From 32718d54501b9062c7d7f9f3b3435da477cb4398 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 00:55:49 +0800 Subject: [PATCH 398/965] Update mw.tpl --- scripts/init.d/mw.tpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 62392e727..9e35c232b 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -352,7 +352,8 @@ mw_debug(){ if [ -d ${PANEL_DIR}/web ];then cd ${PANEL_DIR}/web fi - gunicorn -b :$port -k eventlet -w 1 app:app + gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + # gunicorn -b :$port -k eventlet -w 1 app:app } From 25527d09f509ec03603de9a098a7a9b425dac67f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:07:00 +0800 Subject: [PATCH 399/965] Update firewall.py --- web/utils/firewall.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index e2e8aff5b..0aebdf227 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -55,6 +55,9 @@ class Firewall(object): def AIF_Firewalld(self): # firewall-cmd --list-all | grep ' ports' t = mw.execShell("firewall-cmd --list-all | grep ' ports'") + if t[1] != '': + return True + all_port = t[0].strip() data = all_port.split(":") ports_str = data[1] From 83f48cb05e6fc50a7dd99676720cb1a9decf6fec Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:18:57 +0800 Subject: [PATCH 400/965] update --- cli.sh | 8 ++++---- scripts/init.d/mw.tpl | 4 ++-- web/admin/__init__.py | 5 +++-- web/setting.py | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cli.sh b/cli.sh index 83d1ab515..6c0fbf97c 100755 --- a/cli.sh +++ b/cli.sh @@ -59,8 +59,8 @@ mw_start_debug(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - # cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } mw_start_panel(){ @@ -68,8 +68,8 @@ mw_start_panel(){ if [ -f ${DIR}/data/port.pl ];then port=$(cat ${DIR}/data/port.pl) fi - # cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app - cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 9e35c232b..b71995626 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -352,8 +352,8 @@ mw_debug(){ if [ -d ${PANEL_DIR}/web ];then cd ${PANEL_DIR}/web fi - gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app - # gunicorn -b :$port -k eventlet -w 1 app:app + # gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + gunicorn -b :$port -k eventlet -w 1 app:app } diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 4b95c6533..6a076d707 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -110,7 +110,6 @@ def requestCheck(): if not auth: return sendAuthenticated() - # print(auth.username.strip(),auth.password.strip()) salt = basic_auth['salt'] basic_user = mw.md5(auth.username.strip() + salt) basic_pwd = mw.md5(auth.password.strip() + salt) @@ -156,7 +155,9 @@ def inject_global_variables(): # socketio = SocketIO(manage_session=False, async_mode='threading', # logger=False, engineio_logger=False, debug=False, # ping_interval=25, ping_timeout=120) -socketio = SocketIO() +socketio = SocketIO(logger=True, + engineio_logger=True, + cors_allowed_origins="*") socketio.init_app(app) @socketio.on('webssh_websocketio') diff --git a/web/setting.py b/web/setting.py index fa464ed05..93d67d636 100755 --- a/web/setting.py +++ b/web/setting.py @@ -77,8 +77,8 @@ threads = workers * 1 backlog = 512 reload = False daemon = True -worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -# worker_class = 'eventlet' +# worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' +worker_class = 'eventlet' timeout = 600 keepalive = 60 preload_app = True From 655c2c22684da2b70f1e41e68a36c78fb559a601 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:19:44 +0800 Subject: [PATCH 401/965] Update app.py --- web/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app.py b/web/app.py index d6df771e1..010f63869 100644 --- a/web/app.py +++ b/web/app.py @@ -18,8 +18,8 @@ import config from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler -from gevent import monkey -monkey.patch_all() +# from gevent import monkey +# monkey.patch_all() # from requests.packages.urllib3.util.ssl_ import create_urllib3_context From af3ceeefe7c816e261d3ff15bcb9544bae663264 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:21:14 +0800 Subject: [PATCH 402/965] Update version.py --- web/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/version.py b/web/version.py index 3ade5933b..a3e8ed7f7 100644 --- a/web/version.py +++ b/web/version.py @@ -13,7 +13,7 @@ # 应用程序版本号组件 APP_RELEASE = 0 APP_REVISION = 18 -APP_SMALL_VERSION = 0.3 +APP_SMALL_VERSION = 0.4 # 应用程序版本后缀,例如“beta1”、“dev”。通常为空字符串GA发布。 APP_SUFFIX = '' From ef59c3a654d3b13be05a105b77db5526db43cee5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:24:24 +0800 Subject: [PATCH 403/965] Update app.py --- web/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/app.py b/web/app.py index 010f63869..038977063 100644 --- a/web/app.py +++ b/web/app.py @@ -21,6 +21,9 @@ from geventwebsocket.handler import WebSocketHandler # from gevent import monkey # monkey.patch_all() +import eventlet +eventlet.monkey_patch() + # from requests.packages.urllib3.util.ssl_ import create_urllib3_context # create_urllib3_context() From 0df069b9a4c90cf539005a3701a5926e25899f09 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:28:45 +0800 Subject: [PATCH 404/965] update --- web/admin/__init__.py | 4 ++-- web/app.py | 30 +++++++----------------------- web/setting.py | 3 +++ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 6a076d707..b2586281f 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -155,8 +155,8 @@ def inject_global_variables(): # socketio = SocketIO(manage_session=False, async_mode='threading', # logger=False, engineio_logger=False, debug=False, # ping_interval=25, ping_timeout=120) -socketio = SocketIO(logger=True, - engineio_logger=True, +socketio = SocketIO(logger=False, + engineio_logger=False, cors_allowed_origins="*") socketio.init_app(app) diff --git a/web/app.py b/web/app.py index 038977063..0816196fe 100644 --- a/web/app.py +++ b/web/app.py @@ -9,24 +9,13 @@ # --------------------------------------------------------------------------------- + import sys import os from admin import app, socketio import config -from gevent.pywsgi import WSGIServer -from geventwebsocket.handler import WebSocketHandler - -# from gevent import monkey -# monkey.patch_all() - -import eventlet -eventlet.monkey_patch() - - -# from requests.packages.urllib3.util.ssl_ import create_urllib3_context -# create_urllib3_context() if sys.version_info < (3, 6): raise RuntimeError('This application must be run under Python 3.6 or later.') @@ -36,20 +25,15 @@ if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) -# print(config.DEFAULT_SERVER,config.DEFAULT_SERVER_PORT) -# app = create_app() - def main(): - + import eventlet + eventlet.monkey_patch() try: - http_server = WSGIServer( - (HOST, PORT), app, handler_class=WebSocketHandler) - http_server.serve_forever() - socketio.run(app, host=config.DEFAULT_SERVER,debug=config.DEBUG) + socketio.run(app) + # app.run(debug=True) except Exception as e: - print(str(e)) - - # app.run(debug=True) + pass if __name__ == '__main__': + main() diff --git a/web/setting.py b/web/setting.py index 93d67d636..2abc9582f 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,6 +12,9 @@ # 配置文件 # --------------------------------------------------------------------------------- +# import eventlet +# eventlet.monkey_patch() + import time import sys import random From 49867fadf05bb5d59e6392fcc2b565f69363c11f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:31:27 +0800 Subject: [PATCH 405/965] Update setting.py --- web/setting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/setting.py b/web/setting.py index 2abc9582f..5d32e0210 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,8 +12,8 @@ # 配置文件 # --------------------------------------------------------------------------------- -# import eventlet -# eventlet.monkey_patch() +import eventlet +eventlet.monkey_patch() import time import sys From 1d1dbfc54c593be175fe7eb0df75a24f867771c0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:36:00 +0800 Subject: [PATCH 406/965] Update mw.tpl --- scripts/init.d/mw.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index b71995626..b4492d548 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -97,8 +97,8 @@ mw_start_task() mw_start() { - mw_start_task mw_start_panel + mw_start_task } # /www/server/mdserver-web/tmp/panelTask.pl && service mw restart_task From e5ef7d3e4d4cdf8e0dc3d620300b360ab196e4a0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:40:52 +0800 Subject: [PATCH 407/965] Update app.py --- web/app.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/web/app.py b/web/app.py index 0816196fe..1bbceee74 100644 --- a/web/app.py +++ b/web/app.py @@ -25,15 +25,21 @@ if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) -def main(): - import eventlet - eventlet.monkey_patch() +def try_eventlet(): try: + import eventlet + eventlet.monkey_patch() + except Exception as e: + pass + + +def main(): + try: + try_eventlet() socketio.run(app) # app.run(debug=True) except Exception as e: pass if __name__ == '__main__': - main() From b02a6a2d1923d5a817569acbc7f24f03201f76e1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:49:17 +0800 Subject: [PATCH 408/965] update --- web/app.py | 2 +- web/setting.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app.py b/web/app.py index 1bbceee74..fa6ea323e 100644 --- a/web/app.py +++ b/web/app.py @@ -35,7 +35,7 @@ def try_eventlet(): def main(): try: - try_eventlet() + # try_eventlet() socketio.run(app) # app.run(debug=True) except Exception as e: diff --git a/web/setting.py b/web/setting.py index 5d32e0210..2abc9582f 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,8 +12,8 @@ # 配置文件 # --------------------------------------------------------------------------------- -import eventlet -eventlet.monkey_patch() +# import eventlet +# eventlet.monkey_patch() import time import sys From 9cbec5a68f59b6a3d5dc48e2835026963fa5e1ba Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 01:52:10 +0800 Subject: [PATCH 409/965] Update setting.py --- web/setting.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/web/setting.py b/web/setting.py index 2abc9582f..93d67d636 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,9 +12,6 @@ # 配置文件 # --------------------------------------------------------------------------------- -# import eventlet -# eventlet.monkey_patch() - import time import sys import random From c2104290867c166bbddf283226ce887963236743 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 16:26:14 +0800 Subject: [PATCH 410/965] update --- requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index d50874f51..238d9c682 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ setuptools>=33.1.1 -Werkzeug>=1.0.1,<3.0.0 +Werkzeug>=1.0.1 wheel>=0.37.1 gunicorn>=21.2.0 requests>=2.27.1 urllib3>=1.21.1 -flask>=2.0.3,<3.0.0 +flask>=2.0.3 flask-session==0.3.2 flask-helper==0.19 flask-bcrypt==1.0.1 @@ -23,10 +23,10 @@ Flask-Migrate==4.* pyOpenSSL==23.2.0 cryptography>=40.0.2 configparser==5.2.0 -python-engineio==4.3.2 +python-engineio>=4.3.2 python-socketio>=4.2.0 -flask-socketio==5.2.0 -flask-sockets==0.2.1 +flask-socketio>=5.2.0 +flask-sockets>=0.2.1 zmq==0.0.0 paramiko>=2.8.0 pymongo From 51d092ca19219a025a92af44243685d5ac572a54 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:00:04 +0800 Subject: [PATCH 411/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index 93d67d636..9f0364288 100755 --- a/web/setting.py +++ b/web/setting.py @@ -78,7 +78,7 @@ backlog = 512 reload = False daemon = True # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -worker_class = 'eventlet' +worker_class = 'sync' timeout = 600 keepalive = 60 preload_app = True From dfef78fc8424287b0f721c53088815b958257948 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:00:26 +0800 Subject: [PATCH 412/965] Update mw.py --- web/core/mw.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 9369abed7..cf60c8222 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -463,16 +463,16 @@ def checkDomainPanel(): if client_ip in ['127.0.0.1', 'localhost', '::1']: return False - # ip = getHostAddr() - # if domain == '': - # if current_host.strip().lower() != ip.strip().lower(): - # to = scheme + "://" + ip + ":" + str(port) - # return redirect(to, code=302) - # return False - # else: - # if current_host.strip().lower() != domain.strip().lower(): - # to = scheme + "://" + domain + ":" + str(port) - # return redirect(to, code=302) + ip = getHostAddr() + if domain == '': + if current_host.strip().lower() != ip.strip().lower(): + to = scheme + "://" + ip + ":" + str(port) + return redirect(to, code=302) + return False + else: + if current_host.strip().lower() != domain.strip().lower(): + to = scheme + "://" + domain + ":" + str(port) + return redirect(to, code=302) return False def getLocalIp(): From 2a943efcd7cde8b3ff1b42e459cbd404c39cdbd4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:03:31 +0800 Subject: [PATCH 413/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index 9f0364288..460d57e9d 100755 --- a/web/setting.py +++ b/web/setting.py @@ -78,7 +78,7 @@ backlog = 512 reload = False daemon = True # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -worker_class = 'sync' +worker_class = 'gthread' timeout = 600 keepalive = 60 preload_app = True From 1dc55d5e1a73875cbbc948af52fe56ae140b6028 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:05:23 +0800 Subject: [PATCH 414/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index 460d57e9d..57f4652ed 100755 --- a/web/setting.py +++ b/web/setting.py @@ -73,7 +73,7 @@ if panel_ssl_data['open']: if workers > 2: workers = 1 -threads = workers * 1 +threads = workers * 2 backlog = 512 reload = False daemon = True From 74817313f6933a3ea80105e95e4687caae2a91b3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:13:31 +0800 Subject: [PATCH 415/965] Update setting.py --- web/setting.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/web/setting.py b/web/setting.py index 57f4652ed..32f74e3ce 100755 --- a/web/setting.py +++ b/web/setting.py @@ -22,15 +22,11 @@ import core.mw as mw import utils.system as system import thisdb -# 初始化db -from admin import setup -setup.init() - cpu_info = system.getCpuInfo() workers = cpu_info[1] +# workers = 1 panel_dir = mw.getPanelDir() - log_dir = mw.getMWLogs() if not os.path.exists(log_dir): os.mkdir(log_dir) @@ -71,14 +67,14 @@ if panel_ssl_data['open']: ssl_version = 2 if workers > 2: - workers = 1 + workers = 2 threads = workers * 2 backlog = 512 reload = False daemon = True -# worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -worker_class = 'gthread' +# # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' +worker_class = 'eventlet' timeout = 600 keepalive = 60 preload_app = True From 009be7e9200f456d89fee64d640c95a89d512e32 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:20:11 +0800 Subject: [PATCH 416/965] Update config.py --- web/config.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web/config.py b/web/config.py index 286d0c517..afe82f97d 100644 --- a/web/config.py +++ b/web/config.py @@ -12,7 +12,6 @@ # 配置信息 # --------------------------------------------------------------------------------- - import builtins import logging import os @@ -41,6 +40,18 @@ CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW = 100 CONSOLE_LOG_LEVEL = logging.WARNING FILE_LOG_LEVEL = logging.WARNING +# Number of values to trust for X-Forwarded-For +PROXY_X_FOR_COUNT = 1 +# Number of values to trust for X-Forwarded-Proto. +PROXY_X_PROTO_COUNT = 1 +# Number of values to trust for X-Forwarded-Host. +PROXY_X_HOST_COUNT = 0 +# Number of values to trust for X-Forwarded-Port. +PROXY_X_PORT_COUNT = 1 +# Number of values to trust for X-Forwarded-Prefix. +PROXY_X_PREFIX_COUNT = 0 + + DATA_DIR = mw.getPanelDataDir() # 日志文件名。这将进入数据目录,服务器模式下的非Windows平台除外。 From 58101f889d0c162a4ea3acaea1a765abcc0f1160 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:43:49 +0800 Subject: [PATCH 417/965] Update app.py --- web/app.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/web/app.py b/web/app.py index fa6ea323e..6903df9fd 100644 --- a/web/app.py +++ b/web/app.py @@ -25,21 +25,12 @@ if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) -def try_eventlet(): - try: - import eventlet - eventlet.monkey_patch() - except Exception as e: - pass - - def main(): - try: - # try_eventlet() - socketio.run(app) - # app.run(debug=True) - except Exception as e: - pass + + print(config.DEBUG) + socketio.run(app,debug=config.DEBUG) + # app.run(debug=True) + if __name__ == '__main__': main() From 7d1c2bac14f2d218db7f4075cbbf5fac28aac054 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:44:17 +0800 Subject: [PATCH 418/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index 32f74e3ce..217801034 100755 --- a/web/setting.py +++ b/web/setting.py @@ -77,7 +77,7 @@ daemon = True worker_class = 'eventlet' timeout = 600 keepalive = 60 -preload_app = True +preload_app = False capture_output = True access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' loglevel = 'info' From 769f36c560a7fce3c1673a4e8603ef0a797f603e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:47:24 +0800 Subject: [PATCH 419/965] Update app.py --- web/app.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/web/app.py b/web/app.py index 6903df9fd..cb1bac80b 100644 --- a/web/app.py +++ b/web/app.py @@ -26,11 +26,8 @@ if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): def main(): - - print(config.DEBUG) socketio.run(app,debug=config.DEBUG) # app.run(debug=True) - if __name__ == '__main__': main() From 17b40e5d1d121044a079d7f81bab5b6a8fec5a07 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 18:58:55 +0800 Subject: [PATCH 420/965] Update setting.py --- web/setting.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/setting.py b/web/setting.py index 217801034..ddb8d5566 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,6 +12,10 @@ # 配置文件 # --------------------------------------------------------------------------------- +# 初始化db +from admin import setup +setup.init() + import time import sys import random From 852fc6e5a7e0f3fae15665d90cbc0608beab8b57 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:04:25 +0800 Subject: [PATCH 421/965] Update mw.py --- web/core/mw.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/core/mw.py b/web/core/mw.py index cf60c8222..a410e909a 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -465,11 +465,13 @@ def checkDomainPanel(): ip = getHostAddr() if domain == '': + print(current_host.strip().lower(), ip.strip().lower()) if current_host.strip().lower() != ip.strip().lower(): to = scheme + "://" + ip + ":" + str(port) return redirect(to, code=302) return False else: + print(current_host.strip().lower(), domain.strip().lower()) if current_host.strip().lower() != domain.strip().lower(): to = scheme + "://" + domain + ":" + str(port) return redirect(to, code=302) From ee206f014d187932d34c4627f3f9aff70622e5a7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:07:59 +0800 Subject: [PATCH 422/965] Update mw.py --- web/core/mw.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/core/mw.py b/web/core/mw.py index a410e909a..95a6f9c04 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -466,6 +466,8 @@ def checkDomainPanel(): ip = getHostAddr() if domain == '': print(current_host.strip().lower(), ip.strip().lower()) + if ip in ['127.0.0.1', 'localhost', '::1']: + return False if current_host.strip().lower() != ip.strip().lower(): to = scheme + "://" + ip + ":" + str(port) return redirect(to, code=302) From de3f2ab4d211f496c274a726f06718c705fadaef Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:08:22 +0800 Subject: [PATCH 423/965] Update mw.py --- web/core/mw.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/core/mw.py b/web/core/mw.py index 95a6f9c04..3ba9a2763 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -465,7 +465,6 @@ def checkDomainPanel(): ip = getHostAddr() if domain == '': - print(current_host.strip().lower(), ip.strip().lower()) if ip in ['127.0.0.1', 'localhost', '::1']: return False if current_host.strip().lower() != ip.strip().lower(): From 2ac28e9d80d6d573117588d837b953387fe524ce Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:12:10 +0800 Subject: [PATCH 424/965] Update plugin.py --- web/utils/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index ee2b89a46..f50c2afe8 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -134,7 +134,7 @@ class plugin(object): def init(self): plugin_names = { - 'openresty': '1.25.3', + 'openresty': '1.27.1', 'php': '56', 'swap': '1.1', 'mysql': '5.7', From a059e0924455c8220d9f5f23d23feeeed35e1a9a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:28:17 +0800 Subject: [PATCH 425/965] Update setting.py --- web/utils/setting.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/utils/setting.py b/web/utils/setting.py index 19b16ccfc..c6dd8aedc 100644 --- a/web/utils/setting.py +++ b/web/utils/setting.py @@ -121,8 +121,7 @@ class setting(object): if not mw.inArray(['local','nginx'], choose): return mw.returnData(True, '删除错误面板SSL类型!') - port_pl = mw.getPanelDir()+'/data/port.pl' - port = mw.readFile(port_pl).strip() + port = mw.getPanelPort() to_panel_url = 'http://'+ip+":"+port+'/config' From 12d5ce5eeaf03d72434213ac0ffade5ae5402a93 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:31:45 +0800 Subject: [PATCH 426/965] update --- cli.sh | 5 +++-- web/setting.py | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cli.sh b/cli.sh index 6c0fbf97c..fc9ef386f 100755 --- a/cli.sh +++ b/cli.sh @@ -39,9 +39,10 @@ mw_start_task() } mw_start(){ - cd ${DIR}/web && gunicorn -c setting.py app:app - #安全启动 + # 后台任务 mw_start_task + + cd ${DIR}/web && gunicorn -c setting.py app:app } diff --git a/web/setting.py b/web/setting.py index ddb8d5566..217801034 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,10 +12,6 @@ # 配置文件 # --------------------------------------------------------------------------------- -# 初始化db -from admin import setup -setup.init() - import time import sys import random From 7ba7272a7cdc81acc62ce6e350544d844dd4d598 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:43:44 +0800 Subject: [PATCH 427/965] Update panel_tools.py --- panel_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index 888886b7a..6e2f536c5 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -27,8 +27,8 @@ from utils.firewall import Firewall as MwFirewall import core.mw as mw import thisdb -# from admin import setup -# setup.init() +from admin import setup +setup.init() INIT_DIR = "/etc/rc.d/init.d" From 0d1d4b66d6216c876e2c497b371957cad942ef4d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:47:24 +0800 Subject: [PATCH 428/965] Update panel_task.py --- panel_task.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/panel_task.py b/panel_task.py index 441572d4c..325f93e25 100755 --- a/panel_task.py +++ b/panel_task.py @@ -27,9 +27,6 @@ sys.path.append(web_dir) import core.mw as mw import thisdb -from admin import setup -setup.init() - g_log_file = mw.getPanelTaskLog() if not os.path.exists(g_log_file): os.system("touch " + g_log_file) From 493553ec4bb21e020a929f0e2c5a70d040d4f257 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 19:48:36 +0800 Subject: [PATCH 429/965] update --- panel_task.py | 3 +++ panel_tools.py | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/panel_task.py b/panel_task.py index 325f93e25..441572d4c 100755 --- a/panel_task.py +++ b/panel_task.py @@ -27,6 +27,9 @@ sys.path.append(web_dir) import core.mw as mw import thisdb +from admin import setup +setup.init() + g_log_file = mw.getPanelTaskLog() if not os.path.exists(g_log_file): os.system("touch " + g_log_file) diff --git a/panel_tools.py b/panel_tools.py index 6e2f536c5..058281264 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -27,8 +27,6 @@ from utils.firewall import Firewall as MwFirewall import core.mw as mw import thisdb -from admin import setup -setup.init() INIT_DIR = "/etc/rc.d/init.d" From 8e39af00a3bbd138d25df4c477ad3d2cb53631dd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:04:35 +0800 Subject: [PATCH 430/965] update --- .gitignore | 4 +++- logs/README.md | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 logs/README.md diff --git a/.gitignore b/.gitignore index 5de67f7af..2ea0db831 100644 --- a/.gitignore +++ b/.gitignore @@ -119,7 +119,9 @@ debug.out mdioks.session-journal *.session -logs/* +logs/panel.log +logs/panel_task.log +logs/panel.pid ssl/local ssl/nginx diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 000000000..cd3d22536 --- /dev/null +++ b/logs/README.md @@ -0,0 +1 @@ +logs \ No newline at end of file From 8b439801ab889fc376fc9bcf109940d7ac707b3e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:05:09 +0800 Subject: [PATCH 431/965] Update .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2ea0db831..f7d56fe65 100644 --- a/.gitignore +++ b/.gitignore @@ -119,9 +119,11 @@ debug.out mdioks.session-journal *.session +logs/panel.pid logs/panel.log logs/panel_task.log -logs/panel.pid +logs/panel_exec.log + ssl/local ssl/nginx From ab287d42ef7bf69d65f4dd6a79822826d3eed794 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:12:41 +0800 Subject: [PATCH 432/965] Update __init__.py --- web/admin/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index b2586281f..37509d4cd 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -42,10 +42,6 @@ import thisdb from admin import setup setup.init() -# from threading import Lock -# thread = None -# thread_lock = Lock() - app = Flask(__name__, template_folder='templates/default') # 缓存配置 From 179b00e8a1950e15224b3d4bd5433973cf1be965 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:16:02 +0800 Subject: [PATCH 433/965] Update __init__.py --- web/admin/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 37509d4cd..c9bc598fd 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -51,6 +51,7 @@ cache.init_app(app, config={'CACHE_TYPE': 'simple'}) # 静态文件配置 from whitenoise import WhiteNoise app.wsgi_app = WhiteNoise(app.wsgi_app, root="../web/static/", prefix="static/", max_age=604800) +app.jinja_env.trim_blocks = True # session配置 # app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] From e20341d3dafb7f67393a435ff034aaf73af857d3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:16:58 +0800 Subject: [PATCH 434/965] Update setting.py --- web/setting.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/setting.py b/web/setting.py index 217801034..85d5e112e 100755 --- a/web/setting.py +++ b/web/setting.py @@ -17,6 +17,9 @@ import sys import random import os +# 初始化db +from admin import setup +setup.init() import core.mw as mw import utils.system as system From 11ba771db3c61b96b579684c6384033a6743ab1c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:17:23 +0800 Subject: [PATCH 435/965] Update __init__.py --- web/admin/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index c9bc598fd..4345899a1 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -38,10 +38,6 @@ import config import utils.config as utils_config import thisdb -# 初始化db -from admin import setup -setup.init() - app = Flask(__name__, template_folder='templates/default') # 缓存配置 From 2c8108a0be244a547d7eb70ee6e30b55d317398c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:21:40 +0800 Subject: [PATCH 436/965] Update mw.tpl --- scripts/init.d/mw.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index b4492d548..b71995626 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -97,8 +97,8 @@ mw_start_task() mw_start() { - mw_start_panel mw_start_task + mw_start_panel } # /www/server/mdserver-web/tmp/panelTask.pl && service mw restart_task From 630e518ae38c095509ae4478a9963d21540f21a0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:22:01 +0800 Subject: [PATCH 437/965] Update mw.tpl --- scripts/init.d/mw.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index b71995626..106ea781e 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -101,7 +101,7 @@ mw_start() mw_start_panel } -# /www/server/mdserver-web/tmp/panelTask.pl && service mw restart_task +# /www/server/mdserver-web/logs/panel_task.lock && service mw restart_task mw_stop_task() { if [ -f ${PANEL_DIR}/logs/panel_task.lock ];then From e2d800a167d56e50d599bbd221756b750a8d4fd2 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:29:11 +0800 Subject: [PATCH 438/965] Update init.py --- web/thisdb/init.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 20654721b..1f6050df9 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -14,9 +14,9 @@ import core.mw as mw def initPanelData(): - _dbfile = mw.getPanelDataDir() + '/panel.db' - if os.path.exists(_dbfile): - return True + # _dbfile = mw.getPanelDataDir() + '/panel.db' + # if os.path.exists(_dbfile): + # return True sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') content = mw.readFile(sql_file) From 2beae29d25fecaa1eebd8927918d715c8f9c49e9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:29:30 +0800 Subject: [PATCH 439/965] Update init.py --- web/thisdb/init.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 1f6050df9..b6b690b3a 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -14,9 +14,9 @@ import core.mw as mw def initPanelData(): - # _dbfile = mw.getPanelDataDir() + '/panel.db' - # if os.path.exists(_dbfile): - # return True + __dbfile = mw.getPanelDataDir() + '/panel.db' + if os.path.exists(__dbfile): + return True sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') content = mw.readFile(sql_file) From d6972bae753dfac07aada7ee2452f538751574ea Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:29:48 +0800 Subject: [PATCH 440/965] Update init.py --- web/thisdb/init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index b6b690b3a..1f21c341f 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -22,7 +22,7 @@ def initPanelData(): content = mw.readFile(sql_file) csql_data = content.split(';') for i in range(len(csql_data)): - # print(csql_data[i]) - # print(sql.execute(csql_data[i], ())) + print(csql_data[i]) + print(sql.execute(csql_data[i], ())) sql.execute(csql_data[i], ()) return True \ No newline at end of file From 82e4e5c1d3fd2bfb7418e305ccf841e6759296fe Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:39:52 +0800 Subject: [PATCH 441/965] Update mw.tpl --- scripts/init.d/mw.tpl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 106ea781e..11d0c91d5 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -116,6 +116,13 @@ mw_stop_task() do kill -9 $p > /dev/null 2>&1 done + + zzpids=$(ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}') + arr=($zzpids) + for p in ${arr[@]} + do + kill -9 $p > /dev/null 2>&1 + done echo -e "\033[32mdone\033[0m" } From 6f7c26adc487e60ce1e0fefdb0e74979949c7fd1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:42:53 +0800 Subject: [PATCH 442/965] Update init.py --- web/thisdb/init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index 1f21c341f..c4858b90f 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -23,6 +23,6 @@ def initPanelData(): csql_data = content.split(';') for i in range(len(csql_data)): print(csql_data[i]) + # print(sql.execute(csql_data[i], ())) print(sql.execute(csql_data[i], ())) - sql.execute(csql_data[i], ()) return True \ No newline at end of file From 9569b64d26c14f2c3b9a58308c451ef0c0d45113 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:43:17 +0800 Subject: [PATCH 443/965] Update __init__.py --- web/admin/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/admin/__init__.py b/web/admin/__init__.py index 4345899a1..c9bc598fd 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -38,6 +38,10 @@ import config import utils.config as utils_config import thisdb +# 初始化db +from admin import setup +setup.init() + app = Flask(__name__, template_folder='templates/default') # 缓存配置 From c3d6ec90e83c2daf5c88a8a18740de36d8fb522a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:43:33 +0800 Subject: [PATCH 444/965] Update panel_tools.py --- panel_tools.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/panel_tools.py b/panel_tools.py index 058281264..18709a8d9 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -27,8 +27,6 @@ from utils.firewall import Firewall as MwFirewall import core.mw as mw import thisdb - - INIT_DIR = "/etc/rc.d/init.d" if mw.isAppleSystem(): INIT_DIR = mw.getPanelDir() + "/scripts/init.d" From c71a59cce1dba10837f9c72d44d5fd039f926f4f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 20:48:57 +0800 Subject: [PATCH 445/965] Update init.py --- web/thisdb/init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/thisdb/init.py b/web/thisdb/init.py index c4858b90f..b6b690b3a 100644 --- a/web/thisdb/init.py +++ b/web/thisdb/init.py @@ -22,7 +22,7 @@ def initPanelData(): content = mw.readFile(sql_file) csql_data = content.split(';') for i in range(len(csql_data)): - print(csql_data[i]) + # print(csql_data[i]) # print(sql.execute(csql_data[i], ())) - print(sql.execute(csql_data[i], ())) + sql.execute(csql_data[i], ()) return True \ No newline at end of file From 58cb061ef2463d837ccf665827ade94ba08ddaae Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Thu, 28 Nov 2024 23:28:58 +0800 Subject: [PATCH 446/965] Update index.py --- plugins/gogs/index.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/gogs/index.py b/plugins/gogs/index.py index e4643edb8..ee168eef6 100755 --- a/plugins/gogs/index.py +++ b/plugins/gogs/index.py @@ -103,8 +103,7 @@ def getHomeDir(): def getRunUser(): if mw.isAppleSystem(): - user = mw.execShell( - "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() + user = mw.execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() return user else: return 'root' @@ -566,8 +565,7 @@ def repoList(): # print(list_data) list_data = checkRepoListIsHasScript(list_data) - data['list'] = mw.getPage({'count': count, 'p': page, - 'row': page_size, 'tojs': 'gogsRepoListPage'}) + data['list'] = mw.getPage({'count': count, 'p': page, 'row': page_size, 'tojs': 'gogsRepoListPage'}) data['page'] = page data['page_size'] = page_size data['page_count'] = int(math.ceil(count / page_size)) @@ -826,8 +824,7 @@ def projectScriptSelf_Create(): name = args['name'] + '.git' file = args['file'] - self_path = path = getRootPath() + '/' + user + '/' + \ - name + '/custom_hooks/self' + self_path = path = getRootPath() + '/' + user + '/' + name + '/custom_hooks/self' if not os.path.exists(self_path): os.mkdir(self_path) From 1846af72713cde41eb3f85ff257c497b3f74a223 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 00:23:07 +0800 Subject: [PATCH 447/965] update --- plugins/php/versions/53/install.sh | 2 +- plugins/php/versions/54/install.sh | 2 +- plugins/php/versions/55/install.sh | 2 +- plugins/php/versions/56/install.sh | 2 +- plugins/php/versions/70/install.sh | 2 +- plugins/php/versions/71/install.sh | 2 +- plugins/php/versions/72/install.sh | 2 +- plugins/php/versions/73/install.sh | 2 +- plugins/php/versions/74/install.sh | 2 +- plugins/php/versions/80/install.sh | 2 +- plugins/php/versions/81/install.sh | 2 +- plugins/php/versions/82/install.sh | 2 +- plugins/php/versions/83/install.sh | 2 +- plugins/php/versions/84/install.sh | 5 +++-- plugins/php/versions/all_mac.sh | 4 ++-- 15 files changed, 18 insertions(+), 17 deletions(-) diff --git a/plugins/php/versions/53/install.sh b/plugins/php/versions/53/install.sh index 5bd5a99e9..421caefd7 100755 --- a/plugins/php/versions/53/install.sh +++ b/plugins/php/versions/53/install.sh @@ -88,7 +88,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/54/install.sh b/plugins/php/versions/54/install.sh index 149acba46..e32ace308 100755 --- a/plugins/php/versions/54/install.sh +++ b/plugins/php/versions/54/install.sh @@ -90,7 +90,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/55/install.sh b/plugins/php/versions/55/install.sh index 01e26ecb6..96f869c46 100755 --- a/plugins/php/versions/55/install.sh +++ b/plugins/php/versions/55/install.sh @@ -83,7 +83,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/56/install.sh b/plugins/php/versions/56/install.sh index d9c5fcf18..3b7e8c060 100755 --- a/plugins/php/versions/56/install.sh +++ b/plugins/php/versions/56/install.sh @@ -87,7 +87,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/70/install.sh b/plugins/php/versions/70/install.sh index f2792f258..0e7d474e5 100755 --- a/plugins/php/versions/70/install.sh +++ b/plugins/php/versions/70/install.sh @@ -82,7 +82,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/71/install.sh b/plugins/php/versions/71/install.sh index 542db0efa..b5881c8f0 100755 --- a/plugins/php/versions/71/install.sh +++ b/plugins/php/versions/71/install.sh @@ -82,7 +82,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/72/install.sh b/plugins/php/versions/72/install.sh index 01f63f99e..5e2bb9142 100755 --- a/plugins/php/versions/72/install.sh +++ b/plugins/php/versions/72/install.sh @@ -80,7 +80,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/73/install.sh b/plugins/php/versions/73/install.sh index 2a538c05f..3d619b7d0 100755 --- a/plugins/php/versions/73/install.sh +++ b/plugins/php/versions/73/install.sh @@ -97,7 +97,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/74/install.sh b/plugins/php/versions/74/install.sh index 226affc6f..e21252768 100755 --- a/plugins/php/versions/74/install.sh +++ b/plugins/php/versions/74/install.sh @@ -119,7 +119,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/80/install.sh b/plugins/php/versions/80/install.sh index 57ae1c0aa..5ef789c72 100755 --- a/plugins/php/versions/80/install.sh +++ b/plugins/php/versions/80/install.sh @@ -110,7 +110,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/81/install.sh b/plugins/php/versions/81/install.sh index 784bc27cf..efbec9804 100755 --- a/plugins/php/versions/81/install.sh +++ b/plugins/php/versions/81/install.sh @@ -100,7 +100,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh index 3a2bdd117..dc7b2c14a 100755 --- a/plugins/php/versions/82/install.sh +++ b/plugins/php/versions/82/install.sh @@ -101,7 +101,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index 406040830..979282dfd 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -101,7 +101,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh index 67ad8147e..ea47ee991 100755 --- a/plugins/php/versions/84/install.sh +++ b/plugins/php/versions/84/install.sh @@ -57,7 +57,8 @@ cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" + # OPTIONS="${OPTIONS} --with-curl" + echo "curl pass" else OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-readline" @@ -82,7 +83,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" diff --git a/plugins/php/versions/all_mac.sh b/plugins/php/versions/all_mac.sh index a771bd087..0a72317db 100644 --- a/plugins/php/versions/all_mac.sh +++ b/plugins/php/versions/all_mac.sh @@ -30,7 +30,7 @@ ROOT_DIR=$(cd "$(dirname "$0")"; pwd) # echo "php${PHP_VER} -- end" -PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83) +PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83 84) # PHP_VER_LIST=(81) for PHP_VER in ${PHP_VER_LIST[@]}; do echo "php${PHP_VER} -- start" @@ -41,7 +41,7 @@ for PHP_VER in ${PHP_VER_LIST[@]}; do done cd $DIR -PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82) +PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83 84) # yar PHP_EXT_LIST=(ZendGuardLoader pdo mysqlnd sqlite3 openssl opcache mcrypt fileinfo \ exif gd intl pcntl memcache memcached redis imagemagick xdebug \ From e04214c14d72ba327e7de0585b3e71cc175b6951 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 00:23:17 +0800 Subject: [PATCH 448/965] Update install.sh --- plugins/php/install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/php/install.sh b/plugins/php/install.sh index d6ba610a0..f2ab97549 100755 --- a/plugins/php/install.sh +++ b/plugins/php/install.sh @@ -63,6 +63,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then # cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 56 # cd /www/server/mdserver-web/plugins/php/versions/common && bash openssl.sh install 56 # cd /www/server/mdserver-web/plugins/php/versions/common && bash fileinfo.sh install 81 + # cd ${rootPath}/plugins/php/versions/common && bash curl.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash gd.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash iconv.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash exif.sh install ${type} @@ -75,6 +76,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then cd ${rootPath}/plugins/php/versions/common && bash memcached.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash pcntl.sh install ${type} + if [ "${type}" -gt "73" ];then From dc5a81ea89c1f8a7280e496c77f528fed22b33bb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:07:57 +0800 Subject: [PATCH 449/965] Create curl.sh --- plugins/php/versions/common/curl.sh | 123 ++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 plugins/php/versions/common/curl.sh diff --git a/plugins/php/versions/common/curl.sh b/plugins/php/versions/common/curl.sh new file mode 100755 index 000000000..609974209 --- /dev/null +++ b/plugins/php/versions/common/curl.sh @@ -0,0 +1,123 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH=$PATH:/opt/homebrew/bin + +curPath=`pwd` + +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php +SYS_ARCH=`arch` + +actionType=$1 +version=$2 + + +LIBNAME=curl +LIBV=0 + +LIB_PATH_NAME=lib/php +if [ -d $serverPath/php/${version}/lib64 ];then + LIB_PATH_NAME=lib64 +fi + +NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts` +extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so + +sysName=`uname` +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + + +Install_lib() +{ + + isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + + if [ ! -d $sourcePath/php${version}/ext ];then + cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install + fi + + cd $sourcePath/php${version}/ext/${LIBNAME} + if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then + OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" + fi + + if [ "$sysName" == "Darwin" ];then + + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` + OPTIONS="$OPTIONS --with-curl=$(brew --prefix curl)" + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LIB_DEPEND_DIR/lib/pkgconfig + fi + + + $serverPath/php/$version/bin/phpize + echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS" + ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS + + make clean && make && make install && make clean + + if [ -d $sourcePath/php${version} ];then + cd ${sourcePath} && rm -rf $sourcePath/php${version} + fi + fi + + if [ ! -f "$extFile" ];then + echo "ERROR!" + return + fi + + + echo "" >> $serverPath/php/$version/etc/php.ini + echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini + echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini + + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ ! -f "$serverPath/php/$version/bin/php-config" ];then + echo "php-$version 未安装,请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + echo "php-$version 未安装${LIBNAME},请选择其它版本!" + return + fi + + echo $serverPath/php/$version/etc/php.ini + sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini + sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini + + rm -f $extFile + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===============================================' + echo 'successful!' +} + + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file From 71dc92f59dd870b269f2ea7d249c95bb43e6b4d0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:08:01 +0800 Subject: [PATCH 450/965] Update phplib.conf --- plugins/php/versions/phplib.conf | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plugins/php/versions/phplib.conf b/plugins/php/versions/phplib.conf index ca5226d60..0dd87e352 100755 --- a/plugins/php/versions/phplib.conf +++ b/plugins/php/versions/phplib.conf @@ -371,6 +371,30 @@ "shell": "gd.sh", "check": "gd.so" }, + { + "name": "curl", + "versions": [ + "52", + "53", + "54", + "55", + "56", + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "通用扩展", + "msg": "通用网络", + "shell": "curl.sh", + "check": "curl.so" + }, { "name": "intl", "versions": [ From 3182bac95e23a195ba9f546c16f45a9695b964a8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:08:08 +0800 Subject: [PATCH 451/965] Update install.sh --- plugins/php/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php/install.sh b/plugins/php/install.sh index f2ab97549..77b87bbfa 100755 --- a/plugins/php/install.sh +++ b/plugins/php/install.sh @@ -63,7 +63,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then # cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 56 # cd /www/server/mdserver-web/plugins/php/versions/common && bash openssl.sh install 56 # cd /www/server/mdserver-web/plugins/php/versions/common && bash fileinfo.sh install 81 - # cd ${rootPath}/plugins/php/versions/common && bash curl.sh install ${type} + cd ${rootPath}/plugins/php/versions/common && bash curl.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash gd.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash iconv.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash exif.sh install ${type} From 37e3850b43ba4e7fb263d7bd425121a2ed9de59b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:13:22 +0800 Subject: [PATCH 452/965] update --- plugins/php/versions/52/install.sh | 9 +-------- plugins/php/versions/53/install.sh | 5 ----- plugins/php/versions/54/install.sh | 10 ---------- plugins/php/versions/55/install.sh | 5 ----- plugins/php/versions/56/install.sh | 5 ----- plugins/php/versions/70/install.sh | 5 ----- plugins/php/versions/71/install.sh | 4 ---- plugins/php/versions/72/install.sh | 8 +------- plugins/php/versions/73/install.sh | 1 - plugins/php/versions/74/install.sh | 12 ------------ plugins/php/versions/80/install.sh | 6 +----- plugins/php/versions/81/install.sh | 9 +++------ plugins/php/versions/82/install.sh | 15 ++++++--------- plugins/php/versions/83/install.sh | 13 +++---------- plugins/php/versions/84/install.sh | 11 ++++------- plugins/php/versions/phplib.conf | 24 ++++++++++++++++++++++++ 16 files changed, 43 insertions(+), 99 deletions(-) diff --git a/plugins/php/versions/52/install.sh b/plugins/php/versions/52/install.sh index 28ef0b968..daf12c254 100755 --- a/plugins/php/versions/52/install.sh +++ b/plugins/php/versions/52/install.sh @@ -82,18 +82,11 @@ if [ -f $serverPath/php/${PHP_VER}/bin/php ];then return fi -OPTIONS='' +OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS='--without-iconv' OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=${serverPath}/lib/curl" -else - OPTIONS='--without-iconv' - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" fi - IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then OPTIONS="${OPTIONS} --with-libdir=lib64" diff --git a/plugins/php/versions/53/install.sh b/plugins/php/versions/53/install.sh index 421caefd7..839d8dcb0 100755 --- a/plugins/php/versions/53/install.sh +++ b/plugins/php/versions/53/install.sh @@ -66,12 +66,7 @@ fi # OPTIONS="${OPTIONS} --with-jpeg --with-jpeg-dir=/usr/lib" OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" -else - # OPTIONS=" --with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/54/install.sh b/plugins/php/versions/54/install.sh index e32ace308..58af0987f 100755 --- a/plugins/php/versions/54/install.sh +++ b/plugins/php/versions/54/install.sh @@ -59,21 +59,11 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - - BREW_DIR=`which brew` - BREW_DIR=${BREW_DIR/\/bin\/brew/} - CURL_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` - OPTIONS="$OPTIONS --with-curl=${CURL_DEPEND_DIR}" # OPTIONS="${OPTIONS} --with-pcre-dir=${serverPath}/lib/pcre" # OPTIONS="${OPTIONS} --with-external-pcre=${serverPath}/lib/pcre" - else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --enable-mbstring" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/55/install.sh b/plugins/php/versions/55/install.sh index 96f869c46..59f5712c6 100755 --- a/plugins/php/versions/55/install.sh +++ b/plugins/php/versions/55/install.sh @@ -59,14 +59,9 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --enable-mbstring" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/56/install.sh b/plugins/php/versions/56/install.sh index 3b7e8c060..b26aa6606 100755 --- a/plugins/php/versions/56/install.sh +++ b/plugins/php/versions/56/install.sh @@ -63,14 +63,9 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" OPTIONS="${OPTIONS} --with-zlib-dir=$(brew --prefix zlib)" - # OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --enable-mbstring" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/70/install.sh b/plugins/php/versions/70/install.sh index 0e7d474e5..ebe243815 100755 --- a/plugins/php/versions/70/install.sh +++ b/plugins/php/versions/70/install.sh @@ -60,12 +60,7 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl" # OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" -else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/71/install.sh b/plugins/php/versions/71/install.sh index b5881c8f0..20bcb6c94 100755 --- a/plugins/php/versions/71/install.sh +++ b/plugins/php/versions/71/install.sh @@ -61,11 +61,7 @@ OPTIONS='' OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/72/install.sh b/plugins/php/versions/72/install.sh index 5e2bb9142..1cc80f08a 100755 --- a/plugins/php/versions/72/install.sh +++ b/plugins/php/versions/72/install.sh @@ -58,13 +58,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-zlib-dir=$serverPath/lib/zlib" - OPTIONS="${OPTIONS} --with-readline" -fi +OPTIONS="${OPTIONS} --with-zlib-dir=$serverPath/lib/zlib" IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then diff --git a/plugins/php/versions/73/install.sh b/plugins/php/versions/73/install.sh index 3d619b7d0..1900b0838 100755 --- a/plugins/php/versions/73/install.sh +++ b/plugins/php/versions/73/install.sh @@ -75,7 +75,6 @@ if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" OPTIONS="${OPTIONS} --with-pcre-dir=$(brew --prefix pcre2)" else - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-zlib-dir=$serverPath/lib/zlib" OPTIONS="${OPTIONS} ${ZIP_OPTION}" OPTIONS="${OPTIONS} --with-readline" diff --git a/plugins/php/versions/74/install.sh b/plugins/php/versions/74/install.sh index e21252768..17ee9609e 100755 --- a/plugins/php/versions/74/install.sh +++ b/plugins/php/versions/74/install.sh @@ -80,19 +80,7 @@ cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" - - # BREW_DIR=`which brew` - # BREW_DIR=${BREW_DIR/\/bin\/brew/} - # XML_LIB_DEPEND_DIR=`brew info libxml2 | grep /opt/homebrew/Cellar/libxml2 | cut -d \ -f 1 | awk 'END {print}'` - # XML_LIB_DEPEND_DIR=`brew info libxml2 | grep ${BREW_DIR}/Cellar/libxml2 | cut -d \ -f 1 | awk 'END {print}'` - # OPTIONS="${OPTIONS} --with-libxml=${XML_LIB_DEPEND_DIR}" - -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/80/install.sh b/plugins/php/versions/80/install.sh index 5ef789c72..1d84e4685 100755 --- a/plugins/php/versions/80/install.sh +++ b/plugins/php/versions/80/install.sh @@ -71,13 +71,9 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' + if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" -else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` diff --git a/plugins/php/versions/81/install.sh b/plugins/php/versions/81/install.sh index efbec9804..aef036d86 100755 --- a/plugins/php/versions/81/install.sh +++ b/plugins/php/versions/81/install.sh @@ -71,12 +71,9 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-curl" +# fi argon_version=`pkg-config libargon2 --modversion` if [ "$?" == "0" ];then diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh index dc7b2c14a..b10c42953 100755 --- a/plugins/php/versions/82/install.sh +++ b/plugins/php/versions/82/install.sh @@ -72,15 +72,12 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - BREW_DIR=`which brew` - BREW_DIR=${BREW_DIR/\/bin\/brew/} - LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` - OPTIONS="${OPTIONS} --with-curl=$LIB_DEPEND_DIR" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi +# if [ $sysName == 'Darwin' ]; then +# BREW_DIR=`which brew` +# BREW_DIR=${BREW_DIR/\/bin\/brew/} +# LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` +# OPTIONS="${OPTIONS} --with-curl=$LIB_DEPEND_DIR" +# fi IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index 979282dfd..334864df3 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -71,16 +71,9 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - # BREW_DIR=`which brew` - # BREW_DIR=${BREW_DIR/\/bin\/brew/} - # LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` - # OPTIONS="${OPTIONS} --with-curl=$LIB_DEPEND_DIR" - echo "curl" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-curl" +# fi argon_version=`pkg-config libargon2 --modversion` if [ "$?" == "0" ];then diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh index ea47ee991..5b1d0bac2 100755 --- a/plugins/php/versions/84/install.sh +++ b/plugins/php/versions/84/install.sh @@ -56,13 +56,10 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - # OPTIONS="${OPTIONS} --with-curl" - echo "curl pass" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi + +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-curl" +# fi argon_version=`pkg-config libargon2 --modversion` if [ "$?" == "0" ];then diff --git a/plugins/php/versions/phplib.conf b/plugins/php/versions/phplib.conf index 0dd87e352..f74c87cc5 100755 --- a/plugins/php/versions/phplib.conf +++ b/plugins/php/versions/phplib.conf @@ -419,6 +419,30 @@ "shell": "intl.sh", "check": "intl.so" }, + { + "name": "readline", + "versions": [ + "52", + "53", + "54", + "55", + "56", + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "通用扩展", + "msg": "命令行的支持", + "shell": "readline.sh", + "check": "readline.so" + }, { "name": "memcache", "versions": [ From bd873b48d02c004013ae3be4f09a21b9fe3c49ea Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:16:13 +0800 Subject: [PATCH 453/965] Update readline.sh --- plugins/php/versions/common/readline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php/versions/common/readline.sh b/plugins/php/versions/common/readline.sh index bde833361..6390b69fa 100755 --- a/plugins/php/versions/common/readline.sh +++ b/plugins/php/versions/common/readline.sh @@ -62,7 +62,7 @@ Install_lib() OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" fi - OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" + OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" $serverPath/php/$version/bin/phpize ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS From fa1819feb8936c356ce943adc041ee59c502b14a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:21:32 +0800 Subject: [PATCH 454/965] Update readline.sh --- plugins/php/versions/common/readline.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/php/versions/common/readline.sh b/plugins/php/versions/common/readline.sh index 6390b69fa..5ffadc220 100755 --- a/plugins/php/versions/common/readline.sh +++ b/plugins/php/versions/common/readline.sh @@ -47,8 +47,6 @@ Install_lib() return fi - cd ${rootPath}/plugins/php/lib && /bin/bash libedit.sh - if [ ! -f "$extFile" ];then if [ ! -d $sourcePath/php${version}/ext ];then @@ -62,7 +60,12 @@ Install_lib() OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" fi - OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" + if [ "$sysName" == "Darwin" ];then + cd ${rootPath}/plugins/php/lib && /bin/bash libedit.sh + OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" + else + OPTIONS="$OPTIONS --with-readline" + fi $serverPath/php/$version/bin/phpize ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS From 1ce5cac3baa0f6fc44686251254b929ebdf5c3b8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:23:55 +0800 Subject: [PATCH 455/965] Update openssl.sh --- plugins/php/versions/common/openssl.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/php/versions/common/openssl.sh b/plugins/php/versions/common/openssl.sh index c710de021..55251a562 100755 --- a/plugins/php/versions/common/openssl.sh +++ b/plugins/php/versions/common/openssl.sh @@ -46,6 +46,10 @@ Install_lib() cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh fi + if [ "$version" -gt "82" ];then + cd ${rootPath}/plugins/php/lib && /bin/bash openssl.sh + fi + if [ "$sysName" == "Darwin" ] ;then BREW_DIR=`which brew` BREW_DIR=${BREW_DIR/\/bin\/brew/} @@ -72,15 +76,18 @@ Install_lib() export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig fi + if [ "$version" -gt "82" ] && [ "$sysName" == "Darwin" ];then + export PKG_CONFIG_PATH=$serverPath/lib/openssl/lib/pkgconfig + fi + OPTIONS="" if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" fi $serverPath/php/$version/bin/phpize - ./configure --with-php-config=$serverPath/php/$version/bin/php-config \ - $OPTIONS \ - --with-openssl + echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS --with-openssl" + ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS --with-openssl make clean && make && make install && make clean if [ -d $sourcePath/php${version} ];then From fb3cd54ce0a91bc35e00ef0a364fc5ac4d9c2cac Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 01:54:48 +0800 Subject: [PATCH 456/965] Update install.sh --- plugins/php/versions/84/install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh index 5b1d0bac2..cdb855bd2 100755 --- a/plugins/php/versions/84/install.sh +++ b/plugins/php/versions/84/install.sh @@ -104,6 +104,8 @@ if version_lt "$libzip_version" "0.11.0" ;then ZIP_OPTION="--with-zip=$serverPath/lib/libzip" fi +# OPTIONS="${OPTIONS} --enable-debug" +# OPTIONS="${OPTIONS} --enable-dtrace" echo "$sourcePath/php/php${PHP_VER}" From 9553add27ab982896412d05f36a72605d36b693a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 02:07:07 +0800 Subject: [PATCH 457/965] Update readline.sh --- plugins/php/versions/common/readline.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/php/versions/common/readline.sh b/plugins/php/versions/common/readline.sh index 5ffadc220..40e13c9af 100755 --- a/plugins/php/versions/common/readline.sh +++ b/plugins/php/versions/common/readline.sh @@ -64,7 +64,8 @@ Install_lib() cd ${rootPath}/plugins/php/lib && /bin/bash libedit.sh OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" else - OPTIONS="$OPTIONS --with-readline" + cd ${rootPath}/plugins/php/lib && /bin/bash libedit.sh + OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" fi $serverPath/php/$version/bin/phpize From 5b9a4146881168c61ae557a6fd5010ef61ee2e5a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 02:24:29 +0800 Subject: [PATCH 458/965] Update openssl.sh --- plugins/php/versions/common/openssl.sh | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/plugins/php/versions/common/openssl.sh b/plugins/php/versions/common/openssl.sh index 55251a562..75ad433b3 100755 --- a/plugins/php/versions/common/openssl.sh +++ b/plugins/php/versions/common/openssl.sh @@ -73,26 +73,37 @@ Install_lib() # openssl_version=`pkg-config openssl --modversion` # export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig if [ "$version" -lt "81" ] && [ "$sysName" != "Darwin" ];then - export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig fi - if [ "$version" -gt "82" ] && [ "$sysName" == "Darwin" ];then - export PKG_CONFIG_PATH=$serverPath/lib/openssl/lib/pkgconfig + if [ "$sysName" == "Darwin" ];then + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'` + OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)" + export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig + export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include" + export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz" fi + # if [ "$version" -gt "82" ] && [ "$sysName" == "Darwin" ];then + # export PKG_CONFIG_PATH=$serverPath/lib/openssl/lib/pkgconfig + # fi + OPTIONS="" if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" fi $serverPath/php/$version/bin/phpize - echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS --with-openssl" - ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS --with-openssl + # --with-openssl + echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS" + ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS make clean && make && make install && make clean - if [ -d $sourcePath/php${version} ];then - cd ${sourcePath} && rm -rf $sourcePath/php${version} - fi + # if [ -d $sourcePath/php${version} ];then + # cd ${sourcePath} && rm -rf $sourcePath/php${version} + # fi fi From 8ec3b66f20abb82f65d520c4bd5ccd29d80c2974 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 02:24:37 +0800 Subject: [PATCH 459/965] Update install.sh --- plugins/php/versions/53/install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/php/versions/53/install.sh b/plugins/php/versions/53/install.sh index 839d8dcb0..151fafc88 100755 --- a/plugins/php/versions/53/install.sh +++ b/plugins/php/versions/53/install.sh @@ -65,9 +65,10 @@ fi # OPTIONS="${OPTIONS} --with-gd --enable-gd-native-ttf" # OPTIONS="${OPTIONS} --with-jpeg --with-jpeg-dir=/usr/lib" OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" -fi + +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" +# fi IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then From 573e3192fc1b40ef1c2c3ab7dd1eba95a4100ca4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 02:36:40 +0800 Subject: [PATCH 460/965] Update swoole.sh --- plugins/php/versions/common/swoole.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php/versions/common/swoole.sh b/plugins/php/versions/common/swoole.sh index b0cb4bdfd..d39e624aa 100755 --- a/plugins/php/versions/common/swoole.sh +++ b/plugins/php/versions/common/swoole.sh @@ -26,7 +26,7 @@ elif [ "$version" == "71" ];then elif [ "$version" == "72" ] || [ "$version" == "73" ] || [ "$version" == "74" ];then LIBV=4.8.10 elif [ "$version" -gt "74" ];then - LIBV=5.1.1 + LIBV=5.1.6 else echo 'other?' exit 0 From d604c966b23f1c833c28e10041515081396c83ef Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 02:47:43 +0800 Subject: [PATCH 461/965] update --- plugins/php/versions/81/install.sh | 4 ++-- plugins/php/versions/82/install.sh | 13 +++---------- plugins/php/versions/83/install.sh | 4 ++-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/plugins/php/versions/81/install.sh b/plugins/php/versions/81/install.sh index aef036d86..cf65d0da4 100755 --- a/plugins/php/versions/81/install.sh +++ b/plugins/php/versions/81/install.sh @@ -15,7 +15,7 @@ function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } -version=8.1.30 +version=8.1.31 PHP_VER=81 Install_php() { @@ -55,7 +55,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi #检测文件是否损坏. - md5_file_ok=f24a6007f0b25a53cb7fbaee69c85017e0345b62089c2425a0afb7e177192ed1 + md5_file_ok=c4f244d46ba51c72f7d13d4f66ce6a9e9a8d6b669c51be35e01765ba58e7afca if [ -f $sourcePath/php/php-${version}.tar.xz ];then md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'` if [ "${md5_file}" != "${md5_file_ok}" ]; then diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh index b10c42953..28405bcbc 100755 --- a/plugins/php/versions/82/install.sh +++ b/plugins/php/versions/82/install.sh @@ -15,7 +15,7 @@ function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } -version=8.2.24 +version=8.2.26 PHP_VER=82 Install_php() { @@ -56,7 +56,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi #检测文件是否损坏. - md5_file_ok=80a5225746a9eb484475b312d4c626c63a88a037d8e56d214f30205e1ba1411a + md5_file_ok=54747400cb4874288ad41a785e6147e2ff546cceeeb55c23c00c771ac125c6ef if [ -f $sourcePath/php/php-${version}.tar.xz ];then md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'` if [ "${md5_file}" != "${md5_file_ok}" ]; then @@ -72,12 +72,6 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -# if [ $sysName == 'Darwin' ]; then -# BREW_DIR=`which brew` -# BREW_DIR=${BREW_DIR/\/bin\/brew/} -# LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` -# OPTIONS="${OPTIONS} --with-curl=$LIB_DEPEND_DIR" -# fi IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then @@ -123,8 +117,7 @@ if version_lt "$libzip_version" "0.11.0" ;then fi -echo "$sourcePath/php/php${PHP_VER}" - +# echo "$sourcePath/php/php${PHP_VER}" if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} ./buildconf --force diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index 334864df3..fb703e87b 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -14,7 +14,7 @@ function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } -version=8.3.12 +version=8.3.14 PHP_VER=83 Install_php() { @@ -55,7 +55,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi #检测文件是否损坏. - md5_file_ok=f774e28633e26fc8c5197f4dae58ec9e3ff87d1b4311cbc61ab05a7ad24bd131 + md5_file_ok=58b4cb9019bf70c0cbcdb814c7df79b9065059d14cf7dbf48d971f8e56ae9be7 if [ -f $sourcePath/php/php-${version}.tar.xz ];then md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'` if [ "${md5_file}" != "${md5_file_ok}" ]; then From 7e55e1e29a4d8123471b34b572e694b91563b0e9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:13:04 +0800 Subject: [PATCH 462/965] Update plugin.py --- web/utils/plugin.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index f50c2afe8..be3166db9 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -78,6 +78,7 @@ class plugin(object): ] __plugin_dir = 'plugins' + __tasks = None # lock _instance_lock = threading.Lock() @@ -366,6 +367,31 @@ class plugin(object): return True return False + def checkSetupTask(self, name, version, coexist): + if not self.__tasks: + self.__tasks = thisdb.getTaskRunAll() + isTask = '1' + for task in self.__tasks: + tmpt = mw.getStrBetween('[', ']', task['name']) + if not tmpt: + continue + task_sign = tmpt.split('-') + task_len = len(task_sign) + + task_name = task_sign[0].lower() + task_ver = task_sign[1] + if task_len > 2: + nameArr = task_sign[0:task_len - 1] + task_name = '-'.join(nameArr).lower() + task_ver = task_sign[task_len - 1] + if coexist: + if task_name == name and task_ver == version: + isTask = task['status'] + else: + if task_name == name: + isTask = task['status'] + return isTask + def checkDisplayIndex(self, name, version, coexist): indexList = thisdb.getOptionByJson('display_index',default=[]) if coexist: @@ -451,6 +477,7 @@ class plugin(object): if path.find('VERSION') > -1: pInfo['path'] = path.replace('VERSION', info['versions']) + pInfo['task'] = self.checkSetupTask(pInfo['name'], info['versions'], coexist) pInfo['display'] = self.checkDisplayIndex(info['name'], pInfo['versions'], coexist) pInfo['setup'] = os.path.exists(pInfo['install_checks']) From 325a113ac6068fdc74b1f6fe8c5f5750178e61be Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:13:30 +0800 Subject: [PATCH 463/965] Update mw.py --- web/core/mw.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 3ba9a2763..d4fd928ba 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -50,13 +50,24 @@ def execShell(cmdstring, cwd=None, timeout=None, shell=True): return sub.communicate() data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) + success = data[0] + error = data[1] + # python3 fix 返回byte数据 + if isinstance(success, bytes): + # success = str(success, encoding='utf-8') + try: + success = success.decode('utf-8') + except Exception as e: + success = str(e) + + if isinstance(error, bytes): + # error = str(error, encoding='utf-8') + try: + error = error.decode('utf-8') + except Exception as e: + error = str(e) + return (success, error) def getTracebackInfo(): From bdfc044783262c278ac67989288b7f78a037b1e4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:13:34 +0800 Subject: [PATCH 464/965] Update tasks.py --- web/thisdb/tasks.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py index 656bc4313..7ee8ab8c5 100644 --- a/web/thisdb/tasks.py +++ b/web/thisdb/tasks.py @@ -98,7 +98,7 @@ def getTaskPage( return rdata def getTaskFirstByRun() -> None: - data = mw.M('tasks').where('status=? or status=0', (-1,)).field(__FIELD).order('id asc').find() + data = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).order('id asc').find() if data is None: return None return data @@ -109,6 +109,10 @@ def getTaskRunList(page = 1,size = 10): task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id asc').select() return task_list +def getTaskRunAll(): + task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).order('id asc').select() + return task_list + def getTaskRunPage(page = 1, size = 10): start = (page - 1) * size limit = str(start) + ',' + str(size) From 407dc56f4080b51d90b586db2065a887dba7ba43 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:13:41 +0800 Subject: [PATCH 465/965] Update install.sh --- plugins/php/versions/83/install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index fb703e87b..93e237c5d 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -71,9 +71,6 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -# if [ $sysName == 'Darwin' ]; then -# OPTIONS="${OPTIONS} --with-curl" -# fi argon_version=`pkg-config libargon2 --modversion` if [ "$?" == "0" ];then From dc17d67dc9bccbd0397cbcc99f1bf75598b6ccc7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:32:10 +0800 Subject: [PATCH 466/965] Update plugin.py --- web/utils/plugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/utils/plugin.py b/web/utils/plugin.py index be3166db9..57ccc1a6f 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -368,8 +368,7 @@ class plugin(object): return False def checkSetupTask(self, name, version, coexist): - if not self.__tasks: - self.__tasks = thisdb.getTaskRunAll() + self.__tasks = thisdb.getTaskRunAll() isTask = '1' for task in self.__tasks: tmpt = mw.getStrBetween('[', ']', task['name']) From 70aca29ce1169c67986f96cf049c10c88edea0cd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:34:38 +0800 Subject: [PATCH 467/965] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f7d56fe65..af6f71703 100644 --- a/.gitignore +++ b/.gitignore @@ -192,3 +192,4 @@ plugins/dztasks +logs/panel_task.lock From d1d276d026c45a7b8595dd51fd23d3319c011d46 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:35:01 +0800 Subject: [PATCH 468/965] Update .gitignore --- .gitignore | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index af6f71703..9bde8372c 100644 --- a/.gitignore +++ b/.gitignore @@ -123,7 +123,7 @@ logs/panel.pid logs/panel.log logs/panel_task.log logs/panel_exec.log - +logs/panel_task.lock ssl/local ssl/nginx @@ -189,7 +189,3 @@ plugins/goedge-admin plugins/goedge-node plugins/goedge-happy plugins/dztasks - - - -logs/panel_task.lock From 7750fb7b386af3d74811b0f41aaf134af0143352 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:54:18 +0800 Subject: [PATCH 469/965] Update readline.sh --- plugins/php/versions/common/readline.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/php/versions/common/readline.sh b/plugins/php/versions/common/readline.sh index 40e13c9af..0c9554019 100755 --- a/plugins/php/versions/common/readline.sh +++ b/plugins/php/versions/common/readline.sh @@ -54,7 +54,10 @@ Install_lib() fi cd $sourcePath/php${version}/ext/${LIBNAME} - + if [ ! -f "config.m4" ];then + mv config0.m4 config.m4 + fi + OPTIONS="" if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" @@ -68,10 +71,10 @@ Install_lib() OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" fi + cd $sourcePath/php${version}/ext/${LIBNAME} $serverPath/php/$version/bin/phpize ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS make clean && make && make install && make clean - fi if [ ! -f "$extFile" ];then From 49858199a51b45dfad05ec7ac418d044321d07c3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 03:55:03 +0800 Subject: [PATCH 470/965] Update install.sh --- plugins/php/install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/php/install.sh b/plugins/php/install.sh index 77b87bbfa..c90bdbc6b 100755 --- a/plugins/php/install.sh +++ b/plugins/php/install.sh @@ -65,6 +65,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then # cd /www/server/mdserver-web/plugins/php/versions/common && bash fileinfo.sh install 81 cd ${rootPath}/plugins/php/versions/common && bash curl.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash gd.sh install ${type} + cd ${rootPath}/plugins/php/versions/common && bash readline.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash iconv.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash exif.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash intl.sh install ${type} From d0bc04e53ba2e00fbf96704134c5af51c61b7a17 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 04:05:33 +0800 Subject: [PATCH 471/965] Update index.py --- plugins/php/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/php/index.py b/plugins/php/index.py index 1a6e8d6c9..e8997a43f 100755 --- a/plugins/php/index.py +++ b/plugins/php/index.py @@ -344,8 +344,8 @@ def phpOp(version, method): def start(version): - mw.execShell( - 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib') + cmd = 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib' + mw.execShell(cmd) return phpOp(version, 'start') From dbe9fd422265d8eb3d52f123b4d2e7ab4b978b63 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 04:49:47 +0800 Subject: [PATCH 472/965] update --- plugins/php/index.py | 2 +- plugins/php/versions/common/openssl.sh | 37 +++++++++++++++++++------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/plugins/php/index.py b/plugins/php/index.py index e8997a43f..915d9915b 100755 --- a/plugins/php/index.py +++ b/plugins/php/index.py @@ -344,7 +344,7 @@ def phpOp(version, method): def start(version): - cmd = 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib' + cmd = 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib:/opt/homebrew/lib' mw.execShell(cmd) return phpOp(version, 'start') diff --git a/plugins/php/versions/common/openssl.sh b/plugins/php/versions/common/openssl.sh index 75ad433b3..b76a61535 100755 --- a/plugins/php/versions/common/openssl.sh +++ b/plugins/php/versions/common/openssl.sh @@ -70,13 +70,36 @@ Install_lib() mv config0.m4 config.m4 fi + OPTIONS="" + if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then + OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" + fi + # openssl_version=`pkg-config openssl --modversion` # export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig if [ "$version" -lt "81" ] && [ "$sysName" != "Darwin" ];then export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig fi - if [ "$sysName" == "Darwin" ];then + # Darwin + # otool -L /Users/midoks/Desktop/mwdev/server/php/83/bin/php + # lldb /Users/midoks/Desktop/mwdev/server/php/83/bin/php -r 'phpinfo()' + # otool -L /Users/midoks/Desktop/mwdev/server/php/83/lib/php/extensions/no-debug-non-zts-20230831/openssl.so + # ldd /www/server/php/83/bin/php + + if [ "$version" -lt "83" ] && [ "$sysName" == "Darwin" ];then + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'` + OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)" + export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig + export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include" + export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz" + + echo "$LIB_DEPEND_DIR/lib/pkgconfig" + fi + + if [ "$version" -ge "84" ] && [ "$sysName" == "Darwin" ];then BREW_DIR=`which brew` BREW_DIR=${BREW_DIR/\/bin\/brew/} LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'` @@ -90,20 +113,16 @@ Install_lib() # export PKG_CONFIG_PATH=$serverPath/lib/openssl/lib/pkgconfig # fi - OPTIONS="" - if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then - OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" - fi - + $serverPath/php/$version/bin/phpize # --with-openssl echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS" ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS make clean && make && make install && make clean - # if [ -d $sourcePath/php${version} ];then - # cd ${sourcePath} && rm -rf $sourcePath/php${version} - # fi + if [ -d $sourcePath/php${version} ];then + cd ${sourcePath} && rm -rf $sourcePath/php${version} + fi fi From 8f4fca5a6c75c57595f1efc150eeb4a462d29c49 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 05:10:44 +0800 Subject: [PATCH 473/965] update --- plugins/php/versions/74/install.sh | 7 ++++--- scripts/init.d/mw.tpl | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/php/versions/74/install.sh b/plugins/php/versions/74/install.sh index 17ee9609e..606cb7f90 100755 --- a/plugins/php/versions/74/install.sh +++ b/plugins/php/versions/74/install.sh @@ -79,9 +79,10 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" -fi + +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" +# fi IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 11d0c91d5..1919765b3 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -531,10 +531,10 @@ mw_default(){ port=$(cat ${PANEL_DIR}/data/port.pl) fi - # if [ ! -f ${PANEL_DIR}/data/default.pl ];then - # echo -e "\033[33mInstall Failed\033[0m" - # exit 1 - # fi + if [ ! -f ${PANEL_DIR}/data/default.pl ];then + echo -e "\033[33mInstall Failed\033[0m" + exit 1 + fi password=$(cat ${PANEL_DIR}/data/default.pl) From d2d79092cadc531755e2cc1047bae2577279f68e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 05:12:09 +0800 Subject: [PATCH 474/965] up --- plugins/php/versions/common/mcrypt.sh | 2 +- plugins/php/versions/phplib.conf | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/php/versions/common/mcrypt.sh b/plugins/php/versions/common/mcrypt.sh index 19fb0aa87..f1fa160ac 100755 --- a/plugins/php/versions/common/mcrypt.sh +++ b/plugins/php/versions/common/mcrypt.sh @@ -22,7 +22,7 @@ if [ "$version" -lt "72" ];then fi if [ "$version" == "84" ];then - LIBV=1.0.7 + LIBV=1.0.6 fi diff --git a/plugins/php/versions/phplib.conf b/plugins/php/versions/phplib.conf index f74c87cc5..9e993fdd0 100755 --- a/plugins/php/versions/phplib.conf +++ b/plugins/php/versions/phplib.conf @@ -160,8 +160,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "通用扩展", "msg": "加密软件!", From db79be96edbc6cd7da5789303f8a57febe5930cf Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 05:16:39 +0800 Subject: [PATCH 475/965] Update mcrypt.sh --- plugins/php/versions/common/mcrypt.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/php/versions/common/mcrypt.sh b/plugins/php/versions/common/mcrypt.sh index f1fa160ac..697b69b99 100755 --- a/plugins/php/versions/common/mcrypt.sh +++ b/plugins/php/versions/common/mcrypt.sh @@ -14,7 +14,7 @@ actionType=$1 version=$2 LIBNAME=mcrypt -LIBV=1.0.6 +LIBV=1.0.7 if [ "$version" -lt "72" ];then echo "not need" @@ -22,7 +22,9 @@ if [ "$version" -lt "72" ];then fi if [ "$version" == "84" ];then - LIBV=1.0.6 + LIBV=1.0.7 + echo "not need" + exit 1 fi @@ -70,7 +72,12 @@ Install_lib() fi if [ "$sysName" == "Darwin" ];then + + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info mcrypt | grep ${BREW_DIR}/Cellar/mcrypt | cut -d \ -f 1 | awk 'END {print}'` OPTIONS="$OPTIONS --with-mcrypt=$(brew --prefix mcrypt)" + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LIB_DEPEND_DIR/lib/pkgconfig fi $serverPath/php/$version/bin/phpize From 6436a933c9b56c2f00245a750a3ab14541f356c0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 05:19:53 +0800 Subject: [PATCH 476/965] Update panel_task.py --- panel_task.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/panel_task.py b/panel_task.py index 441572d4c..9f46ab710 100755 --- a/panel_task.py +++ b/panel_task.py @@ -102,7 +102,9 @@ def runPanelTask(): end = int(time.time()) thisdb.setTaskData(run_task['id'], end=end) thisdb.setTaskStatus(run_task['id'], 1) - os.remove(lock_file) + + if thisdb.getTaskUnexecutedCount() < 1: + os.remove(lock_file) except Exception as e: pass From 96d5a817d5b317feb09c44d8f120bec78f6398c7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 15:31:26 +0800 Subject: [PATCH 477/965] Update install.sh --- plugins/memcached/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/memcached/install.sh b/plugins/memcached/install.sh index ec5c8e65f..490dde8ca 100755 --- a/plugins/memcached/install.sh +++ b/plugins/memcached/install.sh @@ -9,7 +9,7 @@ serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl sys_os=`uname` -VERSION=1.6.29 +VERSION=1.6.32 echo $sys_os From d62531c7006a1f833ea552c1c1cece1800fd3413 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 15:31:37 +0800 Subject: [PATCH 478/965] Update info.json --- plugins/memcached/info.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/memcached/info.json b/plugins/memcached/info.json index b0a9b6463..389213894 100755 --- a/plugins/memcached/info.json +++ b/plugins/memcached/info.json @@ -5,7 +5,7 @@ "title": "Memcached", "shell": "install.sh", "versions":["1.6"], - "updates":["1.6.21"], + "updates":["1.6.32"], "tip": "soft", "checks": "server/memcached", "path":"server/memcached", From b8d6d9ed60f0f92d1c71b5b67176be368582a515 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 15:54:29 +0800 Subject: [PATCH 479/965] update --- plugins/redis/info.json | 4 ++-- plugins/redis/install.sh | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/redis/info.json b/plugins/redis/info.json index 4793a12d7..334e636a9 100755 --- a/plugins/redis/info.json +++ b/plugins/redis/info.json @@ -4,8 +4,8 @@ "name": "redis", "title": "Redis", "shell": "install.sh", - "versions":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.5"], - "updates":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.5"], + "versions":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.6", "7.4.1"], + "updates":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.6", "7.4.1"], "tip": "soft", "checks": "server/redis", "path": "server/redis", diff --git a/plugins/redis/install.sh b/plugins/redis/install.sh index 444726156..0ec2ecd72 100755 --- a/plugins/redis/install.sh +++ b/plugins/redis/install.sh @@ -18,12 +18,11 @@ serverPath=$(dirname "$rootPath") # /Users/midoks/Desktop/mwdev/server/redis/bin/redis-cli -h 127.0.0.1 -p 6399 # /www/server/redis/bin/redis-cli -h 127.0.0.1 -p 6399 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/source/redis From 70507e9f652f61a00ef7a038c32d4963abfd251c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 16:05:06 +0800 Subject: [PATCH 480/965] Update index.py --- plugins/redis/index.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/redis/index.py b/plugins/redis/index.py index 494064594..b52f33d74 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/index.py @@ -133,7 +133,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): @@ -158,8 +158,7 @@ def initDreplace(): if not os.path.exists(dst_conf_init): conf_content = mw.readFile(getConfTpl()) conf_content = conf_content.replace('{$SERVER_PATH}', service_path) - conf_content = conf_content.replace( - '{$REDIS_PASS}', mw.getRandomString(10)) + conf_content = conf_content.replace('{$REDIS_PASS}', mw.getRandomString(10)) mw.writeFile(dst_conf, conf_content) mw.writeFile(dst_conf_init, 'ok') From 4826dbd8a957939409756b775e11e0ec390c98fe Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 16:05:27 +0800 Subject: [PATCH 481/965] Update index.py --- plugins/redis/index.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/redis/index.py b/plugins/redis/index.py index b52f33d74..cc3ea8742 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/index.py @@ -169,9 +169,9 @@ def initDreplace(): if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl' service_path = mw.getServerDir() - se_content = mw.readFile(systemServiceTpl) - se_content = se_content.replace('{$SERVER_PATH}', service_path) - mw.writeFile(systemService, se_content) + content = mw.readFile(systemServiceTpl) + content = content.replace('{$SERVER_PATH}', service_path) + mw.writeFile(systemService, content) mw.execShell('systemctl daemon-reload') return file_bin From 777bfa1145cc4e3968f2939c23511adb7157f2f1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 16:06:02 +0800 Subject: [PATCH 482/965] Update index.py --- plugins/redis/index.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/redis/index.py b/plugins/redis/index.py index cc3ea8742..914bdcc16 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/index.py @@ -150,6 +150,7 @@ def initDreplace(): # log dataLog = getServerDir() + '/data' if not os.path.exists(dataLog): + mw.execShell('mkdir -p ' + dataLog) mw.execShell('chmod +x ' + file_bin) # config replace From 0903589e5b06b1a7a250141011b1b4c009b284cf Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:04:09 +0800 Subject: [PATCH 483/965] Update info.json --- plugins/redis/info.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/redis/info.json b/plugins/redis/info.json index 334e636a9..9ede6b27c 100755 --- a/plugins/redis/info.json +++ b/plugins/redis/info.json @@ -4,8 +4,8 @@ "name": "redis", "title": "Redis", "shell": "install.sh", - "versions":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.6", "7.4.1"], - "updates":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.6", "7.4.1"], + "versions":["4.0.14","5.0.14","6.0.20","6.2.16","7.0.15","7.2.6", "7.4.1"], + "updates":["4.0.14","5.0.14","6.0.20","6.2.16","7.0.15","7.2.6", "7.4.1"], "tip": "soft", "checks": "server/redis", "path": "server/redis", From 79c8081c307a611a4d90aa2a955c7260263be74e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:04:15 +0800 Subject: [PATCH 484/965] Update index.py --- plugins/redis/index.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/plugins/redis/index.py b/plugins/redis/index.py index 914bdcc16..a0c361030 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/index.py @@ -224,7 +224,7 @@ def getPort(): conf = getServerDir() + '/redis.conf' content = mw.readFile(conf) - rep = r"^(" + r'port' + r')\s*([.0-9A-Za-z_& ~]+)' + rep = r"^(" + r'port' + r'\)\s*([.0-9A-Za-z_& ~]+)' tmp = re.search(rep, content, re.M) if tmp: return tmp.groups()[1] @@ -236,7 +236,7 @@ def getRedisCmd(): requirepass = "" conf = getServerDir() + '/redis.conf' content = mw.readFile(conf) - rep = r"^(requirepass" + r')\s*([.0-9A-Za-z_& ~]+)' + rep = r"^(requirepass" + r"\)\s*([.0-9A-Za-z_& ~]+)" tmp = re.search(rep, content, re.M) if tmp: requirepass = tmp.groups()[1] @@ -246,12 +246,10 @@ def getRedisCmd(): # findDebian = mw.execShell('cat /etc/issue |grep Debian') # if findDebian[0] != '': # default_ip = mw.getLocalIp() - cmd = getServerDir() + "/bin/redis-cli -h " + \ - default_ip + ' -p ' + port + " " + cmd = getServerDir() + "/bin/redis-cli -h " + default_ip + ' -p ' + port + " " if requirepass != "": - cmd = getServerDir() + '/bin/redis-cli -h ' + default_ip + \ - ' -p ' + port + ' -a "' + requirepass + '" ' + cmd = getServerDir() + '/bin/redis-cli -h ' + default_ip + ' -p ' + port + ' -a "' + requirepass + '" ' return cmd From 637a4dacb9df2bb73594b311bee1b4a1040877e4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:11:28 +0800 Subject: [PATCH 485/965] update --- plugins/mongodb/index.py | 2 +- plugins/mongodb/info.json | 2 +- plugins/mongodb/versions/8.0/centos.sh | 108 +++++++++++++++++++++++ plugins/mongodb/versions/8.0/debian.sh | 96 ++++++++++++++++++++ plugins/mongodb/versions/8.0/euler.sh | 108 +++++++++++++++++++++++ plugins/mongodb/versions/8.0/macos.sh | 81 +++++++++++++++++ plugins/mongodb/versions/8.0/opensuse.sh | 95 ++++++++++++++++++++ plugins/mongodb/versions/8.0/ubuntu.sh | 91 +++++++++++++++++++ 8 files changed, 581 insertions(+), 2 deletions(-) create mode 100644 plugins/mongodb/versions/8.0/centos.sh create mode 100644 plugins/mongodb/versions/8.0/debian.sh create mode 100644 plugins/mongodb/versions/8.0/euler.sh create mode 100644 plugins/mongodb/versions/8.0/macos.sh create mode 100644 plugins/mongodb/versions/8.0/opensuse.sh create mode 100644 plugins/mongodb/versions/8.0/ubuntu.sh diff --git a/plugins/mongodb/index.py b/plugins/mongodb/index.py index 07a1b5a3c..3396e44e3 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -226,7 +226,7 @@ def initDreplace(): mw.execShell("chmod 400 "+mg_key) 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/mongodb/info.json b/plugins/mongodb/info.json index 70fd76297..46b147829 100755 --- a/plugins/mongodb/info.json +++ b/plugins/mongodb/info.json @@ -7,7 +7,7 @@ "shell": "install.sh", "install_pre_inspection":true, "uninstall_pre_inspection":true, - "versions":["4.4","5.0","6.0","7.0"], + "versions":["4.4","5.0","6.0","7.0","8.0"], "tip": "soft", "checks": "server/mongodb", "path": "server/mongodb", diff --git a/plugins/mongodb/versions/8.0/centos.sh b/plugins/mongodb/versions/8.0/centos.sh new file mode 100644 index 000000000..ddbcf22be --- /dev/null +++ b/plugins/mongodb/versions/8.0/centos.sh @@ -0,0 +1,108 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl +VERSION=7.0.14 +SYS_ARCH=`arch` + +SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` +SYS_NAME=${SYS_VERSION_ID/./} +SYS_NAME_LEN=`echo "$SYS_NAME" | wc -L` + +if [ "$SYS_NAME_LEN" == "1" ];then + SYS_NAME=${SYS_NAME}0 +fi + +if [ "$SYS_ARCH" == "aarch64" ];then + if [ "$SYS_NAME" -gt "90" ];then + SYS_NAME="90" + fi + + if [ "$SYS_NAME" -lt "82" ];then + SYS_NAME="82" + fi +else + + if [ "$SYS_NAME" -gt "90" ];then + SYS_NAME="90" + fi + + if [ "$SYS_NAME" -lt "70" ];then + SYS_NAME="70" + fi +fi + +MG_DIR=$serverPath/source/mongodb +mkdir -p $MG_DIR + +FILE_NAME=mongodb-linux-${SYS_ARCH}-rhel${SYS_NAME}-${VERSION} +FILE_NAME_TGZ=${FILE_NAME}.tgz + +if [ ! -f $MG_DIR/${FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${FILE_NAME} ];then + cd $MG_DIR && tar -zxvf ${FILE_NAME_TGZ} +fi + +if [ ! -d $serverPath/mongodb/bin ];then + mkdir -p $serverPath/mongodb + cd $MG_DIR/${FILE_NAME} && cp -rf ./bin $serverPath/mongodb +fi + +cd ${MG_DIR} && rm -rf ${MG_DIR}/${FILE_NAME} + +#--------------- mongosh tool install ------------------ # +TOOL_VERSION=2.2.5 +TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-x64 +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-arm64 +fi +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-rhel90-aarch64-100.9.4.tgz +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-rhel90-x86_64-100.9.4.tgz +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-rhel83-s390x-100.9.4.tgz +#--------------- mongodb database install ------------------ # +TOOL_VERSION=100.9.4 +TOOL_FILE_NAME=mongodb-database-tools-rhel${SYS_NAME}-x86_64-${TOOL_VERSION} +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-rhel${SYS_NAME}-aarch64-${TOOL_VERSION} +fi + +if [ "arm64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-rhel${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://fastdl.mongodb.org/tools/db/${TOOL_FILE_NAME_TGZ} +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + diff --git a/plugins/mongodb/versions/8.0/debian.sh b/plugins/mongodb/versions/8.0/debian.sh new file mode 100644 index 000000000..3b5e0b18b --- /dev/null +++ b/plugins/mongodb/versions/8.0/debian.sh @@ -0,0 +1,96 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl +VERSION=7.0.14 +SYS_ARCH=`arch` +SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` +SYS_NAME=${SYS_VERSION_ID/./} + + +if [ "$SYS_NAME" -gt "11" ];then + SYS_NAME="11" +fi + +if [ "$SYS_NAME" -lt "11" ];then + SYS_NAME="11" +fi + +if [ "$SYS_NAME" == "" ];then + SYS_NAME="11" +fi + +FILE_NAME=mongodb-linux-${SYS_ARCH}-debian${SYS_NAME}-${VERSION} +FILE_NAME_TGZ=${FILE_NAME}.tgz + + +MG_DIR=$serverPath/source/mongodb +mkdir -p $MG_DIR + +if [ ! -f $MG_DIR/${FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${FILE_NAME_TGZ} +fi + +if [ ! -d $serverPath/mongodb/bin ];then + mkdir -p $serverPath/mongodb + cd $MG_DIR/${FILE_NAME} && cp -rf ./bin $serverPath/mongodb +fi + +cd ${MG_DIR} && rm -rf ${MG_DIR}/${FILE_NAME} + + +#--------------- mongosh tool install ------------------ # +TOOL_VERSION=2.2.5 +TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-x64 +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-arm64 +fi +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + + +#--------------- mongodb database install ------------------ # +TOOL_VERSION=100.9.4 +TOOL_FILE_NAME=mongodb-database-tools-debian${SYS_NAME}-x86_64-${TOOL_VERSION} +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-debian${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +if [ "arm64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-debian${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://fastdl.mongodb.org/tools/db/${TOOL_FILE_NAME_TGZ} +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + diff --git a/plugins/mongodb/versions/8.0/euler.sh b/plugins/mongodb/versions/8.0/euler.sh new file mode 100644 index 000000000..ddbcf22be --- /dev/null +++ b/plugins/mongodb/versions/8.0/euler.sh @@ -0,0 +1,108 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl +VERSION=7.0.14 +SYS_ARCH=`arch` + +SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` +SYS_NAME=${SYS_VERSION_ID/./} +SYS_NAME_LEN=`echo "$SYS_NAME" | wc -L` + +if [ "$SYS_NAME_LEN" == "1" ];then + SYS_NAME=${SYS_NAME}0 +fi + +if [ "$SYS_ARCH" == "aarch64" ];then + if [ "$SYS_NAME" -gt "90" ];then + SYS_NAME="90" + fi + + if [ "$SYS_NAME" -lt "82" ];then + SYS_NAME="82" + fi +else + + if [ "$SYS_NAME" -gt "90" ];then + SYS_NAME="90" + fi + + if [ "$SYS_NAME" -lt "70" ];then + SYS_NAME="70" + fi +fi + +MG_DIR=$serverPath/source/mongodb +mkdir -p $MG_DIR + +FILE_NAME=mongodb-linux-${SYS_ARCH}-rhel${SYS_NAME}-${VERSION} +FILE_NAME_TGZ=${FILE_NAME}.tgz + +if [ ! -f $MG_DIR/${FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${FILE_NAME} ];then + cd $MG_DIR && tar -zxvf ${FILE_NAME_TGZ} +fi + +if [ ! -d $serverPath/mongodb/bin ];then + mkdir -p $serverPath/mongodb + cd $MG_DIR/${FILE_NAME} && cp -rf ./bin $serverPath/mongodb +fi + +cd ${MG_DIR} && rm -rf ${MG_DIR}/${FILE_NAME} + +#--------------- mongosh tool install ------------------ # +TOOL_VERSION=2.2.5 +TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-x64 +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-arm64 +fi +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-rhel90-aarch64-100.9.4.tgz +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-rhel90-x86_64-100.9.4.tgz +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-rhel83-s390x-100.9.4.tgz +#--------------- mongodb database install ------------------ # +TOOL_VERSION=100.9.4 +TOOL_FILE_NAME=mongodb-database-tools-rhel${SYS_NAME}-x86_64-${TOOL_VERSION} +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-rhel${SYS_NAME}-aarch64-${TOOL_VERSION} +fi + +if [ "arm64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-rhel${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://fastdl.mongodb.org/tools/db/${TOOL_FILE_NAME_TGZ} +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + diff --git a/plugins/mongodb/versions/8.0/macos.sh b/plugins/mongodb/versions/8.0/macos.sh new file mode 100644 index 000000000..6df92f5d4 --- /dev/null +++ b/plugins/mongodb/versions/8.0/macos.sh @@ -0,0 +1,81 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +SYS_ARCH=`arch` + +install_tmp=${rootPath}/tmp/mw_install.pl +VERSION=7.0.14 + +MG_DIR=$serverPath/source/mongodb +mkdir -p $MG_DIR + +if [ ! -f $MG_DIR/mongodb-macos-x86_64-${VERSION}.tgz ]; then + wget --no-check-certificate -O $MG_DIR/mongodb-macos-x86_64-${VERSION}.tgz https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-${VERSION}.tgz + echo "wget --no-check-certificate -O $MG_DIR/mongodb-macos-x86_64-${VERSION}.tgz https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-${VERSION}.tgz" +fi + +if [ ! -d $MG_DIR/mongodb-macos-x86_64-${VERSION} ];then + cd $MG_DIR && tar -zxvf mongodb-macos-x86_64-${VERSION}.tgz +fi + +if [ ! -d $serverPath/mongodb/bin ];then + mkdir -p $serverPath/mongodb + cd $MG_DIR/mongodb-macos-x86_64-${VERSION} && cp -rf ./bin $serverPath/mongodb +fi + +# https://downloads.mongodb.com/compass/mongosh-2.2.5-darwin-x64.zip +# https://downloads.mongodb.com/compass/mongosh-2.2.5-darwin-arm64.zip +#--------------- mongosh tool install ------------------ # +TOOL_VERSION=2.2.5 +TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-darwin-x64 +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-darwin-arm64 +fi + +if [ "arm64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-darwin-arm64 +fi +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.zip +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && unzip ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + + + +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-macos-arm64-100.9.4.zip +TOOL_VERSION=100.9.4 +TOOL_FILE_NAME=mongodb-database-tools-macos-x86_64-${TOOL_VERSION} +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-macos-arm64-${TOOL_VERSION} +fi + +if [ "arm64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-macos-arm64-${TOOL_VERSION} +fi + +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.zip +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://fastdl.mongodb.org/tools/db/${TOOL_FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://fastdl.mongodb.org/tools/db/${TOOL_FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && unzip ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} \ No newline at end of file diff --git a/plugins/mongodb/versions/8.0/opensuse.sh b/plugins/mongodb/versions/8.0/opensuse.sh new file mode 100644 index 000000000..66f8ee4b8 --- /dev/null +++ b/plugins/mongodb/versions/8.0/opensuse.sh @@ -0,0 +1,95 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl +VERSION=7.0.14 +SYS_ARCH=`arch` + +SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}' | awk -F . '{print $1}'` + +SYS_NAME="15" +if [ "$SYS_VERSION_ID" -gt "15" ];then + SYS_NAME="15" +fi + +if [ "$SYS_NAME" -lt "12" ];then + SYS_NAME="12" +fi + +# https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-suse15-4.4.23.tgz +# https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-suse12-4.4.23.tgz + +MG_DIR=$serverPath/source/mongodb +mkdir -p $MG_DIR + +FILE_NAME=mongodb-linux-${SYS_ARCH}-suse${SYS_NAME}-${VERSION} +FILE_NAME_TGZ=${FILE_NAME}.tgz + +if [ ! -f $MG_DIR/${FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${FILE_NAME} ];then + cd $MG_DIR && tar -zxvf ${FILE_NAME_TGZ} +fi + +if [ ! -d $serverPath/mongodb/bin ];then + mkdir -p $serverPath/mongodb + cd $MG_DIR/${FILE_NAME} && cp -rf ./bin $serverPath/mongodb +fi + +cd ${MG_DIR} && rm -rf ${MG_DIR}/${FILE_NAME} + + +#--------------- mongosh tool install ------------------ # +TOOL_VERSION=2.2.5 +TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-x64 +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-arm64 +fi +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + + +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-suse12-x86_64-100.9.4.tgz +#--------------- mongodb database install ------------------ # +TOOL_VERSION=100.9.4 +TOOL_FILE_NAME=mongodb-database-tools-suse${SYS_NAME}-x86_64-${TOOL_VERSION} +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-suse${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +if [ "arm64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-suse${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://fastdl.mongodb.org/tools/db/${TOOL_FILE_NAME_TGZ} +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + diff --git a/plugins/mongodb/versions/8.0/ubuntu.sh b/plugins/mongodb/versions/8.0/ubuntu.sh new file mode 100644 index 000000000..a5d10a5be --- /dev/null +++ b/plugins/mongodb/versions/8.0/ubuntu.sh @@ -0,0 +1,91 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl +VERSION=8.0.3 +SYS_ARCH=`arch` + +SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` +SYS_NAME=${SYS_VERSION_ID/./} +# https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2004-4.4.23.tgz + +if [ "$SYS_NAME" -gt "2204" ];then + SYS_NAME="2204" +fi + +if [ "$SYS_NAME" -lt "2004" ];then + SYS_NAME="2004" +fi + +MG_DIR=$serverPath/source/mongodb +mkdir -p $MG_DIR + +FILE_NAME=mongodb-linux-${SYS_ARCH}-ubuntu${SYS_NAME}-${VERSION} +FILE_NAME_TGZ=${FILE_NAME}.tgz + +if [ ! -f $MG_DIR/${FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/linux/${FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${FILE_NAME} ];then + cd $MG_DIR && tar -zxvf ${FILE_NAME_TGZ} +fi + +if [ ! -d $serverPath/mongodb/bin ];then + mkdir -p $serverPath/mongodb + cd $MG_DIR/${FILE_NAME} && cp -rf ./bin $serverPath/mongodb +fi + +cd ${MG_DIR} && rm -rf ${MG_DIR}/${FILE_NAME} + + +#--------------- mongosh tool install ------------------ # +TOOL_VERSION=2.2.5 +TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-x64 +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongosh-${TOOL_VERSION}-linux-arm64 +fi +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://downloads.mongodb.com/compass/${TOOL_FILE_NAME_TGZ}" +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} + +# https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2204-x86_64-100.9.4.tgz +#--------------- mongodb database install ------------------ # +TOOL_VERSION=100.9.4 +TOOL_FILE_NAME=mongodb-database-tools-ubuntu${SYS_NAME}-x86_64-${TOOL_VERSION} +if [ "aarch64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-ubuntu${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +if [ "arm64" == ${SYS_ARCH} ];then + TOOL_FILE_NAME=mongodb-database-tools-ubuntu${SYS_NAME}-arm64-${TOOL_VERSION} +fi + +TOOL_FILE_NAME_TGZ=${TOOL_FILE_NAME}.tgz +if [ ! -f $MG_DIR/${TOOL_FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${TOOL_FILE_NAME_TGZ} https://fastdl.mongodb.org/tools/db/${TOOL_FILE_NAME_TGZ} +fi + +if [ ! -d $MG_DIR/${TOOL_FILE_NAME_TGZ} ];then + cd $MG_DIR && tar -zxvf ${TOOL_FILE_NAME_TGZ} +fi + +cd ${MG_DIR}/${TOOL_FILE_NAME} && cp -rf ./bin $serverPath/mongodb +cd ${MG_DIR} && rm -rf ${MG_DIR}/${TOOL_FILE_NAME} \ No newline at end of file From a660014740eca98b223451e933573146e463cd74 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:15:07 +0800 Subject: [PATCH 486/965] update --- plugins/mongodb/versions/5.0/centos.sh | 2 +- plugins/mongodb/versions/5.0/debian.sh | 2 +- plugins/mongodb/versions/5.0/macos.sh | 2 +- plugins/mongodb/versions/5.0/opensuse.sh | 2 +- plugins/mongodb/versions/5.0/ubuntu.sh | 2 +- plugins/mongodb/versions/6.0/debian.sh | 2 +- plugins/mongodb/versions/6.0/macos.sh | 2 +- plugins/mongodb/versions/6.0/opensuse.sh | 2 +- plugins/mongodb/versions/6.0/ubuntu.sh | 2 +- plugins/mongodb/versions/7.0/centos.sh | 2 +- plugins/mongodb/versions/7.0/debian.sh | 2 +- plugins/mongodb/versions/7.0/euler.sh | 2 +- plugins/mongodb/versions/7.0/macos.sh | 3 +-- plugins/mongodb/versions/7.0/opensuse.sh | 3 +-- plugins/mongodb/versions/7.0/ubuntu.sh | 3 +-- plugins/mongodb/versions/8.0/centos.sh | 2 +- 16 files changed, 16 insertions(+), 19 deletions(-) diff --git a/plugins/mongodb/versions/5.0/centos.sh b/plugins/mongodb/versions/5.0/centos.sh index 7d16d1e9d..7a3041691 100644 --- a/plugins/mongodb/versions/5.0/centos.sh +++ b/plugins/mongodb/versions/5.0/centos.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=5.0.29 +VERSION=5.0.30 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/5.0/debian.sh b/plugins/mongodb/versions/5.0/debian.sh index 664cc763b..044fa39d6 100644 --- a/plugins/mongodb/versions/5.0/debian.sh +++ b/plugins/mongodb/versions/5.0/debian.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=5.0.29 +VERSION=5.0.30 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` SYS_NAME=${SYS_VERSION_ID/./} diff --git a/plugins/mongodb/versions/5.0/macos.sh b/plugins/mongodb/versions/5.0/macos.sh index 819392c1f..d2df41f84 100644 --- a/plugins/mongodb/versions/5.0/macos.sh +++ b/plugins/mongodb/versions/5.0/macos.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=5.0.29 +VERSION=5.0.30 MG_DIR=$serverPath/source/mongodb mkdir -p $MG_DIR diff --git a/plugins/mongodb/versions/5.0/opensuse.sh b/plugins/mongodb/versions/5.0/opensuse.sh index 260de1bc6..d5f914029 100644 --- a/plugins/mongodb/versions/5.0/opensuse.sh +++ b/plugins/mongodb/versions/5.0/opensuse.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=5.0.29 +VERSION=5.0.30 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}' | awk -F . '{print $1}'` diff --git a/plugins/mongodb/versions/5.0/ubuntu.sh b/plugins/mongodb/versions/5.0/ubuntu.sh index d59417dbf..17e51fb8a 100644 --- a/plugins/mongodb/versions/5.0/ubuntu.sh +++ b/plugins/mongodb/versions/5.0/ubuntu.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=5.0.29 +VERSION=5.0.30 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/6.0/debian.sh b/plugins/mongodb/versions/6.0/debian.sh index 507dc023e..b71774f4c 100644 --- a/plugins/mongodb/versions/6.0/debian.sh +++ b/plugins/mongodb/versions/6.0/debian.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=6.0.18 +VERSION=6.0.19 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` SYS_NAME=${SYS_VERSION_ID/./} diff --git a/plugins/mongodb/versions/6.0/macos.sh b/plugins/mongodb/versions/6.0/macos.sh index f6e2c64a3..2427bffb7 100644 --- a/plugins/mongodb/versions/6.0/macos.sh +++ b/plugins/mongodb/versions/6.0/macos.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=6.0.18 +VERSION=6.0.19 MG_DIR=$serverPath/source/mongodb mkdir -p $MG_DIR diff --git a/plugins/mongodb/versions/6.0/opensuse.sh b/plugins/mongodb/versions/6.0/opensuse.sh index f8fc098a5..104da1c4c 100644 --- a/plugins/mongodb/versions/6.0/opensuse.sh +++ b/plugins/mongodb/versions/6.0/opensuse.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=6.0.18 +VERSION=6.0.19 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}' | awk -F . '{print $1}'` diff --git a/plugins/mongodb/versions/6.0/ubuntu.sh b/plugins/mongodb/versions/6.0/ubuntu.sh index c6d524ebc..15dfa7af2 100644 --- a/plugins/mongodb/versions/6.0/ubuntu.sh +++ b/plugins/mongodb/versions/6.0/ubuntu.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=6.0.18 +VERSION=6.0.19 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/7.0/centos.sh b/plugins/mongodb/versions/7.0/centos.sh index ddbcf22be..f3805a0a3 100644 --- a/plugins/mongodb/versions/7.0/centos.sh +++ b/plugins/mongodb/versions/7.0/centos.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=7.0.15 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/7.0/debian.sh b/plugins/mongodb/versions/7.0/debian.sh index 3b5e0b18b..e820e6ac3 100644 --- a/plugins/mongodb/versions/7.0/debian.sh +++ b/plugins/mongodb/versions/7.0/debian.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=7.0.15 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` SYS_NAME=${SYS_VERSION_ID/./} diff --git a/plugins/mongodb/versions/7.0/euler.sh b/plugins/mongodb/versions/7.0/euler.sh index ddbcf22be..f3805a0a3 100644 --- a/plugins/mongodb/versions/7.0/euler.sh +++ b/plugins/mongodb/versions/7.0/euler.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=7.0.15 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/7.0/macos.sh b/plugins/mongodb/versions/7.0/macos.sh index 6df92f5d4..5ae23731b 100644 --- a/plugins/mongodb/versions/7.0/macos.sh +++ b/plugins/mongodb/versions/7.0/macos.sh @@ -9,8 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=7.0.15 MG_DIR=$serverPath/source/mongodb mkdir -p $MG_DIR diff --git a/plugins/mongodb/versions/7.0/opensuse.sh b/plugins/mongodb/versions/7.0/opensuse.sh index 66f8ee4b8..22d1db08a 100644 --- a/plugins/mongodb/versions/7.0/opensuse.sh +++ b/plugins/mongodb/versions/7.0/opensuse.sh @@ -8,8 +8,7 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=7.0.15 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}' | awk -F . '{print $1}'` diff --git a/plugins/mongodb/versions/7.0/ubuntu.sh b/plugins/mongodb/versions/7.0/ubuntu.sh index 1592d6e3c..9ea088118 100644 --- a/plugins/mongodb/versions/7.0/ubuntu.sh +++ b/plugins/mongodb/versions/7.0/ubuntu.sh @@ -8,8 +8,7 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=7.0.15 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/8.0/centos.sh b/plugins/mongodb/versions/8.0/centos.sh index ddbcf22be..adf50ec71 100644 --- a/plugins/mongodb/versions/8.0/centos.sh +++ b/plugins/mongodb/versions/8.0/centos.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=8.0.3 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` From ad503863ad35aedb0a8438e57dd2283677d15238 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:16:23 +0800 Subject: [PATCH 487/965] update --- plugins/mongodb/versions/4.4/centos.sh | 1 - plugins/mongodb/versions/4.4/debian.sh | 1 - plugins/mongodb/versions/4.4/macos.sh | 1 - plugins/mongodb/versions/4.4/opensuse.sh | 1 - plugins/mongodb/versions/4.4/rhel.sh | 1 - plugins/mongodb/versions/4.4/ubuntu.sh | 1 - plugins/mongodb/versions/5.0/centos.sh | 1 - plugins/mongodb/versions/5.0/debian.sh | 1 - plugins/mongodb/versions/5.0/macos.sh | 1 - plugins/mongodb/versions/5.0/opensuse.sh | 1 - plugins/mongodb/versions/5.0/ubuntu.sh | 1 - plugins/mongodb/versions/6.0/centos.sh | 3 +-- plugins/mongodb/versions/6.0/debian.sh | 1 - plugins/mongodb/versions/6.0/macos.sh | 1 - plugins/mongodb/versions/6.0/opensuse.sh | 1 - plugins/mongodb/versions/6.0/ubuntu.sh | 1 - plugins/mongodb/versions/7.0/centos.sh | 1 - plugins/mongodb/versions/7.0/debian.sh | 1 - plugins/mongodb/versions/7.0/euler.sh | 1 - plugins/mongodb/versions/8.0/centos.sh | 1 - plugins/mongodb/versions/8.0/debian.sh | 3 +-- plugins/mongodb/versions/8.0/euler.sh | 3 +-- plugins/mongodb/versions/8.0/macos.sh | 3 +-- plugins/mongodb/versions/8.0/opensuse.sh | 3 +-- plugins/mongodb/versions/8.0/ubuntu.sh | 1 - 25 files changed, 5 insertions(+), 30 deletions(-) diff --git a/plugins/mongodb/versions/4.4/centos.sh b/plugins/mongodb/versions/4.4/centos.sh index c9fdf25b4..7d79c8eeb 100644 --- a/plugins/mongodb/versions/4.4/centos.sh +++ b/plugins/mongodb/versions/4.4/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=4.4.23 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/4.4/debian.sh b/plugins/mongodb/versions/4.4/debian.sh index b890c6ef2..8647f8c05 100644 --- a/plugins/mongodb/versions/4.4/debian.sh +++ b/plugins/mongodb/versions/4.4/debian.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=4.4.23 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/4.4/macos.sh b/plugins/mongodb/versions/4.4/macos.sh index df28a3da5..1c2ff9a70 100644 --- a/plugins/mongodb/versions/4.4/macos.sh +++ b/plugins/mongodb/versions/4.4/macos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=4.4.23 MG_DIR=$serverPath/source/mongodb diff --git a/plugins/mongodb/versions/4.4/opensuse.sh b/plugins/mongodb/versions/4.4/opensuse.sh index b9ef65cd4..016ba6cbb 100644 --- a/plugins/mongodb/versions/4.4/opensuse.sh +++ b/plugins/mongodb/versions/4.4/opensuse.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=4.4.23 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/4.4/rhel.sh b/plugins/mongodb/versions/4.4/rhel.sh index c28adbc8f..6413d0639 100644 --- a/plugins/mongodb/versions/4.4/rhel.sh +++ b/plugins/mongodb/versions/4.4/rhel.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=4.4.23 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/4.4/ubuntu.sh b/plugins/mongodb/versions/4.4/ubuntu.sh index db969ee75..4dd2b3423 100644 --- a/plugins/mongodb/versions/4.4/ubuntu.sh +++ b/plugins/mongodb/versions/4.4/ubuntu.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=4.4.23 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/5.0/centos.sh b/plugins/mongodb/versions/5.0/centos.sh index 7a3041691..4ecaf059f 100644 --- a/plugins/mongodb/versions/5.0/centos.sh +++ b/plugins/mongodb/versions/5.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=5.0.30 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/5.0/debian.sh b/plugins/mongodb/versions/5.0/debian.sh index 044fa39d6..bfc72cf2a 100644 --- a/plugins/mongodb/versions/5.0/debian.sh +++ b/plugins/mongodb/versions/5.0/debian.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=5.0.30 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/5.0/macos.sh b/plugins/mongodb/versions/5.0/macos.sh index d2df41f84..8f811860d 100644 --- a/plugins/mongodb/versions/5.0/macos.sh +++ b/plugins/mongodb/versions/5.0/macos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=5.0.30 MG_DIR=$serverPath/source/mongodb diff --git a/plugins/mongodb/versions/5.0/opensuse.sh b/plugins/mongodb/versions/5.0/opensuse.sh index d5f914029..af3e43044 100644 --- a/plugins/mongodb/versions/5.0/opensuse.sh +++ b/plugins/mongodb/versions/5.0/opensuse.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=5.0.30 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/5.0/ubuntu.sh b/plugins/mongodb/versions/5.0/ubuntu.sh index 17e51fb8a..1b5ef0315 100644 --- a/plugins/mongodb/versions/5.0/ubuntu.sh +++ b/plugins/mongodb/versions/5.0/ubuntu.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=5.0.30 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/6.0/centos.sh b/plugins/mongodb/versions/6.0/centos.sh index d77a133fa..8c836bb6c 100644 --- a/plugins/mongodb/versions/6.0/centos.sh +++ b/plugins/mongodb/versions/6.0/centos.sh @@ -8,8 +8,7 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=6.0.9 +VERSION=6.0.19 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/6.0/debian.sh b/plugins/mongodb/versions/6.0/debian.sh index b71774f4c..b0590596b 100644 --- a/plugins/mongodb/versions/6.0/debian.sh +++ b/plugins/mongodb/versions/6.0/debian.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=6.0.19 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/6.0/macos.sh b/plugins/mongodb/versions/6.0/macos.sh index 2427bffb7..ed763d4ce 100644 --- a/plugins/mongodb/versions/6.0/macos.sh +++ b/plugins/mongodb/versions/6.0/macos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=6.0.19 MG_DIR=$serverPath/source/mongodb diff --git a/plugins/mongodb/versions/6.0/opensuse.sh b/plugins/mongodb/versions/6.0/opensuse.sh index 104da1c4c..5262433b3 100644 --- a/plugins/mongodb/versions/6.0/opensuse.sh +++ b/plugins/mongodb/versions/6.0/opensuse.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=6.0.19 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/6.0/ubuntu.sh b/plugins/mongodb/versions/6.0/ubuntu.sh index 15dfa7af2..b754a49b8 100644 --- a/plugins/mongodb/versions/6.0/ubuntu.sh +++ b/plugins/mongodb/versions/6.0/ubuntu.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=6.0.19 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/7.0/centos.sh b/plugins/mongodb/versions/7.0/centos.sh index f3805a0a3..ff808f549 100644 --- a/plugins/mongodb/versions/7.0/centos.sh +++ b/plugins/mongodb/versions/7.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=7.0.15 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/7.0/debian.sh b/plugins/mongodb/versions/7.0/debian.sh index e820e6ac3..d66088db6 100644 --- a/plugins/mongodb/versions/7.0/debian.sh +++ b/plugins/mongodb/versions/7.0/debian.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=7.0.15 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/7.0/euler.sh b/plugins/mongodb/versions/7.0/euler.sh index f3805a0a3..ff808f549 100644 --- a/plugins/mongodb/versions/7.0/euler.sh +++ b/plugins/mongodb/versions/7.0/euler.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=7.0.15 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/8.0/centos.sh b/plugins/mongodb/versions/8.0/centos.sh index adf50ec71..c66fa6c31 100644 --- a/plugins/mongodb/versions/8.0/centos.sh +++ b/plugins/mongodb/versions/8.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=8.0.3 SYS_ARCH=`arch` diff --git a/plugins/mongodb/versions/8.0/debian.sh b/plugins/mongodb/versions/8.0/debian.sh index 3b5e0b18b..a4d41d663 100644 --- a/plugins/mongodb/versions/8.0/debian.sh +++ b/plugins/mongodb/versions/8.0/debian.sh @@ -8,8 +8,7 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=8.0.3 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` SYS_NAME=${SYS_VERSION_ID/./} diff --git a/plugins/mongodb/versions/8.0/euler.sh b/plugins/mongodb/versions/8.0/euler.sh index ddbcf22be..c66fa6c31 100644 --- a/plugins/mongodb/versions/8.0/euler.sh +++ b/plugins/mongodb/versions/8.0/euler.sh @@ -8,8 +8,7 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=8.0.3 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/mongodb/versions/8.0/macos.sh b/plugins/mongodb/versions/8.0/macos.sh index 6df92f5d4..20cdd6c58 100644 --- a/plugins/mongodb/versions/8.0/macos.sh +++ b/plugins/mongodb/versions/8.0/macos.sh @@ -9,8 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=8.0.3 MG_DIR=$serverPath/source/mongodb mkdir -p $MG_DIR diff --git a/plugins/mongodb/versions/8.0/opensuse.sh b/plugins/mongodb/versions/8.0/opensuse.sh index 66f8ee4b8..6926dd1bc 100644 --- a/plugins/mongodb/versions/8.0/opensuse.sh +++ b/plugins/mongodb/versions/8.0/opensuse.sh @@ -8,8 +8,7 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl -VERSION=7.0.14 +VERSION=8.0.3 SYS_ARCH=`arch` SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}' | awk -F . '{print $1}'` diff --git a/plugins/mongodb/versions/8.0/ubuntu.sh b/plugins/mongodb/versions/8.0/ubuntu.sh index a5d10a5be..01e0af931 100644 --- a/plugins/mongodb/versions/8.0/ubuntu.sh +++ b/plugins/mongodb/versions/8.0/ubuntu.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=8.0.3 SYS_ARCH=`arch` From fe9cd42210e9459d0ba247c094f7f47fddd45d4c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:21:15 +0800 Subject: [PATCH 488/965] Update debian.sh --- plugins/mongodb/versions/8.0/debian.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/mongodb/versions/8.0/debian.sh b/plugins/mongodb/versions/8.0/debian.sh index a4d41d663..4ecfbe0ee 100644 --- a/plugins/mongodb/versions/8.0/debian.sh +++ b/plugins/mongodb/versions/8.0/debian.sh @@ -15,15 +15,15 @@ SYS_NAME=${SYS_VERSION_ID/./} if [ "$SYS_NAME" -gt "11" ];then - SYS_NAME="11" + SYS_NAME="12" fi if [ "$SYS_NAME" -lt "11" ];then - SYS_NAME="11" + SYS_NAME="12" fi if [ "$SYS_NAME" == "" ];then - SYS_NAME="11" + SYS_NAME="12" fi FILE_NAME=mongodb-linux-${SYS_ARCH}-debian${SYS_NAME}-${VERSION} From d940a12ed56ced0bdf62c6212960e9b9d0ea013f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:22:28 +0800 Subject: [PATCH 489/965] Update ubuntu.sh --- plugins/mongodb/versions/8.0/ubuntu.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/mongodb/versions/8.0/ubuntu.sh b/plugins/mongodb/versions/8.0/ubuntu.sh index 01e0af931..0f08622d9 100644 --- a/plugins/mongodb/versions/8.0/ubuntu.sh +++ b/plugins/mongodb/versions/8.0/ubuntu.sh @@ -23,6 +23,10 @@ if [ "$SYS_NAME" -lt "2004" ];then SYS_NAME="2004" fi +if [ "$SYS_NAME" == "2404" ];then + SYS_NAME="2404" +fi + MG_DIR=$serverPath/source/mongodb mkdir -p $MG_DIR From bb6a64c0dca6d5c28e053043280c4e6cc6ab543e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:23:02 +0800 Subject: [PATCH 490/965] Update ubuntu.sh --- plugins/mongodb/versions/8.0/ubuntu.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/mongodb/versions/8.0/ubuntu.sh b/plugins/mongodb/versions/8.0/ubuntu.sh index 0f08622d9..2a9230b05 100644 --- a/plugins/mongodb/versions/8.0/ubuntu.sh +++ b/plugins/mongodb/versions/8.0/ubuntu.sh @@ -15,14 +15,14 @@ SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | a SYS_NAME=${SYS_VERSION_ID/./} # https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2004-4.4.23.tgz -if [ "$SYS_NAME" -gt "2204" ];then - SYS_NAME="2204" -fi - if [ "$SYS_NAME" -lt "2004" ];then SYS_NAME="2004" fi +if [ "$SYS_NAME" == "2204" ];then + SYS_NAME="2204" +fi + if [ "$SYS_NAME" == "2404" ];then SYS_NAME="2404" fi From 30e0db5adb3f4111475045d43909a91765b962dc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:25:08 +0800 Subject: [PATCH 491/965] Update debian.sh --- plugins/mongodb/versions/8.0/debian.sh | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/mongodb/versions/8.0/debian.sh b/plugins/mongodb/versions/8.0/debian.sh index 4ecfbe0ee..556dcb41b 100644 --- a/plugins/mongodb/versions/8.0/debian.sh +++ b/plugins/mongodb/versions/8.0/debian.sh @@ -14,18 +14,7 @@ SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | a SYS_NAME=${SYS_VERSION_ID/./} -if [ "$SYS_NAME" -gt "11" ];then - SYS_NAME="12" -fi - -if [ "$SYS_NAME" -lt "11" ];then - SYS_NAME="12" -fi - -if [ "$SYS_NAME" == "" ];then - SYS_NAME="12" -fi - +SYS_NAME="12" FILE_NAME=mongodb-linux-${SYS_ARCH}-debian${SYS_NAME}-${VERSION} FILE_NAME_TGZ=${FILE_NAME}.tgz From 0cef478d712d4200e2864ca65fcdcce09a798adb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:36:34 +0800 Subject: [PATCH 492/965] Update index.py --- plugins/mongodb/index.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/mongodb/index.py b/plugins/mongodb/index.py index 3396e44e3..da464e440 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -1525,6 +1525,12 @@ def runLog(): return getServerDir() + '/logs.pl' +def installPreInspectionDebainCheck(sysId,version): + if version.startwidth('8.0'): + if sysId != '12': + return "[%s]需要debain[12]" % (version,) + return '' + def installPreInspection(version): if mw.isAppleSystem(): return 'ok' @@ -1543,6 +1549,12 @@ def installPreInspection(version): supportOs = ['centos', 'ubuntu', 'debian', 'opensuse'] if not sysName in supportOs: return '暂时仅支持{}'.format(','.join(supportOs)) + + if sysName == 'debian': + check = installPreInspectionDebainCheck(sysId, version) + if check != '': + return check + return 'ok' def uninstallPreInspection(version): @@ -1556,7 +1568,7 @@ def uninstallPreInspection(version): if __name__ == "__main__": func = sys.argv[1] - version = "4.4" + version = '4.4' if (len(sys.argv) > 2): version = sys.argv[2] From 6da57d6ccc1973f967986063db677d6abb43dd44 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:38:03 +0800 Subject: [PATCH 493/965] Update index.py --- plugins/mongodb/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mongodb/index.py b/plugins/mongodb/index.py index da464e440..0be3bacb4 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -1526,7 +1526,7 @@ def runLog(): def installPreInspectionDebainCheck(sysId,version): - if version.startwidth('8.0'): + if version.startswidth('8.0'): if sysId != '12': return "[%s]需要debain[12]" % (version,) return '' From 98899cd76ef6ac1ca00f92025253a021f0440f2c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:41:31 +0800 Subject: [PATCH 494/965] Update index.py --- plugins/mongodb/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mongodb/index.py b/plugins/mongodb/index.py index 0be3bacb4..6a54f7b1a 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -1535,8 +1535,8 @@ def installPreInspection(version): if mw.isAppleSystem(): return 'ok' - sys = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'") + cmd = "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'" + sys = mw.execShell(cmd) if sys[1] != '': return '暂时不支持该系统' From 40bc9798a9f17f76964d40cca74782ec9a09cb02 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:44:05 +0800 Subject: [PATCH 495/965] Update index.py --- plugins/mongodb/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mongodb/index.py b/plugins/mongodb/index.py index 6a54f7b1a..ee4e752c4 100755 --- a/plugins/mongodb/index.py +++ b/plugins/mongodb/index.py @@ -1526,7 +1526,7 @@ def runLog(): def installPreInspectionDebainCheck(sysId,version): - if version.startswidth('8.0'): + if version == '8.0': if sysId != '12': return "[%s]需要debain[12]" % (version,) return '' From f4859ef7f69f6d02301553552553896370e0a2de Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:53:25 +0800 Subject: [PATCH 496/965] Update index.py --- plugins/pgadmin/index.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/pgadmin/index.py b/plugins/pgadmin/index.py index 85984f5d6..2559430f9 100755 --- a/plugins/pgadmin/index.py +++ b/plugins/pgadmin/index.py @@ -62,7 +62,7 @@ def getConf(): def getPort(): file = getConf() content = mw.readFile(file) - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -136,8 +136,8 @@ def returnCfg(): def __release_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'pgAdmin默认端口', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'pgAdmin默认端口', 'port') return port except Exception as e: return "Release failed {}".format(e) @@ -146,8 +146,8 @@ def __release_port(port): def __delete_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().delAcceptPortArgs(port, 'tcp') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().delAcceptPort(port, 'tcp') return port except Exception as e: return "Release failed {}".format(e) From 346f07657d2acde994b6ea2df8161885882780f5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 17:55:37 +0800 Subject: [PATCH 497/965] Update index.py --- plugins/pgadmin/index.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/pgadmin/index.py b/plugins/pgadmin/index.py index 2559430f9..fd973ebff 100755 --- a/plugins/pgadmin/index.py +++ b/plugins/pgadmin/index.py @@ -184,8 +184,9 @@ def getPythonName(): return data[0].strip(); def initPgConfFile(): + pyname = getPythonName() file_tpl = getPluginDir() + '/conf/config_local.py' - dst_file = getServerDir()+'/run/lib/python3.10/site-packages/pgadmin4/config_local.py' + dst_file = getServerDir()+'/run/lib/'+pyname+'/site-packages/pgadmin4/config_local.py' if not os.path.exists(dst_file): service_path = mw.getServerDir() content = mw.readFile(file_tpl) From 7b5d9bb3c1dfa52395c4f1cd81d07305da989b59 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:09:33 +0800 Subject: [PATCH 498/965] Update index.py --- plugins/openresty/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py index 2b2ea8b59..9ba91330c 100755 --- a/plugins/openresty/index.py +++ b/plugins/openresty/index.py @@ -208,7 +208,7 @@ def confReplace(): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' From d078b285a26831a0f42e458093e737f09f8a98d3 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:14:02 +0800 Subject: [PATCH 499/965] Update index.py --- plugins/openresty/index.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py index 9ba91330c..d323af92f 100755 --- a/plugins/openresty/index.py +++ b/plugins/openresty/index.py @@ -564,11 +564,11 @@ def installPreInspection(): if __name__ == "__main__": + + version = '1.27.1' version_pl = getServerDir() + "/version.pl" - if not os.path.exists(version_pl): - print('error') - exit(-1) - version = mw.readFile(version_pl) + if os.path.exists(version_pl): + version = mw.readFile(version_pl) func = sys.argv[1] From b350747eab28d1c3873b3104ea7974e7b9bbead8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:21:12 +0800 Subject: [PATCH 500/965] update --- plugins/acme_pandominassl_apply/index.py | 1 - plugins/alist/index.py | 2 +- plugins/cloudreve/index.py | 2 +- plugins/fail2ban/index.py | 3 +-- plugins/gorse/index.py | 2 +- plugins/haproxy/index.py | 2 +- plugins/keepalived/index.py | 5 ++--- plugins/lvs/index.py | 2 +- plugins/mosquitto/index.py | 2 +- plugins/simpleping/index.py | 3 +-- plugins/sphinx/index.py | 3 +-- 11 files changed, 11 insertions(+), 16 deletions(-) diff --git a/plugins/acme_pandominassl_apply/index.py b/plugins/acme_pandominassl_apply/index.py index 1679e0bf9..cd21cf2a8 100755 --- a/plugins/acme_pandominassl_apply/index.py +++ b/plugins/acme_pandominassl_apply/index.py @@ -167,7 +167,6 @@ def pSqliteDb(dbname='dnsapi'): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): diff --git a/plugins/alist/index.py b/plugins/alist/index.py index 1d0ded778..277920786 100755 --- a/plugins/alist/index.py +++ b/plugins/alist/index.py @@ -115,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/index.py b/plugins/cloudreve/index.py index 8f26a8e82..d01cf0865 100755 --- a/plugins/cloudreve/index.py +++ b/plugins/cloudreve/index.py @@ -115,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/fail2ban/index.py b/plugins/fail2ban/index.py index 4d478ab10..6d9994da4 100755 --- a/plugins/fail2ban/index.py +++ b/plugins/fail2ban/index.py @@ -147,7 +147,7 @@ def initJailD(): 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): @@ -171,7 +171,6 @@ def initDreplace(): systemService = systemDir + '/' + getPluginName() + '.service' if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl' - service_path = mw.getServerDir() content = mw.readFile(systemServiceTpl) content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, content) diff --git a/plugins/gorse/index.py b/plugins/gorse/index.py index 090c48576..f734ae4b4 100755 --- a/plugins/gorse/index.py +++ b/plugins/gorse/index.py @@ -151,7 +151,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/haproxy/index.py b/plugins/haproxy/index.py index 843ac5090..65812d034 100755 --- a/plugins/haproxy/index.py +++ b/plugins/haproxy/index.py @@ -118,7 +118,7 @@ def status(): 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/keepalived/index.py b/plugins/keepalived/index.py index ecd5e8a80..3ee34712d 100755 --- a/plugins/keepalived/index.py +++ b/plugins/keepalived/index.py @@ -126,7 +126,7 @@ def status(): def contentReplace(content): - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PLUGIN_PATH}', getPluginDir()) @@ -166,7 +166,7 @@ def copyScripts(): 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): @@ -202,7 +202,6 @@ def initDreplace(): systemService = systemDir + '/' + getPluginName() + '.service' if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl' - service_path = mw.getServerDir() content = mw.readFile(systemServiceTpl) content = contentReplace(content) mw.writeFile(systemService, content) diff --git a/plugins/lvs/index.py b/plugins/lvs/index.py index 82a9f21a3..e1f43e370 100755 --- a/plugins/lvs/index.py +++ b/plugins/lvs/index.py @@ -79,7 +79,7 @@ def status(): 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/mosquitto/index.py b/plugins/mosquitto/index.py index a1ffaf440..5739b1a14 100755 --- a/plugins/mosquitto/index.py +++ b/plugins/mosquitto/index.py @@ -88,7 +88,7 @@ def status(): 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/simpleping/index.py b/plugins/simpleping/index.py index dcc6853f0..6fc8e92fa 100755 --- a/plugins/simpleping/index.py +++ b/plugins/simpleping/index.py @@ -145,7 +145,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): @@ -166,7 +166,6 @@ def initDreplace(): systemService = systemDir + '/' + getPluginName() + '.service' if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl' - service_path = mw.getServerDir() content = mw.readFile(systemServiceTpl) content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, content) diff --git a/plugins/sphinx/index.py b/plugins/sphinx/index.py index 515d94a0a..981a4d4db 100755 --- a/plugins/sphinx/index.py +++ b/plugins/sphinx/index.py @@ -129,7 +129,7 @@ def mkdirAll(): 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): @@ -155,7 +155,6 @@ def initDreplace(): systemService = systemDir + '/sphinx.service' systemServiceTpl = getPluginDir() + '/init.d/sphinx.service.tpl' if os.path.exists(systemDir) and not os.path.exists(systemService): - service_path = mw.getServerDir() se_content = mw.readFile(systemServiceTpl) se_content = se_content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, se_content) From 3e9b47cbf34614d3d31a8a65cf13dcef7c51f0bd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:23:25 +0800 Subject: [PATCH 501/965] Update index.py --- plugins/swap/index.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/swap/index.py b/plugins/swap/index.py index 3fb07947c..db79fa596 100755 --- a/plugins/swap/index.py +++ b/plugins/swap/index.py @@ -79,7 +79,7 @@ def getInitDTpl(): 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): @@ -101,12 +101,11 @@ def initDreplace(): if os.path.exists(systemDir) and not os.path.exists(systemService): swapon_bin = mw.execShell('which swapon')[0].strip() swapoff_bin = mw.execShell('which swapoff')[0].strip() - service_path = mw.getServerDir() - se_content = mw.readFile(systemServiceTpl) - se_content = se_content.replace('{$SERVER_PATH}', service_path) - se_content = se_content.replace('{$SWAPON_BIN}', swapon_bin) - se_content = se_content.replace('{$SWAPOFF_BIN}', swapoff_bin) - mw.writeFile(systemService, se_content) + content = mw.readFile(systemServiceTpl) + content = content.replace('{$SERVER_PATH}', service_path) + content = content.replace('{$SWAPON_BIN}', swapon_bin) + content = content.replace('{$SWAPOFF_BIN}', swapoff_bin) + mw.writeFile(systemService, content) mw.execShell('systemctl daemon-reload') return file_bin From 264dd392f696aef3cbcba04e1ac274ac83140ee5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:28:18 +0800 Subject: [PATCH 502/965] Update install.sh --- plugins/postgresql/versions/17/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/postgresql/versions/17/install.sh b/plugins/postgresql/versions/17/install.sh index 3e2f53152..b46b78c2b 100755 --- a/plugins/postgresql/versions/17/install.sh +++ b/plugins/postgresql/versions/17/install.sh @@ -13,7 +13,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql -VERSION=17.0 +VERSION=17.2 # su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data" From 48d193141e297e931e5ef681c1b7a07c3da989eb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:31:04 +0800 Subject: [PATCH 503/965] Update install.sh --- plugins/pgadmin/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pgadmin/install.sh b/plugins/pgadmin/install.sh index 8de77dec5..8c5a876c0 100755 --- a/plugins/pgadmin/install.sh +++ b/plugins/pgadmin/install.sh @@ -85,7 +85,7 @@ Install_pgadmin() pip install gunicorn # pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.10/pip/pgadmin4-8.10-py3-none-any.whl - pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.12/pip/pgadmin4-8.12-py3-none-any.whl + pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.13/pip/pgadmin4-8.13-py3-none-any.whl cd ${rootPath} && python3 ${rootPath}/plugins/pgadmin/index.py start echo '安装完成' From 45645a52b570745827e3224c1734dbf6bc29375a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:40:11 +0800 Subject: [PATCH 504/965] up --- plugins/mariadb/info.json | 2 +- plugins/mariadb/versions/11.6/install.sh | 121 +++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100755 plugins/mariadb/versions/11.6/install.sh diff --git a/plugins/mariadb/info.json b/plugins/mariadb/info.json index 9496a9fba..effaf5f8a 100755 --- a/plugins/mariadb/info.json +++ b/plugins/mariadb/info.json @@ -8,7 +8,7 @@ "uninstall_pre_inspection":true, "checks": "server/mariadb", "path": "server/mariadb", - "versions":["10.6","10.7","10.8","10.9","10.11","11.0","11.1","11.2","11.3","11.4","11.5"], + "versions":["10.6","10.7","10.8","10.9","10.11","11.0","11.1","11.2","11.3","11.4","11.5","11.6"], "shell":"install.sh", "checks":"server/mariadb", "path":"server/mariadb", diff --git a/plugins/mariadb/versions/11.6/install.sh b/plugins/mariadb/versions/11.6/install.sh new file mode 100755 index 000000000..56b13925f --- /dev/null +++ b/plugins/mariadb/versions/11.6/install.sh @@ -0,0 +1,121 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +#https://dev.mysql.com/downloads/mysql/5.5.html#downloads +#https://dev.mysql.com/downloads/file/?id=480541 + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sysName=`uname` + +install_tmp=${rootPath}/tmp/mw_install.pl +mariadbDir=${serverPath}/source/mariadb + +MY_VER=11.6.2 + +Install_app() +{ + mkdir -p ${mariadbDir} + echo '正在安装脚本文件...' > $install_tmp + + if [ "$sysName" != "Darwin" ];then + mkdir -p /var/log/mariadb + touch /var/log/mariadb/mariadb.log + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + if [ "$cpuCore" -gt "2" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + else + cpuCore="1" + fi + # ----- cpu end ------ + + # if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then + # wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz + # fi + + # https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz + if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then + wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz + fi + + if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then + cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz + fi + + INSTALL_CMD=cmake + # check cmake version + CMAKE_VERSION=`cmake -version | grep version | awk '{print $3}' | awk -F '.' '{print $1}'` + if [ "$CMAKE_VERSION" -eq "2" ];then + mkdir -p /var/log/mariadb + touch /var/log/mariadb/mariadb.log + INSTALL_CMD=cmake3 + fi + + if [ ! -d $serverPath/mariadb ];then + cd ${mariadbDir}/mariadb-${MY_VER} && ${INSTALL_CMD} \ + -DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \ + -DMYSQL_DATADIR=$serverPath/mariadb/data/ \ + -DMYSQL_USER=mysql \ + -DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \ + -DWITH_MYISAM_STORAGE_ENGINE=1 \ + -DWITH_INNOBASE_STORAGE_ENGINE=1 \ + -DWITH_MEMORY_STORAGE_ENGINE=1 \ + -DENABLED_LOCAL_INFILE=1 \ + -DWITH_PARTITION_STORAGE_ENGINE=1 \ + -DEXTRA_CHARSETS=all \ + -DDEFAULT_CHARSET=utf8mb4 \ + -DDEFAULT_COLLATION=utf8mb4_general_ci \ + -DCMAKE_C_COMPILER=/usr/bin/gcc \ + -DCMAKE_CXX_COMPILER=/usr/bin/g++ + make -j${cpuCore} && make install && make clean + + if [ -d $serverPath/mariadb ];then + echo '11.4' > $serverPath/mariadb/version.pl + echo '安装完成' + else + echo '安装失败' + echo 'install fail'>&2 + exit 1 + fi + fi + + if [ -d ${mariadbDir}/mariadb-${MY_VER} ];then + rm -rf ${mariadbDir}/mariadb-${MY_VER} + fi + +} + +Uninstall_app() +{ + rm -rf $serverPath/mariadb + echo '卸载完成' > $install_tmp +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_app +else + Uninstall_app +fi From abbe16eb87620883280ce7c6ef327ebe576684b7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:41:02 +0800 Subject: [PATCH 505/965] up --- plugins/mariadb/versions/11.5/install.sh | 2 +- plugins/mariadb/versions/11.6/install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mariadb/versions/11.5/install.sh b/plugins/mariadb/versions/11.5/install.sh index 55604cad2..881e93ce8 100755 --- a/plugins/mariadb/versions/11.5/install.sh +++ b/plugins/mariadb/versions/11.5/install.sh @@ -92,7 +92,7 @@ Install_app() make -j${cpuCore} && make install && make clean if [ -d $serverPath/mariadb ];then - echo '11.4' > $serverPath/mariadb/version.pl + echo '11.5' > $serverPath/mariadb/version.pl echo '安装完成' else echo '安装失败' diff --git a/plugins/mariadb/versions/11.6/install.sh b/plugins/mariadb/versions/11.6/install.sh index 56b13925f..3597e2e22 100755 --- a/plugins/mariadb/versions/11.6/install.sh +++ b/plugins/mariadb/versions/11.6/install.sh @@ -92,7 +92,7 @@ Install_app() make -j${cpuCore} && make install && make clean if [ -d $serverPath/mariadb ];then - echo '11.4' > $serverPath/mariadb/version.pl + echo '11.6' > $serverPath/mariadb/version.pl echo '安装完成' else echo '安装失败' From 7855df5aa254f288a068267b6eb572ea607d8a33 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:41:43 +0800 Subject: [PATCH 506/965] up --- plugins/mariadb/versions/11.2/install.sh | 2 +- plugins/mariadb/versions/11.4/install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mariadb/versions/11.2/install.sh b/plugins/mariadb/versions/11.2/install.sh index 67b1b7408..12778da85 100755 --- a/plugins/mariadb/versions/11.2/install.sh +++ b/plugins/mariadb/versions/11.2/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=11.2.4 +MY_VER=11.2.6 Install_app() { diff --git a/plugins/mariadb/versions/11.4/install.sh b/plugins/mariadb/versions/11.4/install.sh index 6122b7caf..e6c1aefa5 100755 --- a/plugins/mariadb/versions/11.4/install.sh +++ b/plugins/mariadb/versions/11.4/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=11.4.3 +MY_VER=11.4.4 Install_app() { From 65ec08d40f82ea34a3840c89e38abea41c1650d7 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:42:55 +0800 Subject: [PATCH 507/965] Update install.sh --- plugins/mariadb/versions/10.6/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mariadb/versions/10.6/install.sh b/plugins/mariadb/versions/10.6/install.sh index 04789763d..b116971b3 100755 --- a/plugins/mariadb/versions/10.6/install.sh +++ b/plugins/mariadb/versions/10.6/install.sh @@ -13,7 +13,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.6.18 +MY_VER=10.6.20 Install_app() { From efc715678b6c0554a4ed64744e579db570fcc9a6 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:43:25 +0800 Subject: [PATCH 508/965] Update install.sh --- plugins/mariadb/versions/10.11/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mariadb/versions/10.11/install.sh b/plugins/mariadb/versions/10.11/install.sh index 33708c610..0e22e2f7d 100755 --- a/plugins/mariadb/versions/10.11/install.sh +++ b/plugins/mariadb/versions/10.11/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.11.8 +MY_VER=10.11.10 Install_app() { From 7a1bc2ee3971386fc8bfbc51b8bf7aae9665148a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:45:45 +0800 Subject: [PATCH 509/965] Update ftp_client.py --- plugins/backup_ftp/class/ftp_client.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/backup_ftp/class/ftp_client.py b/plugins/backup_ftp/class/ftp_client.py index 97df92e32..2dc603911 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 From 3030849bd28bd4670b5403d5500a6e7d1721c862 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:46:15 +0800 Subject: [PATCH 510/965] Update ftp_client.py --- plugins/backup_ftp/class/ftp_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/backup_ftp/class/ftp_client.py b/plugins/backup_ftp/class/ftp_client.py index 2dc603911..53a0e7ea8 100644 --- a/plugins/backup_ftp/class/ftp_client.py +++ b/plugins/backup_ftp/class/ftp_client.py @@ -140,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: From 5f6df46c90d2c8a584b37c3e148b33e489db2224 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 18:52:38 +0800 Subject: [PATCH 511/965] Update mw.py --- web/core/mw.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/web/core/mw.py b/web/core/mw.py index d4fd928ba..fcf4d5fd5 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -1390,6 +1390,20 @@ def checkWebConfig(): return result[1] return True +def isIpAddr(ip): + check_ip = re.compile(r'^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\\d)$') + if check_ip.match(ip): + return True + else: + return False + +def getWebStatus(): + pid = getServerDir() + '/openresty/nginx/logs/nginx.pid' + if os.path.exists(pid): + return True + return False + + def restartWeb(): return opWeb("reload") From 0397090458952cffd2df29dab47760b8864e99f1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 19:08:11 +0800 Subject: [PATCH 512/965] Update macos.sh --- plugins/mongodb/versions/8.0/macos.sh | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/mongodb/versions/8.0/macos.sh b/plugins/mongodb/versions/8.0/macos.sh index 20cdd6c58..43b4ecbb4 100644 --- a/plugins/mongodb/versions/8.0/macos.sh +++ b/plugins/mongodb/versions/8.0/macos.sh @@ -14,18 +14,25 @@ VERSION=8.0.3 MG_DIR=$serverPath/source/mongodb mkdir -p $MG_DIR -if [ ! -f $MG_DIR/mongodb-macos-x86_64-${VERSION}.tgz ]; then - wget --no-check-certificate -O $MG_DIR/mongodb-macos-x86_64-${VERSION}.tgz https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-${VERSION}.tgz - echo "wget --no-check-certificate -O $MG_DIR/mongodb-macos-x86_64-${VERSION}.tgz https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-${VERSION}.tgz" +# https://fastdl.mongodb.org/osx/mongodb-macos-arm64-8.0.3.tgz +FILE_NAME=mongodb-macos-x86_64-${VERSION} +if [ "arm64" == "${SYS_ARCH}" ];then + FILE_NAME=mongodb-macos-arm64-${VERSION} +fi +FILE_NAME_TGZ=${FILE_NAME}.tgz + +if [ ! -f $MG_DIR/${FILE_NAME_TGZ} ]; then + wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/osx/${FILE_NAME_TGZ} + echo "wget --no-check-certificate -O $MG_DIR/${FILE_NAME_TGZ} https://fastdl.mongodb.org/osx/${FILE_NAME_TGZ}" fi -if [ ! -d $MG_DIR/mongodb-macos-x86_64-${VERSION} ];then - cd $MG_DIR && tar -zxvf mongodb-macos-x86_64-${VERSION}.tgz +if [ ! -d $MG_DIR/${FILE_NAME} ];then + cd $MG_DIR && tar -zxvf ${FILE_NAME_TGZ} fi if [ ! -d $serverPath/mongodb/bin ];then mkdir -p $serverPath/mongodb - cd $MG_DIR/mongodb-macos-x86_64-${VERSION} && cp -rf ./bin $serverPath/mongodb + cd $MG_DIR/${FILE_NAME} && cp -rf ./bin $serverPath/mongodb fi # https://downloads.mongodb.com/compass/mongosh-2.2.5-darwin-x64.zip From acc57ee99300b08b550dfc50b6c689380386991d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 19:19:21 +0800 Subject: [PATCH 513/965] Update mw.py --- web/core/mw.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index fcf4d5fd5..0ce49aeda 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -556,7 +556,8 @@ def getOsID(): return data[0].strip() # 获取文件权限描述 -def getFileStatsDesc(filename,path): +def getFileStatsDesc(filename, path=None): + import pwd if path == '' or filename == '': return ';;;;;' try: @@ -569,12 +570,13 @@ def getFileStatsDesc(filename,path): user = str(pwd.getpwuid(stat.st_uid).pw_name) except: user = str(stat.st_uid) + size = str(stat.st_size) link = '' if os.path.islink(filename): link = ' -> ' + os.readlink(filename) - if path != '': + if path: tmp_path = (path + '/').replace('//', '/') filename = filename.replace(tmp_path, '', 1) From eb0fe483b1d4cec10ed0f1b7ceea1581975a1b9d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 19:24:31 +0800 Subject: [PATCH 514/965] Update index.py --- plugins/gorse/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gorse/index.py b/plugins/gorse/index.py index f734ae4b4..6de72ef1a 100755 --- a/plugins/gorse/index.py +++ b/plugins/gorse/index.py @@ -125,7 +125,7 @@ def initRedisConf(): requirepass = "" conf = mw.getServerDir() + '/redis/redis.conf' content = mw.readFile(conf) - rep = r"^(requirepass" + r')\s*([.0-9A-Za-z_& ~]+)' + rep = r"^(requirepass" + r"\)\s*([.0-9A-Za-z_& ~]+)" tmp = re.search(rep, content, re.M) if tmp: requirepass = tmp.groups()[1] From 52d66b33cbf0451004565242ad9ad5b874a5b8fd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 19:43:36 +0800 Subject: [PATCH 515/965] update --- plugins/php/versions/common/brotli.sh | 114 ++++++++++++++++++++++++++ plugins/php/versions/common/redis.sh | 2 +- plugins/php/versions/phplib.conf | 19 +++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100755 plugins/php/versions/common/brotli.sh diff --git a/plugins/php/versions/common/brotli.sh b/plugins/php/versions/common/brotli.sh new file mode 100755 index 000000000..aeec6c6c5 --- /dev/null +++ b/plugins/php/versions/common/brotli.sh @@ -0,0 +1,114 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH=$PATH:/opt/homebrew/bin + +curPath=`pwd` + +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php +SYS_ARCH=`arch` +LIBNAME=brotli +LIBV=0.15.2 +sysName=`uname` +actionType=$1 +version=$2 + +if [ "$version" -lt "70" ];then + echo "not need" + exit 1 +fi + +LIB_PATH_NAME=lib/php +if [ -d $serverPath/php/${version}/lib64 ];then + LIB_PATH_NAME=lib64 +fi + +NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts` +extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so + +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + + +Install_lib() +{ + isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + php_lib=$sourcePath/php_lib + mkdir -p $php_lib + if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then + if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then + wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz + fi + cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz + fi + cd $php_lib/${LIBNAME}-${LIBV}/extension + + OPTIONS='' + if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then + OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" + fi + + $serverPath/php/$version/bin/phpize + ./configure --enable-brotli \ + --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS + make clean && make && make install && make clean + + cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV} + fi + + if [ ! -f "$extFile" ];then + echo "ERROR!" + return + fi + + echo "" >> $serverPath/php/$version/etc/php.ini + echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini + echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini + + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ ! -f "$serverPath/php/$version/bin/php-config" ];then + echo "php-$version 未安装,请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + echo "php-$version 未安装${LIBNAME},请选择其它版本!" + echo "php-$version not install ${LIBNAME}, Plese select other version!" + return + fi + + sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini + sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini + + rm -f $extFile + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===============================================' + echo 'successful!' +} + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file diff --git a/plugins/php/versions/common/redis.sh b/plugins/php/versions/common/redis.sh index 8b4214744..0b94f387b 100755 --- a/plugins/php/versions/common/redis.sh +++ b/plugins/php/versions/common/redis.sh @@ -100,7 +100,7 @@ Uninstall_lib() if [ ! -f "$extFile" ];then echo "php-$version 未安装${LIBNAME},请选择其它版本!" - echo "php-$version not install memcache, Plese select other version!" + echo "php-$version not install ${LIBNAME}, Plese select other version!" return fi diff --git a/plugins/php/versions/phplib.conf b/plugins/php/versions/phplib.conf index 9e993fdd0..73d4c7026 100755 --- a/plugins/php/versions/phplib.conf +++ b/plugins/php/versions/phplib.conf @@ -85,6 +85,25 @@ "shell": "sodium.sh", "check": "sodium.so" }, + { + "name": "brotli", + "versions": [ + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "压缩库", + "msg": "压缩算法", + "shell": "brotli.sh", + "check": "brotli.so" + }, { "name": "gmp", "versions": [ From fbf1a36e0096c0cf81805de10b7685fb5da0ff1a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 19:49:55 +0800 Subject: [PATCH 516/965] Update brotli.sh --- plugins/php/versions/common/brotli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php/versions/common/brotli.sh b/plugins/php/versions/common/brotli.sh index aeec6c6c5..87141bf8d 100755 --- a/plugins/php/versions/common/brotli.sh +++ b/plugins/php/versions/common/brotli.sh @@ -54,7 +54,7 @@ Install_lib() fi cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz fi - cd $php_lib/${LIBNAME}-${LIBV}/extension + cd $php_lib/${LIBNAME}-${LIBV} OPTIONS='' if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then From fd1f64a6e269130f596f4a6b7d0e9f18008f437e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 21:14:08 +0800 Subject: [PATCH 517/965] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9bde8372c..d5d8772b0 100644 --- a/.gitignore +++ b/.gitignore @@ -119,6 +119,8 @@ debug.out mdioks.session-journal *.session +temp + logs/panel.pid logs/panel.log logs/panel_task.log @@ -189,3 +191,4 @@ plugins/goedge-admin plugins/goedge-node plugins/goedge-happy plugins/dztasks + From 2dcb8b257e2db770aaa516feb943c8335e0308da Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 21:14:18 +0800 Subject: [PATCH 518/965] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/data_query/nosql_memcached.py | 4 +- web/admin/plugins/__init__.py | 17 +++++++ web/utils/plugin.py | 70 +++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/plugins/data_query/nosql_memcached.py b/plugins/data_query/nosql_memcached.py index 18b844edf..b32ef5690 100755 --- a/plugins/data_query/nosql_memcached.py +++ b/plugins/data_query/nosql_memcached.py @@ -69,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()) @@ -106,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/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py index 8d7c72fb5..d152ae3e3 100644 --- a/web/admin/plugins/__init__.py +++ b/web/admin/plugins/__init__.py @@ -148,6 +148,23 @@ def file(): content = open(file, 'rb').read() return content + +# 插件上传 +@blueprint.route('/update_zip', endpoint='update_zip', methods=['POST']) +@panel_login_required +def update_zip(): + request_zip = request.files['plugin_zip'] + return MwPlugin.instance().updateZip(request_zip) + + +@blueprint.route('/input_zip', endpoint='input_zip', methods=['POST']) +@panel_login_required +def input_zip(): + plugin_name = request.form.get('plugin_name', '') + tmp_path = request.form.get('tmp_path', '') + return MwPlugin.instance().inputZipApi(plugin_name,tmp_path) + + # 插件设置页 @blueprint.route('/setting', endpoint='setting', methods=['GET']) @panel_login_required diff --git a/web/utils/plugin.py b/web/utils/plugin.py index 57ccc1a6f..24c2cea02 100644 --- a/web/utils/plugin.py +++ b/web/utils/plugin.py @@ -634,6 +634,76 @@ class plugin(object): rdata['list'] = mw.getPage({'count':data[1],'p':page,'tojs':'getSList','row':size}) return rdata + def updateZip(self, request_zip): + tmp_path = mw.getPanelDir() + '/temp' + if not os.path.exists(tmp_path): + os.makedirs(tmp_path) + mw.execShell("rm -rf " + tmp_path + '/*') + + tmp_file = tmp_path + '/plugin_tmp.zip' + if request_zip.filename[-4:] != '.zip': + return mw.returnData(False, '仅支持zip文件!') + + request_zip.save(tmp_file) + mw.execShell('cd ' + tmp_path + ' && unzip ' + tmp_file) + os.remove(tmp_file) + + p_info = tmp_path + '/info.json' + if not os.path.exists(p_info): + d_path = None + for df in os.walk(tmp_path): + if len(df[2]) < 3: + continue + if not 'info.json' in df[2]: + continue + if not 'install.sh' in df[2]: + continue + if not os.path.exists(df[0] + '/info.json'): + continue + d_path = df[0] + if d_path: + tmp_path = d_path + p_info = tmp_path + '/info.json' + try: + data = json.loads(mw.readFile(p_info)) + data['size'] = mw.getPathSize(tmp_path) + if not 'author' in data: + data['author'] = '未知' + if not 'home' in data: + data['home'] = 'https://github.com/midoks/mdserver-web' + plugin_path = mw.getPluginDir() + data['name'] + '/info.json' + data['old_version'] = '0' + data['tmp_path'] = tmp_path + if os.path.exists(plugin_path): + try: + old_info = json.loads(mw.readFile(plugin_path)) + data['old_version'] = old_info['versions'] + except: + pass + except: + mw.execShell("rm -rf " + tmp_path) + return mw.returnData(False, '在压缩包中没有找到插件信息,请检查插件包!') + protectPlist = ('openresty', 'mysql', 'php', 'redis', 'memcached' + 'mongodb', 'swap', 'gogs', 'pureftp') + if data['name'] in protectPlist: + return mw.returnData(False, '[' + data['name'] + '],重要插件不可修改!') + return data + + def inputZipApi(self, plugin_name,tmp_path): + if not os.path.exists(tmp_path): + return mw.returnData(False, '临时文件不存在,请重新上传!') + plugin_path = mw.getPluginDir() + '/' + plugin_name + if not os.path.exists(plugin_path): + print(mw.execShell('mkdir -p ' + plugin_path)) + mw.execShell("cp -rf " + tmp_path + '/* ' + plugin_path + '/') + mw.execShell('chmod -R 755 ' + plugin_path) + p_info = mw.readFile(plugin_path + '/info.json') + if p_info: + mw.writeLog('软件管理', '安装第三方插件[%s]' %json.loads(p_info)['title']) + return mw.returnData(True, '安装成功!') + mw.execShell("rm -rf " + plugin_path) + return mw.returnData(False, '安装失败!') + # shell/bash方式调用 def run(self, name, func, version = '', From a5bc06acf6fa19a699626d2ab9943c5898721c95 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Fri, 29 Nov 2024 22:08:54 +0800 Subject: [PATCH 519/965] Update app.js --- plugins/data_query/static/js/app.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index 89cb49b95..b8850c4c0 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -353,6 +353,11 @@ function mysqlGetTableList(p){ // console.log('mysqlGetTableList',p); var sid = mysqlGetSid(); var db = mysqlGetDbName(); + + if (!db){ + return; + } + myPostCB('get_table_list',{'sid':sid,'db':db} ,function(rdata){ if (rdata.data.status){ From 00002a8540b6e29fc3a4363d4696671df6992732 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 03:32:33 +0800 Subject: [PATCH 520/965] update --- plugins/data_query/sql_mysql.py | 99 +++++++++++- plugins/data_query/static/html/index.html | 10 +- plugins/data_query/static/js/app.js | 179 ++++++++++++++++++++++ plugins/task_manager/js/task_manager.js | 38 ++--- web/core/orm.py | 8 +- 5 files changed, 309 insertions(+), 25 deletions(-) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index 6b9dc35e4..19df8bd09 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -90,7 +90,7 @@ class nosqlMySQL(): keys = ["bind_ip", "port"] result['host'] = '127.0.0.1' - rep = 'port\s*=\s*(.*)' + rep = r'port\s*=\s*(.*)' port_re = re.search(rep, my_content) if port_re: @@ -98,7 +98,7 @@ class nosqlMySQL(): else: result['port'] = 3306 - socket_rep = 'socket\s*=\s*(.*)' + socket_rep = r'socket\s*=\s*(.*)' socket_re = re.search(socket_rep, my_content) if socket_re: result['socket'] = socket_re.groups()[0].strip() @@ -281,6 +281,88 @@ 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["@@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 10") + if data is None: + return mw.returnData(False, "查询失败!") + return mw.returnData(True, 'ok', data) + # ---------------------------------- run ---------------------------------- # 获取 mysql 列表 def get_db_list(args): @@ -308,6 +390,19 @@ 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 test(args): diff --git a/plugins/data_query/static/html/index.html b/plugins/data_query/static/html/index.html index c6bcd3ab0..dd589c749 100644 --- a/plugins/data_query/static/html/index.html +++ b/plugins/data_query/static/html/index.html @@ -51,9 +51,11 @@
                    - + +
                    + + +
                    @@ -78,6 +80,8 @@ +
                    +
                    diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index b8850c4c0..07648cb98 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -267,6 +267,185 @@ function initTabMySQL(){ var name = $(this).data('name'); mysqlRunMysqlTab(name); }); + + mysqlCommonFunc(); +} + +function mysqlCommonFuncMysqlNSQL(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_topn_list',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+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 += ''; + 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: '
                    \ +
                    \ +
                    \ + 实时监控\ +
                    \ + \ + \ +
                    \ +
                    \ +
                    \ +
                    \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    SQL数据名最近时间总次数最大时间平均时间
                    \ +
                    ', + success:function(i,l){ + renderSQL(); + + $('#real_time_label').click(function(){ + sql_timer = setInterval(function(){ + var t = $('#real_time_monitoring').is(':checked'); + if (t){ + renderSQL(); + } else{ + clearInterval(sql_timer); + } + }, 3000); + }); + } + }); +} + +function mysqlCommonFuncMysqlNet(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_net_list',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+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 += ''; + 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: ['700px', '220px'], + closeBtn: 1, + shadeClose: false, + content: '
                    \ +
                    \ +
                    \ + 实时监控\ +
                    \ + \ + \ +
                    \ +
                    \ +
                    \ +
                    \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    时间SelectInsertUpdateDeleteConnMax_connRecvSend
                    \ +
                    ', + success:function(i,l){ + renderSQL(); + + $('#real_qps_label').click(function(){ + sql_timer = setInterval(function(){ + var t = $('#real_qps_monitoring').is(':checked'); + if (t){ + renderSQL(); + } else{ + clearInterval(sql_timer); + } + }, 3000); + }); + } + }); +} + +function mysqlCommonFunc(){ + $('#mysql_common').click(function(){ + layer.open({ + type: 1, + title: "MySQL常用功能", + area: ['600px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
                    \ + \ + \ +
                    ', + success:function(i,l){ + $('#mysql_top_nsql').click(function(){ + mysqlCommonFuncMysqlNSQL(); + }); + + $('#mysql_net_stat').click(function(){ + mysqlCommonFuncMysqlNet(); + }); + } + }); + }); } function mysqlRunMysqlTab(name){ diff --git a/plugins/task_manager/js/task_manager.js b/plugins/task_manager/js/task_manager.js index 6c3f7f36e..16eab7e58 100644 --- a/plugins/task_manager/js/task_manager.js +++ b/plugins/task_manager/js/task_manager.js @@ -506,25 +506,25 @@ function get_resource_list() { buildRealProcess() var tbody_tr = createProcessTable(true); var tbody = '\ - \ - 应用名称\ - PID\ - 线程\ - 用户\ - CPU\ - 内存\ - io读\ - io写\ - 上行\ - 下行\ - 连接\ - 状态\ - 操作\ - \ - \ - ' + tbody_tr + '' - $('#taskResourceTable').html(tbody); - $(".table-cont").css("height", "220px"); + \ + 应用名称\ + PID\ + 线程\ + 用户\ + CPU\ + 内存\ + io读\ + io写\ + 上行\ + 下行\ + 连接\ + 状态\ + 操作\ + \ + \ + ' + tbody_tr + ''; + $('#taskResourceTable').html(tbody); + $(".table-cont").css("height", "220px"); }) $("#load_average").html('') diff --git a/web/core/orm.py b/web/core/orm.py index 89ba623e9..6baaeb790 100755 --- a/web/core/orm.py +++ b/web/core/orm.py @@ -119,6 +119,12 @@ class ORM: print(e) return True + def find(self, sql): + d = self.query(sql) + if d is not None: + return d[0] + return None + def query(self, sql): # 执行SQL语句返回数据集 if not self.__Conn(): @@ -132,7 +138,7 @@ class ORM: self.__Close() return result except Exception as ex: - return ex + return None def __Close(self): # 关闭连接 From d3b3b526dbd19de82203426cb5a01495da2e9182 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 03:52:58 +0800 Subject: [PATCH 521/965] update --- plugins/data_query/sql_mysql.py | 23 ++++++++++++ plugins/data_query/static/js/app.js | 58 ++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index 19df8bd09..68ac3c6ad 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -363,6 +363,24 @@ class nosqlMySQLCtr(): return mw.returnData(False, "查询失败!") 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) # ---------------------------------- run ---------------------------------- # 获取 mysql 列表 def get_db_list(args): @@ -400,6 +418,11 @@ def get_net_list(args): t = nosqlMySQLCtr() return t.getNetList(args) +# 查看重复或冗余的索引 +def get_redundant_indexes(args): + t = nosqlMySQLCtr() + return t.getRedundantIndexes(args) + diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index 07648cb98..b97fe5558 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -423,6 +423,55 @@ function mysqlCommonFuncMysqlNet(){ }); } +function mysqlCommonFuncRedundantIndexes(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_redundant_indexes',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+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 += ''; + tbody += t; + } + $('#redundant_indexes tbody').html(tbody); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + layer.open({ + type: 1, + title: "查看重复或冗余的索引", + area: ['1100px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
                    \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    数据库名表名冗余索引名冗余索引列名删除冗余索引SQL
                    \ +
                    ', + success:function(i,l){ + renderSQL(); + } + }); +} + function mysqlCommonFunc(){ $('#mysql_common').click(function(){ layer.open({ @@ -432,8 +481,9 @@ function mysqlCommonFunc(){ closeBtn: 1, shadeClose: false, content: '
                    \ - \ - \ + \ + \ + \
                    ', success:function(i,l){ $('#mysql_top_nsql').click(function(){ @@ -443,6 +493,10 @@ function mysqlCommonFunc(){ $('#mysql_net_stat').click(function(){ mysqlCommonFuncMysqlNet(); }); + + $('#mysql_redundant_indexes').click(function(){ + mysqlCommonFuncRedundantIndexes(); + }); } }); }); From 152fadfc8706a45d852ad609e251f3e581012b99 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 04:07:29 +0800 Subject: [PATCH 522/965] Update app.js --- plugins/data_query/static/js/app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index b97fe5558..b5c65d95f 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -438,6 +438,7 @@ function mysqlCommonFuncRedundantIndexes(){ t += ''+items[i]['redundant_index_name']+''; t += ''+items[i]['redundant_index_columns']+''; t += ''+items[i]['sql_drop_index']+''; + t += '执行'; t += ''; tbody += t; } @@ -462,6 +463,7 @@ function mysqlCommonFuncRedundantIndexes(){ 冗余索引名\ 冗余索引列名\ 删除冗余索引SQL\ + 操作\ \ \ \ From bd2ab7d5293a05d744b8e28f1cebac903c7718f9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 04:21:43 +0800 Subject: [PATCH 523/965] update --- plugins/data_query/sql_mysql.py | 28 ++++++++++++++++++++++++++++ plugins/data_query/static/js/app.js | 13 ++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index 68ac3c6ad..c5ac17434 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -381,6 +381,29 @@ class nosqlMySQLCtr(): 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, '执行成功!') # ---------------------------------- run ---------------------------------- # 获取 mysql 列表 def get_db_list(args): @@ -423,6 +446,11 @@ def get_redundant_indexes(args): t = nosqlMySQLCtr() return t.getRedundantIndexes(args) +# 查看重复或冗余的索引 +def redundant_indexes_cmd(args): + t = nosqlMySQLCtr() + return t.redundantIndexesCmd(args) + diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index b5c65d95f..36c07f127 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -438,11 +438,22 @@ function mysqlCommonFuncRedundantIndexes(){ t += ''+items[i]['redundant_index_name']+''; t += ''+items[i]['redundant_index_columns']+''; t += ''+items[i]['sql_drop_index']+''; - t += '执行'; + t += '执行'; 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}); } From 6874de5cda56a9f76fa0dd3da16786c1d06d45f9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 04:26:28 +0800 Subject: [PATCH 524/965] Update app.js --- plugins/data_query/static/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index 36c07f127..7fbf9134c 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -486,7 +486,7 @@ function mysqlCommonFuncRedundantIndexes(){ } function mysqlCommonFunc(){ - $('#mysql_common').click(function(){ + $('#mysql_common').unbind('click').click(function(){ layer.open({ type: 1, title: "MySQL常用功能", From 7e2c192d4095c18b43af3c226a7fba301cbfb087 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 16:42:42 +0800 Subject: [PATCH 525/965] update --- plugins/data_query/sql_mysql.py | 93 ++++++++++++++++++- plugins/data_query/static/js/app.js | 137 ++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 2 deletions(-) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index c5ac17434..869699bd8 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -404,6 +404,88 @@ class nosqlMySQLCtr(): 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; + """ + ) + + 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 == '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" + ) + + # 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) # ---------------------------------- run ---------------------------------- # 获取 mysql 列表 def get_db_list(args): @@ -446,13 +528,20 @@ 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) # 测试 diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index 7fbf9134c..e9141c284 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -485,6 +485,133 @@ function mysqlCommonFuncRedundantIndexes(){ }); } +function mysqlCommonFuncTableInfo(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_table_info',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+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 += ''; + 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: '
                    \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    库名表名储存引擎数据大小(GB)索引大小(GB)总计(GB)主键自增字段主键字段属性主键自增当前主键自增剩余
                    \ +
                    ', + success:function(i,l){ + renderSQL(); + } + }); +} +function mysqlCommonFuncConnCount(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_conn_count',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['user']+''; + t += ''+items[i]['db']+''; + t += ''+items[i]['Client_IP']+''; + t += ''+items[i]['count']+''; + 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: '
                    \ +
                    \ +
                    \ + 实时监控\ +
                    \ + \ + \ +
                    \ +
                    \ +
                    \ +
                    \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    连接用户数据库名应用端IP数量
                    \ +
                    ', + success:function(i,l){ + renderSQL(); + + $('#app_ip_label').click(function(){ + sql_timer = setInterval(function(){ + var t = $('#app_ip_monitoring').is(':checked'); + if (t){ + renderSQL(); + } else{ + clearInterval(sql_timer); + } + }, 3000); + }); + } + }); +} + function mysqlCommonFunc(){ $('#mysql_common').unbind('click').click(function(){ layer.open({ @@ -497,6 +624,8 @@ function mysqlCommonFunc(){ \ \ \ + \ + \
                    ', success:function(i,l){ $('#mysql_top_nsql').click(function(){ @@ -510,6 +639,14 @@ function mysqlCommonFunc(){ $('#mysql_redundant_indexes').click(function(){ mysqlCommonFuncRedundantIndexes(); }); + + $('#mysql_table_info').click(function(){ + mysqlCommonFuncTableInfo(); + }); + + $('#mysql_conn_count').click(function(){ + mysqlCommonFuncConnCount(); + }); } }); }); From ab7c4792e2a13b11c19518d7b9d49daa8877a40c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 16:51:26 +0800 Subject: [PATCH 526/965] Update sql_mysql.py --- plugins/data_query/sql_mysql.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index 869699bd8..caeb4318f 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -482,6 +482,13 @@ class nosqlMySQLCtr(): "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) From 371cc9c4c874f051c178ec4524ecc3b5ce6bca26 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 16:57:58 +0800 Subject: [PATCH 527/965] Update debian.sh --- scripts/install/debian.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/install/debian.sh b/scripts/install/debian.sh index ae0ac0a46..37e4d97ba 100644 --- a/scripts/install/debian.sh +++ b/scripts/install/debian.sh @@ -74,7 +74,9 @@ apt install -y rar apt install -y unrar apt install -y pv apt install -y bc -apt install -y python3-pip python3-dev python3-venv +apt install -y python3-pip +apt install -y python3-dev +apt install -y python3-venv apt install -y libncurses5 apt install -y libncurses5-dev apt install -y bzip2 From 76b9298efd84ba06fc4eda1972d57bde819c3bcf Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 17:11:00 +0800 Subject: [PATCH 528/965] update --- plugins/data_query/sql_mysql.py | 28 +++++++++++++++++ plugins/data_query/static/js/app.js | 49 +++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index caeb4318f..8591e9a0d 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -493,6 +493,29 @@ class nosqlMySQLCtr(): # 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) # ---------------------------------- run ---------------------------------- # 获取 mysql 列表 def get_db_list(args): @@ -550,6 +573,11 @@ def get_conn_count(args): t = nosqlMySQLCtr() return t.getConnCount(args) +# 快速找出没有主键的表 +def get_fpk_info(args): + t = nosqlMySQLCtr() + return t.getFpkInfo(args) + # 测试 def test(args): diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index e9141c284..4518f6f60 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -543,6 +543,7 @@ function mysqlCommonFuncTableInfo(){ } }); } + function mysqlCommonFuncConnCount(){ function renderSQL(){ var sid = mysqlGetSid(); @@ -612,6 +613,49 @@ function mysqlCommonFuncConnCount(){ }); } +function mysqlCommonFuncFpkInfo(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_fpk_info',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['table_schema']+''; + t += ''+items[i]['table_name']+''; + 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: '
                    \ + \ + \ + \ + \ + \ + \ +
                    库名表名
                    \ +
                    ', + success:function(i,l){ + renderSQL(); + } + }); +} + function mysqlCommonFunc(){ $('#mysql_common').unbind('click').click(function(){ layer.open({ @@ -626,6 +670,7 @@ function mysqlCommonFunc(){ \ \ \ + \
                    ', success:function(i,l){ $('#mysql_top_nsql').click(function(){ @@ -647,6 +692,10 @@ function mysqlCommonFunc(){ $('#mysql_conn_count').click(function(){ mysqlCommonFuncConnCount(); }); + + $('#mysql_fpk_info').click(function(){ + mysqlCommonFuncFpkInfo(); + }); } }); }); From 778e1860df90562a8a8b77bd5d08891963829873 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 18:01:09 +0800 Subject: [PATCH 529/965] Update python-app-op.yml --- .github/workflows/python-app-op.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app-op.yml b/.github/workflows/python-app-op.yml index 53ab5f7fc..64ce10ca2 100644 --- a/.github/workflows/python-app-op.yml +++ b/.github/workflows/python-app-op.yml @@ -31,7 +31,7 @@ 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 - name: Start DEBUG run: | gunicorn -c setting.py app:app From 1d9bc68e28911359073f23f4769337a35ecfbf32 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 18:07:22 +0800 Subject: [PATCH 530/965] update --- plugins/data_query/sql_mysql.py | 58 +++++++++++++++++++++ plugins/data_query/static/js/app.js | 80 +++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index 8591e9a0d..36f97d159 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -516,6 +516,54 @@ class nosqlMySQLCtr(): """ ) 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 + """ + ) + + print(data) + return mw.returnData(True, 'ok', data) + + 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', '无锁表') # ---------------------------------- run ---------------------------------- # 获取 mysql 列表 def get_db_list(args): @@ -578,6 +626,16 @@ def get_fpk_info(args): t = nosqlMySQLCtr() return t.getFpkInfo(args) +# 查看当前锁阻塞的SQL +def get_lock_sql(args): + t = nosqlMySQLCtr() + return t.getLockSql(args) + +# 查看死锁信息 +def get_deadlock_info(args): + t = nosqlMySQLCtr() + return t.getDeadlockInfo(args) + # 测试 def test(args): diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index 4518f6f60..27e4ed041 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -656,6 +656,76 @@ function mysqlCommonFuncFpkInfo(){ }); } +function mysqlCommonFuncLockSQL(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_lock_sql',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['table_schema']+''; + t += ''+items[i]['table_name']+''; + t += ''; + tbody += t; + } + $('#mysql_data_id tbody').html(tbody); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + layer.open({ + type: 1, + title: "查看当前锁阻塞的SQL", + area: ['800px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
                    \ + \ + \ + \ + \ + \ + \ +
                    库名表名
                    \ +
                    ', + success:function(i,l){ + renderSQL(); + } + }); +} + +function mysqlCommonFuncDeadlockInfo(){ + + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_deadlock_info',{'sid':sid} ,function(rdata){ + var data = rdata.data; + $('#info_log').html(data.data); + var ob = document.getElementById('info_log'); + ob.scrollTop = ob.scrollHeight; + }); + } + + layer.open({ + type: 1, + title: "查看死锁信息", + area: ['800px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
                    \ + \ +
                    ', + success:function(i,l){ + renderSQL(); + } + }); +} + function mysqlCommonFunc(){ $('#mysql_common').unbind('click').click(function(){ layer.open({ @@ -671,6 +741,8 @@ function mysqlCommonFunc(){ \ \ \ + \ + \
                    ', success:function(i,l){ $('#mysql_top_nsql').click(function(){ @@ -696,6 +768,14 @@ function mysqlCommonFunc(){ $('#mysql_fpk_info').click(function(){ mysqlCommonFuncFpkInfo(); }); + + $('#mysql_lock_sql').click(function(){ + mysqlCommonFuncLockSQL(); + }); + + $('#mysql_deadlock_info').click(function(){ + mysqlCommonFuncDeadlockInfo(); + }); } }); }); From c355a120b4eba0c6b094ba604706c9d483263333 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 21:16:21 +0800 Subject: [PATCH 531/965] Update install.sh --- plugins/op_waf/install.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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" From 97829cd944705cec04b10004fa2002036f201fd8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 21:16:59 +0800 Subject: [PATCH 532/965] Update install.sh --- plugins/op_load_balance/install.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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(){ From 9915ed35d0504f1d57616fac77163852dcfd5f0a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 21:44:09 +0800 Subject: [PATCH 533/965] update --- plugins/acme_pandominassl_apply/install.sh | 3 +-- plugins/alist/install.sh | 1 - plugins/backup_ftp/install.sh | 3 +-- plugins/clean/install.sh | 7 +++---- plugins/cloudreve/install.sh | 1 - plugins/data_query/install.sh | 3 +-- plugins/docker/install.sh | 2 +- plugins/dynamic-tracking/install.sh | 9 ++++----- plugins/dynamic-tracking/versions/centos.sh | 1 - plugins/dynamic-tracking/versions/debian.sh | 1 - plugins/dynamic-tracking/versions/macos.sh | 1 - plugins/dynamic-tracking/versions/ubuntu.sh | 1 - plugins/fail2ban/install.sh | 3 +-- plugins/gdrive/install.sh | 9 ++++----- plugins/gitea/install.sh | 4 +--- plugins/gogs/install.sh | 8 +++----- plugins/gorse/install.sh | 4 +--- plugins/haproxy/install.sh | 2 -- plugins/haproxy/versions/2.6/install.sh | 5 +---- plugins/imail/install.sh | 6 ++---- plugins/imail/versions/0.0.19/install.sh | 7 +++---- plugins/keepalived/install.sh | 2 -- plugins/lvs/install.sh | 1 - plugins/mariadb/install.sh | 2 -- plugins/mariadb/versions/10.11/install.sh | 5 ++--- plugins/mariadb/versions/10.6/install.sh | 5 ++--- plugins/mariadb/versions/10.7/install.sh | 5 ++--- plugins/mariadb/versions/10.8/install.sh | 5 ++--- plugins/mariadb/versions/10.9/install.sh | 5 ++--- plugins/mariadb/versions/11.0/install.sh | 5 ++--- plugins/mariadb/versions/11.1/install.sh | 5 ++--- plugins/mariadb/versions/11.2/install.sh | 5 ++--- plugins/mariadb/versions/11.3/install.sh | 5 ++--- plugins/mariadb/versions/11.4/install.sh | 5 ++--- plugins/mariadb/versions/11.5/install.sh | 5 ++--- plugins/mariadb/versions/11.6/install.sh | 5 ++--- plugins/memcached/install.sh | 3 +-- plugins/migration_api/install.sh | 3 +-- plugins/mongodb/install.bk1.sh | 7 +++---- plugins/mongodb/install.bk2.sh | 7 +++---- plugins/mongodb/install.sh | 1 - plugins/msonedrive/install.sh | 5 ++--- plugins/mysql-apt/install.sh | 1 - plugins/mysql-apt/versions/5.7/install.sh | 11 +++++------ plugins/mysql-apt/versions/8.0/install.sh | 13 ++++++------- plugins/mysql-apt/versions/8.2/install.sh | 13 ++++++------- plugins/mysql-apt/versions/8.3/install.sh | 13 ++++++------- plugins/mysql-apt/versions/8.4/install.sh | 9 ++++----- plugins/mysql-apt/versions/9.0/install.sh | 7 +++---- plugins/mysql-yum/install.sh | 6 ++---- plugins/mysql-yum/versions/5.7/install.sh | 7 +++---- plugins/mysql-yum/versions/8.0/install.sh | 4 +--- plugins/mysql-yum/versions/8.2/install.sh | 3 +-- plugins/mysql-yum/versions/8.3/install.sh | 4 +--- plugins/mysql-yum/versions/8.4/install.sh | 4 +--- plugins/mysql-yum/versions/9.0/install.sh | 4 +--- plugins/mysql/install.sh | 8 ++------ plugins/mysql/versions/5.5/install.sh | 5 ++--- plugins/mysql/versions/5.6/install.sh | 3 +-- plugins/mysql/versions/5.7/install.sh | 4 +--- plugins/mysql/versions/8.0/install.sh | 4 +--- plugins/mysql/versions/8.2/install.sh | 4 +--- plugins/mysql/versions/8.3/install.sh | 4 +--- plugins/mysql/versions/8.4/install.sh | 4 +--- plugins/mysql/versions/9.0/install.sh | 3 +-- plugins/nezha/install.sh | 6 ++---- plugins/nezha/versions/0.15.2/install.sh | 9 ++++----- plugins/openresty/install.sh | 1 - plugins/openresty/versions/1.17.8/install.sh | 2 -- plugins/openresty/versions/1.19.3/install.sh | 2 -- plugins/openresty/versions/1.21.4/install.sh | 1 - plugins/openresty/versions/1.25.3/install.sh | 1 - plugins/openresty/versions/1.27.1/install.sh | 1 - plugins/openresty/versions/rtmp/install.sh | 1 - plugins/pgadmin/install.sh | 2 -- plugins/php-guard/install.sh | 3 --- plugins/php/versions/80/install.sh | 2 +- plugins/php/versions/81/install.sh | 2 +- plugins/php/versions/82/install.sh | 2 +- plugins/php/versions/83/install.sh | 2 +- plugins/php/versions/84/install.sh | 2 +- plugins/phpmyadmin/install.sh | 3 --- plugins/postgresql/install.sh | 2 -- plugins/postgresql/versions/14/install.sh | 3 +-- plugins/postgresql/versions/15/install.sh | 4 +--- plugins/postgresql/versions/16/install.sh | 3 +-- plugins/postgresql/versions/17/install.sh | 3 +-- plugins/rsyncd/install.sh | 10 +++++----- plugins/simpleping/install.sh | 4 +--- plugins/sphinx/install.sh | 1 - plugins/supervisor/install.sh | 1 - plugins/swap/install.sh | 8 +++----- plugins/sys-opt/install.sh | 10 +++------- plugins/system_safe/install.sh | 7 +++---- plugins/tamper_proof_py/install.sh | 7 +++---- plugins/task_manager/install.sh | 3 +-- plugins/tgbot/install.sh | 9 +++------ plugins/tgclient/install.sh | 9 +++------ plugins/varnish/install.sh | 7 +++---- plugins/webhook/install.sh | 8 +++----- plugins/webssh/install.sh | 9 +++------ plugins/webstats/install.sh | 1 - plugins/xhprof/install.sh | 5 ++--- plugins/zabbix/install.sh | 1 - plugins/zabbix/versions/6.0/alma.sh | 1 - plugins/zabbix/versions/6.0/centos.sh | 1 - plugins/zabbix/versions/6.0/debian.sh | 2 +- plugins/zabbix/versions/6.0/opensuse.sh | 1 - plugins/zabbix/versions/6.0/rocky.sh | 1 - plugins/zabbix/versions/6.0/ubuntu.sh | 2 +- plugins/zabbix/versions/7.0/alma.sh | 1 - plugins/zabbix/versions/7.0/centos.sh | 1 - plugins/zabbix/versions/7.0/debian.sh | 2 -- plugins/zabbix/versions/7.0/opensuse.sh | 1 - plugins/zabbix/versions/7.0/rocky.sh | 2 +- plugins/zabbix/versions/7.0/ubuntu.sh | 1 - plugins/zabbix_agent/install.sh | 1 - plugins/zabbix_agent/versions/6.0/debian.sh | 1 - plugins/zabbix_agent/versions/7.0/alma.sh | 2 +- plugins/zabbix_agent/versions/7.0/centos.sh | 1 - plugins/zabbix_agent/versions/7.0/debian.sh | 2 +- plugins/zabbix_agent/versions/7.0/opensuse.sh | 2 +- plugins/zabbix_agent/versions/7.0/rocky.sh | 1 - plugins/zabbix_agent/versions/7.0/ubuntu.sh | 2 +- 124 files changed, 170 insertions(+), 325 deletions(-) diff --git a/plugins/acme_pandominassl_apply/install.sh b/plugins/acme_pandominassl_apply/install.sh index 4f4ce632f..42591f085 100755 --- a/plugins/acme_pandominassl_apply/install.sh +++ b/plugins/acme_pandominassl_apply/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # curl https://get.acme.sh | sh @@ -31,7 +30,7 @@ fi # pip install cloudflare Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/acme_pandominassl_apply echo "${VERSION}" > $serverPath/acme_pandominassl_apply/version.pl diff --git a/plugins/alist/install.sh b/plugins/alist/install.sh index a0b26c1a6..900ab6181 100755 --- a/plugins/alist/install.sh +++ b/plugins/alist/install.sh @@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath") # cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4 # cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysArch=`arch` 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/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/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/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/docker/install.sh b/plugins/docker/install.sh index c0679a66e..75ec2fa1f 100755 --- a/plugins/docker/install.sh +++ b/plugins/docker/install.sh @@ -41,7 +41,7 @@ Install_Docker() { # which docker # if [ "$?" == "0" ];then - # echo '安装已经完成docker' > $install_tmp + # echo '安装已经完成docker' # exit 0 # fi diff --git a/plugins/dynamic-tracking/install.sh b/plugins/dynamic-tracking/install.sh index 0ddca4b3a..cb0995050 100755 --- a/plugins/dynamic-tracking/install.sh +++ b/plugins/dynamic-tracking/install.sh @@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web/plugins/dynamic-tracking && /bin/bash install.sh uninstall 1.0 # cd /www/server/mdserver-web/plugins/dynamic-tracking && /bin/bash install.sh install 1.0 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 if [ -f ${rootPath}/bin/activate ];then @@ -31,7 +30,7 @@ fi Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/dynamic-tracking @@ -57,7 +56,7 @@ Install_App() shell_file=${curPath}/versions/${OSNAME}.sh echo $shell_file if [ ! -f $shell_file ];then - echo '不支持...' > $install_tmp + echo '不支持...' exit 1 fi @@ -67,7 +66,7 @@ Install_App() cp -rf ${curPath}/shell $serverPath/dynamic-tracking echo "${VERSION}" > $serverPath/dynamic-tracking/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/dynamic-tracking/index.py start # cd ${rootPath} && python3 ${rootPath}/plugins/dynamic-tracking/index.py initd_install @@ -77,7 +76,7 @@ Install_App() Uninstall_App() { rm -rf $serverPath/dynamic-tracking - echo "Uninstall_App" > $install_tmp + echo "Uninstall_App" } action=$1 diff --git a/plugins/dynamic-tracking/versions/centos.sh b/plugins/dynamic-tracking/versions/centos.sh index f71fbaa58..e74a8469f 100644 --- a/plugins/dynamic-tracking/versions/centos.sh +++ b/plugins/dynamic-tracking/versions/centos.sh @@ -7,5 +7,4 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` \ No newline at end of file diff --git a/plugins/dynamic-tracking/versions/debian.sh b/plugins/dynamic-tracking/versions/debian.sh index c42eff5eb..c87f300fb 100644 --- a/plugins/dynamic-tracking/versions/debian.sh +++ b/plugins/dynamic-tracking/versions/debian.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` # apt-get install linux-perf diff --git a/plugins/dynamic-tracking/versions/macos.sh b/plugins/dynamic-tracking/versions/macos.sh index 812f66746..f0522e5c3 100644 --- a/plugins/dynamic-tracking/versions/macos.sh +++ b/plugins/dynamic-tracking/versions/macos.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` # 关于macosx使用dtrace diff --git a/plugins/dynamic-tracking/versions/ubuntu.sh b/plugins/dynamic-tracking/versions/ubuntu.sh index b9dcff40b..b12e95d0f 100644 --- a/plugins/dynamic-tracking/versions/ubuntu.sh +++ b/plugins/dynamic-tracking/versions/ubuntu.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` # export GOPROXY=https://goproxy.io diff --git a/plugins/fail2ban/install.sh b/plugins/fail2ban/install.sh index ced567e74..09f8b1806 100755 --- a/plugins/fail2ban/install.sh +++ b/plugins/fail2ban/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # cd /www/server/mdserver-web/plugins/fail2ban && bash install.sh install 1.1.0 @@ -16,7 +15,7 @@ VERSION=$2 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source apt install -y fail2ban diff --git a/plugins/gdrive/install.sh b/plugins/gdrive/install.sh index 43b8895b5..a10c64155 100644 --- a/plugins/gdrive/install.sh +++ b/plugins/gdrive/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl PATH=$PATH:${rootPath}/bin export PATH @@ -58,13 +57,13 @@ Install_App() pip install -I google-api-python-client==2.39.0 google-auth-httplib2==0.1.0 google-auth-oauthlib==0.5.0 -i https://pypi.Python.org/simple pip install -I httplib2==0.18.1 -i https://pypi.Python.org/simple fi - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/gdrive echo "${VERSION}" > $serverPath/gdrive/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' else - echo '服务器连接不上谷歌云!安装失败!' > $install_tmp + echo '服务器连接不上谷歌云!安装失败!' exit 1 fi } @@ -72,7 +71,7 @@ Install_App() Uninstall_App() { rm -rf $serverPath/gdrive - echo '卸载完成' > $install_tmp + echo '卸载完成' } diff --git a/plugins/gitea/install.sh b/plugins/gitea/install.sh index 437e431ef..c03be4b16 100755 --- a/plugins/gitea/install.sh +++ b/plugins/gitea/install.sh @@ -7,8 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate fi @@ -115,7 +113,7 @@ Uninstall_App() fi rm -rf $serverPath/gitea - echo 'uninstall success' > $install_tmp + echo 'uninstall success' } diff --git a/plugins/gogs/install.sh b/plugins/gogs/install.sh index 23da6e351..d9a3af681 100755 --- a/plugins/gogs/install.sh +++ b/plugins/gogs/install.sh @@ -7,8 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate fi @@ -36,7 +34,7 @@ Install_gogs() mkdir -p $serverPath/source/gogs - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' version=$1 os=`getOs` @@ -78,7 +76,7 @@ Install_gogs() # echo "gogs ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers # fi - echo 'install success' > $install_tmp + echo 'install success' } Uninstall_gogs() @@ -96,7 +94,7 @@ Uninstall_gogs() fi rm -rf $serverPath/gogs - echo 'uninstall success' > $install_tmp + echo 'uninstall success' } diff --git a/plugins/gorse/install.sh b/plugins/gorse/install.sh index e34f521c7..3edbb195e 100755 --- a/plugins/gorse/install.sh +++ b/plugins/gorse/install.sh @@ -12,13 +12,11 @@ sysArch=`arch` # cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/gorse && bash install.sh install 0.4.15 # cd /www/server/mdserver-web/plugins/gorse && bash install.sh install 0.4.15 - -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/source/gorse diff --git a/plugins/haproxy/install.sh b/plugins/haproxy/install.sh index d22eb66d4..8844a1df8 100755 --- a/plugins/haproxy/install.sh +++ b/plugins/haproxy/install.sh @@ -8,8 +8,6 @@ curPath=`pwd` rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - action=$1 type=$2 diff --git a/plugins/haproxy/versions/2.6/install.sh b/plugins/haproxy/versions/2.6/install.sh index 43854cb7a..f49b5d395 100755 --- a/plugins/haproxy/versions/2.6/install.sh +++ b/plugins/haproxy/versions/2.6/install.sh @@ -8,9 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - - bash ${rootPath}/scripts/getos.sh OSNAME=`cat ${rootPath}/data/osname.pl` OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` @@ -63,7 +60,7 @@ Install_App() fi echo $MIN_VERSION > $serverPath/haproxy/version.pl - echo '安装Haproxy成功' > $install_tmp + echo '安装Haproxy成功' #Haproxy日志配置 if [ -f /etc/rsyslog.conf ];then diff --git a/plugins/imail/install.sh b/plugins/imail/install.sh index 04a563fe9..631a4a26f 100755 --- a/plugins/imail/install.sh +++ b/plugins/imail/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/imail/versions/0.0.19/install.sh b/plugins/imail/versions/0.0.19/install.sh index 3e10c02e0..dbfe92501 100755 --- a/plugins/imail/versions/0.0.19/install.sh +++ b/plugins/imail/versions/0.0.19/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=0.0.19 # bash install.sh install 0.0.19 @@ -67,7 +66,7 @@ download_file() { Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source load_vars @@ -93,7 +92,7 @@ Install_App() cd ${rootPath} && python3 ${rootPath}/plugins/imail/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/imail/index.py initd_install fi - echo 'install successful' > $install_tmp + echo 'install successful' } Uninstall_App() @@ -101,7 +100,7 @@ Uninstall_App() cd ${rootPath} && python3 ${rootPath}/plugins/imail/index.py initd_uninstall cd ${rootPath} && python3 ${rootPath}/plugins/imail/index.py stop rm -rf $serverPath/imail - echo "install fail" > $install_tmp + echo "install fail" } action=$1 diff --git a/plugins/keepalived/install.sh b/plugins/keepalived/install.sh index 067777cbb..d3a869a2d 100755 --- a/plugins/keepalived/install.sh +++ b/plugins/keepalived/install.sh @@ -18,8 +18,6 @@ serverPath=$(dirname "$rootPath") # systemctl restart keepalived # ifconfig -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 Install_App() diff --git a/plugins/lvs/install.sh b/plugins/lvs/install.sh index fd6bb9f25..9ecaccca4 100755 --- a/plugins/lvs/install.sh +++ b/plugins/lvs/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` VERSION=1.6.22 diff --git a/plugins/mariadb/install.sh b/plugins/mariadb/install.sh index ae2cbf9c6..ff3f0ac0f 100755 --- a/plugins/mariadb/install.sh +++ b/plugins/mariadb/install.sh @@ -14,8 +14,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py init_slave_status # cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py install_pre_inspection -install_tmp=${rootPath}/tmp/mw_install.pl - action=$1 type=$2 diff --git a/plugins/mariadb/versions/10.11/install.sh b/plugins/mariadb/versions/10.11/install.sh index 0e22e2f7d..b76f7d90d 100755 --- a/plugins/mariadb/versions/10.11/install.sh +++ b/plugins/mariadb/versions/10.11/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=10.11.10 @@ -19,7 +18,7 @@ MY_VER=10.11.10 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/10.6/install.sh b/plugins/mariadb/versions/10.6/install.sh index b116971b3..93d5c34ab 100755 --- a/plugins/mariadb/versions/10.6/install.sh +++ b/plugins/mariadb/versions/10.6/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=10.6.20 @@ -18,7 +17,7 @@ MY_VER=10.6.20 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -113,7 +112,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/10.7/install.sh b/plugins/mariadb/versions/10.7/install.sh index b6d13f624..c680889a3 100755 --- a/plugins/mariadb/versions/10.7/install.sh +++ b/plugins/mariadb/versions/10.7/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=10.7.8 @@ -19,7 +18,7 @@ MY_VER=10.7.8 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -100,7 +99,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/10.8/install.sh b/plugins/mariadb/versions/10.8/install.sh index 92e08c542..d85218384 100755 --- a/plugins/mariadb/versions/10.8/install.sh +++ b/plugins/mariadb/versions/10.8/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=10.8.7 @@ -19,7 +18,7 @@ MY_VER=10.8.7 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -100,7 +99,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/10.9/install.sh b/plugins/mariadb/versions/10.9/install.sh index 4e86094f5..6f6fcbf1e 100755 --- a/plugins/mariadb/versions/10.9/install.sh +++ b/plugins/mariadb/versions/10.9/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=10.9.8 @@ -19,7 +18,7 @@ MY_VER=10.9.8 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/11.0/install.sh b/plugins/mariadb/versions/11.0/install.sh index 18303fd84..7b590149f 100755 --- a/plugins/mariadb/versions/11.0/install.sh +++ b/plugins/mariadb/versions/11.0/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=11.0.5 @@ -19,7 +18,7 @@ MY_VER=11.0.5 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/11.1/install.sh b/plugins/mariadb/versions/11.1/install.sh index 40cd366bb..3617d9cf3 100755 --- a/plugins/mariadb/versions/11.1/install.sh +++ b/plugins/mariadb/versions/11.1/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=11.1.4 @@ -19,7 +18,7 @@ MY_VER=11.1.4 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/11.2/install.sh b/plugins/mariadb/versions/11.2/install.sh index 12778da85..04690307e 100755 --- a/plugins/mariadb/versions/11.2/install.sh +++ b/plugins/mariadb/versions/11.2/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=11.2.6 @@ -19,7 +18,7 @@ MY_VER=11.2.6 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/11.3/install.sh b/plugins/mariadb/versions/11.3/install.sh index b49e06a00..fa7e2eb30 100755 --- a/plugins/mariadb/versions/11.3/install.sh +++ b/plugins/mariadb/versions/11.3/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=11.3.2 @@ -19,7 +18,7 @@ MY_VER=11.3.2 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/11.4/install.sh b/plugins/mariadb/versions/11.4/install.sh index e6c1aefa5..1dd951772 100755 --- a/plugins/mariadb/versions/11.4/install.sh +++ b/plugins/mariadb/versions/11.4/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=11.4.4 @@ -19,7 +18,7 @@ MY_VER=11.4.4 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/11.5/install.sh b/plugins/mariadb/versions/11.5/install.sh index 881e93ce8..8b3581135 100755 --- a/plugins/mariadb/versions/11.5/install.sh +++ b/plugins/mariadb/versions/11.5/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=11.5.2 @@ -19,7 +18,7 @@ MY_VER=11.5.2 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mariadb/versions/11.6/install.sh b/plugins/mariadb/versions/11.6/install.sh index 3597e2e22..654f225e0 100755 --- a/plugins/mariadb/versions/11.6/install.sh +++ b/plugins/mariadb/versions/11.6/install.sh @@ -11,7 +11,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb MY_VER=11.6.2 @@ -19,7 +18,7 @@ MY_VER=11.6.2 Install_app() { mkdir -p ${mariadbDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -110,7 +109,7 @@ Install_app() Uninstall_app() { rm -rf $serverPath/mariadb - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/memcached/install.sh b/plugins/memcached/install.sh index 490dde8ca..5744718c6 100755 --- a/plugins/memcached/install.sh +++ b/plugins/memcached/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` VERSION=1.6.32 @@ -16,7 +15,7 @@ echo $sys_os Install_mem(){ mkdir -p $serverPath/source mkdir -p $serverPath/source/memcached - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ ! -f $serverPath/source/memcached.tar.gz ];then wget --no-check-certificate -O $serverPath/source/memcached/memcached.tar.gz https://www.memcached.org/files/memcached-${VERSION}.tar.gz diff --git a/plugins/migration_api/install.sh b/plugins/migration_api/install.sh index 9a1ad2098..a60523ca2 100755 --- a/plugins/migration_api/install.sh +++ b/plugins/migration_api/install.sh @@ -8,14 +8,13 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=1.0 Install_App(){ mkdir -p $serverPath/migration_api echo "${VERSION}" > $serverPath/migration_api/version.pl - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' } Uninstall_App() diff --git a/plugins/mongodb/install.bk1.sh b/plugins/mongodb/install.bk1.sh index 7cbe54115..e0c7a57e9 100755 --- a/plugins/mongodb/install.bk1.sh +++ b/plugins/mongodb/install.bk1.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysName=`uname` @@ -228,7 +227,7 @@ Install_app_linux() Install_app() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source if [ "macos" == "$OSNAME" ];then @@ -240,7 +239,7 @@ Install_app() if [ "$?" == "0" ];then mkdir -p $serverPath/mongodb echo "${VERSION}" > $serverPath/mongodb/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' #初始化 cd ${rootPath} && python3 ${rootPath}/plugins/mongodb/index.py start @@ -276,7 +275,7 @@ Uninstall_app() fi rm -rf $serverPath/mongodb - echo "Uninstall_mongodb" > $install_tmp + echo "Uninstall_mongodb" } action=$1 diff --git a/plugins/mongodb/install.bk2.sh b/plugins/mongodb/install.bk2.sh index eb2a67e9c..3c45158da 100755 --- a/plugins/mongodb/install.bk2.sh +++ b/plugins/mongodb/install.bk2.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysName=`uname` @@ -48,7 +47,7 @@ Install_app() fi # ----- cpu end ------ - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' MG_DIR=$serverPath/source/mongodb mkdir -p $MG_DIR @@ -83,7 +82,7 @@ Install_app() if [ "$?" == "0" ];then mkdir -p $serverPath/mongodb echo "${VERSION}" > $serverPath/mongodb/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' #初始化 cd ${rootPath} && python3 ${rootPath}/plugins/mongodb/index.py start @@ -96,7 +95,7 @@ Uninstall_app() { rm -rf $serverPath/mongodb - echo "Uninstall_mongodb" > $install_tmp + echo "Uninstall_mongodb" } action=$1 diff --git a/plugins/mongodb/install.sh b/plugins/mongodb/install.sh index eac49d078..a59ce4e29 100755 --- a/plugins/mongodb/install.sh +++ b/plugins/mongodb/install.sh @@ -15,7 +15,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysName=`uname` diff --git a/plugins/msonedrive/install.sh b/plugins/msonedrive/install.sh index 3c0b352da..c1e72bd28 100644 --- a/plugins/msonedrive/install.sh +++ b/plugins/msonedrive/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # cd /www/server/mdserver-web/plugins/msonedrive && bash install.sh install 1.0 @@ -20,10 +19,10 @@ Install_App() { pip install requests-oauthlib==1.3.0 mkdir -p $serverPath/msonedrive - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' echo "${VERSION}" > $serverPath/msonedrive/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' echo "Successify" } 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/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/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/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/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/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/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/openresty/install.sh b/plugins/openresty/install.sh index 35680f32d..4be1cad85 100755 --- a/plugins/openresty/install.sh +++ b/plugins/openresty/install.sh @@ -15,7 +15,6 @@ action=$1 type=$2 VERSION=$2 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty if id www &> /dev/null ;then diff --git a/plugins/openresty/versions/1.17.8/install.sh b/plugins/openresty/versions/1.17.8/install.sh index 4259f71ea..544c97b9f 100644 --- a/plugins/openresty/versions/1.17.8/install.sh +++ b/plugins/openresty/versions/1.17.8/install.sh @@ -15,8 +15,6 @@ action=$1 type=$2 VERSION=1.17.8.2 - -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() diff --git a/plugins/openresty/versions/1.19.3/install.sh b/plugins/openresty/versions/1.19.3/install.sh index 555a7a609..2af69a9b1 100644 --- a/plugins/openresty/versions/1.19.3/install.sh +++ b/plugins/openresty/versions/1.19.3/install.sh @@ -15,8 +15,6 @@ action=$1 type=$2 VERSION=1.19.3.1 - -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() diff --git a/plugins/openresty/versions/1.21.4/install.sh b/plugins/openresty/versions/1.21.4/install.sh index 817699157..0ceaa3a91 100644 --- a/plugins/openresty/versions/1.21.4/install.sh +++ b/plugins/openresty/versions/1.21.4/install.sh @@ -16,7 +16,6 @@ type=$2 VERSION=1.21.4.4 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() diff --git a/plugins/openresty/versions/1.25.3/install.sh b/plugins/openresty/versions/1.25.3/install.sh index 473fe1938..0becee2f0 100644 --- a/plugins/openresty/versions/1.25.3/install.sh +++ b/plugins/openresty/versions/1.25.3/install.sh @@ -16,7 +16,6 @@ type=$2 VERSION=1.25.3.2 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() diff --git a/plugins/openresty/versions/1.27.1/install.sh b/plugins/openresty/versions/1.27.1/install.sh index 8e4d01ca6..a53019d98 100644 --- a/plugins/openresty/versions/1.27.1/install.sh +++ b/plugins/openresty/versions/1.27.1/install.sh @@ -16,7 +16,6 @@ type=$2 VERSION=1.27.1.1 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() diff --git a/plugins/openresty/versions/rtmp/install.sh b/plugins/openresty/versions/rtmp/install.sh index eab364971..11a8f5d88 100644 --- a/plugins/openresty/versions/rtmp/install.sh +++ b/plugins/openresty/versions/rtmp/install.sh @@ -16,7 +16,6 @@ type=$2 VERSION=1.27.1.1 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() diff --git a/plugins/pgadmin/install.sh b/plugins/pgadmin/install.sh index 8c5a876c0..55f861f2a 100755 --- a/plugins/pgadmin/install.sh +++ b/plugins/pgadmin/install.sh @@ -26,8 +26,6 @@ echo "python:$P_VER" # cd /www/server/mdserver-web && python3 plugins/pgadmin/index.py start # cd /www/server/mdserver-web && python3 plugins/pgadmin/index.py stop -install_tmp=${rootPath}/tmp/mw_install.pl - if [ "$sys_os" == "Darwin" ];then BAK='_bak' else diff --git a/plugins/php-guard/install.sh b/plugins/php-guard/install.sh index eb6b1c80f..2623847c8 100755 --- a/plugins/php-guard/install.sh +++ b/plugins/php-guard/install.sh @@ -7,9 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - - Install_pg() { echo '卸载PHP守护中...' diff --git a/plugins/php/versions/80/install.sh b/plugins/php/versions/80/install.sh index 1d84e4685..e9bacab5f 100755 --- a/plugins/php/versions/80/install.sh +++ b/plugins/php/versions/80/install.sh @@ -160,7 +160,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/81/install.sh b/plugins/php/versions/81/install.sh index cf65d0da4..693975011 100755 --- a/plugins/php/versions/81/install.sh +++ b/plugins/php/versions/81/install.sh @@ -151,7 +151,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh index 28405bcbc..a4c9b2edd 100755 --- a/plugins/php/versions/82/install.sh +++ b/plugins/php/versions/82/install.sh @@ -176,7 +176,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index 93e237c5d..d4b8790aa 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -154,7 +154,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh index cdb855bd2..9e4e421e8 100755 --- a/plugins/php/versions/84/install.sh +++ b/plugins/php/versions/84/install.sh @@ -145,7 +145,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/phpmyadmin/install.sh b/plugins/phpmyadmin/install.sh index 43d28fdef..9dfb0b45a 100755 --- a/plugins/phpmyadmin/install.sh +++ b/plugins/phpmyadmin/install.sh @@ -7,9 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - - if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate fi diff --git a/plugins/postgresql/install.sh b/plugins/postgresql/install.sh index 6e8af5591..8d9b534ca 100755 --- a/plugins/postgresql/install.sh +++ b/plugins/postgresql/install.sh @@ -9,8 +9,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web/plugins/postgresql && bash install.sh install 16 -install_tmp=${rootPath}/tmp/mw_install.pl - action=$1 type=$2 diff --git a/plugins/postgresql/versions/14/install.sh b/plugins/postgresql/versions/14/install.sh index 43ae5035b..f61f065e1 100755 --- a/plugins/postgresql/versions/14/install.sh +++ b/plugins/postgresql/versions/14/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql VERSION=14.12 @@ -20,7 +19,7 @@ VERSION=14.12 Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/postgresql/versions/15/install.sh b/plugins/postgresql/versions/15/install.sh index a36706732..76acaff37 100755 --- a/plugins/postgresql/versions/15/install.sh +++ b/plugins/postgresql/versions/15/install.sh @@ -10,9 +10,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql - VERSION=15.7 # su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data" @@ -20,7 +18,7 @@ VERSION=15.7 Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/postgresql/versions/16/install.sh b/plugins/postgresql/versions/16/install.sh index 3392612ee..052e11873 100755 --- a/plugins/postgresql/versions/16/install.sh +++ b/plugins/postgresql/versions/16/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql VERSION=16.4 @@ -20,7 +19,7 @@ VERSION=16.4 Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/postgresql/versions/17/install.sh b/plugins/postgresql/versions/17/install.sh index b46b78c2b..c103a3c06 100755 --- a/plugins/postgresql/versions/17/install.sh +++ b/plugins/postgresql/versions/17/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql VERSION=17.2 @@ -20,7 +19,7 @@ VERSION=17.2 Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/rsyncd/install.sh b/plugins/rsyncd/install.sh index d339a5866..a8a13d86a 100755 --- a/plugins/rsyncd/install.sh +++ b/plugins/rsyncd/install.sh @@ -28,11 +28,11 @@ else useradd -g www -s /bin/bash www fi -echo $OSNAME -install_tmp=${rootPath}/tmp/mw_install.pl +# echo $OSNAME + Install_rsyncd() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$OSNAME" == "debian" ] || [ "$OSNAME" == "ubuntu" ];then @@ -55,7 +55,7 @@ Install_rsyncd() mkdir -p $serverPath/rsyncd/send echo '2.0' > $serverPath/rsyncd/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py initd_install } @@ -83,7 +83,7 @@ Uninstall_rsyncd() fi rm -rf $serverPath/rsyncd - echo "卸载完成" > $install_tmp + echo "卸载完成" } action=$1 diff --git a/plugins/simpleping/install.sh b/plugins/simpleping/install.sh index 647106bbd..7a39c14de 100755 --- a/plugins/simpleping/install.sh +++ b/plugins/simpleping/install.sh @@ -14,11 +14,9 @@ VERSION=$2 # 开启可以PING # sysctl -w net.ipv4.ping_group_range="0 2147483647" -install_tmp=${rootPath}/tmp/mw_install.pl - Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/source/simpleping diff --git a/plugins/sphinx/install.sh b/plugins/sphinx/install.sh index 17dc0f5f2..277029f12 100755 --- a/plugins/sphinx/install.sh +++ b/plugins/sphinx/install.sh @@ -9,7 +9,6 @@ serverPath=$(dirname "$rootPath") sysName=`uname` sysArch=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate diff --git a/plugins/supervisor/install.sh b/plugins/supervisor/install.sh index 2695ae7e6..96ece61ac 100755 --- a/plugins/supervisor/install.sh +++ b/plugins/supervisor/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # /www/server/mdserver-web/bin/supervisorctl -c /www/server/supervisor/supervisor.conf diff --git a/plugins/swap/install.sh b/plugins/swap/install.sh index 875345c3d..1f698fd13 100755 --- a/plugins/swap/install.sh +++ b/plugins/swap/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") SYSOS=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 # cd /www/server/mdserver-web/plugins/swap && /bin/bash install.sh install 1.1 @@ -20,7 +18,7 @@ Install_swap() exit 0 fi - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/swap echo "${VERSION}" > $serverPath/swap/version.pl @@ -34,7 +32,7 @@ Install_swap() swapon $serverPath/swap/swapfile fi - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/swap/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/swap/index.py initd_install @@ -59,7 +57,7 @@ Uninstall_swap() rm -rf $serverPath/swap - echo "Uninstall_swap" > $install_tmp + echo "Uninstall_swap" } action=$1 diff --git a/plugins/sys-opt/install.sh b/plugins/sys-opt/install.sh index d6370cc71..03e7c82ff 100755 --- a/plugins/sys-opt/install.sh +++ b/plugins/sys-opt/install.sh @@ -8,23 +8,19 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - - Install_sysopt() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/sys-opt echo '1.0' > $serverPath/sys-opt/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_sysopt() { rm -rf $serverPath/sys-opt - echo "卸载完成" > $install_tmp + echo "卸载完成" } action=$1 diff --git a/plugins/system_safe/install.sh b/plugins/system_safe/install.sh index 7179c2499..d053a898b 100755 --- a/plugins/system_safe/install.sh +++ b/plugins/system_safe/install.sh @@ -7,17 +7,16 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYSOS=`uname` VERSION=$2 APP_NAME=system_safe Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/system_safe echo "$VERSION" > $serverPath/system_safe/version.pl - echo 'install complete' > $install_tmp + echo 'install complete' #初始化 cd ${serverPath}/mdserver-web && python3 plugins/system_safe/system_safe.py start $VERSION @@ -36,7 +35,7 @@ Uninstall_App() fi rm -rf $serverPath/system_safe - echo "uninstall completed" > $install_tmp + echo "uninstall completed" } action=$1 diff --git a/plugins/tamper_proof_py/install.sh b/plugins/tamper_proof_py/install.sh index efa2d12f7..f3b963984 100755 --- a/plugins/tamper_proof_py/install.sh +++ b/plugins/tamper_proof_py/install.sh @@ -13,17 +13,16 @@ if [ -f ${rootPath}/bin/activate ];then fi pip install pyinotify -install_tmp=${rootPath}/tmp/mw_install.pl SYSOS=`uname` VERSION=$2 APP_NAME=tamper_proof_py Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/tamper_proof_py echo "$VERSION" > $serverPath/tamper_proof_py/version.pl - echo 'install complete' > $install_tmp + echo 'install complete' #初始化 cd ${serverPath}/mdserver-web && python3 plugins/tamper_proof_py/index.py start $VERSION @@ -41,7 +40,7 @@ Uninstall_App() fi rm -rf $serverPath/tamper_proof_py - echo "uninstall completed" > $install_tmp + echo "uninstall completed" } action=$1 diff --git a/plugins/task_manager/install.sh b/plugins/task_manager/install.sh index 1884c74a8..896b1af89 100755 --- a/plugins/task_manager/install.sh +++ b/plugins/task_manager/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # python3 plugins/task_manager/task_manager_index.py @@ -20,7 +19,7 @@ fi Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/task_manager if [ -f /usr/bin/apt ]; then diff --git a/plugins/tgbot/install.sh b/plugins/tgbot/install.sh index cec3fab83..31d91f8e0 100755 --- a/plugins/tgbot/install.sh +++ b/plugins/tgbot/install.sh @@ -7,9 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 # pip3 install ccxt @@ -22,7 +19,7 @@ pip3 install telebot Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/tgbot echo "${VERSION}" > $serverPath/tgbot/version.pl @@ -31,7 +28,7 @@ Install_App() cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py initd_install - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_App() @@ -48,7 +45,7 @@ Uninstall_App() fi rm -rf $serverPath/tgbot - echo "Uninstall_redis" > $install_tmp + echo "Uninstall_redis" } action=$1 diff --git a/plugins/tgclient/install.sh b/plugins/tgclient/install.sh index cf06a2d04..6757ba902 100755 --- a/plugins/tgclient/install.sh +++ b/plugins/tgclient/install.sh @@ -7,9 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 # pip3 install ccxt @@ -21,7 +18,7 @@ pip3 install telethon Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/tgclient echo "${VERSION}" > $serverPath/tgclient/version.pl @@ -30,7 +27,7 @@ Install_App() cd ${rootPath} && python3 ${rootPath}/plugins/tgclient/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/tgclient/index.py initd_install - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_App() @@ -47,7 +44,7 @@ Uninstall_App() fi rm -rf $serverPath/tgclient - echo "Uninstall_redis" > $install_tmp + echo "Uninstall_redis" } action=$1 diff --git a/plugins/varnish/install.sh b/plugins/varnish/install.sh index d47114112..2ce3ad69f 100755 --- a/plugins/varnish/install.sh +++ b/plugins/varnish/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 @@ -18,7 +17,7 @@ OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F Install_varnish() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source if [ "${OSNAME}" == "macos" ]; then @@ -38,7 +37,7 @@ Install_varnish() mkdir -p $serverPath/varnish echo "1.0" > $serverPath/varnish/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py initd_install @@ -63,7 +62,7 @@ Uninstall_varnish() echo "I won't support it" fi rm -rf $serverPath/varnish - echo "uninstall varnish" > $install_tmp + echo "uninstall varnish" } action=$1 diff --git a/plugins/webhook/install.sh b/plugins/webhook/install.sh index e3018b0a9..6743e6162 100755 --- a/plugins/webhook/install.sh +++ b/plugins/webhook/install.sh @@ -7,22 +7,20 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=1.0 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/webhook echo "${VERSION}" > $serverPath/webhook/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_App() { rm -rf $serverPath/webhook - echo "Uninstall_App" > $install_tmp + echo "Uninstall_App" } action=$1 diff --git a/plugins/webssh/install.sh b/plugins/webssh/install.sh index 6c7ecedec..31782e34c 100755 --- a/plugins/webssh/install.sh +++ b/plugins/webssh/install.sh @@ -8,24 +8,21 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 Install_webssh() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/webssh echo "${VERSION}" > $serverPath/webssh/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_webssh() { rm -rf $serverPath/webssh - echo "卸载完成" > $install_tmp + echo "卸载完成" } action=$1 diff --git a/plugins/webstats/install.sh b/plugins/webstats/install.sh index bb4395bd2..3545c87e7 100755 --- a/plugins/webstats/install.sh +++ b/plugins/webstats/install.sh @@ -12,7 +12,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sys_os=`uname` diff --git a/plugins/xhprof/install.sh b/plugins/xhprof/install.sh index 745164925..45eabd0e1 100755 --- a/plugins/xhprof/install.sh +++ b/plugins/xhprof/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` Install_xh() { @@ -18,7 +17,7 @@ Install_xh() fi echo "${1}" > ${serverPath}/xhprof/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' if [ "$sys_os" != "Darwin" ];then cd $rootPath && python3 ${rootPath}/plugins/xhprof/index.py start @@ -37,7 +36,7 @@ Uninstall_xh() if [ -f ${serverPath}/web_conf/nginx/vhost/xhprof.conf ];then rm -f ${serverPath}/web_conf/nginx/vhost/xhprof.conf fi - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/zabbix/install.sh b/plugins/zabbix/install.sh index a49c4689f..349265683 100755 --- a/plugins/zabbix/install.sh +++ b/plugins/zabbix/install.sh @@ -14,7 +14,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysName=`uname` diff --git a/plugins/zabbix/versions/6.0/alma.sh b/plugins/zabbix/versions/6.0/alma.sh index 5858a76d3..d432a18e4 100644 --- a/plugins/zabbix/versions/6.0/alma.sh +++ b/plugins/zabbix/versions/6.0/alma.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/centos.sh b/plugins/zabbix/versions/6.0/centos.sh index 562d58e70..5ddb82e7a 100644 --- a/plugins/zabbix/versions/6.0/centos.sh +++ b/plugins/zabbix/versions/6.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/debian.sh b/plugins/zabbix/versions/6.0/debian.sh index 27915ea75..3df4767ee 100644 --- a/plugins/zabbix/versions/6.0/debian.sh +++ b/plugins/zabbix/versions/6.0/debian.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + debian_suffix= if [ "$SYS_ARCH" == "aarch64" ];then debian_suffix="-arm64" diff --git a/plugins/zabbix/versions/6.0/opensuse.sh b/plugins/zabbix/versions/6.0/opensuse.sh index 748fc705d..3b316159b 100644 --- a/plugins/zabbix/versions/6.0/opensuse.sh +++ b/plugins/zabbix/versions/6.0/opensuse.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/rocky.sh b/plugins/zabbix/versions/6.0/rocky.sh index 2b3348512..b9e8cfbec 100644 --- a/plugins/zabbix/versions/6.0/rocky.sh +++ b/plugins/zabbix/versions/6.0/rocky.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/ubuntu.sh b/plugins/zabbix/versions/6.0/ubuntu.sh index a65f647e7..b564164a0 100644 --- a/plugins/zabbix/versions/6.0/ubuntu.sh +++ b/plugins/zabbix/versions/6.0/ubuntu.sh @@ -9,8 +9,8 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` ubuntu_suffix= diff --git a/plugins/zabbix/versions/7.0/alma.sh b/plugins/zabbix/versions/7.0/alma.sh index dd1cc523c..d53b56da3 100644 --- a/plugins/zabbix/versions/7.0/alma.sh +++ b/plugins/zabbix/versions/7.0/alma.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/centos.sh b/plugins/zabbix/versions/7.0/centos.sh index db9c4a3b0..8bfddec8c 100644 --- a/plugins/zabbix/versions/7.0/centos.sh +++ b/plugins/zabbix/versions/7.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/debian.sh b/plugins/zabbix/versions/7.0/debian.sh index a7abe09cd..3492f1786 100644 --- a/plugins/zabbix/versions/7.0/debian.sh +++ b/plugins/zabbix/versions/7.0/debian.sh @@ -10,8 +10,6 @@ sourcePath=${serverPath}/source sysName=`uname` sysArch=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - debian_suffix= if [ "$sysArch" == "aarch64" ];then debian_suffix="-arm64" diff --git a/plugins/zabbix/versions/7.0/opensuse.sh b/plugins/zabbix/versions/7.0/opensuse.sh index 805f6419c..6b093949f 100644 --- a/plugins/zabbix/versions/7.0/opensuse.sh +++ b/plugins/zabbix/versions/7.0/opensuse.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/rocky.sh b/plugins/zabbix/versions/7.0/rocky.sh index 8bc6c05a9..7d19a292f 100644 --- a/plugins/zabbix/versions/7.0/rocky.sh +++ b/plugins/zabbix/versions/7.0/rocky.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/ubuntu.sh b/plugins/zabbix/versions/7.0/ubuntu.sh index 7bc4eb851..1e34aa830 100644 --- a/plugins/zabbix/versions/7.0/ubuntu.sh +++ b/plugins/zabbix/versions/7.0/ubuntu.sh @@ -11,7 +11,6 @@ sourcePath=${serverPath}/source sysName=`uname` sysArch=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl ubuntu_suffix= if [ "$sysArch" == "aarch64" ];then ubuntu_suffix="-arm64" diff --git a/plugins/zabbix_agent/install.sh b/plugins/zabbix_agent/install.sh index 0bbaf897b..955c8c072 100755 --- a/plugins/zabbix_agent/install.sh +++ b/plugins/zabbix_agent/install.sh @@ -15,7 +15,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysName=`uname` diff --git a/plugins/zabbix_agent/versions/6.0/debian.sh b/plugins/zabbix_agent/versions/6.0/debian.sh index 80283fa0b..dd284d4e2 100644 --- a/plugins/zabbix_agent/versions/6.0/debian.sh +++ b/plugins/zabbix_agent/versions/6.0/debian.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/alma.sh b/plugins/zabbix_agent/versions/7.0/alma.sh index 39f54df27..0c72c43b7 100644 --- a/plugins/zabbix_agent/versions/7.0/alma.sh +++ b/plugins/zabbix_agent/versions/7.0/alma.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/centos.sh b/plugins/zabbix_agent/versions/7.0/centos.sh index a456ee761..8c4e391d5 100644 --- a/plugins/zabbix_agent/versions/7.0/centos.sh +++ b/plugins/zabbix_agent/versions/7.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/debian.sh b/plugins/zabbix_agent/versions/7.0/debian.sh index 3278f253c..f29fb7992 100644 --- a/plugins/zabbix_agent/versions/7.0/debian.sh +++ b/plugins/zabbix_agent/versions/7.0/debian.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/opensuse.sh b/plugins/zabbix_agent/versions/7.0/opensuse.sh index 6ec3cd19c..2b08e20a5 100644 --- a/plugins/zabbix_agent/versions/7.0/opensuse.sh +++ b/plugins/zabbix_agent/versions/7.0/opensuse.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/rocky.sh b/plugins/zabbix_agent/versions/7.0/rocky.sh index c7107c7d6..9d1f8e95c 100644 --- a/plugins/zabbix_agent/versions/7.0/rocky.sh +++ b/plugins/zabbix_agent/versions/7.0/rocky.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/ubuntu.sh b/plugins/zabbix_agent/versions/7.0/ubuntu.sh index e12309ca5..98d7c1cf6 100644 --- a/plugins/zabbix_agent/versions/7.0/ubuntu.sh +++ b/plugins/zabbix_agent/versions/7.0/ubuntu.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` From da9c57cf84434779e5b9d6e5f7b859aa9bfe7ef5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 21:57:46 +0800 Subject: [PATCH 534/965] update --- scripts/install_dev.sh | 1 - scripts/install_new.sh | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/install_dev.sh b/scripts/install_dev.sh index a44795a5c..cde638325 100755 --- a/scripts/install_dev.sh +++ b/scripts/install_dev.sh @@ -11,7 +11,6 @@ if [ -f /www/server/mdserver-web/tools.py ];then exit 0 fi - echo -e "您正在安装的是\033[31mmdserver-web测试版\033[0m,非开发测试用途请使用正式版 install.sh !" echo -e "You are installing\033[31m mdserver-web dev version\033[0m, normally use install.sh for production.\n" sleep 1 diff --git a/scripts/install_new.sh b/scripts/install_new.sh index 608a2d556..cde638325 100755 --- a/scripts/install_new.sh +++ b/scripts/install_new.sh @@ -4,6 +4,13 @@ export PATH # LANG=en_US.UTF-8 is64bit=`getconf LONG_BIT` +if [ -f /www/server/mdserver-web/tools.py ];then + echo -e "存在旧版代码,不能安装!,已知风险的情况下" + echo -e "rm -rf /www/server/mdserver-web" + echo -e "可安装!" + exit 0 +fi + echo -e "您正在安装的是\033[31mmdserver-web测试版\033[0m,非开发测试用途请使用正式版 install.sh !" echo -e "You are installing\033[31m mdserver-web dev version\033[0m, normally use install.sh for production.\n" sleep 1 From 3d3cde43537f338d36ff2864320486a30ee6c355 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 21:58:06 +0800 Subject: [PATCH 535/965] Update update_new.sh --- scripts/update_new.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/update_new.sh b/scripts/update_new.sh index 7c4d2ebd6..1f8caa7aa 100755 --- a/scripts/update_new.sh +++ b/scripts/update_new.sh @@ -6,6 +6,13 @@ is64bit=`getconf LONG_BIT` startTime=`date +%s` +if [ -f /www/server/mdserver-web/tools.py ];then + echo -e "存在旧版代码,不能安装!,已知风险的情况下" + echo -e "rm -rf /www/server/mdserver-web" + echo -e "可安装!" + exit 0 +fi + _os=`uname` echo "use system: ${_os}" From b0c3e5b7e5981794ced5cefb2d57c3369aa75511 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 22:41:39 +0800 Subject: [PATCH 536/965] update --- README.md | 5 -- scripts/install_new.sh | 189 ----------------------------------------- scripts/update_new.sh | 134 ----------------------------- web/setting.py | 2 +- web/utils/firewall.py | 12 +++ 5 files changed, 13 insertions(+), 329 deletions(-) delete mode 100755 scripts/install_new.sh delete mode 100755 scripts/update_new.sh diff --git a/README.md b/README.md index b4d210106..76298c0bd 100644 --- a/README.md +++ b/README.md @@ -182,11 +182,6 @@ 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/update_new.sh | bash ``` ### 旧版安装 diff --git a/scripts/install_new.sh b/scripts/install_new.sh deleted file mode 100755 index cde638325..000000000 --- a/scripts/install_new.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin -export PATH -# LANG=en_US.UTF-8 -is64bit=`getconf LONG_BIT` - -if [ -f /www/server/mdserver-web/tools.py ];then - echo -e "存在旧版代码,不能安装!,已知风险的情况下" - echo -e "rm -rf /www/server/mdserver-web" - echo -e "可安装!" - exit 0 -fi - -echo -e "您正在安装的是\033[31mmdserver-web测试版\033[0m,非开发测试用途请使用正式版 install.sh !" -echo -e "You are installing\033[31m mdserver-web dev version\033[0m, normally use install.sh for production.\n" -sleep 1 - -LOG_FILE=/var/log/mw-install.log - -{ - -if [ -f /etc/motd ];then - echo "welcome to mdserver-web panel" > /etc/motd -fi - -startTime=`date +%s` - -_os=`uname` -echo "use system: ${_os}" - - -if [ ${_os} == "Darwin" ]; then - OSNAME='macos' -elif grep -Eq "openSUSE" /etc/*-release; then - OSNAME='opensuse' - zypper refresh - zypper install -y wget curl zip unzip unrar rar -elif grep -Eq "FreeBSD" /etc/*-release; then - OSNAME='freebsd' - pkg install -y wget curl zip unzip unrar rar -elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then - OSNAME='euler' - yum install -y wget curl zip unzip tar crontabs -elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then - OSNAME='rhel' - yum install -y wget zip unzip tar -elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then - OSNAME='rhel' - yum install -y wget zip unzip tar -elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then - OSNAME='rhel' - yum install -y wget zip unzip -elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then - OSNAME='rhel' - yum install -y wget zip unzip tar -elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then - OSNAME='amazon' - yum install -y wget zip unzip tar -elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then - OSNAME='ubuntu' - apt install -y wget zip unzip tar -elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then - OSNAME='debian' - apt update -y - apt install -y devscripts - apt install -y wget zip unzip tar -else - OSNAME='unknow' -fi - -if [ "$EUID" -ne 0 ] && [ "$OSNAME" != "macos" ];then - echo "Please run as root!" - exit -fi - -# HTTP_PREFIX="https://" -# LOCAL_ADDR=common -# ping -c 1 github.com > /dev/null 2>&1 -# if [ "$?" != "0" ];then -# LOCAL_ADDR=cn -# HTTP_PREFIX="https://mirror.ghproxy.com/" -# fi - -HTTP_PREFIX="https://" -LOCAL_ADDR=common -cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") -if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then - LOCAL_ADDR=cn - HTTP_PREFIX="https://mirror.ghproxy.com/" -fi - -echo "local:${LOCAL_ADDR}" -echo "OSNAME:${OSNAME}" - -if [ $OSNAME != "macos" ];then - - if id www &> /dev/null ;then - echo "" - else - groupadd www - useradd -g www -s /usr/sbin/nologin www - fi - - mkdir -p /www/server - mkdir -p /www/wwwroot - mkdir -p /www/wwwlogs - mkdir -p /www/backup/database - mkdir -p /www/backup/site - - if [ ! -d /www/server/mdserver-web ];then - - if [ "$LOCAL_ADDR" == "common" ];then - curl --insecure -sSLo /tmp/dev.zip ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/heads/dev.zip - cd /tmp && unzip /tmp/dev.zip - mv -f /tmp/mdserver-web-dev /www/server/mdserver-web - rm -rf /tmp/dev.zip - rm -rf /tmp/mdserver-web-dev - else - # curl --insecure -sSLo /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip - wget --no-check-certificate -O /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip - cd /tmp && unzip /tmp/dev.zip - mv -f /tmp/mdserver-web /www/server/mdserver-web - rm -rf /tmp/dev.zip - rm -rf /tmp/mdserver-web - fi - fi - - # install acme.sh - if [ ! -d /root/.acme.sh ];then - if [ "$LOCAL_ADDR" != "common" ];then - # curl -sSL -o /tmp/acme.tar.gz ${HTTP_PREFIX}github.com/acmesh-official/acme.sh/archive/master.tar.gz - curl --insecure -sSLo /tmp/acme.tar.gz https://gitee.com/neilpang/acme.sh/repository/archive/master.tar.gz - tar xvzf /tmp/acme.tar.gz -C /tmp - cd /tmp/acme.sh-master - bash acme.sh install - fi - - if [ ! -d /root/.acme.sh ];then - curl https://get.acme.sh | sh - fi - fi -fi - -echo "use system version: ${OSNAME}" - -if [ "${OSNAME}" == "macos" ];then - curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install/macos.sh | bash -else - cd /www/server/mdserver-web && bash scripts/install/${OSNAME}.sh -fi - -if [ "${OSNAME}" == "macos" ];then - echo "macos end" - exit 0 -fi - -cd /www/server/mdserver-web && bash cli.sh start -isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'` -n=0 -while [ ! -f /etc/rc.d/init.d/mw ]; -do - echo -e ".\c" - sleep 1 - let n+=1 - if [ $n -gt 20 ];then - echo -e "start mw fail" - exit 1 - fi -done - -cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop -cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start -cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw default - -sleep 2 -if [ ! -e /usr/bin/mw ]; then - if [ -f /etc/rc.d/init.d/mw ];then - ln -s /etc/rc.d/init.d/mw /usr/bin/mw - fi -fi - -endTime=`date +%s` -((outTime=(${endTime}-${startTime})/60)) -echo -e "Time consumed:\033[32m $outTime \033[0mMinute!" - -} 1> >(tee $LOG_FILE) 2>&1 - -echo -e "\nInstall completed. If error occurs, please contact us with the log file mw-install.log ." -echo "安装完毕,如果出现错误,请带上同目录下的安装日志 mw-install.log 联系我们反馈." \ No newline at end of file diff --git a/scripts/update_new.sh b/scripts/update_new.sh deleted file mode 100755 index 1f8caa7aa..000000000 --- a/scripts/update_new.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin -export PATH -# LANG=en_US.UTF-8 -is64bit=`getconf LONG_BIT` - -startTime=`date +%s` - -if [ -f /www/server/mdserver-web/tools.py ];then - echo -e "存在旧版代码,不能安装!,已知风险的情况下" - echo -e "rm -rf /www/server/mdserver-web" - echo -e "可安装!" - exit 0 -fi - -_os=`uname` -echo "use system: ${_os}" - -if [ "$EUID" -ne 0 ] - then echo "Please run as root!" - exit -fi - -if [ ${_os} != "Darwin" ] && [ ! -d /www/server/mdserver-web/logs ]; then - mkdir -p /www/server/mdserver-web/logs -fi - -LOG_FILE=/var/log/mw-update.log - -{ - -if [ ${_os} == "Darwin" ]; then - OSNAME='macos' -elif grep -Eqi "openSUSE" /etc/*-release; then - OSNAME='opensuse' - zypper refresh -elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then - OSNAME='euler' -elif grep -Eqi "FreeBSD" /etc/*-release; then - OSNAME='freebsd' -elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then - OSNAME='centos' - yum install -y wget zip unzip -elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then - OSNAME='rhel' - yum install -y wget zip unzip -elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then - OSNAME='rhel' - yum install -y wget zip unzip -elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then - OSNAME='rhel' - yum install -y wget zip unzip -elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then - OSNAME='amazon' - yum install -y wget zip unzip -elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then - OSNAME='debian' - apt install -y wget zip unzip -elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then - OSNAME='ubuntu' - apt install -y wget zip unzip -elif grep -Eqi "Raspbian" /etc/issue || grep -Eqi "Raspbian" /etc/*-release; then - OSNAME='raspbian' -else - OSNAME='unknow' -fi - -# HTTP_PREFIX="https://" -# LOCAL_ADDR=common -# ping -c 1 github.com > /dev/null 2>&1 -# if [ "$?" != "0" ];then -# LOCAL_ADDR=cn -# HTTP_PREFIX="https://mirror.ghproxy.com/" -# fi -HTTP_PREFIX="https://" -LOCAL_ADDR=common -cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") -if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then - LOCAL_ADDR=cn - HTTP_PREFIX="https://mirror.ghproxy.com/" -fi - -echo "local:${LOCAL_ADDR}" - -CP_CMD=/usr/bin/cp -if [ -f /bin/cp ];then - CP_CMD=/bin/cp -fi - -echo "update mdserver-web code start" - -if [ "$LOCAL_ADDR" != "common" ];then - wget --no-check-certificate -O /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip - # curl --insecure -sSLo /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip - cd /tmp && unzip /tmp/dev.zip - - $CP_CMD -rf /tmp/mdserver-web/* /www/server/mdserver-web - rm -rf /tmp/master.zip - rm -rf /tmp/mdserver-web -else - curl --insecure -sSLo /tmp/dev.zip ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/heads/dev.zip - cd /tmp && unzip /tmp/dev.zip - $CP_CMD -rf /tmp/mdserver-web-dev/* /www/server/mdserver-web - rm -rf /tmp/dev.zip - rm -rf /tmp/mdserver-web-dev -fi - -echo "update mdserver-web code end" - -rm -rf /tmp/dev.zip -rm -rf /tmp/mdserver-web-dev - -#pip uninstall public -echo "use system version: ${OSNAME}" -cd /www/server/mdserver-web && bash scripts/update/${OSNAME}.sh - -bash /etc/rc.d/init.d/mw restart -bash /etc/rc.d/init.d/mw default - -if [ -f /usr/bin/mw ];then - rm -rf /usr/bin/mw -fi - -if [ ! -e /usr/bin/mw ]; then - if [ ! -f /usr/bin/mw ];then - ln -s /etc/rc.d/init.d/mw /usr/bin/mw - fi -fi - -endTime=`date +%s` -((outTime=($endTime-$startTime)/60)) -echo -e "Time consumed:\033[32m $outTime \033[0mMinute!" - -} 1> >(tee $LOG_FILE) 2>&1 \ No newline at end of file diff --git a/web/setting.py b/web/setting.py index 85d5e112e..a677e0ed8 100755 --- a/web/setting.py +++ b/web/setting.py @@ -47,7 +47,7 @@ if os.path.exists(default_port_file): else: from utils.firewall import Firewall as MwFirewall panel_port = str(random.randint(10000, 65530)) - MwFirewall.instance().addAcceptPort(panel_port, 'PANEL端口', 'port') + MwFirewall.instance().addPanelPort(panel_port) mw.writeFile(default_port_file, panel_port) bind = [] diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 0aebdf227..993fc7004 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -309,6 +309,18 @@ class Firewall(object): mw.writeLog("防火墙管理", msg) return mw.returnData(True, msg) + def addPanelPort(self, port): + protocol = 'tcp' + ps = 'PANEL端口' + + thisdb.addFirewall(port, ps=ps,protocol=protocol) + self.addAcceptPortCmd(port, protocol=protocol) + self.reload() + + msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,)) + mw.writeLog("防火墙管理", msg) + return mw.returnData(True, msg) + def delAcceptPort(self, firewall_id, port, protocol='tcp' ): From a36d2f2665c503a556ae2733db4803fcfa430b3c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 22:43:17 +0800 Subject: [PATCH 537/965] Update firewall.py --- web/utils/firewall.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 993fc7004..c6550c590 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -310,6 +310,8 @@ class Firewall(object): return mw.returnData(True, msg) def addPanelPort(self, port): + self.setFw(0) + protocol = 'tcp' ps = 'PANEL端口' From f040b590d55e0f84bf1c7ed3fdbcf89916cf3cdc Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 22:53:40 +0800 Subject: [PATCH 538/965] Update firewall.py --- web/utils/firewall.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index c6550c590..b2e72ee32 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -51,6 +51,41 @@ class Firewall(object): def aIF(self): if self.__isFirewalld: self.AIF_Firewalld() + if self.__isUfw: + self.aIF_Ufw() + + + def aIF_Ufw(self): + t = mw.execShell("ufw status|awk '{print $1}' | grep -v 'Status'|grep -v 'To'|grep -v '-'") + if t[1] != '': + return True + + print(t) + + # all_port = t[0].strip() + # data = all_port.split(":") + # ports_str = data[1] + # ports_list = ports_str.strip().split(' ') + + # ports_all = [] + # for pinfo in ports_list: + # info = pinfo.split('/') + + # is_same = False + # for i in range(len(ports_all)): + # if ports_all[i]['port'] == info[0] and ports_all[i]['protocol'] != info[1]: + # ports_all[i]['protocol'] = ports_all[i]['protocol']+'/'+info[1] + # is_same = True + + # if not is_same: + # t = {} + # t['port'] = info[0].replace('-',':') + # t['protocol'] = info[1] + # ports_all.append(t) + + # for add_info in ports_all: + # if thisdb.getFirewallCountByPort(add_info['port']) == 0: + # thisdb.addFirewall(add_info['port'], ps='自动识别',protocol=add_info['protocol']) def AIF_Firewalld(self): # firewall-cmd --list-all | grep ' ports' From c66ad2b7fbd95a36ab738ed42c6aec3ced76019a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 22:58:24 +0800 Subject: [PATCH 539/965] Update firewall.py --- web/utils/firewall.py | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index b2e72ee32..d7a8771da 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -60,32 +60,27 @@ class Firewall(object): if t[1] != '': return True - print(t) + all_port = t[0].strip() + ports_list = all_port.split('\n') - # all_port = t[0].strip() - # data = all_port.split(":") - # ports_str = data[1] - # ports_list = ports_str.strip().split(' ') + ports_all = [] + for pinfo in ports_list: + info = pinfo.split('/') - # ports_all = [] - # for pinfo in ports_list: - # info = pinfo.split('/') + is_same = False + for i in range(len(ports_all)): + if ports_all[i]['port'] == info[0] and ports_all[i]['protocol'] != info[1]: + ports_all[i]['protocol'] = ports_all[i]['protocol']+'/'+info[1] + is_same = True - # is_same = False - # for i in range(len(ports_all)): - # if ports_all[i]['port'] == info[0] and ports_all[i]['protocol'] != info[1]: - # ports_all[i]['protocol'] = ports_all[i]['protocol']+'/'+info[1] - # is_same = True - - # if not is_same: - # t = {} - # t['port'] = info[0].replace('-',':') - # t['protocol'] = info[1] - # ports_all.append(t) - - # for add_info in ports_all: - # if thisdb.getFirewallCountByPort(add_info['port']) == 0: - # thisdb.addFirewall(add_info['port'], ps='自动识别',protocol=add_info['protocol']) + if not is_same: + t = {} + t['port'] = info[0].replace('-',':') + t['protocol'] = info[1] + ports_all.append(t) + for add_info in ports_all: + if thisdb.getFirewallCountByPort(add_info['port']) == 0: + thisdb.addFirewall(add_info['port'], ps='自动识别',protocol=add_info['protocol']) def AIF_Firewalld(self): # firewall-cmd --list-all | grep ' ports' From 27b3bc1150c663362c4115e31257f7facf869247 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 23:00:46 +0800 Subject: [PATCH 540/965] Update firewall.py --- web/utils/firewall.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index d7a8771da..175a704eb 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -345,6 +345,9 @@ class Firewall(object): protocol = 'tcp' ps = 'PANEL端口' + if thisdb.getFirewallCountByPort(port) > 0: + return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') + thisdb.addFirewall(port, ps=ps,protocol=protocol) self.addAcceptPortCmd(port, protocol=protocol) self.reload() From b97b8c2c9b291c2c66ea8c5f83fcc73d1e6ea58b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 23:01:21 +0800 Subject: [PATCH 541/965] Update setting.py --- web/setting.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index a677e0ed8..f97cab341 100755 --- a/web/setting.py +++ b/web/setting.py @@ -40,12 +40,13 @@ if not os.path.exists(data_dir): # default port panel_port = '7200' +from utils.firewall import Firewall as MwFirewall default_port_file = panel_dir+'/data/port.pl' if os.path.exists(default_port_file): panel_port = mw.readFile(default_port_file) panel_port.strip() + MwFirewall.instance().addPanelPort(panel_port) else: - from utils.firewall import Firewall as MwFirewall panel_port = str(random.randint(10000, 65530)) MwFirewall.instance().addPanelPort(panel_port) mw.writeFile(default_port_file, panel_port) From 519951ad69e7f17caaa301b27799c75742352bfd Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 23:02:08 +0800 Subject: [PATCH 542/965] Update setting.py --- web/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/setting.py b/web/setting.py index f97cab341..ae6b782ff 100755 --- a/web/setting.py +++ b/web/setting.py @@ -45,7 +45,7 @@ default_port_file = panel_dir+'/data/port.pl' if os.path.exists(default_port_file): panel_port = mw.readFile(default_port_file) panel_port.strip() - MwFirewall.instance().addPanelPort(panel_port) + MwFirewall.instance().addAcceptPort(panel_port,'PANEL端口', 'port') else: panel_port = str(random.randint(10000, 65530)) MwFirewall.instance().addPanelPort(panel_port) From 1cc6953415fcb8f67b1da297304fb3495e1ca5f4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 23:10:58 +0800 Subject: [PATCH 543/965] Update install.sh --- plugins/openresty/versions/1.27.1/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/openresty/versions/1.27.1/install.sh b/plugins/openresty/versions/1.27.1/install.sh index a53019d98..5567b2e64 100644 --- a/plugins/openresty/versions/1.27.1/install.sh +++ b/plugins/openresty/versions/1.27.1/install.sh @@ -167,12 +167,12 @@ Install_openresty() if [ -d $openrestyDir/openresty-${VERSION} ];then rm -rf $openrestyDir/openresty-${VERSION} fi - echo '安装完成' + echo 'Installation of Openresty completed' } Uninstall_openresty() { - echo '卸载完成' + echo 'Uninstalling Openresty completed' } action=$1 From 000a4cccf1f4f61c1c5cdb64aa2224c7b2a31814 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 23:35:34 +0800 Subject: [PATCH 544/965] Update python-app-op.yml --- .github/workflows/python-app-op.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app-op.yml b/.github/workflows/python-app-op.yml index 64ce10ca2..44472d5dd 100644 --- a/.github/workflows/python-app-op.yml +++ b/.github/workflows/python-app-op.yml @@ -34,5 +34,5 @@ jobs: cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27 - name: Start DEBUG run: | - gunicorn -c setting.py app:app + cd web && gunicorn -c setting.py app:app python3 task.py & From 31db2207193dc6e790dc4503b01135e1d569e98b Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sat, 30 Nov 2024 23:35:48 +0800 Subject: [PATCH 545/965] Update python-app-op.yml --- .github/workflows/python-app-op.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app-op.yml b/.github/workflows/python-app-op.yml index 44472d5dd..a1818efc2 100644 --- a/.github/workflows/python-app-op.yml +++ b/.github/workflows/python-app-op.yml @@ -31,7 +31,7 @@ jobs: sudo bash scripts/install_dev.sh - name: Install OpenResty run: | - cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27 + cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27.1 - name: Start DEBUG run: | cd web && gunicorn -c setting.py app:app From 7352f30d9180d116ef1a8426b17d833617a12dce Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 00:38:35 +0800 Subject: [PATCH 546/965] Update ubuntu.sh --- scripts/install/ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install/ubuntu.sh b/scripts/install/ubuntu.sh index 8af0c274f..37f8ef8bf 100644 --- a/scripts/install/ubuntu.sh +++ b/scripts/install/ubuntu.sh @@ -70,7 +70,7 @@ echo "SSH PORT:${SSH_PORT}" if [ -f /usr/sbin/ufw ];then # look # ufw status - ufw enable + echo 'y' | ufw enable if [ "$SSH_PORT" != "" ];then ufw allow $SSH_PORT/tcp From eafaf13a3dc5052a2e15a8639febcff662e6231f Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 01:14:12 +0800 Subject: [PATCH 547/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index fd75bc2fb..f9ed2bfe8 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -44,6 +44,8 @@ def init_cmd(): if os.path.exists('/etc/init.d'): initd_bin = '/etc/init.d/mw' + if not os.access(initd_bin, os.W_OK): + return False shutil.copyfile(script_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) # 加入自启动 From 1cc4aee6f7cd3b5bc9b16b89d286954d4088a73a Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 01:23:14 +0800 Subject: [PATCH 548/965] Update index.py --- plugins/openresty/index.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py index d323af92f..d36710ab7 100755 --- a/plugins/openresty/index.py +++ b/plugins/openresty/index.py @@ -196,7 +196,9 @@ def confReplace(): # vhost vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost' vhost_tpl_dir = getPluginDir() + '/conf/vhost' - # print(vhost_dir, vhost_tpl_dir) + if os.path.exists(vhost_dir): + mw.execShell('mkdir -p ' + vhost_dir) + vhost_list = ['0.websocket.conf', '0.nginx_status.conf'] for f in vhost_list: a_conf = vhost_dir + '/' + f From ce6f7153b3af9c814bab9b187d80f855aa1e6bc1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 01:31:08 +0800 Subject: [PATCH 549/965] Update firewall.py --- web/utils/firewall.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 175a704eb..dee7a4ddd 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -83,7 +83,6 @@ class Firewall(object): thisdb.addFirewall(add_info['port'], ps='自动识别',protocol=add_info['protocol']) def AIF_Firewalld(self): - # firewall-cmd --list-all | grep ' ports' t = mw.execShell("firewall-cmd --list-all | grep ' ports'") if t[1] != '': return True From b432b1c165ba055598b9d28ebae0670e836fedd5 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 01:40:56 +0800 Subject: [PATCH 550/965] update --- plugins/openresty/index.py | 2 +- plugins/openresty/install.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py index d36710ab7..2a93c65f4 100755 --- a/plugins/openresty/index.py +++ b/plugins/openresty/index.py @@ -196,7 +196,7 @@ def confReplace(): # vhost vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost' vhost_tpl_dir = getPluginDir() + '/conf/vhost' - if os.path.exists(vhost_dir): + if not os.path.exists(vhost_dir): mw.execShell('mkdir -p ' + vhost_dir) vhost_list = ['0.websocket.conf', '0.nginx_status.conf'] diff --git a/plugins/openresty/install.sh b/plugins/openresty/install.sh index 4be1cad85..7b6f9b7fd 100755 --- a/plugins/openresty/install.sh +++ b/plugins/openresty/install.sh @@ -2,8 +2,8 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin export PATH -# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 -# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 +# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4 +# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4 curPath=`pwd` rootPath=$(dirname "$curPath") From 7b8bb6380d7138970c22d6b9c10a23fa080cebb1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 01:49:02 +0800 Subject: [PATCH 551/965] Update mw.py --- web/core/mw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/core/mw.py b/web/core/mw.py index 0ce49aeda..8b74195aa 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -331,7 +331,7 @@ def writeFile(filename, content, mode='w+'): fp.close() return True except Exception as e: - print(getTracebackInfo()) + writeFileLog(getTracebackInfo()) return False @@ -968,7 +968,7 @@ def writeDbLog(stype, msg, args=(), uid=1): def writeSpeed(title, used, total, speed=0): path = getPanelDir() - speed_file= path + '/data/panelSpeed.pl' + speed_file= path + '/data/panel_speed.pl' # 写进度 if not title: data = {'title': None, 'progress': 0,'total': 0, 'used': 0, 'speed': 0} @@ -981,7 +981,7 @@ def writeSpeed(title, used, total, speed=0): def getSpeed(): path = getPanelDir() - speed_file= path + '/data/panelSpeed.pl' + speed_file= path + '/data/panel_speed.pl' # 取进度 path = getPanelDir() data = readFile(speed_file) From 1f6e6957ea18c2f010d32ac7af3949b7edcf8e0c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 02:15:32 +0800 Subject: [PATCH 552/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index f9ed2bfe8..2b3f1fe25 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -37,6 +37,8 @@ def init_cmd(): # initd if os.path.exists('/etc/rc.d/init.d'): initd_bin = '/etc/rc.d/init.d/mw' + if not os.access(initd_bin, os.W_OK): + return False shutil.copyfile(script_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) # 加入自启动 From cc165c4cde69a4435ffe47fd30e700acc37cf4c9 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 03:23:13 +0800 Subject: [PATCH 553/965] update --- plugins/data_query/sql_mysql.py | 43 +++++++++++++++++++++++++++++ plugins/data_query/static/js/app.js | 32 +++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index 36f97d159..0f945860f 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -564,6 +564,44 @@ class nosqlMySQLCtr(): 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 len(slave_info) == 0: + 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 = '[bold red]主从复制报错,请检查. Slave_IO_Running状态值是:%s\ + | Slave_SQL_Running状态值是:%s \n \tLast_Error错误信息是:%s\ + \n\n \tLast_SQL_Error错误信息是:%s [/bold red]' \ + % (r_dict['Slave_IO_Running'], r_dict['Slave_SQL_Running'], \ + r_dict['Last_Error'], r_dict['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' % error_dict['LAST_ERROR_NUMBER'] + msg +='错误信息是:%s' % 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 ---------------------------------- # 获取 mysql 列表 def get_db_list(args): @@ -636,6 +674,11 @@ 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/js/app.js b/plugins/data_query/static/js/app.js index 27e4ed041..b630235ee 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -726,6 +726,33 @@ function mysqlCommonFuncDeadlockInfo(){ }); } +function mysqlCommonFuncSlaveStatus(){ + + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_slave_status',{'sid':sid} ,function(rdata){ + var data = rdata.data; + $('#info_log').html(data.data); + var ob = document.getElementById('info_log'); + ob.scrollTop = ob.scrollHeight; + }); + } + + layer.open({ + type: 1, + title: "查看主从复制信息", + area: ['800px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
                    \ + \ +
                    ', + success:function(i,l){ + renderSQL(); + } + }); +} + function mysqlCommonFunc(){ $('#mysql_common').unbind('click').click(function(){ layer.open({ @@ -743,6 +770,7 @@ function mysqlCommonFunc(){ \ \ \ + \ ', success:function(i,l){ $('#mysql_top_nsql').click(function(){ @@ -776,6 +804,10 @@ function mysqlCommonFunc(){ $('#mysql_deadlock_info').click(function(){ mysqlCommonFuncDeadlockInfo(); }); + + $('#mysql_slave_status').click(function(){ + mysqlCommonFuncSlaveStatus(); + }); } }); }); From d33ce43ad33d79e7be995d67354be1307535163d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 03:26:57 +0800 Subject: [PATCH 554/965] Update sql_mysql.py --- plugins/data_query/sql_mysql.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index 0f945860f..e84622398 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -572,7 +572,6 @@ class nosqlMySQLCtr(): return mw.returnData(False,'无法链接') slave_info = my_instance.find('SHOW SLAVE STATUS') - if len(slave_info) == 0: return mw.returnData(True, 'ok', '未开启从库!') From a8019d1ec487483ee66d1900f37941bbb25c7acb Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 03:38:09 +0800 Subject: [PATCH 555/965] Update sql_mysql.py --- plugins/data_query/sql_mysql.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index e84622398..d96ce5f75 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -587,17 +587,14 @@ class nosqlMySQLCtr(): else: return mw.returnData(True, 'ok', '同步正常,但有延迟,延迟时间为:%s' % slave_info['Seconds_Behind_Master']) else: - msg = '[bold red]主从复制报错,请检查. Slave_IO_Running状态值是:%s\ - | Slave_SQL_Running状态值是:%s \n \tLast_Error错误信息是:%s\ - \n\n \tLast_SQL_Error错误信息是:%s [/bold red]' \ - % (r_dict['Slave_IO_Running'], r_dict['Slave_SQL_Running'], \ - r_dict['Last_Error'], r_dict['Last_SQL_Error']) + 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' % error_dict['LAST_ERROR_NUMBER'] - msg +='错误信息是:%s' % error_dict['LAST_ERROR_MESSAGE'] - msg +='报错时间是:%s\n' % error_dict['LAST_ERROR_TIMESTAMP'] + 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) From bdd0634f61aa8ddd8dc997c0591f824ad6d17f02 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 04:17:28 +0800 Subject: [PATCH 556/965] update --- plugins/data_query/sql_mysql.py | 45 +++++++++++++++++++--- plugins/data_query/static/js/app.js | 59 ++++++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index d96ce5f75..e1d4007be 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -546,10 +546,33 @@ class nosqlMySQLCtr(): a.trx_started """ ) - - print(data) 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: + my_instance.execute('kill %s' % x['processlist_id']) + return mw.returnData(True, '执行成功!') + + + def getDeadlockInfo(self, args): sid = args['sid'] my_instance = self.getInstanceBySid(sid).conn() @@ -589,9 +612,7 @@ class nosqlMySQLCtr(): 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') + 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'] @@ -665,6 +686,16 @@ 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() @@ -676,6 +707,10 @@ def get_slave_status(args): return t.getSlaveStatus(args) + + + + # 测试 def test(args): sid = args['sid'] diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index b630235ee..d46aa31c0 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -666,12 +666,35 @@ function mysqlCommonFuncLockSQL(){ var tbody = ''; for (var i = 0; i < items.length; i++) { var t = ''; - t += ''+items[i]['table_schema']+''; - t += ''+items[i]['table_name']+''; + t += ''+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 += ''; 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}); } @@ -681,20 +704,46 @@ function mysqlCommonFuncLockSQL(){ layer.open({ type: 1, title: "查看当前锁阻塞的SQL", - area: ['800px', '400px'], + area: ['1000px', '400px'], closeBtn: 1, shadeClose: false, content: '
                    \ +
                    \ + \ +
                    \ +
                    \ \ \ - \ - \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ \ \
                    库名表名事务ID事务状态执行时间线程IDInfouserhostdbcommandstatekill操作
                    \
                    ', success:function(i,l){ renderSQL(); + + $('#kill_all').unbind('click').click(function(){ + var sid = mysqlGetSid(); + myPostCB('kill_all_lock', {'sid':sid}, function(rdata){ + var data = rdata.data; + showMsg(data.msg,function(){ + if (data.status){ + renderSQL(); + } + },{icon: data.status ? 1 : 2}, 2000); + }); + }); } }); } From 6e197f81ef59bda380b50664fb167c3b452ecfdf Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 04:18:14 +0800 Subject: [PATCH 557/965] Update app.js --- plugins/data_query/static/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index d46aa31c0..c97e80d53 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -807,7 +807,7 @@ function mysqlCommonFunc(){ layer.open({ type: 1, title: "MySQL常用功能", - area: ['600px', '400px'], + area: ['600px', '200px'], closeBtn: 1, shadeClose: false, content: '
                    \ From 37448dc827105f7380d2aadbf2b1cc2cfa80526d Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 04:19:00 +0800 Subject: [PATCH 558/965] update --- class/core/cert_api.py | 1621 ------------------- class/core/cert_request.py | 1472 ------------------ class/core/common.py | 146 -- class/core/config_api.py | 1191 -------------- class/core/crontab_api.py | 692 --------- class/core/db.py | 401 ----- class/core/files_api.py | 1160 -------------- class/core/firewall_api.py | 513 ------ class/core/logs_api.py | 355 ----- class/core/mw.py | 2286 --------------------------- class/core/page.py | 245 --- class/core/plugins_api.py | 1105 ------------- class/core/site_api.py | 2909 ----------------------------------- class/core/ssh_local.py | 120 -- class/core/ssh_terminal.py | 467 ------ class/core/system_api.py | 978 ------------ class/core/task_api.py | 103 -- class/core/vilidate.py | 143 -- class/core/vip_api.py | 48 - class/fonts/2.ttf | Bin 28328 -> 0 bytes class/os/README.md | 0 class/os/win32/README.md | 0 class/plugin/README.md | 5 - class/plugin/fcgi_client.py | 408 ----- class/plugin/fpm.py | 208 --- class/plugin/memail.py | 43 - class/plugin/orm.py | 140 -- class/plugin/ormDb.py | 115 -- 28 files changed, 16874 deletions(-) delete mode 100644 class/core/cert_api.py delete mode 100644 class/core/cert_request.py delete mode 100755 class/core/common.py delete mode 100755 class/core/config_api.py delete mode 100755 class/core/crontab_api.py delete mode 100755 class/core/db.py delete mode 100755 class/core/files_api.py delete mode 100755 class/core/firewall_api.py delete mode 100644 class/core/logs_api.py delete mode 100755 class/core/mw.py delete mode 100755 class/core/page.py delete mode 100755 class/core/plugins_api.py delete mode 100755 class/core/site_api.py delete mode 100644 class/core/ssh_local.py delete mode 100644 class/core/ssh_terminal.py delete mode 100755 class/core/system_api.py delete mode 100755 class/core/task_api.py delete mode 100755 class/core/vilidate.py delete mode 100644 class/core/vip_api.py delete mode 100755 class/fonts/2.ttf delete mode 100644 class/os/README.md delete mode 100644 class/os/win32/README.md delete mode 100644 class/plugin/README.md delete mode 100644 class/plugin/fcgi_client.py delete mode 100644 class/plugin/fpm.py delete mode 100644 class/plugin/memail.py delete mode 100755 class/plugin/orm.py delete mode 100755 class/plugin/ormDb.py diff --git a/class/core/cert_api.py b/class/core/cert_api.py deleted file mode 100644 index 8fbc15967..000000000 --- a/class/core/cert_api.py +++ /dev/null @@ -1,1621 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 证书申请 -# --------------------------------------------------------------------------------- - -import re -import fcntl -import datetime -import binascii -import hashlib -import base64 -import json -import time -import os -import sys -import argparse - -if os.path.exists('/www/server/mdserver-web'): - os.chdir('/www/server/mdserver-web') - -import mw - -try: - import OpenSSL -except: - mw.execShell("pip install pyopenssl") - import OpenSSL - - -def echoErr(msg): - writeLog("\033[31m=" * 65) - writeLog("|-错误:{}\033[0m".format(msg)) - exit() - - -def writeLog(log_str, mode="ab+"): - # 写日志 - if __name__ == "__main__": - print(log_str) - return - _log_file = 'logs/letsencrypt.log' - f = open(_log_file, mode) - log_str += "\n" - f.write(log_str.encode('utf-8')) - f.close() - return True - - -class cert_api: - __debug = False - __user_agent = "MW-Panel" - __apis = None - __url = None - __replay_nonce = None - __acme_timeout = 30 - __max_check_num = 5 - __wait_time = 5 - __bits = 2048 - __digest = "sha256" - __verify = False - __config = {} - __dns_class = None - __auto_wildcard = False - __mod_index = {True: "Staging", False: "Production"} - __save_path = 'data/letsencrypt' - __cfg_file = 'data/letsencrypt.json' - - def __init__(self): - self.__user_agent = 'MW-Panel:' + mw.getRandomString(8) - self.__save_path = mw.getServerDir() + '/web_conf/letsencrypt' - if not os.path.exists(self.__save_path): - os.makedirs(self.__save_path) - if self.__debug: - self.__url = 'https://acme-staging-v02.api.letsencrypt.org/directory' - else: - self.__url = 'https://acme-v02.api.letsencrypt.org/directory' - self.__config = self.readConfig() - - def D(self, name, val): - if self.__debug: - print('---------{} start--------'.format(name)) - print(val) - print('---------{} end--------'.format(name)) - - # 读配置文件 - def readConfig(self): - if not os.path.exists(self.__cfg_file): - self.__config['orders'] = {} - self.__config['account'] = {} - self.__config['apis'] = {} - self.__config['email'] = mw.M('users').where( - 'id=?', (1,)).getField('email') - if self.__config['email'] in ['midoks@163.com']: - self.__config['email'] = None - self.saveConfig() - return self.__config - tmp_config = mw.readFile(self.__cfg_file) - if not tmp_config: - return self.__config - try: - self.__config = json.loads(tmp_config) - except: - self.saveConfig() - return self.__config - return self.__config - - def saveConfig(self): - # 写配置文件 - fp = open(self.__cfg_file, 'w+') - fcntl.flock(fp, fcntl.LOCK_EX) # 加锁 - fp.write(json.dumps(self.__config)) - fcntl.flock(fp, fcntl.LOCK_UN) # 解锁 - fp.close() - return True - - def createCertCron(self): - - if mw.isAppleSystem(): - print('在Macos上不支持创建证书自动续签任务!') - return '' - - # 创建证书自动续签任务 - try: - import crontab_api - api = crontab_api.crontab_api() - - echo = mw.md5(mw.md5('panel_renew_lets_cron')) - cron_id = mw.M('crontab').where('echo=?', (echo,)).getField('id') - - cron_path = mw.getServerDir() + '/cron' - if not os.path.exists(cron_path): - mw.execShell('mkdir -p ' + cron_path) - - shell = 'python3 -u {}/class/core/cert_api.py --renew=1'.format( - mw.getRunDir()) - - logs_file = cron_path + '/' + echo + '.log' - - cmd = '''#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin -export PATH - -dst_dir=%s -logs_file=%s -cd $dst_dir - -if [ -f bin/activate ];then - source bin/activate -fi - -''' % (mw.getRunDir(), logs_file) - cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n" - cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n" - cmd += 'cd $dst_dir && ' + shell + ' >> $logs_file 2>&1' + "\n" - cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n" - cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n" - - file = cron_path + '/' + echo - - if type(cron_id) != int: - - mw.writeFile(file, cmd) - mw.execShell('chmod 750 ' + file) - - info = {} - info['type'] = 'day' - info['minute'] = '10' - info['hour'] = '0' - shell_cron, rinfo, name = api.getCrondCycle(info) - shell_cron += ' ' + cron_path + '/' + echo + \ - ' >> ' + logs_file + ' 2>&1' - - api.writeShell(shell_cron) - - insert_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress', ( - "[勿删]续签Let's Encrypt证书", 'day', '', '0', '10', echo, time.strftime('%Y-%m-%d %X', time.localtime()), '1', '', 'localhost', 'toShell', '', cmd, '')) - - if insert_id > 0: - print('创建证书自动续签任务成功!') - else: - mw.writeFile(file, cmd) - mw.execShell('chmod 750 ' + file) - mw.M('crontab').where('id=?', (cron_id)).save('sbody', (cmd,)) - except Exception as e: - print(mw.getTracebackInfo()) - - def getApis(self): - if not self.__apis: - # 尝试从配置文件中获取 - api_index = self.__mod_index[self.__debug] - if not 'apis' in self.__config: - self.__config['apis'] = {} - - if api_index in self.__config['apis']: - if 'expires' in self.__config['apis'][api_index] and 'directory' in self.__config['apis'][api_index]: - if time.time() < self.__config['apis'][api_index]['expires']: - self.__apis = self.__config[ - 'apis'][api_index]['directory'] - return self.__apis - - # 尝试从官方获取获取 - try: - res = mw.httpGet(self.__url) - result = json.loads(res) - self.__apis = {} - self.__apis['newAccount'] = result['newAccount'] - self.__apis['newNonce'] = result['newNonce'] - self.__apis['newOrder'] = result['newOrder'] - self.__apis['revokeCert'] = result['revokeCert'] - self.__apis['keyChange'] = result['keyChange'] - - # 保存到配置文件 - self.__config['apis'][api_index] = {} - self.__config['apis'][api_index]['directory'] = self.__apis - self.__config['apis'][api_index]['expires'] = time.time() + \ - 86400 # 24小时后过期 - self.saveConfig() - except Exception as e: - raise Exception('服务因维护而关闭或发生内部错误,查看 https://letsencrypt.status.io/ 了解更多详细信息。') - return self.__apis - - # 系列化payload - def stringfyItems(self, payload): - if isinstance(payload, str): - return payload - - for k, v in payload.items(): - if isinstance(k, bytes): - k = k.decode("utf-8") - if isinstance(v, bytes): - v = v.decode("utf-8") - payload[k] = v - return payload - - # 构造域名列表 - def formatDomains(self, domains): - if type(domains) != list: - return [] - # 是否自动构造通配符 - if self.__auto_wildcard: - domains = self.autoWildcard(domains) - wildcard = [] - tmp_domains = [] - for domain in domains: - domain = domain.strip() - if domain in tmp_domains: - continue - # 将通配符域名转为验证正则表达式 - f_index = domain.find("*.") - if f_index not in [-1, 0]: - continue - if f_index == 0: - wildcard.append(domain.replace( - "*", r"^[\w-]+").replace(".", r"\.")) - # 添加到申请列表 - tmp_domains.append(domain) - - # 处理通配符包含 - apply_domains = tmp_domains[:] - for domain in tmp_domains: - for w in wildcard: - if re.match(w, domain): - apply_domains.pop(domain) - - return apply_domains - - # 转为无填充的Base64 - def calculateSafeBase64(self, un_encoded_data): - if sys.version_info[0] == 3: - if isinstance(un_encoded_data, str): - un_encoded_data = un_encoded_data.encode("utf8") - r = base64.urlsafe_b64encode(un_encoded_data).rstrip(b"=") - return r.decode("utf8") - - # 创建Key - def createKey(self, key_type=OpenSSL.crypto.TYPE_RSA): - key = OpenSSL.crypto.PKey() - key.generate_key(key_type, self.__bits) - private_key = OpenSSL.crypto.dump_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key) - return private_key - - # 获用户取密钥对 - def getAccountKey(self): - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'key' in self.__config['account'][k]: - self.__config['account'][k]['key'] = self.createKey() - if type(self.__config['account'][k]['key']) == bytes: - self.__config['account'][k]['key'] = self.__config[ - 'account'][k]['key'].decode() - self.saveConfig() - return self.__config['account'][k]['key'] - - # 注册acme帐户 - def register(self, existing=False): - if not 'email' in self.__config: - self.__config['email'] = 'mdioks@163.com' - if existing: - payload = {"onlyReturnExisting": True} - elif self.__config['email']: - payload = { - "termsOfServiceAgreed": True, - "contact": ["mailto:{0}".format(self.__config['email'])], - } - else: - payload = {"termsOfServiceAgreed": True} - - res = self.acmeRequest(url=self.__apis['newAccount'], payload=payload) - - if res.status not in [201, 200, 409]: - raise Exception("注册ACME帐户失败: {}".format(res.json())) - kid = res.headers["Location"] - return kid - - # 获取kid - def getKid(self, force=False): - # 如果配置文件中不存在kid或force = True时则重新注册新的acme帐户 - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'kid' in self.__config['account'][k]: - self.__config['account'][k]['kid'] = self.register() - self.saveConfig() - time.sleep(3) - self.__config = self.readConfig() - return self.__config['account'][k]['kid'] - - def requestsGet(self, url, timeout): - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - - headers = {"User-Agent": self.__user_agent} - req = urllib.request.Request(url=url, headers=headers) - req = urllib.request.urlopen(url, timeout=timeout) - return req - except Exception as ex: - raise Exception("requestsGet: {}".format(str(ex))) - - def requestsPost(self, url, data, timeout): - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - - headers = {"User-Agent": self.__user_agent} - headers.update({"Content-Type": "application/jose+json"}) - data = bytes(data, encoding="utf8") - req = urllib.request.Request( - url, data, headers=headers, method='POST') - response = urllib.request.urlopen(req, timeout=timeout) - return response - except Exception as ex: - # self.getError() - raise Exception("异常: {}".format(self.getError(str(ex)))) - - def getRequestJson(self, response): - try: - data = response.read().decode('utf-8') - return json.loads(data) - except Exception as ex: - raise Exception("getRequestJson: {}".format(str(ex))) - - def getNonce(self, force=False): - # 获取随机数 - # 如果没有保存上一次的随机数或force=True时则重新获取新的随机数 - if not self.__replay_nonce or force: - try: - response = self.requestsGet( - self.__apis['newNonce'], timeout=self.__acme_timeout) - self.__replay_nonce = response.headers["replay-nonce"] - except Exception as e: - raise Exception("获取随机数失败: {}".format(str(e))) - - return self.__replay_nonce - - # 获请ACME请求头 - def getAcmeHeader(self, url): - nonce = self.getNonce() - - header = {"alg": "RS256", "nonce": nonce, "url": url} - if url in [self.__apis['newAccount'], 'GET_THUMBPRINT']: - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import serialization - private_key = serialization.load_pem_private_key( - self.getAccountKey().encode(), - password=None, - backend=default_backend(), - ) - public_key_public_numbers = private_key.public_key().public_numbers() - - exponent = "{0:x}".format(public_key_public_numbers.e) - exponent = "0{0}".format(exponent) if len( - exponent) % 2 else exponent - modulus = "{0:x}".format(public_key_public_numbers.n) - jwk = { - "kty": "RSA", - "e": self.calculateSafeBase64(binascii.unhexlify(exponent)), - "n": self.calculateSafeBase64(binascii.unhexlify(modulus)), - } - header["jwk"] = jwk - else: - header["kid"] = self.getKid() - return header - - # 计算signature - def signMessage(self, message): - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.getAccountKey().encode()) - return OpenSSL.crypto.sign(pk, message.encode("utf8"), self.__digest) - - def getSiteRunPathByid(self, site_id): - if mw.M('sites').where('id=?', (site_id,)).count() >= 1: - site_path = mw.M('sites').where('id=?', site_id).getField('path') - if not site_path: - return None - if not os.path.exists(site_path): - return None - return site_path - else: - return False - - def getSiteRunPath(self, domains): - site_id = 0 - for domain in domains: - site_id = mw.M('domain').where("name=?", domain).getField('pid') - if site_id: - break - - if not site_id: - return None - return self.getSiteRunPathByid(site_id) - - # 清理验证文件 - def clearAuthFile(self, index): - if not self.__config['orders'][index]['auth_type'] in ['http', 'tls']: - return True - acme_path = '{}/.well-known/acme-challenge'.format( - self.__config['orders'][index]['auth_to']) - writeLog("|-验证目录:{}".format(acme_path)) - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - # 获取域名验证方式 - def getAuthType(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - s_type = 'http-01' - if 'auth_type' in self.__config['orders'][index]: - if self.__config['orders'][index]['auth_type'] == 'dns': - s_type = 'dns-01' - elif self.__config['orders'][index]['auth_type'] == 'tls': - s_type = 'tls-alpn-01' - else: - s_type = 'http-01' - return s_type - - # 构造验证信息 - def getIdentifierAuth(self, index, url, auth_info): - s_type = self.getAuthType(index) - writeLog("|-验证类型:{}".format(s_type)) - domain = auth_info['identifier']['value'] - wildcard = False - # 处理通配符 - if 'wildcard' in auth_info: - wildcard = auth_info['wildcard'] - if wildcard: - domain = "*." + domain - - for auth in auth_info['challenges']: - if auth['type'] != s_type: - continue - identifier_auth = { - "domain": domain, - "url": url, - "wildcard": wildcard, - "token": auth['token'], - "dns_challenge_url": auth['url'], - } - return identifier_auth - return None - - # 构造域名验证头和验证值 - def getKeyauthorization(self, token): - acme_header_jwk_json = json.dumps( - self.getAcmeHeader("GET_THUMBPRINT")["jwk"], sort_keys=True, separators=(",", ":") - ) - acme_thumbprint = self.calculateSafeBase64( - hashlib.sha256(acme_header_jwk_json.encode("utf8")).digest() - ) - acme_keyauthorization = "{0}.{1}".format(token, acme_thumbprint) - base64_of_acme_keyauthorization = self.calculateSafeBase64( - hashlib.sha256(acme_keyauthorization.encode("utf8")).digest() - ) - - return acme_keyauthorization, base64_of_acme_keyauthorization - - # 写验证文件 - def writeAuthFile(self, auth_to, token, acme_keyauthorization): - try: - # self.D('writeAuthFile', auth_to) - acme_path = '{}/.well-known/acme-challenge'.format(auth_to) - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - return True - except: - err = mw.getTracebackInfo() - raise Exception("写入验证文件失败: {}".format(err)) - - # 设置验证信息 - def setAuthInfo(self, identifier_auth): - - # 是否手动验证DNS - if identifier_auth['auth_to'] == 'dns': - return None - - # 是否文件验证 - if identifier_auth['type'] in ['http', 'tls']: - self.writeAuthFile(identifier_auth['auth_to'], identifier_auth[ - 'token'], identifier_auth['acme_keyauthorization']) - - # 获取验证信息 - def getAuths(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 检查是否已经获取过授权信息 - if 'auths' in self.__config['orders'][index]: - # 检查授权信息是否过期 - if time.time() < self.__config['orders'][index]['auths'][0]['expires']: - return self.__config['orders'][index]['auths'] - if self.__config['orders'][index]['auth_type'] != 'dns': - site_run_path = self.getSiteRunPath( - self.__config['orders'][index]['domains']) - if site_run_path: - self.__config['orders'][index]['auth_to'] = site_run_path - - # 清理旧验证 - self.clearAuthFile(index) - - auths = [] - for auth_url in self.__config['orders'][index]['authorizations']: - res = self.acmeRequest(auth_url, "") - if res.status not in [200, 201]: - raise Exception("获取授权失败: {}".format(res.json())) - - s_body = self.getRequestJson(res) - if 'status' in s_body: - if s_body['status'] in ['invalid']: - raise Exception("无效订单,此订单当前为验证失败状态!") - if s_body['status'] in ['valid']: # 跳过无需验证的域名 - continue - - s_body['expires'] = self.utcToTime(s_body['expires']) - identifier_auth = self.getIdentifierAuth(index, auth_url, s_body) - if not identifier_auth: - raise Exception("验证信息构造失败!{}") - - acme_keyauthorization, auth_value = self.getKeyauthorization(identifier_auth[ - 'token']) - identifier_auth['acme_keyauthorization'] = acme_keyauthorization - identifier_auth['auth_value'] = auth_value - identifier_auth['expires'] = s_body['expires'] - identifier_auth['auth_to'] = self.__config[ - 'orders'][index]['auth_to'] - identifier_auth['type'] = self.__config[ - 'orders'][index]['auth_type'] - - # print(identifier_auth) - # 设置验证信息 - self.setAuthInfo(identifier_auth) - auths.append(identifier_auth) - self.__config['orders'][index]['auths'] = auths - self.saveConfig() - return auths - - # 更新随机数 - def updateReplayNonce(self, res): - replay_nonce = res.headers.get('replay-nonce') - if replay_nonce: - self.__replay_nonce = replay_nonce - - # 请求到ACME接口 - def acmeRequest(self, url, payload): - headers = {"User-Agent": self.__user_agent} - payload = self.stringfyItems(payload) - - if payload == "": - payload64 = payload - else: - payload64 = self.calculateSafeBase64(json.dumps(payload)) - protected = self.getAcmeHeader(url) - protected64 = self.calculateSafeBase64(json.dumps(protected)) - signature = self.signMessage( - message="{0}.{1}".format(protected64, payload64)) # bytes - signature64 = self.calculateSafeBase64(signature) # str - data = json.dumps({ - "protected": protected64, - "payload": payload64, - "signature": signature64 - }) - - headers.update({"Content-Type": "application/jose+json"}) - response = self.requestsPost( - url, data=data, timeout=self.__acme_timeout) - # 更新随机数 - self.updateReplayNonce(response) - return response - - # UTC时间转时间戳 - def utcToTime(self, utc_string): - try: - utc_string = utc_string.split('.')[0] - utc_date = datetime.datetime.strptime( - utc_string, "%Y-%m-%dT%H:%M:%S") - # 按北京时间返回 - return int(time.mktime(utc_date.timetuple())) + (3600 * 8) - except: - return int(time.time() + 86400 * 7) - - # 保存订单 - def saveOrder(self, order_object, index): - if not 'orders' in self.__config: - self.__config['orders'] = {} - renew = False - if not index: - index = mw.md5(json.dumps(order_object['identifiers'])) - else: - renew = True - order_object['certificate_url'] = self.__config[ - 'orders'][index]['certificate_url'] - order_object['save_path'] = self.__config[ - 'orders'][index]['save_path'] - - order_object['expires'] = self.utcToTime(order_object['expires']) - self.__config['orders'][index] = order_object - self.__config['orders'][index]['index'] = index - if not renew: - self.__config['orders'][index]['create_time'] = int(time.time()) - self.__config['orders'][index]['renew_time'] = 0 - self.saveConfig() - return index - - def getError(self, error): - # 格式化错误输出 - if error.find("Max checks allowed") >= 0: - return "CA无法验证您的域名,请检查域名解析是否正确,或等待5-10分钟后重试." - elif error.find("Max retries exceeded with") >= 0 or error.find('status_code=0 ') != -1: - return "CA服务器连接超时,请稍候重试." - elif error.find("The domain name belongs") >= 0: - return "域名不属于此DNS服务商,请确保域名填写正确." - elif error.find('login token ID is invalid') >= 0: - return 'DNS服务器连接失败,请检查密钥是否正确.' - elif error.find('Error getting validation data') != -1: - return '数据验证失败,CA无法从验证连接中获到正确的验证码.' - elif "too many certificates already issued for exact set of domains" in error: - return '签发失败,该域名%s超出了每周的重复签发次数限制!' % re.findall("exact set of domains: (.+):", error) - elif "Error creating new account :: too many registrations for this IP" in error: - return '签发失败,当前服务器IP已达到每3小时最多创建10个帐户的限制.' - elif "DNS problem: NXDOMAIN looking up A for" in error: - return '验证失败,没有解析域名,或解析未生效!' - elif "Invalid response from" in error: - return '验证失败,域名解析错误或验证URL无法被访问!' - elif error.find('TLS Web Server Authentication') != -1: - return "连接CA服务器失败,请稍候重试." - elif error.find('Name does not end in a public suffix') != -1: - return "不支持的域名%s,请检查域名是否正确!" % re.findall("Cannot issue for \"(.+)\":", error) - elif error.find('No valid IP addresses found for') != -1: - return "域名%s没有找到解析记录,请检查域名是否解析生效!" % re.findall("No valid IP addresses found for (.+)", error) - elif error.find('No TXT record found at') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("No TXT record found at (.+)", error) - elif error.find('Incorrect TXT record') != -1: - return "在%s上发现错误的TXT记录:%s,请检查TXT解析是否正确,如果是DNSAPI方式申请的,请10分钟后重试!" % (re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error)) - elif error.find('Domain not under you or your user') != -1: - return "这个dnspod账户下面不存在这个域名,添加解析失败!" - elif error.find('SERVFAIL looking up TXT for') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("looking up TXT for (.+)", error) - elif error.find('Timeout during connect') != -1: - return "连接超时,CA服务器无法访问您的网站!" - elif error.find("DNS problem: SERVFAIL looking up CAA for") != -1: - return "域名%s当前被要求验证CAA记录,请手动解析CAA记录,或1小时后重新尝试申请!" % re.findall("looking up CAA for (.+)", error) - elif error.find("Read timed out.") != -1: - return "验证超时,请检查域名是否正确解析,若已正确解析,可能服务器与Let'sEncrypt连接异常,请稍候再重试!" - elif error.find('Cannot issue for') != -1: - return "无法为{}颁发证书,不能直接用域名后缀申请通配符证书!".format(re.findall(r'for\s+"(.+)"', error)) - elif error.find('too many failed authorizations recently'): - return '该帐户1小时内失败的订单次数超过5次,请等待1小时再重试!' - elif error.find("Error creating new order") != -1: - return "订单创建失败,请稍候重试!" - elif error.find("Too Many Requests") != -1: - return "1小时内超过5次验证失败,暂时禁止申请,请稍候重试!" - elif error.find('HTTP Error 400: Bad Request') != -1: - return "CA服务器拒绝访问,请稍候重试!" - elif error.find('Temporary failure in name resolution') != -1: - return '服务器DNS故障,无法解析域名,请使用Linux工具箱检查dns配置' - elif error.find('Too Many Requests') != -1: - return '该域名请求申请次数过多,请3小时后重试' - else: - return error - - # 创建订单 - def createOrder(self, domains, auth_type, auth_to, index=None): - domains = self.formatDomains(domains) - if not domains: - raise Exception("至少需要有一个域名") - # 构造标识 - identifiers = [] - for domain_name in domains: - identifiers.append({"type": 'dns', "value": domain_name}) - payload = {"identifiers": identifiers} - - # 请求创建订单 - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status in [201]: # 如果创建失败 - e_body = self.getRequestJson(res) - if 'type' in e_body: - # 如果随机数失效 - if e_body['type'].find('error:badNonce') != -1: - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - - # 如果帐户失效 - if e_body['detail'].find('KeyID header contained an invalid account URL') != -1: - k = self._mod_index[self.__debug] - del(self.__config['account'][k]) - self.getKid() - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status in [201]: - a_auth = e_body - - ret_title = self.getError(str(a_auth)) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - - # 返回验证地址和验证 - s_json = self.getRequestJson(res) - s_json['auth_type'] = auth_type - s_json['domains'] = domains - s_json['auth_to'] = auth_to - index = self.saveOrder(s_json, index) - return index - - # 检查验证状态 - def checkAuthStatus(self, url, desired_status=None): - - desired_status = desired_status or ["pending", "valid", "invalid"] - number_of_checks = 0 - rdata = None - while True: - if desired_status == ['valid', 'invalid']: - writeLog("|-第{}次查询验证结果..".format(number_of_checks + 1)) - time.sleep(self.__wait_time) - check_authorization_status_response = self.acmeRequest(url, "") - a_auth = rdata = self.getRequestJson( - check_authorization_status_response) - authorization_status = a_auth["status"] - number_of_checks += 1 - if authorization_status in desired_status: - if authorization_status == "invalid": - writeLog("|-验证失败!") - try: - if 'error' in a_auth['challenges'][0]: - ret_title = a_auth['challenges'][ - 0]['error']['detail'] - elif 'error' in a_auth['challenges'][1]: - ret_title = a_auth['challenges'][ - 1]['error']['detail'] - elif 'error' in a_auth['challenges'][2]: - ret_title = a_auth['challenges'][ - 2]['error']['detail'] - else: - ret_title = str(a_auth) - ret_title = self.getError(ret_title) - except: - ret_title = str(a_auth) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - break - - if number_of_checks == self.__max_check_num: - raise StopIteration( - "错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format( - number_of_checks, - self.__max_check_num, - self.__wait_time - ) - ) - if desired_status == ['valid', 'invalid']: - writeLog("|-验证成功!") - return rdata - - # 发送验证请求 - def respondToChallenge(self, auth): - payload = {"keyAuthorization": "{0}".format( - auth['acme_keyauthorization'])} - respond_to_challenge_response = self.acmeRequest( - auth['dns_challenge_url'], payload) - return respond_to_challenge_response - - # 检查DNS记录 - def checkDns(self, domain, value, s_type='TXT'): - writeLog( - "|-尝试本地验证DNS记录,域名: {} , 类型: {} 记录值: {}".format(domain, s_type, value)) - time.sleep(10) - n = 0 - while n < 20: - n += 1 - try: - import dns.resolver - ns = dns.resolver.query(domain, s_type) - for j in ns.response.answer: - for i in j.items: - txt_value = i.to_text().replace('"', '').strip() - writeLog("|-第 {} 次验证值: {}".format(n, txt_value)) - if txt_value == value: - write_log("|-本地验证成功!") - return True - except: - try: - import dns.resolver - except: - return False - time.sleep(3) - writeLog("|-本地验证失败!") - return True - - def authDomain(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 开始验证 - for auth in self.__config['orders'][index]['auths']: - res = self.checkAuthStatus(auth['url']) # 检查是否需要验证 - if res['status'] == 'pending': - if auth['type'] == 'dns': # 尝试提前验证dns解析 - self.checkDns( - "_acme-challenge.{}".format( - auth['domain'].replace('*.', '')), - auth['auth_value'], - "TXT" - ) - self.respondToChallenge(auth) - - # 检查验证结果 - for i in range(len(self.__config['orders'][index]['auths'])): - self.checkAuthStatus(self.__config['orders'][index]['auths'][i]['url'], [ - 'valid', 'invalid']) - self.__config['orders'][index]['status'] = 'valid' - - # 构造可选名称 - def getAltNames(self, index): - domain_name = self.__config['orders'][index]['domains'][0] - domain_alt_names = [] - if len(self.__config['orders'][index]['domains']) > 1: - domain_alt_names = self.__config['orders'][index]['domains'][1:] - return domain_name, domain_alt_names - - # 创建CSR - def createCsr(self, index): - if 'csr' in self.__config['orders'][index]: - return self.__config['orders']['csr'] - domain_name, domain_alt_names = self.getAltNames(index) - X509Req = OpenSSL.crypto.X509Req() - X509Req.get_subject().CN = domain_name - if domain_alt_names: - SAN = "DNS:{0}, ".format(domain_name).encode("utf8") + ", ".join( - "DNS:" + i for i in domain_alt_names - ).encode("utf8") - else: - SAN = "DNS:{0}".format(domain_name).encode("utf8") - - X509Req.add_extensions( - [ - OpenSSL.crypto.X509Extension( - "subjectAltName".encode("utf8"), critical=False, value=SAN - ) - ] - ) - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.createCertificateKey( - index).encode() - ) - X509Req.set_pubkey(pk) - # X509Req.set_version(2) - X509Req.set_version(0) - X509Req.sign(pk, self.__digest) - return OpenSSL.crypto.dump_certificate_request(OpenSSL.crypto.FILETYPE_ASN1, X509Req) - - # 获取证书密钥对 - def createCertificateKey(self, index): - # 判断是否已经创建private_key - if 'private_key' in self.__config['orders'][index]: - return self.__config['orders'][index]['private_key'] - # 创建新的私钥 - private_key = self.createKey() - if type(private_key) == bytes: - private_key = private_key.decode() - # 保存私钥到订单配置文件 - self.__config['orders'][index]['private_key'] = private_key - self.saveConfig() - return private_key - - # 发送CSR - def sendCsr(self, index): - csr = self.createCsr(index) - payload = {"csr": self.calculateSafeBase64(csr)} - send_csr_response = self.acmeRequest( - url=self.__config['orders'][index]['finalize'], payload=payload) - send_csr_response_json = self.getRequestJson(send_csr_response) - if send_csr_response.status not in [200, 201]: - raise ValueError( - "错误: 发送CSR: 响应状态{status_code} 响应值:{response}".format( - status_code=send_csr_response.status, - response=send_csr_response_json, - ) - ) - certificate_url = send_csr_response_json["certificate"] - self.__config['orders'][index]['certificate_url'] = certificate_url - self.saveConfig() - return certificate_url - - # 转换时间 - def strfDate(self, sdate): - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) - - # 证书转为DER - def dumpDer(self, cert_path): - cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(cert_path + '/cert.csr')) - return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert) - - # 证书转为pkcs12 - def dumpPkcs12(self, key_pem=None, cert_pem=None, ca_pem=None, friendly_name=None): - p12 = OpenSSL.crypto.PKCS12() - if cert_pem: - p12.set_certificate(OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cert_pem.encode())) - if key_pem: - p12.set_privatekey(OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key_pem.encode())) - if ca_pem: - p12.set_ca_certificates((OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, ca_pem.encode()),)) - if friendly_name: - p12.set_friendlyname(friendly_name.encode()) - return p12.export() - - # 拆分根证书 - def splitCaData(self, cert): - sp_key = '-----END CERTIFICATE-----\n' - datas = cert.split(sp_key) - return {"cert": datas[0] + sp_key, "root": sp_key.join(datas[1:])} - - # 获取指定证书基本信息 - def getCertInit(self, pem_file): - if not os.path.exists(pem_file): - return None - try: - result = {} - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file)) - # 取产品名称 - issuer = x509.get_issuer() - result['issuer'] = '' - if hasattr(issuer, 'CN'): - result['issuer'] = issuer.CN - if not result['issuer']: - is_key = [b'0', '0'] - issue_comp = issuer.get_components() - if len(issue_comp) == 1: - is_key = [b'CN', 'CN'] - for iss in issue_comp: - if iss[0] in is_key: - result['issuer'] = iss[1].decode() - break - # 取到期时间 - result['notAfter'] = self.strfDate( - bytes.decode(x509.get_notAfter())[:-1]) - # 取申请时间 - result['notBefore'] = self.strfDate( - bytes.decode(x509.get_notBefore())[:-1]) - # 取可选名称 - result['dns'] = [] - for i in range(x509.get_extension_count()): - s_name = x509.get_extension(i) - if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: - s_dns = str(s_name).split(',') - for d in s_dns: - result['dns'].append(d.split(':')[1]) - subject = x509.get_subject().get_components() - # 取主要认证名称 - if len(subject) == 1: - result['subject'] = subject[0][1].decode() - else: - result['subject'] = result['dns'][0] - return result - except: - return None - - # 替换服务器上的同域名同品牌证书 - def subAllCert(self, key_file, pem_file): - cert_init = self.getCertInit(pem_file) # 获取新证书的基本信息 - paths = ['/www/server/mdserver-web/data/letsencrypt'] - is_panel = False - for path in paths: - if not os.path.exists(path): - continue - for p_name in os.listdir(path): - to_path = path + '/' + p_name - to_pem_file = to_path + '/fullchain.pem' - to_key_file = to_path + '/privkey.pem' - to_info = to_path + '/info.json' - # 判断目标证书是否存在 - if not os.path.exists(to_pem_file): - if not p_name in ['ssl']: - continue - to_pem_file = to_path + '/certificate.pem' - to_key_file = to_path + '/privateKey.pem' - if not os.path.exists(to_pem_file): - continue - if path == paths[-1]: - is_panel = True - # 获取目标证书的基本信息 - to_cert_init = self.getCertInit(to_pem_file) - # 判断证书品牌是否一致 - try: - if to_cert_init['issuer'] != cert_init['issuer'] and to_cert_init['issuer'].find("Let's Encrypt") == -1 and to_cert_init['issuer'] != 'R3': - continue - except: - continue - # 判断目标证书的到期时间是否较早 - if to_cert_init['notAfter'] > cert_init['notAfter']: - continue - # 判断认识名称是否一致 - if len(to_cert_init['dns']) != len(cert_init['dns']): - continue - is_copy = True - for domain in to_cert_init['dns']: - if not domain in cert_init['dns']: - is_copy = False - if not is_copy: - continue - - # 替换新的证书文件和基本信息 - mw.writeFile(to_pem_file, mw.readFile(pem_file, 'rb'), 'wb') - mw.writeFile(to_key_file, mw.readFile(key_file, 'rb'), 'wb') - mw.writeFile(to_info, json.dumps(cert_init)) - writeLog("|-检测到{}下的证书与本次申请的证书重叠,且到期时间较早,已替换为新证书!".format(to_path)) - # 重载web服务 - mw.restartWeb() - - # 保存证书到文件 - def saveCert(self, cert, index): - try: - domain_name = self.__config['orders'][index]['domains'][0] - path = self.__config['orders'][index]['save_path'] - if not os.path.exists(path): - os.makedirs(path, 384) - - # 存储证书 - key_file = path + "/privkey.pem" - pem_file = path + "/fullchain.pem" - mw.writeFile(key_file, cert['private_key']) - mw.writeFile(pem_file, cert['cert'] + cert['root']) - mw.writeFile(path + "/cert.csr", cert['cert']) - mw.writeFile(path + "/root_cert.csr", cert['root']) - - # 转为IIS证书 - pfx_buffer = self.dumpPkcs12( - cert['private_key'], cert['cert'] + cert['root'], cert['root'], domain_name) - mw.writeFile(path + "/fullchain.pfx", pfx_buffer, 'wb+') - - ps = '''文件说明: -privkey.pem 证书私钥 -fullchain.pem 包含证书链的PEM格式证书(nginx/apache) -root_cert.csr 根证书 -cert.csr 域名证书 -fullchain.pfx 用于IIS的证书格式 - -如何在MW面板使用: -privkey.pem 粘贴到密钥输入框 -fullchain.pem 粘贴到证书输入框 -''' - mw.writeFile(path + '/readme.txt', ps) - self.subAllCert(key_file, pem_file) - except: - writeLog(mw.getTracebackInfo()) - - # 获取证书到期时间 - def getCertTimeout(self, cret_data): - try: - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cret_data) - cert_timeout = bytes.decode(x509.get_notAfter())[:-1] - return int(time.mktime(time.strptime(cert_timeout, '%Y%m%d%H%M%S'))) - except: - return int(time.time() + (86400 * 90)) - - # 下载证书 - def downloadCert(self, index): - res = self.acmeRequest( - self.__config['orders'][index]['certificate_url'], "") - - pem_certificate = res.read().decode('utf-8') - if res.status not in [200, 201]: - raise Exception("下载证书失败: {}".format(json.loads(pem_certificate))) - - cert = self.splitCaData(pem_certificate) - cert['cert_timeout'] = self.getCertTimeout(cert['cert']) - cert['private_key'] = self.__config['orders'][index]['private_key'] - cert['domains'] = self.__config['orders'][index]['domains'] - del(self.__config['orders'][index]['private_key']) - del(self.__config['orders'][index]['auths']) - del(self.__config['orders'][index]['expires']) - del(self.__config['orders'][index]['authorizations']) - del(self.__config['orders'][index]['finalize']) - del(self.__config['orders'][index]['identifiers']) - if 'cert' in self.__config['orders'][index]: - del(self.__config['orders'][index]['cert']) - self.__config['orders'][index]['status'] = 'valid' - self.__config['orders'][index]['cert_timeout'] = cert['cert_timeout'] - domain_name = self.__config['orders'][index]['domains'][0] - self.__config['orders'][index]['save_path'] = '{}/{}'.format( - self.__save_path, domain_name) - cert['save_path'] = self.__config['orders'][index]['save_path'] - self.saveConfig() - self.saveCert(cert, index) - return cert - - def applyCert(self, domains, auth_type='http', auth_to='Dns_com|None|None', args={}): - writeLog("", "wb+") - try: - self.getApis() - index = None - if 'index' in args and args.index: - index = args.index - if not index: - # 判断是否只想验证域名 - writeLog("|-正在创建订单..") - index = self.createOrder(domains, auth_type, auth_to) - writeLog("|-正在获取验证信息..") - self.getAuths(index) - if auth_to == 'dns' and len(self.__config['orders'][index]['auths']) > 0: - return self.__config['orders'][index] - writeLog("|-正在验证域名..") - self.authDomain(index) - - writeLog("|-正在发送CSR..") - self.sendCsr(index) - - writeLog("|-正在下载证书..") - cert = self.downloadCert(index) - - # 保存证书配置 - self.saveConfig() - cert['status'] = True - cert['msg'] = '申请成功!' - writeLog("|-申请成功,正在部署到站点..") - - self.clearAuthFile(index) - - if os.path.exists(auth_to): - mw.execShell("rm -rf {}/.well-known".format(auth_to)) - return cert - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - - cert = {} - cert['status'] = False - cert['msg'] = msg - return cert - - # 取根域名和记录值 - def extractZone(self, domain_name): - top_domain_list = ['.ac.cn', '.ah.cn', '.bj.cn', '.com.cn', '.cq.cn', '.fj.cn', '.gd.cn', '.gov.cn', '.gs.cn', - '.gx.cn', '.gz.cn', '.ha.cn', '.hb.cn', '.he.cn', '.hi.cn', '.hk.cn', '.hl.cn', '.hn.cn', - '.jl.cn', '.js.cn', '.jx.cn', '.ln.cn', '.mo.cn', '.net.cn', '.nm.cn', '.nx.cn', '.org.cn', - '.my.id', '.com.ac', '.com.ad', '.com.ae', '.com.af', '.com.ag', '.com.ai', '.com.al', '.com.am', - '.com.an', '.com.ao', '.com.aq', '.com.ar', '.com.as', '.com.as', '.com.at', '.com.au', '.com.aw', - '.com.az', '.com.ba', '.com.bb', '.com.bd', '.com.be', '.com.bf', '.com.bg', '.com.bh', '.com.bi', - '.com.bj', '.com.bm', '.com.bn', '.com.bo', '.com.br', '.com.bs', '.com.bt', '.com.bv', '.com.bw', - '.com.by', '.com.bz', '.com.ca', '.com.ca', '.com.cc', '.com.cd', '.com.cf', '.com.cg', '.com.ch', - '.com.ci', '.com.ck', '.com.cl', '.com.cm', '.com.cn', '.com.co', '.com.cq', '.com.cr', '.com.cu', - '.com.cv', '.com.cx', '.com.cy', '.com.cz', '.com.de', '.com.dj', '.com.dk', '.com.dm', '.com.do', - '.com.dz', '.com.ec', '.com.ee', '.com.eg', '.com.eh', '.com.es', '.com.et', '.com.eu', '.com.ev', - '.com.fi', '.com.fj', '.com.fk', '.com.fm', '.com.fo', '.com.fr', '.com.ga', '.com.gb', '.com.gd', - '.com.ge', '.com.gf', '.com.gh', '.com.gi', '.com.gl', '.com.gm', '.com.gn', '.com.gp', '.com.gr', - '.com.gt', '.com.gu', '.com.gw', '.com.gy', '.com.hm', '.com.hn', '.com.hr', '.com.ht', '.com.hu', - '.com.id', '.com.id', '.com.ie', '.com.il', '.com.il', '.com.in', '.com.io', '.com.iq', '.com.ir', - '.com.is', '.com.it', '.com.jm', '.com.jo', '.com.jp', '.com.ke', '.com.kg', '.com.kh', '.com.ki', - '.com.km', '.com.kn', '.com.kp', '.com.kr', '.com.kw', '.com.ky', '.com.kz', '.com.la', '.com.lb', - '.com.lc', '.com.li', '.com.lk', '.com.lr', '.com.ls', '.com.lt', '.com.lu', '.com.lv', '.com.ly', - '.com.ma', '.com.mc', '.com.md', '.com.me', '.com.mg', '.com.mh', '.com.ml', '.com.mm', '.com.mn', - '.com.mo', '.com.mp', '.com.mq', '.com.mr', '.com.ms', '.com.mt', '.com.mv', '.com.mw', '.com.mx', - '.com.my', '.com.mz', '.com.na', '.com.nc', '.com.ne', '.com.nf', '.com.ng', '.com.ni', '.com.nl', - '.com.no', '.com.np', '.com.nr', '.com.nr', '.com.nt', '.com.nu', '.com.nz', '.com.om', '.com.pa', - '.com.pe', '.com.pf', '.com.pg', '.com.ph', '.com.pk', '.com.pl', '.com.pm', '.com.pn', '.com.pr', - '.com.pt', '.com.pw', '.com.py', '.com.qa', '.com.re', '.com.ro', '.com.rs', '.com.ru', '.com.rw', - '.com.sa', '.com.sb', '.com.sc', '.com.sd', '.com.se', '.com.sg', '.com.sh', '.com.si', '.com.sj', - '.com.sk', '.com.sl', '.com.sm', '.com.sn', '.com.so', '.com.sr', '.com.st', '.com.su', '.com.sy', - '.com.sz', '.com.tc', '.com.td', '.com.tf', '.com.tg', '.com.th', '.com.tj', '.com.tk', '.com.tl', - '.com.tm', '.com.tn', '.com.to', '.com.tp', '.com.tr', '.com.tt', '.com.tv', '.com.tw', '.com.tz', - '.com.ua', '.com.ug', '.com.uk', '.com.uk', '.com.us', '.com.uy', '.com.uz', '.com.va', '.com.vc', - '.com.ve', '.com.vg', '.com.vn', '.com.vu', '.com.wf', '.com.ws', '.com.ye', '.com.za', '.com.zm', - '.com.zw', '.mil.cn', '.qh.cn', '.sc.cn', '.sd.cn', '.sh.cn', '.sx.cn', '.tj.cn', '.tw.cn', '.tw.cn', - '.xj.cn', '.xz.cn', '.yn.cn', '.zj.cn', '.bj.cn', '.edu.kg' - ] - old_domain_name = domain_name - top_domain = "." + ".".join(domain_name.rsplit('.')[-2:]) - new_top_domain = "." + top_domain.replace(".", "") - is_tow_top = False - if top_domain in top_domain_list: - is_tow_top = True - domain_name = domain_name[:-len(top_domain)] + new_top_domain - - if domain_name.count(".") > 1: - zone, middle, last = domain_name.rsplit(".", 2) - if is_tow_top: - last = top_domain[1:] - root = ".".join([middle, last]) - else: - zone = "" - root = old_domain_name - return root, zone - - # 外部API - START ---------------------------------------------------------- - def getHostConf(self, siteName): - return mw.getServerDir() + '/web_conf/nginx/vhost/' + siteName + '.conf' - - def getSitePath(self, siteName): - file = self.getHostConf(siteName) - if os.path.exists(file): - conf = mw.readFile(file) - rep = r'\s*root\s*(.+);' - path = re.search(rep, conf).groups()[0] - return path - return '' - - def applyCertApi(self, args): - ''' - 申请证书 - api - ''' - return self.applyCert(args['domains'], args['auth_type'], args['auth_to']) - # 外部API - END ---------------------------------------------------------- - - def getSiteNameByDomains(self, domains): - # 通过域名获取网站名称 - sql = mw.M('domain') - site_sql = mw.M('sites') - siteName = None - for domain in domains: - pid = sql.where('name=?', (domain,)).getField('pid') - if pid: - siteName = site_sql.where('id=?', (pid,)).getField('name') - break - return siteName - - def renewCertTo(self, domains, auth_type, auth_to, index=None): - site_name = None - cert = {} - - import site_api - api = site_api.site_api() - if os.path.exists(auth_to): - if mw.M('sites').where('path=?', auth_to).count() == 1: - site_id = m.M('sites').where('path=?', auth_to).getField('id') - site_name = m.M('sites').where( - 'path=?', auth_to).getField('name') - - rdata = api.getSiteRunPath(site_id) - runPath = rdata['runPath'] - if runPath and not runPath in ['/']: - path = auth_to + '/' + runPath - if os.path.exists(path): - auth_to = path.replace('//', '/') - - else: - site_name = self.getSiteNameByDomains(domains) - is_rep = api.httpToHttps(site_name) - api.operateProxyConf(site_name,'stop') - mw.restartWeb() - try: - index = self.createOrder( - domains, - auth_type, - auth_to.replace('//', '/'), - index - ) - - writeLog("|-正在获取验证信息..") - self.getAuths(index) - writeLog("|-正在验证域名..") - self.authDomain(index) - writeLog("|-正在发送CSR..") - self.sendCsr(index) - writeLog("|-正在下载证书..") - cert = self.downloadCert(index) - self.__config['orders'][index]['renew_time'] = int(time.time()) - - # 清理失败重试记录 - self.__config['orders'][index]['retry_count'] = 0 - self.__config['orders'][index]['next_retry_time'] = 0 - - # 保存证书配置 - self.saveConfig() - cert['status'] = True - cert['msg'] = '续签成功!' - writeLog("|-续签成功!") - except Exception as e: - if str(e).find('429') > -1: - msg = '至少超过7天,才能续期!' - writeLog("|-" + msg) - return mw.returnJson(False, msg) - - if str(e).find('请稍候重试') == -1: # 受其它证书影响和连接CA失败的的不记录重试次数 - if index: - # 设置下次重试时间 - self.__config['orders'][index][ - 'next_retry_time'] = int(time.time() + (86400 * 2)) - # 记录重试次数 - if not 'retry_count' in self.__config['orders'][index].keys(): - self.__config['orders'][index]['retry_count'] = 1 - self.__config['orders'][index]['retry_count'] += 1 - # 保存证书配置 - self.saveConfig() - msg = str(e).split('>>>>')[0] - writeLog("|-" + msg) - return mw.returnJson(False, msg) - finally: - is_rep_decode = json.loads(is_rep) - if is_rep_decode['status']: - api.closeToHttps(site_name) - - api.operateProxyConf(site_name,'start') - mw.restartWeb() - writeLog("-" * 70) - return cert - - def checkDomainIsExsits(self, domain): - if mw.M('domain').where("name=?", (domain,)).count() > 0: - return True - - if mw.M('sites').where("name=?", (domain,)).count() > 0: - return True - - if mw.M('binding').where("name=?", (domain,)).count() > 0: - return True - return False - - def renewCert(self, index): - writeLog("", "wb+") - # self.D('renew_cert', index) - try: - order_index = [] - if index: - if type(index) != str: - index = index.index - if not index in self.__config['orders']: - raise Exception("指定订单号不存在,无法续签!") - order_index.append(index) - else: - # 测试一天过期 - now_time = time.time() - # print(self.__config) - if not 'orders' in self.__config: - self.__config['orders'] = {} - - for i in self.__config['orders'].keys(): - # print(self.__config['orders'][i]) - if not 'save_path' in self.__config['orders'][i]: - continue - - if 'cert' in self.__config['orders'][i]: - self.__config['orders'][i]['cert_timeout'] = self.__config['orders'][i]['cert']['cert_timeout'] - - if not 'cert_timeout' in self.__config['orders'][i]: - self.__config['orders'][i]['cert_timeout'] = int(time.time()) - - # print(self.__config['orders'][i]['domains'][0], (self.__config['orders'][i]['cert_timeout'] - now_time)/86400, now_time) - if self.__config['orders'][i]['cert_timeout'] - now_time > 83*86400: - msg = "|-本次跳过域名: {},未过期!".format(self.__config['orders'][i]['domains'][0]) - writeLog(msg) - continue - - # 已删除的网站直接跳过续签 - if self.__config['orders'][i]['auth_to'].find('|') == -1 and self.__config['orders'][i]['auth_to'].find('/') != -1: - if not os.path.exists(self.__config['orders'][i]['auth_to']): - auth_to = self.__config['orders'][i]['auth_to'] - if not auth_to: - continue - - # 域名不存在? - for domain in self.__config['orders'][i]['domains']: - if domain.find('*') != -1: - break - - - if not mw.M('domain').where("name=?", (domain,)).count() and not mw.M('binding').where("domain=?", domain).count(): - auth_to = None - msg = "|-跳过被删除的域名: {}".format( - self.__config['orders'][i]['domains']) - writeLog(msg) - if not auth_to: - continue - - self.__config['orders'][i]['auth_to'] = auth_to - - auth_to = True - for domain in self.__config['orders'][i]['domains']: - if not self.checkDomainIsExsits(domain): - auth_to = False - msg = "|-跳过被删除的域名: {}!".format(self.__config['orders'][i]['domains']) - writeLog(msg) - if not auth_to: - continue - # 是否到了允许重试的时间 - if 'next_retry_time' in self.__config['orders'][i]: - timeout = self.__config['orders'][i][ - 'next_retry_time'] - int(time.time()) - if timeout > 0: - msg = '|-本次跳过域名:{},因第上次续签失败,还需要等待{}小时后再重试'.format( - self.__config['orders'][i]['domains'], int(timeout / 60 / 60)) - writeLog(msg) - continue - - # 是否到了最大重试次数 - if 'retry_count' in self.__config['orders'][i]: - if self.__config['orders'][i]['retry_count'] >= 100: - msg = '|-本次跳过域名:{},因连续100次续签失败,不再续签此证书(可尝试手动续签此证书,成功后错误次数将被重置)'.format( - self.__config['orders'][i]['domains']) - writeLog(msg) - continue - - # 加入到续签订单 - order_index.append(i) - if not order_index: - writeLog("|-所有任务已处理完成!") - return - writeLog("|-共需要续签 {} 张证书".format(len(order_index))) - - n = 0 - self.getApis() - cert = None - for index in order_index: - n += 1 - writeLog("|-正在续签第 {} 张,域名: {}..".format(n, - self.__config['orders'][index]['domains'])) - writeLog("|-正在创建订单..") - cert = self.renewCertTo(self.__config['orders'][index]['domains'], self.__config[ - 'orders'][index]['auth_type'], self.__config['orders'][index]['auth_to'], index) - self.clearAuthFile(index) - return cert - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - return mw.returnJson(False, msg) - - def revokeOrder(self, index): - if not index in self.__config['orders']: - raise Exception("指定订单不存在!") - cert_path = self.__config['orders'][index]['save_path'] - if not os.path.exists(cert_path): - raise Exception("指定订单没有找到可用的证书!") - cert = self.dumpDer(cert_path) - if not cert: - raise Exception("证书读取失败!") - payload = { - "certificate": self.calculateSafeBase64(cert), - "reason": 4 - } - - self.getApis() - res = self.acmeRequest(self.__apis['revokeCert'], payload) - if res.status in [200, 201]: - if os.path.exists(cert_path): - mw.execShell("rm -rf {}".format(cert_path)) - del(self.__config['orders'][index]) - self.saveConfig() - return mw.returnJson(True, "证书吊销成功!") - return res.json() - - def do(self, args): - cert = None - try: - if not args.index: - if not args.domains: - echoErr("请在--domain参数中指定要申请证书的域名,多个以逗号(,)隔开") - if not args.auth_type in ['http', 'tls']: - echoErr("请在--type参数中指定正确的验证类型,http") - - auth_to = '' - if args.auth_type in ['http', 'tls']: - if not args.path: - echoErr("请在--path参数中指定网站根目录!") - if not os.path.exists(args.path): - echoErr("指定网站根目录不存在,请检查:{}".format(args.path)) - auth_to = args.path - else: - echoErr("仅支持文件验证!") - exit() - - domains = args.domains.strip().split(',') - cert = self.applyCert( - domains, auth_type=args.auth_type, auth_to=auth_to, args=args) - else: - # 重新验证 - cert = self.applyCert([], auth_type='dns', - auth_to='dns', index=args.index) - except Exception as e: - writeLog("|-{}".format(mw.getTracebackInfo())) - exit() - - if not cert: - exit() - - # print(cert) - if not cert['status']: - writeLog('|-' + cert['msg'][0]) - exit() - writeLog("=" * 65) - writeLog("|-证书获取成功!") - writeLog("=" * 65) - writeLog("证书到期时间: {}".format( - mw.formatDate(times=cert['cert_timeout']))) - writeLog("证书已保存在: {}/".format(cert['save_path'])) - - -# exp: -''' -// create -python3 class/core/cert_api.py --domain=dev38.cachecha.com --type=http --path=/www/wwwroot/dev38.cachecha.com -// renew -cd /www/server/mdserver-web && python3 class/core/cert_api.py --renew=1 --index=46c558aa1fae96facf36ac7df8eb4750 -// revoke -cd /www/server/mdserver-web && python3 class/core/cert_api.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a - - - -python3 class/core/cert_request.py --domain=dev38.cachecha.com --type=http --path=/Users/midoks/Desktop/mwdev/wwwroot/test - -python3 class/core/cert_api.py --domain=dev38.cachecha.com --type=http --path=/Users/midoks/Desktop/mwdev/wwwroot/test -python3 class/core/cert_api.py --renew=1 -python3 class/core/cert_api.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a -''' -if __name__ == "__main__": - p = argparse.ArgumentParser(usage="必要的参数:--domain 域名列表,多个以逗号隔开!") - p.add_argument('--domain', default=None, - help="请指定要申请证书的域名", dest="domains") - p.add_argument('--type', default=None, help="请指定验证类型", dest="auth_type") - p.add_argument('--path', default=None, help="请指定网站根目录", dest="path") - p.add_argument('--index', default=None, help="指定订单索引", dest="index") - p.add_argument('--renew', default=None, help="续签证书", dest="renew") - p.add_argument('--revoke', default=None, help="吊销证书", dest="revoke") - - args = p.parse_args() - cr = cert_api() - - if args.revoke: - if not args.index: - echoErr("请在--index参数中传入要被吊销的订单索引") - result = cr.revokeOrder(args.index) - writeLog(result) - exit() - - if args.renew: - cr.renewCert(args.index) - exit() - - cr.do(args) diff --git a/class/core/cert_request.py b/class/core/cert_request.py deleted file mode 100644 index 2a2ccc8d3..000000000 --- a/class/core/cert_request.py +++ /dev/null @@ -1,1472 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 证书申请 -# --------------------------------------------------------------------------------- - -import re -import fcntl -import datetime -import binascii -import hashlib -import base64 -import json -import time -import os -import sys -import argparse - -if os.path.exists('/www/server/mdserver-web'): - os.chdir('/www/server/mdserver-web') - -import mw - -try: - import OpenSSL -except: - mw.execShell("pip install pyopenssl") - import OpenSSL - -# import http_requests as requests -# requests.DEFAULT_TYPE = 'curl' -import requests -requests.packages.urllib3.disable_warnings() - - -def echoErr(msg): - writeLog("\033[31m=" * 65) - writeLog("|-错误:{}\033[0m".format(msg)) - exit() - - -def writeLog(log_str, mode="ab+"): - # 写日志 - if __name__ == "__main__": - print(log_str) - return - _log_file = 'logs/letsencrypt.log' - f = open(_log_file, mode) - log_str += "\n" - f.write(log_str.encode('utf-8')) - f.close() - return True - - -class cert_request: - __debug = False - __user_agent = "MW-Panel" - __apis = None - __url = None - __replay_nonce = None - __acme_timeout = 30 - __max_check_num = 5 - __wait_time = 5 - __bits = 2048 - __digest = "sha256" - __verify = False - __config = {} - __dns_class = None - __auto_wildcard = False - __mod_index = {True: "Staging", False: "Production"} - __save_path = 'data/letsencrypt' - __cfg_file = 'data/letsencrypt.json' - - def __init__(self): - self.__save_path = mw.getServerDir() + '/web_conf/letsencrypt' - if not os.path.exists(self.__save_path): - os.makedirs(self.__save_path) - if self.__debug: - self.__url = 'https://acme-staging-v02.api.letsencrypt.org/directory' - else: - self.__url = 'https://acme-v02.api.letsencrypt.org/directory' - self.__config = self.readConfig() - - def D(self, name, val): - if self.__debug: - print('---------{} start--------'.format(name)) - print(val) - print('---------{} end--------'.format(name)) - - # 读配置文件 - def readConfig(self): - if not os.path.exists(self.__cfg_file): - self.__config['orders'] = {} - self.__config['account'] = {} - self.__config['apis'] = {} - self.__config['email'] = mw.M('users').where( - 'id=?', (1,)).getField('email') - if self.__config['email'] in ['midoks@163.com']: - self.__config['email'] = None - self.saveConfig() - return self.__config - tmp_config = mw.readFile(self.__cfg_file) - if not tmp_config: - return self.__config - try: - self.__config = json.loads(tmp_config) - except: - self.saveConfig() - return self.__config - return self.__config - - # 写配置文件 - def saveConfig(self): - fp = open(self.__cfg_file, 'w+') - fcntl.flock(fp, fcntl.LOCK_EX) # 加锁 - fp.write(json.dumps(self.__config)) - fcntl.flock(fp, fcntl.LOCK_UN) # 解锁 - fp.close() - return True - - def getApis(self): - if not self.__apis: - # 尝试从配置文件中获取 - api_index = self.__mod_index[self.__debug] - if not 'apis' in self.__config: - self.__config['apis'] = {} - - if api_index in self.__config['apis']: - if 'expires' in self.__config['apis'][api_index] and 'directory' in self.__config['apis'][api_index]: - if time.time() < self.__config['apis'][api_index]['expires']: - self.__apis = self.__config[ - 'apis'][api_index]['directory'] - return self.__apis - - # 尝试从官方获取获取 - try: - res = mw.httpGet(self.__url) - result = json.loads(res) - self.__apis = {} - self.__apis['newAccount'] = result['newAccount'] - self.__apis['newNonce'] = result['newNonce'] - self.__apis['newOrder'] = result['newOrder'] - self.__apis['revokeCert'] = result['revokeCert'] - self.__apis['keyChange'] = result['keyChange'] - - # 保存到配置文件 - self.__config['apis'][api_index] = {} - self.__config['apis'][api_index]['directory'] = self.__apis - self.__config['apis'][api_index]['expires'] = time.time() + \ - 86400 # 24小时后过期 - self.saveConfig() - except Exception as e: - raise Exception( - '服务因维护而关闭或发生内部错误,查看 https://letsencrypt.status.io/ 了解更多详细信息。') - return self.__apis - - # 系列化payload - def stringfyItems(self, payload): - if isinstance(payload, str): - return payload - - for k, v in payload.items(): - if isinstance(k, bytes): - k = k.decode("utf-8") - if isinstance(v, bytes): - v = v.decode("utf-8") - payload[k] = v - return payload - - # 构造域名列表 - def formatDomains(self, domains): - if type(domains) != list: - return [] - # 是否自动构造通配符 - if self.__auto_wildcard: - domains = self.autoWildcard(domains) - wildcard = [] - tmp_domains = [] - for domain in domains: - domain = domain.strip() - if domain in tmp_domains: - continue - # 将通配符域名转为验证正则表达式 - f_index = domain.find("*.") - if f_index not in [-1, 0]: - continue - if f_index == 0: - wildcard.append(domain.replace( - "*", r"^[\w-]+").replace(".", r"\.")) - # 添加到申请列表 - tmp_domains.append(domain) - - # 处理通配符包含 - apply_domains = tmp_domains[:] - for domain in tmp_domains: - for w in wildcard: - if re.match(w, domain): - apply_domains.pop(domain) - - return apply_domains - - # 转为无填充的Base64 - def calculateSafeBase64(self, un_encoded_data): - if sys.version_info[0] == 3: - if isinstance(un_encoded_data, str): - un_encoded_data = un_encoded_data.encode("utf8") - r = base64.urlsafe_b64encode(un_encoded_data).rstrip(b"=") - return r.decode("utf8") - - # 创建Key - def createKey(self, key_type=OpenSSL.crypto.TYPE_RSA): - key = OpenSSL.crypto.PKey() - key.generate_key(key_type, self.__bits) - private_key = OpenSSL.crypto.dump_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key) - return private_key - - # 获用户取密钥对 - def getAccountKey(self): - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'key' in self.__config['account'][k]: - self.__config['account'][k]['key'] = self.createKey() - if type(self.__config['account'][k]['key']) == bytes: - self.__config['account'][k]['key'] = self.__config[ - 'account'][k]['key'].decode() - self.saveConfig() - return self.__config['account'][k]['key'] - - # 注册acme帐户 - def register(self, existing=False): - if not 'email' in self.__config: - self.__config['email'] = 'xxxx.xxxx@gmail.com' - if existing: - payload = {"onlyReturnExisting": True} - elif self.__config['email']: - payload = { - "termsOfServiceAgreed": True, - "contact": ["mailto:{0}".format(self.__config['email'])], - } - else: - payload = {"termsOfServiceAgreed": True} - - res = self.acmeRequest(url=self.__apis['newAccount'], payload=payload) - - if res.status_code not in [201, 200, 409]: - raise Exception("注册ACME帐户失败: {}".format(res.json())) - kid = res.headers["Location"] - return kid - - # 获取kid - def getKid(self, force=False): - # 如果配置文件中不存在kid或force = True时则重新注册新的acme帐户 - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'kid' in self.__config['account'][k]: - self.__config['account'][k]['kid'] = self.register() - self.saveConfig() - time.sleep(3) - self.__config = self.readConfig() - return self.__config['account'][k]['kid'] - - # 获取随机数 - def getNonce(self, force=False): - return '1AADXO5Sdc3EzuOS8ViYj-MVdhgx3DRE2kSDSwrObJ1jG_w' - # 如果没有保存上一次的随机数或force=True时则重新获取新的随机数 - if not self.__replay_nonce or force: - try: - - import urllib.request - try: - response = urllib.request.urlopen(self.__apis['newNonce'], - timeout=self.__acme_timeout, - headers=headers, - verify=self.__verify) - self.__replay_nonce = response.headers["replay-nonce"] - return self.__replay_nonce - except Exception as e: - pass - - headers = {"User-Agent": self.__user_agent} - response = requests.get(self.__apis['newNonce'], - timeout=self.__acme_timeout, - headers=headers, - verify=self.__verify) - - self.__replay_nonce = response.headers["replay-nonce"] - except Exception as e: - raise Exception("获取随机数失败: {}".format(str(e))) - - return self.__replay_nonce - - # 获请ACME请求头 - def getAcmeHeader(self, url): - nonce = self.getNonce() - - header = {"alg": "RS256", "nonce": nonce, "url": url} - if url in [self.__apis['newAccount'], 'GET_THUMBPRINT']: - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import serialization - private_key = serialization.load_pem_private_key( - self.getAccountKey().encode(), - password=None, - backend=default_backend(), - ) - public_key_public_numbers = private_key.public_key().public_numbers() - - exponent = "{0:x}".format(public_key_public_numbers.e) - exponent = "0{0}".format(exponent) if len( - exponent) % 2 else exponent - modulus = "{0:x}".format(public_key_public_numbers.n) - jwk = { - "kty": "RSA", - "e": self.calculateSafeBase64(binascii.unhexlify(exponent)), - "n": self.calculateSafeBase64(binascii.unhexlify(modulus)), - } - header["jwk"] = jwk - else: - header["kid"] = self.getKid() - return header - - # 计算signature - def signMessage(self, message): - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.getAccountKey().encode()) - return OpenSSL.crypto.sign(pk, message.encode("utf8"), self.__digest) - - def getSiteRunPathByid(self, site_id): - if mw.M('sites').where('id=?', (site_id,)).count() >= 1: - site_path = mw.M('sites').where('id=?', site_id).getField('path') - if not site_path: - return None - if not os.path.exists(site_path): - return None - args = mw.dict_obj() - args.id = site_id - import panelSite - run_path = panelSite.panelSite().GetRunPath(args) - if run_path in ['/']: - run_path = '' - if run_path: - if run_path[0] == '/': - run_path = run_path[1:] - site_run_path = os.path.join(site_path, run_path) - if not os.path.exists(site_run_path): - return site_path - return site_run_path - else: - return False - - def getSiteRunPath(self, domains): - site_id = 0 - for domain in domains: - site_id = mw.M('domain').where("name=?", domain).getField('pid') - if site_id: - break - - if not site_id: - return None - return self.getSiteRunPathByid(site_id) - - # 清理验证文件 - def claerAuthFile(self, index): - if not self.__config['orders'][index]['auth_type'] in ['http', 'tls']: - return True - acme_path = '{}/.well-known/acme-challenge'.format( - self.__config['orders'][index]['auth_to']) - writeLog("|-验证目录:{}".format(acme_path)) - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - # 获取域名验证方式 - def getAuthType(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - s_type = 'http-01' - if 'auth_type' in self.__config['orders'][index]: - if self.__config['orders'][index]['auth_type'] == 'dns': - s_type = 'dns-01' - elif self.__config['orders'][index]['auth_type'] == 'tls': - s_type = 'tls-alpn-01' - else: - s_type = 'http-01' - return s_type - - # 构造验证信息 - def getIdentifierAuth(self, index, url, auth_info): - s_type = self.getAuthType(index) - writeLog("|-验证类型:{}".format(s_type)) - domain = auth_info['identifier']['value'] - wildcard = False - # 处理通配符 - if 'wildcard' in auth_info: - wildcard = auth_info['wildcard'] - if wildcard: - domain = "*." + domain - - for auth in auth_info['challenges']: - if auth['type'] != s_type: - continue - identifier_auth = { - "domain": domain, - "url": url, - "wildcard": wildcard, - "token": auth['token'], - "dns_challenge_url": auth['url'], - } - return identifier_auth - return None - - # 构造域名验证头和验证值 - def getKeyauthorization(self, token): - acme_header_jwk_json = json.dumps( - self.getAcmeHeader("GET_THUMBPRINT")["jwk"], sort_keys=True, separators=(",", ":") - ) - acme_thumbprint = self.calculateSafeBase64( - hashlib.sha256(acme_header_jwk_json.encode("utf8")).digest() - ) - acme_keyauthorization = "{0}.{1}".format(token, acme_thumbprint) - base64_of_acme_keyauthorization = self.calculateSafeBase64( - hashlib.sha256(acme_keyauthorization.encode("utf8")).digest() - ) - - return acme_keyauthorization, base64_of_acme_keyauthorization - - # 写验证文件 - def writeAuthFile(self, auth_to, token, acme_keyauthorization): - try: - # self.D('writeAuthFile', auth_to) - acme_path = '{}/.well-known/acme-challenge'.format(auth_to) - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - return True - except: - err = mw.getTracebackInfo() - raise Exception("写入验证文件失败: {}".format(err)) - - # 设置验证信息 - def setAuthInfo(self, identifier_auth): - - # 是否手动验证DNS - if identifier_auth['auth_to'] == 'dns': - return None - - # 是否文件验证 - if identifier_auth['type'] in ['http', 'tls']: - self.writeAuthFile(identifier_auth['auth_to'], identifier_auth[ - 'token'], identifier_auth['acme_keyauthorization']) - - # 获取验证信息 - def getAuths(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 检查是否已经获取过授权信息 - if 'auths' in self.__config['orders'][index]: - # 检查授权信息是否过期 - if time.time() < self.__config['orders'][index]['auths'][0]['expires']: - return self.__config['orders'][index]['auths'] - if self.__config['orders'][index]['auth_type'] != 'dns': - site_run_path = self.getSiteRunPath( - self.__config['orders'][index]['domains']) - if site_run_path: - self.__config['orders'][index]['auth_to'] = site_run_path - - # 清理旧验证 - self.claerAuthFile(index) - - auths = [] - for auth_url in self.__config['orders'][index]['authorizations']: - res = self.acmeRequest(auth_url, "") - if res.status_code not in [200, 201]: - raise Exception("获取授权失败: {}".format(res.json())) - - s_body = res.json() - if 'status' in s_body: - if s_body['status'] in ['invalid']: - raise Exception("无效订单,此订单当前为验证失败状态!") - if s_body['status'] in ['valid']: # 跳过无需验证的域名 - continue - - s_body['expires'] = self.utcToTime(s_body['expires']) - identifier_auth = self.getIdentifierAuth(index, auth_url, s_body) - if not identifier_auth: - raise Exception("验证信息构造失败!{}") - - acme_keyauthorization, auth_value = self.getKeyauthorization(identifier_auth[ - 'token']) - identifier_auth['acme_keyauthorization'] = acme_keyauthorization - identifier_auth['auth_value'] = auth_value - identifier_auth['expires'] = s_body['expires'] - identifier_auth['auth_to'] = self.__config[ - 'orders'][index]['auth_to'] - identifier_auth['type'] = self.__config[ - 'orders'][index]['auth_type'] - - # print(identifier_auth) - # 设置验证信息 - self.setAuthInfo(identifier_auth) - auths.append(identifier_auth) - self.__config['orders'][index]['auths'] = auths - self.saveConfig() - return auths - - # 更新随机数 - def updateReplayNonce(self, res): - replay_nonce = res.headers.get('replay-nonce') - if replay_nonce: - self.__replay_nonce = replay_nonce - - # 请求到ACME接口 - def acmeRequest(self, url, payload): - headers = {"User-Agent": self.__user_agent} - payload = self.stringfyItems(payload) - - if payload == "": - payload64 = payload - else: - payload64 = self.calculateSafeBase64(json.dumps(payload)) - protected = self.getAcmeHeader(url) - protected64 = self.calculateSafeBase64(json.dumps(protected)) - signature = self.signMessage( - message="{0}.{1}".format(protected64, payload64)) # bytes - signature64 = self.calculateSafeBase64(signature) # str - data = json.dumps( - {"protected": protected64, "payload": payload64, "signature": signature64}) - headers.update({"Content-Type": "application/jose+json"}) - response = requests.post( - url, - data=data.encode("utf8"), - timeout=self.__acme_timeout, - headers=headers, - verify=self.__verify - ) - # 更新随机数 - self.updateReplayNonce(response) - return response - - # UTC时间转时间戳 - def utcToTime(self, utc_string): - try: - utc_string = utc_string.split('.')[0] - utc_date = datetime.datetime.strptime( - utc_string, "%Y-%m-%dT%H:%M:%S") - # 按北京时间返回 - return int(time.mktime(utc_date.timetuple())) + (3600 * 8) - except: - return int(time.time() + 86400 * 7) - - # 保存订单 - def saveOrder(self, order_object, index): - if not 'orders' in self.__config: - self.__config['orders'] = {} - renew = False - if not index: - index = mw.md5(json.dumps(order_object['identifiers'])) - else: - renew = True - order_object['certificate_url'] = self.__config[ - 'orders'][index]['certificate_url'] - order_object['save_path'] = self.__config[ - 'orders'][index]['save_path'] - - order_object['expires'] = self.utcToTime(order_object['expires']) - self.__config['orders'][index] = order_object - self.__config['orders'][index]['index'] = index - if not renew: - self.__config['orders'][index]['create_time'] = int(time.time()) - self.__config['orders'][index]['renew_time'] = 0 - self.saveConfig() - return index - - def getError(self, error): - # 格式化错误输出 - if error.find("Max checks allowed") >= 0: - return "CA无法验证您的域名,请检查域名解析是否正确,或等待5-10分钟后重试." - elif error.find("Max retries exceeded with") >= 0 or error.find('status_code=0 ') != -1: - return "CA服务器连接超时,请稍候重试." - elif error.find("The domain name belongs") >= 0: - return "域名不属于此DNS服务商,请确保域名填写正确." - elif error.find('login token ID is invalid') >= 0: - return 'DNS服务器连接失败,请检查密钥是否正确.' - elif error.find('Error getting validation data') != -1: - return '数据验证失败,CA无法从验证连接中获到正确的验证码.' - elif "too many certificates already issued for exact set of domains" in error: - return '签发失败,该域名%s超出了每周的重复签发次数限制!' % re.findall("exact set of domains: (.+):", error) - elif "Error creating new account :: too many registrations for this IP" in error: - return '签发失败,当前服务器IP已达到每3小时最多创建10个帐户的限制.' - elif "DNS problem: NXDOMAIN looking up A for" in error: - return '验证失败,没有解析域名,或解析未生效!' - elif "Invalid response from" in error: - return '验证失败,域名解析错误或验证URL无法被访问!' - elif error.find('TLS Web Server Authentication') != -1: - return "连接CA服务器失败,请稍候重试." - elif error.find('Name does not end in a public suffix') != -1: - return "不支持的域名%s,请检查域名是否正确!" % re.findall("Cannot issue for \"(.+)\":", error) - elif error.find('No valid IP addresses found for') != -1: - return "域名%s没有找到解析记录,请检查域名是否解析生效!" % re.findall("No valid IP addresses found for (.+)", error) - elif error.find('No TXT record found at') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("No TXT record found at (.+)", error) - elif error.find('Incorrect TXT record') != -1: - return "在%s上发现错误的TXT记录:%s,请检查TXT解析是否正确,如果是DNSAPI方式申请的,请10分钟后重试!" % (re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error)) - elif error.find('Domain not under you or your user') != -1: - return "这个dnspod账户下面不存在这个域名,添加解析失败!" - elif error.find('SERVFAIL looking up TXT for') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("looking up TXT for (.+)", error) - elif error.find('Timeout during connect') != -1: - return "连接超时,CA服务器无法访问您的网站!" - elif error.find("DNS problem: SERVFAIL looking up CAA for") != -1: - return "域名%s当前被要求验证CAA记录,请手动解析CAA记录,或1小时后重新尝试申请!" % re.findall("looking up CAA for (.+)", error) - elif error.find("Read timed out.") != -1: - return "验证超时,请检查域名是否正确解析,若已正确解析,可能服务器与Let'sEncrypt连接异常,请稍候再重试!" - elif error.find('Cannot issue for') != -1: - return "无法为{}颁发证书,不能直接用域名后缀申请通配符证书!".format(re.findall(r'for\s+"(.+)"', error)) - elif error.find('too many failed authorizations recently'): - return '该帐户1小时内失败的订单次数超过5次,请等待1小时再重试!' - elif error.find("Error creating new order") != -1: - return "订单创建失败,请稍候重试!" - elif error.find("Too Many Requests") != -1: - return "1小时内超过5次验证失败,暂时禁止申请,请稍候重试!" - elif error.find('HTTP Error 400: Bad Request') != -1: - return "CA服务器拒绝访问,请稍候重试!" - elif error.find('Temporary failure in name resolution') != -1: - return '服务器DNS故障,无法解析域名,请使用Linux工具箱检查dns配置' - elif error.find('Too Many Requests') != -1: - return '该域名请求申请次数过多,请3小时后重试' - else: - return error - - # 创建订单 - def createOrder(self, domains, auth_type, auth_to, index=None): - domains = self.formatDomains(domains) - if not domains: - raise Exception("至少需要有一个域名") - # 构造标识 - identifiers = [] - for domain_name in domains: - identifiers.append({"type": 'dns', "value": domain_name}) - payload = {"identifiers": identifiers} - - # 请求创建订单 - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status_code in [201]: # 如果创建失败 - e_body = res.json() - if 'type' in e_body: - # 如果随机数失效 - if e_body['type'].find('error:badNonce') != -1: - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - - # 如果帐户失效 - if e_body['detail'].find('KeyID header contained an invalid account URL') != -1: - k = self._mod_index[self.__debug] - del(self.__config['account'][k]) - self.getKid() - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status_code in [201]: - a_auth = res.json() - - ret_title = self.getError(str(a_auth)) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - - # 返回验证地址和验证 - s_json = res.json() - s_json['auth_type'] = auth_type - s_json['domains'] = domains - s_json['auth_to'] = auth_to - index = self.saveOrder(s_json, index) - return index - - # 检查验证状态 - def checkAuthStatus(self, url, desired_status=None): - desired_status = desired_status or ["pending", "valid", "invalid"] - number_of_checks = 0 - while True: - if desired_status == ['valid', 'invalid']: - writeLog("|-第{}次查询验证结果..".format(number_of_checks + 1)) - time.sleep(self.__wait_time) - check_authorization_status_response = self.acmeRequest(url, "") - a_auth = check_authorization_status_response.json() - authorization_status = a_auth["status"] - number_of_checks += 1 - if authorization_status in desired_status: - if authorization_status == "invalid": - writeLog("|-验证失败!") - try: - if 'error' in a_auth['challenges'][0]: - ret_title = a_auth['challenges'][ - 0]['error']['detail'] - elif 'error' in a_auth['challenges'][1]: - ret_title = a_auth['challenges'][ - 1]['error']['detail'] - elif 'error' in a_auth['challenges'][2]: - ret_title = a_auth['challenges'][ - 2]['error']['detail'] - else: - ret_title = str(a_auth) - ret_title = self.getError(ret_title) - except: - ret_title = str(a_auth) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - break - - if number_of_checks == self.__max_check_num: - raise StopIteration( - "错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format( - number_of_checks, - self.__max_check_num, - self.__wait_time - ) - ) - if desired_status == ['valid', 'invalid']: - writeLog("|-验证成功!") - return check_authorization_status_response - - # 发送验证请求 - def respondToChallenge(self, auth): - payload = {"keyAuthorization": "{0}".format( - auth['acme_keyauthorization'])} - respond_to_challenge_response = self.acmeRequest( - auth['dns_challenge_url'], payload) - return respond_to_challenge_response - - # 检查DNS记录 - def checkDns(self, domain, value, s_type='TXT'): - writeLog( - "|-尝试本地验证DNS记录,域名: {} , 类型: {} 记录值: {}".format(domain, s_type, value)) - time.sleep(10) - n = 0 - while n < 20: - n += 1 - try: - import dns.resolver - ns = dns.resolver.query(domain, s_type) - for j in ns.response.answer: - for i in j.items: - txt_value = i.to_text().replace('"', '').strip() - writeLog("|-第 {} 次验证值: {}".format(n, txt_value)) - if txt_value == value: - write_log("|-本地验证成功!") - return True - except: - try: - import dns.resolver - except: - return False - time.sleep(3) - writeLog("|-本地验证失败!") - return True - - def authDomain(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 开始验证 - for auth in self.__config['orders'][index]['auths']: - res = self.checkAuthStatus(auth['url']) # 检查是否需要验证 - if res.json()['status'] == 'pending': - if auth['type'] == 'dns': # 尝试提前验证dns解析 - self.checkDns( - "_acme-challenge.{}".format( - auth['domain'].replace('*.', '')), - auth['auth_value'], - "TXT" - ) - self.respondToChallenge(auth) - - # 检查验证结果 - for i in range(len(self.__config['orders'][index]['auths'])): - self.checkAuthStatus(self.__config['orders'][index]['auths'][i]['url'], [ - 'valid', 'invalid']) - self.__config['orders'][index]['status'] = 'valid' - - # 构造可选名称 - def getAltNames(self, index): - domain_name = self.__config['orders'][index]['domains'][0] - domain_alt_names = [] - if len(self.__config['orders'][index]['domains']) > 1: - domain_alt_names = self.__config['orders'][index]['domains'][1:] - return domain_name, domain_alt_names - - # 创建CSR - def createCsr(self, index): - if 'csr' in self.__config['orders'][index]: - return self.__config['orders']['csr'] - domain_name, domain_alt_names = self.getAltNames(index) - X509Req = OpenSSL.crypto.X509Req() - X509Req.get_subject().CN = domain_name - if domain_alt_names: - SAN = "DNS:{0}, ".format(domain_name).encode("utf8") + ", ".join( - "DNS:" + i for i in domain_alt_names - ).encode("utf8") - else: - SAN = "DNS:{0}".format(domain_name).encode("utf8") - - X509Req.add_extensions( - [ - OpenSSL.crypto.X509Extension( - "subjectAltName".encode("utf8"), critical=False, value=SAN - ) - ] - ) - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.createCertificateKey( - index).encode() - ) - X509Req.set_pubkey(pk) - # X509Req.set_version(2) - X509Req.set_version(0) - X509Req.sign(pk, self.__digest) - return OpenSSL.crypto.dump_certificate_request(OpenSSL.crypto.FILETYPE_ASN1, X509Req) - - # 获取证书密钥对 - def createCertificateKey(self, index): - # 判断是否已经创建private_key - if 'private_key' in self.__config['orders'][index]: - return self.__config['orders'][index]['private_key'] - # 创建新的私钥 - private_key = self.createKey() - if type(private_key) == bytes: - private_key = private_key.decode() - # 保存私钥到订单配置文件 - self.__config['orders'][index]['private_key'] = private_key - self.saveConfig() - return private_key - - # 发送CSR - def sendCsr(self, index): - csr = self.createCsr(index) - payload = {"csr": self.calculateSafeBase64(csr)} - send_csr_response = self.acmeRequest( - url=self.__config['orders'][index]['finalize'], payload=payload) - if send_csr_response.status_code not in [200, 201]: - raise ValueError( - "错误: 发送CSR: 响应状态{status_code} 响应值:{response}".format( - status_code=send_csr_response.status_code, - response=send_csr_response.json(), - ) - ) - send_csr_response_json = send_csr_response.json() - certificate_url = send_csr_response_json["certificate"] - self.__config['orders'][index]['certificate_url'] = certificate_url - self.saveConfig() - return certificate_url - - # 转换时间 - def strfDate(self, sdate): - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) - - # 证书转为DER - def dumpDer(self, cert_path): - cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(cert_path + '/cert.csr')) - return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert) - - # 证书转为pkcs12 - def dumpPkcs12(self, key_pem=None, cert_pem=None, ca_pem=None, friendly_name=None): - p12 = OpenSSL.crypto.PKCS12() - if cert_pem: - p12.set_certificate(OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cert_pem.encode())) - if key_pem: - p12.set_privatekey(OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key_pem.encode())) - if ca_pem: - p12.set_ca_certificates((OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, ca_pem.encode()),)) - if friendly_name: - p12.set_friendlyname(friendly_name.encode()) - return p12.export() - - # 拆分根证书 - def splitCaData(self, cert): - sp_key = '-----END CERTIFICATE-----\n' - datas = cert.split(sp_key) - return {"cert": datas[0] + sp_key, "root": sp_key.join(datas[1:])} - - # 获取指定证书基本信息 - def getCertInit(self, pem_file): - if not os.path.exists(pem_file): - return None - try: - result = {} - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file)) - # 取产品名称 - issuer = x509.get_issuer() - result['issuer'] = '' - if hasattr(issuer, 'CN'): - result['issuer'] = issuer.CN - if not result['issuer']: - is_key = [b'0', '0'] - issue_comp = issuer.get_components() - if len(issue_comp) == 1: - is_key = [b'CN', 'CN'] - for iss in issue_comp: - if iss[0] in is_key: - result['issuer'] = iss[1].decode() - break - # 取到期时间 - result['notAfter'] = self.strfDate( - bytes.decode(x509.get_notAfter())[:-1]) - # 取申请时间 - result['notBefore'] = self.strfDate( - bytes.decode(x509.get_notBefore())[:-1]) - # 取可选名称 - result['dns'] = [] - for i in range(x509.get_extension_count()): - s_name = x509.get_extension(i) - if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: - s_dns = str(s_name).split(',') - for d in s_dns: - result['dns'].append(d.split(':')[1]) - subject = x509.get_subject().get_components() - # 取主要认证名称 - if len(subject) == 1: - result['subject'] = subject[0][1].decode() - else: - result['subject'] = result['dns'][0] - return result - except: - return None - - # 替换服务器上的同域名同品牌证书 - def subAllCert(self, key_file, pem_file): - cert_init = self.getCertInit(pem_file) # 获取新证书的基本信息 - paths = ['/www/server/mdserver-web/data/letsencrypt'] - is_panel = False - for path in paths: - if not os.path.exists(path): - continue - for p_name in os.listdir(path): - to_path = path + '/' + p_name - to_pem_file = to_path + '/fullchain.pem' - to_key_file = to_path + '/privkey.pem' - to_info = to_path + '/info.json' - # 判断目标证书是否存在 - if not os.path.exists(to_pem_file): - if not p_name in ['ssl']: - continue - to_pem_file = to_path + '/certificate.pem' - to_key_file = to_path + '/privateKey.pem' - if not os.path.exists(to_pem_file): - continue - if path == paths[-1]: - is_panel = True - # 获取目标证书的基本信息 - to_cert_init = self.getCertInit(to_pem_file) - # 判断证书品牌是否一致 - try: - if to_cert_init['issuer'] != cert_init['issuer'] and to_cert_init['issuer'].find("Let's Encrypt") == -1 and to_cert_init['issuer'] != 'R3': - continue - except: - continue - # 判断目标证书的到期时间是否较早 - if to_cert_init['notAfter'] > cert_init['notAfter']: - continue - # 判断认识名称是否一致 - if len(to_cert_init['dns']) != len(cert_init['dns']): - continue - is_copy = True - for domain in to_cert_init['dns']: - if not domain in cert_init['dns']: - is_copy = False - if not is_copy: - continue - - # 替换新的证书文件和基本信息 - mw.writeFile(to_pem_file, mw.readFile(pem_file, 'rb'), 'wb') - mw.writeFile(to_key_file, mw.readFile(key_file, 'rb'), 'wb') - mw.writeFile(to_info, json.dumps(cert_init)) - writeLog("|-检测到{}下的证书与本次申请的证书重叠,且到期时间较早,已替换为新证书!".format(to_path)) - # 重载web服务 - mw.restartWeb() - - # 保存证书到文件 - def saveCert(self, cert, index): - try: - domain_name = self.__config['orders'][index]['domains'][0] - path = self.__config['orders'][index]['save_path'] - if not os.path.exists(path): - os.makedirs(path, 384) - - # 存储证书 - key_file = path + "/privkey.pem" - pem_file = path + "/fullchain.pem" - mw.writeFile(key_file, cert['private_key']) - mw.writeFile(pem_file, cert['cert'] + cert['root']) - mw.writeFile(path + "/cert.csr", cert['cert']) - mw.writeFile(path + "/root_cert.csr", cert['root']) - - # 转为IIS证书 - pfx_buffer = self.dumpPkcs12( - cert['private_key'], cert['cert'] + cert['root'], cert['root'], domain_name) - mw.writeFile(path + "/fullchain.pfx", pfx_buffer, 'wb+') - - ps = '''文件说明: -privkey.pem 证书私钥 -fullchain.pem 包含证书链的PEM格式证书(nginx/apache) -root_cert.csr 根证书 -cert.csr 域名证书 -fullchain.pfx 用于IIS的证书格式 - -如何在MW面板使用: -privkey.pem 粘贴到密钥输入框 -fullchain.pem 粘贴到证书输入框 -''' - mw.writeFile(path + '/readme.txt', ps) - self.subAllCert(key_file, pem_file) - except: - writeLog(mw.getTracebackInfo()) - - # 获取证书到期时间 - def getCertTimeout(self, cret_data): - try: - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cret_data) - cert_timeout = bytes.decode(x509.get_notAfter())[:-1] - return int(time.mktime(time.strptime(cert_timeout, '%Y%m%d%H%M%S'))) - except: - return int(time.time() + (86400 * 90)) - - # 下载证书 - def downloadCert(self, index): - res = self.acmeRequest( - self.__config['orders'][index]['certificate_url'], "") - if res.status_code not in [200, 201]: - raise Exception("下载证书失败: {}".format(res.json())) - - pem_certificate = res.content - if type(pem_certificate) == bytes: - pem_certificate = pem_certificate.decode('utf-8') - cert = self.splitCaData(pem_certificate) - cert['cert_timeout'] = self.getCertTimeout(cert['cert']) - cert['private_key'] = self.__config['orders'][index]['private_key'] - cert['domains'] = self.__config['orders'][index]['domains'] - del(self.__config['orders'][index]['private_key']) - del(self.__config['orders'][index]['auths']) - del(self.__config['orders'][index]['expires']) - del(self.__config['orders'][index]['authorizations']) - del(self.__config['orders'][index]['finalize']) - del(self.__config['orders'][index]['identifiers']) - if 'cert' in self.__config['orders'][index]: - del(self.__config['orders'][index]['cert']) - self.__config['orders'][index]['status'] = 'valid' - self.__config['orders'][index]['cert_timeout'] = cert['cert_timeout'] - domain_name = self.__config['orders'][index]['domains'][0] - self.__config['orders'][index]['save_path'] = '{}/{}'.format( - self.__save_path, domain_name) - cert['save_path'] = self.__config['orders'][index]['save_path'] - self.saveConfig() - self.saveCert(cert, index) - return cert - - def applyCert(self, domains, auth_type='http', auth_to='Dns_com|None|None', args={}): - writeLog("", "wb+") - try: - self.getApis() - index = None - if 'index' in args and args.index: - index = args.index - if not index: - # 判断是否只想验证域名 - writeLog("|-正在创建订单..") - index = self.createOrder(domains, auth_type, auth_to) - writeLog("|-正在获取验证信息..") - self.getAuths(index) - if auth_to == 'dns' and len(self.__config['orders'][index]['auths']) > 0: - return self.__config['orders'][index] - writeLog("|-正在验证域名..") - self.authDomain(index) - - writeLog("|-正在发送CSR..") - self.sendCsr(index) - - writeLog("|-正在下载证书..") - cert = self.downloadCert(index) - - # 保存证书配置 - self.saveConfig() - cert['status'] = True - cert['msg'] = '申请成功!' - writeLog("|-申请成功,正在部署到站点..") - return cert - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - - cert = {} - cert['status'] = False - cert['msg'] = msg - return cert - - # 取根域名和记录值 - def extractZone(self, domain_name): - top_domain_list = ['.ac.cn', '.ah.cn', '.bj.cn', '.com.cn', '.cq.cn', '.fj.cn', '.gd.cn', '.gov.cn', '.gs.cn', - '.gx.cn', '.gz.cn', '.ha.cn', '.hb.cn', '.he.cn', '.hi.cn', '.hk.cn', '.hl.cn', '.hn.cn', - '.jl.cn', '.js.cn', '.jx.cn', '.ln.cn', '.mo.cn', '.net.cn', '.nm.cn', '.nx.cn', '.org.cn', - '.my.id', '.com.ac', '.com.ad', '.com.ae', '.com.af', '.com.ag', '.com.ai', '.com.al', '.com.am', - '.com.an', '.com.ao', '.com.aq', '.com.ar', '.com.as', '.com.as', '.com.at', '.com.au', '.com.aw', - '.com.az', '.com.ba', '.com.bb', '.com.bd', '.com.be', '.com.bf', '.com.bg', '.com.bh', '.com.bi', - '.com.bj', '.com.bm', '.com.bn', '.com.bo', '.com.br', '.com.bs', '.com.bt', '.com.bv', '.com.bw', - '.com.by', '.com.bz', '.com.ca', '.com.ca', '.com.cc', '.com.cd', '.com.cf', '.com.cg', '.com.ch', - '.com.ci', '.com.ck', '.com.cl', '.com.cm', '.com.cn', '.com.co', '.com.cq', '.com.cr', '.com.cu', - '.com.cv', '.com.cx', '.com.cy', '.com.cz', '.com.de', '.com.dj', '.com.dk', '.com.dm', '.com.do', - '.com.dz', '.com.ec', '.com.ee', '.com.eg', '.com.eh', '.com.es', '.com.et', '.com.eu', '.com.ev', - '.com.fi', '.com.fj', '.com.fk', '.com.fm', '.com.fo', '.com.fr', '.com.ga', '.com.gb', '.com.gd', - '.com.ge', '.com.gf', '.com.gh', '.com.gi', '.com.gl', '.com.gm', '.com.gn', '.com.gp', '.com.gr', - '.com.gt', '.com.gu', '.com.gw', '.com.gy', '.com.hm', '.com.hn', '.com.hr', '.com.ht', '.com.hu', - '.com.id', '.com.id', '.com.ie', '.com.il', '.com.il', '.com.in', '.com.io', '.com.iq', '.com.ir', - '.com.is', '.com.it', '.com.jm', '.com.jo', '.com.jp', '.com.ke', '.com.kg', '.com.kh', '.com.ki', - '.com.km', '.com.kn', '.com.kp', '.com.kr', '.com.kw', '.com.ky', '.com.kz', '.com.la', '.com.lb', - '.com.lc', '.com.li', '.com.lk', '.com.lr', '.com.ls', '.com.lt', '.com.lu', '.com.lv', '.com.ly', - '.com.ma', '.com.mc', '.com.md', '.com.me', '.com.mg', '.com.mh', '.com.ml', '.com.mm', '.com.mn', - '.com.mo', '.com.mp', '.com.mq', '.com.mr', '.com.ms', '.com.mt', '.com.mv', '.com.mw', '.com.mx', - '.com.my', '.com.mz', '.com.na', '.com.nc', '.com.ne', '.com.nf', '.com.ng', '.com.ni', '.com.nl', - '.com.no', '.com.np', '.com.nr', '.com.nr', '.com.nt', '.com.nu', '.com.nz', '.com.om', '.com.pa', - '.com.pe', '.com.pf', '.com.pg', '.com.ph', '.com.pk', '.com.pl', '.com.pm', '.com.pn', '.com.pr', - '.com.pt', '.com.pw', '.com.py', '.com.qa', '.com.re', '.com.ro', '.com.rs', '.com.ru', '.com.rw', - '.com.sa', '.com.sb', '.com.sc', '.com.sd', '.com.se', '.com.sg', '.com.sh', '.com.si', '.com.sj', - '.com.sk', '.com.sl', '.com.sm', '.com.sn', '.com.so', '.com.sr', '.com.st', '.com.su', '.com.sy', - '.com.sz', '.com.tc', '.com.td', '.com.tf', '.com.tg', '.com.th', '.com.tj', '.com.tk', '.com.tl', - '.com.tm', '.com.tn', '.com.to', '.com.tp', '.com.tr', '.com.tt', '.com.tv', '.com.tw', '.com.tz', - '.com.ua', '.com.ug', '.com.uk', '.com.uk', '.com.us', '.com.uy', '.com.uz', '.com.va', '.com.vc', - '.com.ve', '.com.vg', '.com.vn', '.com.vu', '.com.wf', '.com.ws', '.com.ye', '.com.za', '.com.zm', - '.com.zw', '.mil.cn', '.qh.cn', '.sc.cn', '.sd.cn', '.sh.cn', '.sx.cn', '.tj.cn', '.tw.cn', '.tw.cn', - '.xj.cn', '.xz.cn', '.yn.cn', '.zj.cn', '.bj.cn', '.edu.kg' - ] - old_domain_name = domain_name - top_domain = "." + ".".join(domain_name.rsplit('.')[-2:]) - new_top_domain = "." + top_domain.replace(".", "") - is_tow_top = False - if top_domain in top_domain_list: - is_tow_top = True - domain_name = domain_name[:-len(top_domain)] + new_top_domain - - if domain_name.count(".") > 1: - zone, middle, last = domain_name.rsplit(".", 2) - if is_tow_top: - last = top_domain[1:] - root = ".".join([middle, last]) - else: - zone = "" - root = old_domain_name - return root, zone - - # 获取当前正在使用此证书的网站目录 - def getSslUsedSite(self, save_path): - pkey_file = '{}/privkey.pem'.format(save_path) - pkey = mw.readFile(pkey_file) - if not pkey: - return False - cert_paths = 'vhost/cert' - import panelSite - args = mw.dict_obj() - args.siteName = '' - for c_name in os.listdir(cert_paths): - skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name) - skey = mw.readFile(skey_file) - if not skey: - continue - if skey == pkey: - args.siteName = c_name - run_path = panelSite.panelSite().GetRunPath(args) - if not run_path: - continue - sitePath = mw.M('sites').where( - 'name=?', c_name).getField('path') - if not sitePath: - continue - to_path = "{}/{}".format(sitePath, run_path) - return to_path - return False - - def renewCertOther(self): - cert_path = "{}/vhost/cert".format(mw.getRunDir()) - if not os.path.exists(cert_path): - return - new_time = time.time() + (86400 * 30) - n = 0 - if not 'orders' in self.__config: - self.__config['orders'] = {} - import panelSite - siteObj = panelSite.panelSite() - args = mw.dict_obj() - for siteName in os.listdir(cert_path): - try: - cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName) - if not os.path.exists(cert_file): - continue # 无证书文件 - siteInfo = mw.M('sites').where('name=?', siteName).find() - if not siteInfo: - continue # 无网站信息 - cert_init = self.getCertInit(cert_file) - if not cert_init: - continue # 无法获取证书 - end_time = time.mktime(time.strptime( - cert_init['notAfter'], '%Y-%m-%d')) - if end_time > new_time: - continue # 未到期 - try: - if not cert_init['issuer'] in ['R3', "Let's Encrypt"] and cert_init['issuer'].find("Let's Encrypt") == -1: - continue # 非同品牌证书 - except: - continue - - if isinstance(cert_init['dns'], str): - cert_init['dns'] = [cert_init['dns']] - index = self.getIndex(cert_init['dns']) - if index in self.__config['orders'].keys(): - continue # 已在订单列表 - - n += 1 - writeLog( - "|-正在续签第 {} 张其它证书,域名: {}..".format(n, cert_init['subject'])) - writeLog("|-正在创建订单..") - args.id = siteInfo['id'] - runPath = siteObj.GetRunPath(args) - if runPath and not runPath in ['/']: - path = siteInfo['path'] + '/' + runPath - else: - path = siteInfo['path'] - except: - write_log("|-[{}]续签失败".format(siteName)) - - # 外部API - START ---------------------------------------------------------- - def applyCertApi(self, args): - ''' - 申请证书 - api - ''' - return self.applyCert(args['domains'], args['auth_type'], args['auth_to']) - # 外部API - END ---------------------------------------------------------- - - def renewCert(self, index): - writeLog("", "wb+") - # self.D('renew_cert', index) - try: - order_index = [] - if index: - if type(index) != str: - index = index.index - if not index in self.__config['orders']: - raise Exception("指定订单号不存在,无法续签!") - order_index.append(index) - else: - start_time = time.time() + (30 * 86400) - # print(self.__config) - if not 'orders' in self.__config: - self.__config['orders'] = {} - - for i in self.__config['orders'].keys(): - # print(self.__config['orders'][i]) - if not 'save_path' in self.__config['orders'][i]: - continue - - if 'cert' in self.__config['orders'][i]: - self.__config['orders'][i]['cert_timeout'] = self.__config[ - 'orders'][i]['cert']['cert_timeout'] - - if not 'cert_timeout' in self.__config['orders'][i]: - self.__config['orders'][i][ - 'cert_timeout'] = int(time.time()) - - if self.__config['orders'][i]['cert_timeout'] > start_time: - writeLog( - "|-本次跳过域名: {},未过期!".format(self.__config['orders'][i]['domains'][0])) - continue - - # 已删除的网站直接跳过续签 - if self.__config['orders'][i]['auth_to'].find('|') == -1 and self.__config['orders'][i]['auth_to'].find('/') != -1: - if not os.path.exists(self.__config['orders'][i]['auth_to']): - auth_to = self.getSslUsedSite( - self.__config['orders'][i]['save_path']) - if not auth_to: - continue - - # 域名不存在? - for domain in self.__config['orders'][i]['domains']: - if domain.find('*') != -1: - break - if not mw.M('domain').where("name=?", (domain,)).count() and not mw.M('binding').where("domain=?", domain).count(): - auth_to = None - writeLog( - "|-跳过被删除的域名: {}".format(self.__config['orders'][i]['domains'])) - if not auth_to: - continue - - self.__config['orders'][i]['auth_to'] = auth_to - - # 是否到了允许重试的时间 - if 'next_retry_time' in self._config['orders'][i]: - timeout = self.__config['orders'][i][ - 'next_retry_time'] - int(time.time()) - if timeout > 0: - writeLog('|-本次跳过域名:{},因第上次续签失败,还需要等待{}小时后再重试'.format( - self.__config['orders'][i]['domains'], int(timeout / 60 / 60))) - continue - - # 是否到了最大重试次数 - if 'retry_count' in self.__config['orders'][i]: - if self.__config['orders'][i]['retry_count'] >= 5: - writeLog('|-本次跳过域名:{},因连续5次续签失败,不再续签此证书(可尝试手动续签此证书,成功后错误次数将被重置)'.format( - self.__config['orders'][i]['domains'])) - continue - - # 加入到续签订单 - order_index.append(i) - if not order_index: - writeLog("|-没有找到30天内到期的SSL证书,正在尝试去寻找其它可续签证书!") - self.getApis() - # self.renewCertOther() - writeLog("|-所有任务已处理完成!") - return - writeLog("|-共需要续签 {} 张证书".format(len(order_index))) - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - return mw.returnJson(False, msg) - - def revokeOrder(self, index): - if not index in self.__config['orders']: - raise Exception("指定订单不存在!") - cert_path = self.__config['orders'][index]['save_path'] - if not os.path.exists(cert_path): - raise Exception("指定订单没有找到可用的证书!") - cert = self.dumpDer(cert_path) - if not cert: - raise Exception("证书读取失败!") - payload = { - "certificate": self.calculateSafeBase64(cert), - "reason": 4 - } - - self.getApis() - res = self.acmeRequest(self.__apis['revokeCert'], payload) - if res.status_code in [200, 201]: - if os.path.exists(cert_path): - mw.execShell("rm -rf {}".format(cert_path)) - del(self.__config['orders'][index]) - self.saveConfig() - return mw.returnJson(True, "证书吊销成功!") - return res.json() - - def do(self, args): - cert = None - try: - if not args.index: - if not args.domains: - echoErr("请在--domain参数中指定要申请证书的域名,多个以逗号(,)隔开") - if not args.auth_type in ['http', 'tls']: - echoErr("请在--type参数中指定正确的验证类型,http") - - auth_to = '' - if args.auth_type in ['http', 'tls']: - if not args.path: - echoErr("请在--path参数中指定网站根目录!") - if not os.path.exists(args.path): - echoErr("指定网站根目录不存在,请检查:{}".format(args.path)) - auth_to = args.path - else: - echoErr("仅支持文件验证!") - exit() - - domains = args.domains.strip().split(',') - cert = self.applyCert( - domains, auth_type=args.auth_type, auth_to=auth_to, args=args) - else: - # 重新验证 - cert = self.applyCert([], auth_type='dns', - auth_to='dns', index=args.index) - except Exception as e: - writeLog("|-{}".format(mw.getTracebackInfo())) - exit() - - if not cert: - exit() - - # print(cert) - if not cert['status']: - writeLog('|-' + cert['msg'][0]) - exit() - writeLog("=" * 65) - writeLog("|-证书获取成功!") - writeLog("=" * 65) - writeLog("证书到期时间: {}".format( - mw.formatDate(times=cert['cert_timeout']))) - writeLog("证书已保存在: {}/".format(cert['save_path'])) - - -# exp: -''' -// create -python3 class/core/cert_request.py --domain=dev38.cachecha.com --type=http --path=/www/wwwroot/dev38.cachecha.com -// renew -cd /www/server/mdserver-web && python3 class/core/cert_request.py --renew=1 -// revoke -cd /www/server/mdserver-web && python3 class/core/cert_request.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a - -python3 class/core/cert_request.py --domain=dev38.cachecha.com --type=http --path=/Users/midoks/Desktop/mwdev/wwwroot/test -python3 class/core/cert_request.py --renew=1 -python3 class/core/cert_request.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a -''' -if __name__ == "__main__": - p = argparse.ArgumentParser(usage="必要的参数:--domain 域名列表,多个以逗号隔开!") - p.add_argument('--domain', default=None, - help="请指定要申请证书的域名", dest="domains") - p.add_argument('--type', default=None, help="请指定验证类型", dest="auth_type") - p.add_argument('--path', default=None, help="请指定网站根目录", dest="path") - p.add_argument('--index', default=None, help="指定订单索引", dest="index") - p.add_argument('--renew', default=None, help="续签证书", dest="renew") - p.add_argument('--revoke', default=None, help="吊销证书", dest="revoke") - - args = p.parse_args() - cr = cert_request() - - if args.revoke: - if not args.index: - echoErr("请在--index参数中传入要被吊销的订单索引") - result = cr.revokeOrder(args.index) - writeLog(result) - exit() - - if args.renew: - cr.renewCert(args.index) - exit() - - cr.do(args) diff --git a/class/core/common.py b/class/core/common.py deleted file mode 100755 index 9e91a4811..000000000 --- a/class/core/common.py +++ /dev/null @@ -1,146 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 公共操作 -# --------------------------------------------------------------------------------- - -import os -import sys -import time -import string -import json -import hashlib -import shlex -import datetime -import subprocess -import re -import hashlib -from random import Random - -import mw -import db - -from flask import redirect - - -def init(): - initDB() - initUserInfo() - initInitD() - initInitTask() - return True - - -def local(): - result = checkClose() - if result: - return result - - -# 检查面板是否关闭 -def checkClose(): - if os.path.exists('data/close.pl'): - return redirect('/close') - - -def initDB(): - try: - sql = db.Sql().dbfile('default') - csql = mw.readFile('data/sql/default.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - - except Exception as ex: - print(str(ex)) - - -def doContentReplace(src, dst): - content = mw.readFile(src) - content = content.replace("{$SERVER_PATH}", mw.getRunDir()) - mw.writeFile(dst, content) - - -def initInitD(): - - # systemctl - # sysCfgDir = mw.systemdCfgDir() - # if os.path.exists(sysCfgDir) and mw.getOsName() == 'centos' and mw.getOsID() == '9': - # systemd_mw = sysCfgDir + '/mw.service' - # systemd_mw_task = sysCfgDir + '/mw-task.service' - - # systemd_mw_tpl = mw.getRunDir() + '/scripts/init.d/mw.service.tpl' - # systemd_mw_task_tpl = mw.getRunDir() + '/scripts/init.d/mw-task.service.tpl' - - # if os.path.exists(systemd_mw): - # os.remove(systemd_mw) - # if os.path.exists(systemd_mw_task): - # os.remove(systemd_mw_task) - - # doContentReplace(systemd_mw_tpl, systemd_mw) - # doContentReplace(systemd_mw_task_tpl, systemd_mw_task) - - # mw.execShell('systemctl enable mw') - # mw.execShell('systemctl enable mw-task') - # mw.execShell('systemctl daemon-reload') - - script = mw.getRunDir() + '/scripts/init.d/mw.tpl' - script_bin = mw.getRunDir() + '/scripts/init.d/mw' - doContentReplace(script, script_bin) - mw.execShell('chmod +x ' + script_bin) - - # 在linux系统中,确保/etc/init.d存在 - if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"): - mw.execShell('mkdir -p /etc/rc.d/init.d') - - if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"): - mw.execShell('mkdir -p /etc/init.d') - - # initd - if os.path.exists('/etc/rc.d/init.d'): - initd_bin = '/etc/rc.d/init.d/mw' - if not os.path.exists(initd_bin): - import shutil - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) - # 加入自启动 - mw.execShell('which chkconfig && chkconfig --add mw') - - if os.path.exists('/etc/init.d'): - initd_bin = '/etc/init.d/mw' - if not os.path.exists(initd_bin): - import shutil - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) - # 加入自启动 - mw.execShell('which update-rc.d && update-rc.d -f mw defaults') - - # 获取系统IPV4 - mw.setHostAddr(mw.getLocalIp()) - - -def initInitTask(): - # 创建证书同步命令 - import cert_api - api = cert_api.cert_api() - api.createCertCron() - - -def initUserInfo(): - - data = mw.M('users').where('id=?', (1,)).getField('password') - if data == '21232f297a57a5a743894a0e4a801fc3': - - pwd = mw.getRandomString(8).lower() - file_pw = mw.getRunDir() + '/data/default.pl' - mw.writeFile(file_pw, pwd) - mw.M('users').where('id=?', (1,)).setField( - 'password', mw.md5(pwd)) diff --git a/class/core/config_api.py b/class/core/config_api.py deleted file mode 100755 index 9a89bd4d0..000000000 --- a/class/core/config_api.py +++ /dev/null @@ -1,1191 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 配置操作 -# --------------------------------------------------------------------------------- - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd -import pytz - -from flask import session -from flask import request - - -class config_api: - - __version = '0.17.4' - __api_addr = 'data/api.json' - - # 统一默认配置文件 - __file = { - 'api' : 'data/api.json', # API文件 - 'debug' : 'data/debug.pl', # DEBUG文件 - 'close' : 'data/close.pl', # 识别关闭面板文件 - 'basic_auth' : 'data/basic_auth.json', # 面板Basic验证 - 'admin_path' : 'data/admin_path.pl', # 面板后缀路径设置 - 'ipv6' : 'data/ipv6.pl', # ipv6识别文件 - 'bind_domain' : 'data/bind_domain.pl', # 面板域名绑定 - 'unauth_status' : 'data/unauthorized_status.pl', # URL路径未成功显示状态 - 'auth_secret': 'data/auth_secret.pl', # 二次验证密钥 - 'ssl':'ssl/choose.pl', # ssl设置 - 'hook_database' : 'data/hook_database.json', # 数据库钩子 - 'hook_menu' : 'data/hook_menu.json', # 菜单钩子 - 'hook_global_static' : 'data/hook_global_static.json', # 静态文件钩子 - } - - def __init__(self): - pass - - def getVersion(self): - return self.__version - - ##### ----- start ----- ### - - # 取面板列表 - def getPanelListApi(self): - data = mw.M('panel').field( - 'id,title,url,username,password,click,addtime').order('click desc').select() - return mw.getJson(data) - - def addPanelInfoApi(self): - title = request.form.get('title', '') - url = request.form.get('url', '') - username = request.form.get('username', '') - password = request.form.get('password', '') - # 校验是还是重复 - isAdd = mw.M('panel').where( - 'title=? OR url=?', (title, url)).count() - if isAdd: - return mw.returnJson(False, '备注或面板地址重复!') - isRe = mw.M('panel').add('title,url,username,password,click,addtime', - (title, url, username, password, 0, int(time.time()))) - if isRe: - return mw.returnJson(True, '添加成功!') - return mw.returnJson(False, '添加失败!') - - # 删除面板资料 - def delPanelInfoApi(self): - mid = request.form.get('id', '') - isExists = mw.M('panel').where('id=?', (mid,)).count() - if not isExists: - return mw.returnJson(False, '指定面板资料不存在!') - mw.M('panel').where('id=?', (mid,)).delete() - return mw.returnJson(True, '删除成功!') - - # 修改面板资料 - def setPanelInfoApi(self): - title = request.form.get('title', '') - url = request.form.get('url', '') - username = request.form.get('username', '') - password = request.form.get('password', '') - mid = request.form.get('id', '') - # 校验是还是重复 - isSave = mw.M('panel').where( - '(title=? OR url=?) AND id!=?', (title, url, mid)).count() - if isSave: - return mw.returnJson(False, '备注或面板地址重复!') - - # 更新到数据库 - isRe = mw.M('panel').where('id=?', (mid,)).save( - 'title,url,username,password', (title, url, username, password)) - if isRe: - return mw.returnJson(True, '修改成功!') - return mw.returnJson(False, '修改失败!') - - def syncDateApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发系统不必同步时间!') - - data = mw.execShell('ntpdate -s time.nist.gov') - if data[0] == '': - return mw.returnJson(True, '同步成功!') - return mw.returnJson(False, '同步失败:' + data[0]) - - def setPasswordApi(self): - password1 = request.form.get('password1', '') - password2 = request.form.get('password2', '') - if password1 != password2: - return mw.returnJson(False, '两次输入的密码不一致,请重新输入!') - if len(password1) < 5: - return mw.returnJson(False, '用户密码不能小于5位!') - mw.M('users').where("username=?", (session['username'],)).setField( - 'password', mw.md5(password1.strip())) - return mw.returnJson(True, '密码修改成功!') - - def setNameApi(self): - name1 = request.form.get('name1', '') - name2 = request.form.get('name2', '') - if name1 != name2: - return mw.returnJson(False, '两次输入的用户名不一致,请重新输入!') - if len(name1) < 3: - return mw.returnJson(False, '用户名长度不能少于3位') - - mw.M('users').where("username=?", (session['username'],)).setField( - 'username', name1.strip()) - - session['username'] = name1 - return mw.returnJson(True, '用户修改成功!') - - def setWebnameApi(self): - webname = request.form.get('webname', '') - if webname != mw.getConfig('title'): - mw.setConfig('title', webname) - return mw.returnJson(True, '面板别名保存成功!') - - def setPortApi(self): - port = request.form.get('port', '') - if port != mw.getHostPort(): - import system_api - import firewall_api - - sysCfgDir = mw.systemdCfgDir() - if os.path.exists(sysCfgDir + "/firewalld.service"): - if not firewall_api.firewall_api().getFwStatus(): - return mw.returnJson(False, 'firewalld必须先启动!') - - mw.setHostPort(port) - - msg = mw.getInfo('放行端口[{1}]成功', (port,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime)) - - firewall_api.firewall_api().addAcceptPort(port) - firewall_api.firewall_api().firewallReload() - - system_api.system_api().restartMw() - - return mw.returnJson(True, '端口保存成功!') - - def setIpApi(self): - host_ip = request.form.get('host_ip', '') - if host_ip != mw.getHostAddr(): - mw.setHostAddr(host_ip) - return mw.returnJson(True, 'IP保存成功!') - - def setWwwDirApi(self): - sites_path = request.form.get('sites_path', '') - if sites_path != mw.getWwwDir(): - mw.setWwwDir(sites_path) - return mw.returnJson(True, '修改默认建站目录成功!') - - def setBackupDirApi(self): - backup_path = request.form.get('backup_path', '') - if backup_path != mw.getBackupDir(): - mw.setBackupDir(backup_path) - return mw.returnJson(True, '修改默认备份目录成功!') - - def setBasicAuthApi(self): - basic_user = request.form.get('basic_user', '').strip() - basic_pwd = request.form.get('basic_pwd', '').strip() - basic_open = request.form.get('is_open', '').strip() - - salt = '_md_salt' - path = self.__file['basic_auth'] - is_open = True - - if basic_open == 'false': - if os.path.exists(path): - os.remove(path) - return mw.returnJson(True, '删除BasicAuth成功!') - - if basic_user == '' or basic_pwd == '': - return mw.returnJson(True, '用户和密码不能为空!') - - ba_conf = None - if os.path.exists(path): - try: - ba_conf = json.loads(public.readFile(path)) - except: - os.remove(path) - - if not ba_conf: - ba_conf = { - "basic_user": mw.md5(basic_user + salt), - "basic_pwd": mw.md5(basic_pwd + salt), - "open": is_open - } - else: - ba_conf['basic_user'] = mw.md5(basic_user + salt) - ba_conf['basic_pwd'] = mw.md5(basic_pwd + salt) - ba_conf['open'] = is_open - - mw.writeFile(path, json.dumps(ba_conf)) - os.chmod(path, 384) - mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open) - - mw.restartMw() - return mw.returnJson(True, '设置成功!') - - def setApi(self): - webname = request.form.get('webname', '') - port = request.form.get('port', '') - host_ip = request.form.get('host_ip', '') - domain = request.form.get('domain', '') - sites_path = request.form.get('sites_path', '') - backup_path = request.form.get('backup_path', '') - - if domain != '': - reg = "^([\\w\\-\\*]{1,100}\\.){1,4}(\\w{1,10}|\\w{1,10}\\.\\w{1,10})$" - if not re.match(reg, domain): - return mw.returnJson(False, '主域名格式不正确') - - if int(port) >= 65535 or int(port) < 100: - return mw.returnJson(False, '端口范围不正确!') - - if webname != mw.getConfig('title'): - mw.setConfig('title', webname) - - if sites_path != mw.getWwwDir(): - mw.setWwwDir(sites_path) - - if backup_path != mw.getWwwDir(): - mw.setBackupDir(backup_path) - - if port != mw.getHostPort(): - import system_api - import firewall_api - - sysCfgDir = mw.systemdCfgDir() - if os.path.exists(sysCfgDir + "/firewalld.service"): - if not firewall_api.firewall_api().getFwStatus(): - return mw.returnJson(False, 'firewalld必须先启动!') - - mw.setHostPort(port) - - msg = mw.getInfo('放行端口[{1}]成功', (port,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime)) - - firewall_api.firewall_api().addAcceptPort(port) - firewall_api.firewall_api().firewallReload() - - system_api.system_api().restartMw() - - if host_ip != mw.getHostAddr(): - mw.setHostAddr(host_ip) - - mhost = mw.getHostAddr() - info = { - 'uri': '/config', - 'host': mhost + ':' + port - } - return mw.returnJson(True, '保存成功!', info) - - def setAdminPathApi(self): - admin_path = request.form.get('admin_path', '').strip() - admin_path_checks = ['/', '/close', '/login', - '/do_login', '/site', '/sites', - '/download_file', '/control', '/crontab', - '/firewall', '/files', 'config', - '/soft', '/system', '/code', - '/ssl', '/plugins', '/hook'] - if admin_path == '': - admin_path = '/' - if admin_path != '/': - if len(admin_path) < 6: - return mw.returnJson(False, '安全入口地址长度不能小于6位!') - if admin_path in admin_path_checks: - return mw.returnJson(False, '该入口已被面板占用,请使用其它入口!') - if not re.match("^/[\\w\\./-_]+$", admin_path): - return mw.returnJson(False, '入口地址格式不正确,示例: /mw_rand') - # else: - # domain = mw.readFile('data/bind_domain.pl') - # if not domain: - # domain = '' - # limitip = mw.readFile('data/bind_limitip.pl') - # if not limitip: - # limitip = '' - # if not domain.strip() and not limitip.strip(): - # return mw.returnJson(False, - # '警告,关闭安全入口等于直接暴露你的后台地址在外网,十分危险,至少开启以下一种安全方式才能关闭:
                    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 + '

                    响应状态:' + str(emsg['status']) + '

                    错误类型:' + emsg[ - # 'type'] + '

                    错误代码:' + emsg['detail'] + '

                    ' - # return mw.returnJson(data['status'], msg, data['msg']) - # else: - # is_already_apply = True - - # mw.buildSoftLink(src_csrpath, dst_csrpath, True) - # mw.buildSoftLink(src_keypath, dst_keypath, True) - # mw.execShell('echo "acme" > "' + dst_path + '/README"') - - # tmp_well_know = auth_to + '/.well-known' - # if os.path.exists(tmp_well_know): - # mw.execShell('rm -rf ' + tmp_well_know) - - # if os.path.exists(dst_path): - # choose_file = self.__file['ssl'] - # mw.writeFile(choose_file, 'nginx') - - # data = self.getPanelSslData() - - # if is_already_apply: - # return mw.returnJson(True, '重复申请!', data) - # return mw.returnJson(True, '申请成功!', data) - - def setPanelDomainApi(self): - domain = request.form.get('domain', '') - - cfg_domain = self.__file['bind_domain'] - - port = mw.readFile('data/port.pl').strip() - ip = mw.getLocalIp() - - if domain == '': - if os.path.exists(cfg_domain): - os.remove(cfg_domain) - - to_panel_url = 'http://'+ip+":"+port+'/config' - - mw.restartMw() - return mw.returnJson(True, '清空域名成功!', to_panel_url) - - mw.writeFile(cfg_domain, domain) - to_panel_url = 'http://'+domain+":"+port+'/config' - mw.restartMw() - return mw.returnJson(True, '设置域名成功!',to_panel_url) - - # 设置面板SSL - # def setPanelSslApi(self): - # sslConf = mw.getRunDir() + '/' + self.__file['ssl'] - - # panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf" - # dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" - # if os.path.exists(sslConf): - # os.system('rm -f ' + sslConf) - - # conf = mw.readFile(dst_panel_path) - # if 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 = "\\s+ssl_session_timeout\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_ecdh_curve\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_session_tickets\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_stapling\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_stapling_verify\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl\\s+on;" - # conf = re.sub(rep, '', conf) - # rep = "\\s+error_page\\s497.+;" - # conf = re.sub(rep, '', conf) - # rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}" - # conf = re.sub(rep, '', conf) - # rep = "\\s+listen\\s+443.*;" - # conf = re.sub(rep, '', conf) - # rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;" - # conf = re.sub(rep, '', conf) - # mw.writeFile(dst_panel_path, conf) - - # mw.writeLog('面板配置', '面板SSL关闭成功!') - # mw.restartWeb() - # return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!') - # else: - # try: - # mw.writeFile(sslConf, 'True') - - # keyPath = mw.getRunDir() + '/ssl/private.pem' - # certPath = mw.getRunDir() + '/ssl/cert.pem' - - # conf = mw.readFile(dst_panel_path) - # if conf: - # if conf.find('ssl_certificate') == -1: - # sslStr = """#error_page 404/404.html; - # ssl_certificate %s; - # ssl_certificate_key %s; - # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; - # ssl_prefer_server_ciphers on; - # ssl_session_cache shared:SSL:10m; - # ssl_session_timeout 10m; - # error_page 497 https://$host$request_uri;""" % (certPath, keyPath) - # if(conf.find('ssl_certificate') != -1): - # return mw.returnJson(True, 'SSL开启成功!') - - # conf = conf.replace('#error_page 404/404.html;', sslStr) - - # rep = "listen\\s+([0-9]+)\\s*[default_server]*;" - # tmp = re.findall(rep, conf) - # if not mw.inArray(tmp, '443'): - # listen = re.search(rep, conf).group() - # 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(dst_panel_path) - # mw.writeFile(dst_panel_path, conf) - # isError = mw.checkWebConfig() - # if(isError != True): - # mw.restoreFile(dst_panel_path) - # return mw.returnJson(False, '证书错误:
                    ' + 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, 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 d9556241a..000000000 --- a/class/core/crontab_api.py +++ /dev/null @@ -1,692 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 计划任务操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - -from flask import request - - -class crontab_api: - - field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress' - - def __init__(self): - pass - - ##### ----- start ----- ### - def listApi(self): - p = request.args.get('p', 1) - psize = 10 - - startPage = (int(p) - 1) * psize - pageInfo = str(startPage) + ',' + str(psize) - - _list = mw.M('crontab').where('', ()).field(self.field).limit(pageInfo).order('id desc').select() - - data = [] - for i in range(len(_list)): - tmp = _list[i] - if _list[i]['type'] == "day": - tmp['type'] = '每天' - tmp['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str( - _list[i]['where_hour']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "day-n": - tmp['type'] = mw.getInfo( - '每{1}天', (str(_list[i]['where1']),)) - tmp['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str( - _list[i]['where1']), str(_list[i]['where_hour']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "hour": - tmp['type'] = '每小时' - tmp['cycle'] = mw.getInfo( - '每小时, 第{1}分钟 执行', (str(_list[i]['where_minute']),)) - elif _list[i]['type'] == "hour-n": - tmp['type'] = mw.getInfo( - '每{1}小时', (str(_list[i]['where1']),)) - tmp['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str( - _list[i]['where1']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "minute-n": - tmp['type'] = mw.getInfo( - '每{1}分钟', (str(_list[i]['where1']),)) - tmp['cycle'] = mw.getInfo( - '每隔{1}分钟执行', (str(_list[i]['where1']),)) - elif _list[i]['type'] == "week": - tmp['type'] = '每周' - if not _list[i]['where1']: - _list[i]['where1'] = '0' - tmp['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int( - _list[i]['where1'])), str(_list[i]['where_hour']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "month": - tmp['type'] = '每月' - tmp['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(_list[i]['where1']), str( - _list[i]['where_hour']), str(_list[i]['where_minute']))) - data.append(tmp) - - rdata = {} - rdata['data'] = data - - count = mw.M('crontab').where('', ()).count() - _page = {} - _page['count'] = count - _page['p'] = p - _page['row'] = psize - _page['tojs'] = "getCronData" - - rdata['list'] = mw.getPage(_page) - rdata['p'] = p - - # backup hook - bh_file = mw.getPanelDataDir() + "/hook_backup.json" - if os.path.exists(bh_file): - hb_data = mw.readFile(bh_file) - hb_data = json.loads(hb_data) - rdata['backup_hook'] = hb_data - - return mw.getJson(rdata) - - # 设置计划任务状态 - def setCronStatusApi(self): - mid = request.form.get('id', '') - cronInfo = mw.M('crontab').where( - 'id=?', (mid,)).field(self.field).find() - status = 1 - if cronInfo['status'] == status: - status = 0 - self.removeForCrond(cronInfo['echo']) - else: - cronInfo['status'] = 1 - self.syncToCrond(cronInfo) - - mw.M('crontab').where('id=?', (mid,)).setField('status', status) - mw.writeLog( - '计划任务', '修改计划任务[' + cronInfo['name'] + ']状态为[' + str(status) + ']') - return mw.returnJson(True, '设置成功') - - # 获取指定任务数据 - def getCrondFindApi(self): - sid = request.form.get('id', '') - data = mw.M('crontab').where( - 'id=?', (sid,)).field(self.field).find() - return mw.getJson(data) - - # 参数校验 - def cronCheck(self, params): - - if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path': - if params['save'] == '': - return False, '保留份数不能为空!' - - if params['type'] == 'day': - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'day-n': - if params['where1'] == '': - return False, '天不能为空!' - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - if params['type'] == 'hour': - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'hour-n': - if params['where1'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'minute-n': - if params['where1'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'week': - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'month': - if params['where1'] == '': - return False, '日不能为空!' - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - return True, 'OK' - - def modifyCrondApi(self): - sid = request.form.get('id', '') - iname = request.form.get('name', '') - field_type = request.form.get('type', '') - week = request.form.get('week', '') - where1 = request.form.get('where1', '') - hour = request.form.get('hour', '') - minute = request.form.get('minute', '') - save = request.form.get('save', '') - backup_to = request.form.get('backup_to', '') - stype = request.form.get('stype', '') - sname = request.form.get('sname', '') - sbody = request.form.get('sbody', '') - urladdress = request.form.get('urladdress', '') - - if len(iname) < 1: - return mw.returnJson(False, '任务名称不能为空!') - - params = { - 'name': iname, - 'type': field_type, - 'week': week, - 'where1': where1, - 'hour': hour, - 'minute': minute, - 'save': save, - 'backup_to': backup_to, - 'stype': stype, - 'sname': sname, - 'sbody': sbody, - 'urladdress': urladdress, - } - - is_check_pass, msg = self.cronCheck(params) - if not is_check_pass: - return mw.returnJson(is_check_pass, msg) - - cuonConfig, get, name = self.getCrondCycle(params) - cronInfo = mw.M('crontab').where( - 'id=?', (sid,)).field(self.field).find() - del(cronInfo['id']) - del(cronInfo['addtime']) - cronInfo['name'] = get['name'] - cronInfo['type'] = get['type'] - cronInfo['where1'] = get['where1'] - cronInfo['where_hour'] = get['hour'] - cronInfo['where_minute'] = get['minute'] - cronInfo['save'] = get['save'] - cronInfo['backup_to'] = get['backup_to'] - cronInfo['sbody'] = get['sbody'] - cronInfo['urladdress'] = get['urladdress'] - - addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to, sname, sbody,urladdress', - (iname, field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], sname, get['sbody'], get['urladdress'])) - self.removeForCrond(cronInfo['echo']) - self.syncToCrond(cronInfo) - mw.writeLog('计划任务', '修改计划任务[' + cronInfo['name'] + ']成功') - return mw.returnJson(True, '修改成功') - - def logsApi(self): - sid = request.form.get('id', '') - echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find() - logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log' - if not os.path.exists(logFile): - return mw.returnJson(False, '当前日志为空!') - log = mw.getLastLine(logFile, 500) - return mw.returnJson(True, log) - - def addApi(self): - iname = request.form.get('name', '') - field_type = request.form.get('type', '') - week = request.form.get('week', '') - where1 = request.form.get('where1', '') - hour = request.form.get('hour', '') - minute = request.form.get('minute', '') - save = request.form.get('save', '') - backup_to = request.form.get('backupTo', '') - stype = request.form.get('sType', '') - sname = request.form.get('sName', '') - sbody = request.form.get('sBody', '') - urladdress = request.form.get('urladdress', '') - - if len(iname) < 1: - return mw.returnJson(False, '任务名称不能为空!') - - params = { - 'name': iname, - 'type': field_type, - 'week': week, - 'where1': where1, - 'hour': hour, - 'minute': minute, - 'save': save, - 'backup_to': backup_to, - 'stype': stype, - 'sname': sname, - 'sbody': sbody, - 'urladdress': urladdress, - } - - is_check_pass, msg = self.cronCheck(params) - if not is_check_pass: - return mw.returnJson(is_check_pass, msg) - - addData = self.add(params) - if type(addData) == str: - return addData - - if addData > 0: - return mw.returnJson(True, '添加成功') - return mw.returnJson(False, '添加失败') - - def add(self, params): - - iname = params["name"] - field_type = params["type"] - week = params["week"] - where1 = params["where1"] - hour = params["hour"] - minute = params["minute"] - save = params["save"] - backup_to = params["backup_to"] - stype = params["stype"] - sname = params["sname"] - sbody = params["sbody"] - urladdress = params["urladdress"] - - # print params - cronConfig, get, name = self.getCrondCycle(params) - cronPath = mw.getServerDir() + '/cron' - cronName = self.getShell(params) - - if type(cronName) == dict: - return cronName - - cronConfig += ' ' + cronPath + '/' + cronName + \ - ' >> ' + cronPath + '/' + cronName + '.log 2>&1' - - # print(cronConfig) - if not mw.isAppleSystem(): - wRes = self.writeShell(cronConfig) - if type(wRes) != bool: - return wRes - self.crondReload() - - add_time = time.strftime('%Y-%m-%d %X', time.localtime()) - task_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress', - (iname, field_type, where1, hour, minute, cronName, add_time, 1, save, backup_to, stype, sname, sbody, urladdress,)) - return task_id - - def startTaskApi(self): - sid = request.form.get('id', '') - echo = mw.M('crontab').where('id=?', (sid,)).getField('echo') - execstr = mw.getServerDir() + '/cron/' + echo - os.system('chmod +x ' + execstr) - os.system('nohup ' + execstr + ' >> ' + execstr + '.log 2>&1 &') - return mw.returnJson(True, '任务已执行!') - - def delApi(self): - task_id = request.form.get('id', '') - try: - data = self.delete(task_id) - if not data[0]: - return mw.returnJson(False, data[1]) - return mw.returnJson(True, '删除成功') - except Exception as e: - return mw.returnJson(False, '删除失败:' + str(e)) - - def delete(self, tid): - - find = mw.M('crontab').where("id=?", (tid,)).field('name,echo').find() - if not self.removeForCrond(find['echo']): - return (False, '无法写入文件,请检查是否开启了系统加固功能!') - - cronPath = mw.getServerDir() + '/cron' - sfile = cronPath + '/' + find['echo'] - - if os.path.exists(sfile): - os.remove(sfile) - sfile = cronPath + '/' + find['echo'] + '.log' - if os.path.exists(sfile): - os.remove(sfile) - - mw.M('crontab').where("id=?", (tid,)).delete() - mw.writeLog('计划任务', mw.getInfo('删除计划任务[{1}]成功!', (find['name'],))) - return (True, "OK") - - def delLogsApi(self): - sid = request.form.get('id', '') - try: - echo = mw.M('crontab').where("id=?", (sid,)).getField('echo') - logFile = mw.getServerDir() + '/cron/' + echo + '.log' - os.remove(logFile) - return mw.returnJson(True, '任务日志已清空!') - except: - return mw.returnJson(False, '任务日志清空失败!') - - # 取数据列表 - def getDataListApi(self): - stype = request.form.get('type', '') - - bak_data = [] - if stype == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path': - hookPath = mw.getPanelDataDir() + "/hook_backup.json" - if os.path.exists(hookPath): - t = mw.readFile(hookPath) - bak_data = json.loads(t) - - if stype == 'database' or stype.find('database_') > -1: - sqlite3_name = 'mysql' - path = mw.getServerDir() + '/mysql' - if stype != 'database': - soft_name = stype.replace('database_', '') - path = mw.getServerDir() + '/' + soft_name - - if soft_name == 'postgresql': - sqlite3_name = 'pgsql' - - if soft_name == 'mongodb': - sqlite3_name = 'mongodb' - - db_list = {} - db_list['orderOpt'] = bak_data - - if not os.path.exists(path + '/' + sqlite3_name + '.db'): - db_list['data'] = [] - else: - db_list['data'] = mw.M('databases').dbPos(path, sqlite3_name).field('name,ps').select() - return mw.getJson(db_list) - - if stype == 'path': - db_list = {} - db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}] - db_list['orderOpt'] = bak_data - return mw.getJson(db_list) - - data = {} - data['orderOpt'] = bak_data - - default_db = 'sites' - # if stype == 'site' or stype == 'logs': - # stype == 'sites' - - data['data'] = mw.M(default_db).field('name,ps').select() - return mw.getJson(data) - ##### ----- start ----- ### - - # 转换大写星期 - def toWeek(self, num): - wheres = { - 0: '日', - 1: '一', - 2: '二', - 3: '三', - 4: '四', - 5: '五', - 6: '六' - } - try: - return wheres[num] - except: - return '' - - def getCrondCycle(self, params): - cuonConfig = '' - name = '' - if params['type'] == "day": - cuonConfig = self.getDay(params) - name = '每天' - elif params['type'] == "day-n": - cuonConfig = self.getDay_N(params) - name = mw.getInfo('每{1}天', (params['where1'],)) - elif params['type'] == "hour": - cuonConfig = self.getHour(params) - name = '每小时' - elif params['type'] == "hour-n": - cuonConfig = self.getHour_N(params) - name = '每小时' - elif params['type'] == "minute-n": - cuonConfig = self.minute_N(params) - elif params['type'] == "week": - params['where1'] = params['week'] - cuonConfig = self.week(params) - elif params['type'] == "month": - cuonConfig = self.month(params) - return cuonConfig, params, name - - # 取任务构造Day - def getDay(self, param): - cuonConfig = "{0} {1} * * * ".format(param['minute'], param['hour']) - return cuonConfig - # 取任务构造Day_n - - def getDay_N(self, param): - cuonConfig = "{0} {1} */{2} * * ".format( - param['minute'], param['hour'], param['where1']) - return cuonConfig - - # 取任务构造Hour - def getHour(self, param): - cuonConfig = "{0} * * * * ".format(param['minute']) - return cuonConfig - - # 取任务构造Hour-N - def getHour_N(self, param): - cuonConfig = "{0} */{1} * * * ".format( - param['minute'], param['where1']) - return cuonConfig - - # 取任务构造Minute-N - def minute_N(self, param): - cuonConfig = "*/{0} * * * * ".format(param['where1']) - return cuonConfig - - # 取任务构造week - def week(self, param): - cuonConfig = "{0} {1} * * {2}".format( - param['minute'], param['hour'], param['week']) - return cuonConfig - - # 取任务构造Month - def month(self, param): - cuonConfig = "{0} {1} {2} * * ".format(param['minute'], param['hour'], param['where1']) - return cuonConfig - - # 取执行脚本 - def getShell(self, param): - # try: - stype = param['stype'] - if stype == 'toFile': - shell = param.sFile - else: - head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n" - - start_head = ''' -SCRIPT_RUN_TIME="0s" -MW_ToSeconds() -{ - SEC=$1 - if [ $SEC -lt 60 ]; then - SCRIPT_RUN_TIME="${SEC}s" - elif [ $SEC -ge 60 ] && [ $SEC -lt 3600 ];then - SCRIPT_RUN_TIME="$(( SEC / 60 ))m$(( SEC % 60 ))s" - elif [ $SEC -ge 3600 ]; then - SCRIPT_RUN_TIME="$(( SEC / 3600 ))h$(( (SEC % 3600) / 60 ))m$(( (SEC % 3600) % 60 ))s" - fi -} -START_MW_SHELL_TIME=`date +%s` -''' - - source_bin_activate = ''' -export LANG=en_US.UTF-8 -MW_PATH=%s/bin/activate -if [ -f $MW_PATH ];then - source $MW_PATH -fi''' % (mw.getRunDir(),) - - head = head + start_head + source_bin_activate + "\n" - log = '.log' - - #所有 - if param['sname'] == 'ALL': - log = '' - - script_dir = mw.getRunDir() + "/scripts" - source_stype = 'database' - if stype.find('database_') > -1: - plugin_name = stype.replace('database_', '') - script_dir = mw.getRunDir() + "/plugins/" + plugin_name + "/scripts" - - source_stype = stype - stype = 'database' - - wheres = { - 'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']), - 'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']), - 'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']), - 'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']), - 'rememory': head + "/bin/bash " + script_dir + '/rememory.sh' - } - if param['backup_to'] != 'localhost': - cfile = mw.getPluginDir() + "/" + \ - param['backup_to'] + "/index.py" - - wheres['path'] = head + "python3 " + cfile + \ - " path " + param['sname'] + " " + str(param['save']) - wheres['site'] = head + "python3 " + cfile + \ - " site " + param['sname'] + " " + str(param['save']) - wheres['database'] = head + "python3 " + cfile + " " + \ - source_stype + " " + \ - param['sname'] + " " + str(param['save']) - try: - shell = wheres[stype] - except: - if stype == 'toUrl': - shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \ - param['urladdress'] + "'" - else: - shell = head + param['sbody'].replace("\r\n", "\n") - - shell += ''' -echo "----------------------------------------------------------------------------" -endDate=`date +"%Y-%m-%d %H:%M:%S"` -END_MW_SHELL_TIME=`date +"%s"` -((SHELL_COS_TIME=($END_MW_SHELL_TIME-$START_MW_SHELL_TIME))) -MW_ToSeconds $SHELL_COS_TIME -echo "★[$endDate] Successful | Script Run [$SCRIPT_RUN_TIME] " -echo "----------------------------------------------------------------------------" -''' - cronPath = mw.getServerDir() + '/cron' - if not os.path.exists(cronPath): - mw.execShell('mkdir -p ' + cronPath) - - if not 'echo' in param: - cronName = mw.md5(mw.md5(str(time.time()) + '_mw')) - else: - cronName = param['echo'] - file = cronPath + '/' + cronName - mw.writeFile(file, self.checkScript(shell)) - mw.execShell('chmod 750 ' + file) - return cronName - - # 检查脚本 - def checkScript(self, shell): - keys = ['shutdown', 'init 0', 'mkfs', 'passwd', - 'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs'] - for key in keys: - shell = shell.replace(key, '[***]') - return shell - - # 将Shell脚本写到文件 - def writeShell(self, config): - file = '/var/spool/cron/crontabs/root' - current_os = mw.getOs() - if current_os == 'darwin': - file = '/etc/crontab' - elif current_os.startswith("freebsd"): - file = '/var/cron/tabs/root' - - if not os.path.exists(file): - file = '/var/spool/cron/root' - - if not os.path.exists(file): - mw.writeFile(file, '') - conf = mw.readFile(file) - conf += str(config) + "\n" - if mw.writeFile(file, conf): - if not os.path.exists(file): - mw.execShell("chmod 600 '" + file + - "' && chown root.root " + file) - else: - mw.execShell("chmod 600 '" + file + - "' && chown root.crontab " + file) - return True - return mw.returnJson(False, '文件写入失败,请检查是否开启系统加固功能!') - - # 重载配置 - def crondReload(self): - if mw.isAppleSystem(): - # mw.execShell('/usr/sbin/cron restart') - if os.path.exists('/etc/crontab'): - pass - else: - if os.path.exists('/etc/init.d/crond'): - mw.execShell('/etc/init.d/crond reload') - elif os.path.exists('/etc/init.d/cron'): - mw.execShell('service cron restart') - else: - mw.execShell("systemctl reload crond") - - # 从crond删除 - def removeForCrond(self, echo): - u_file = '/var/spool/cron/crontabs/root' - if not os.path.exists(u_file): - file = '/var/spool/cron/root' - if mw.isAppleSystem(): - file = '/etc/crontab' - - if not os.path.exists(file): - return False - else: - file = u_file - - if mw.isAppleSystem(): - return True - - conf = mw.readFile(file) - rep = ".+" + str(echo) + ".+\n" - conf = re.sub(rep, "", conf) - if not mw.writeFile(file, conf): - return False - self.crondReload() - return True - - def syncToCrond(self, cronInfo): - if 'status' in cronInfo: - if cronInfo['status'] == 0: - return False - if 'where_hour' in cronInfo: - cronInfo['hour'] = cronInfo['where_hour'] - cronInfo['minute'] = cronInfo['where_minute'] - cronInfo['week'] = cronInfo['where1'] - cuonConfig, cronInfo, name = self.getCrondCycle(cronInfo) - cronPath = mw.getServerDir() + '/cron' - cronName = self.getShell(cronInfo) - if type(cronName) == dict: - return cronName - cuonConfig += ' ' + cronPath + '/' + cronName + \ - ' >> ' + cronPath + '/' + cronName + '.log 2>&1' - wRes = self.writeShell(cuonConfig) - if type(wRes) != bool: - return False - self.crondReload() diff --git a/class/core/db.py b/class/core/db.py deleted file mode 100755 index 3ae39f8d4..000000000 --- a/class/core/db.py +++ /dev/null @@ -1,401 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# sqlite3操作 -# --------------------------------------------------------------------------------- - - -import sqlite3 -import os -import sys - - -class Sql(): - #------------------------------ - # 数据库操作类 For sqlite3 - #------------------------------ - __DB_FILE = None # 数据库文件 - __DB_CONN = None # 数据库连接对象 - __DB_TABLE = "" # 被操作的表名称 - __OPT_WHERE = "" # where条件 - __OPT_LIMIT = "" # limit条件 - __OPT_GROUP = "" # group条件 - __OPT_ORDER = "" # order条件 - __OPT_FIELD = "*" # field条件 - __OPT_PARAM = () # where值 - - def __init__(self): - self.__DB_FILE = 'data/default.db' - - def __getConn(self): - # 取数据库对象 - try: - if self.__DB_CONN == None: - self.__DB_CONN = sqlite3.connect(self.__DB_FILE) - self.__DB_CONN.text_factory = str - except Exception as ex: - # print(mw.getTracebackInfo()) - return "error: " + str(ex) - - def changeTextFactoryToBytes(self): - self.__DB_CONN.text_factory = bytes - return True - - def autoTextFactory(self): - if sys.version_info[0] == 3: - self.__DB_CONN.text_factory = lambda x: str( - x, encoding="utf-8", errors='ignore') - else: - self.__DB_CONN.text_factory = lambda x: unicode( - x, "utf-8", "ignore") - - def dbfile(self, name): - self.__DB_FILE = 'data/' + name + '.db' - return self - - def dbPos(self, path, name, suffix_name = 'db'): - self.__DB_FILE = path + '/' + name + '.' + suffix_name - return self - - def table(self, table): - # 设置表名 - self.__DB_TABLE = table - return self - - def where(self, where, param=()): - # WHERE条件 - if where: - self.__OPT_WHERE = " WHERE " + where - self.__OPT_PARAM = param - return self - - def andWhere(self, where, param): - # WHERE条件 - if where: - self.__OPT_WHERE = self.__OPT_WHERE + " and " + where - # print(param) - # print(self.__OPT_PARAM) - self.__OPT_PARAM = self.__OPT_PARAM + param - return self - - def order(self, order): - # ORDER条件 - if len(order): - self.__OPT_ORDER = " ORDER BY " + order - else: - self.__OPT_ORDER = "" - return self - - def group(self, group): - if len(group): - self.__OPT_GROUP = " GROUP BY " + group - else: - self.__OPT_GROUP = "" - return self - - def limit(self, limit): - # LIMIT条件 - if len(limit): - self.__OPT_LIMIT = " LIMIT " + limit - else: - self.__OPT_LIMIT = "" - return self - - def field(self, field): - # FIELD条件 - if len(field): - self.__OPT_FIELD = field - return self - - def select(self): - # 查询数据集 - self.__getConn() - try: - sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ - self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - # print(sql) - # print(self.__OPT_PARAM) - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - data = result.fetchall() - # 构造字曲系列 - if self.__OPT_FIELD != "*": - field = self.__OPT_FIELD.split(',') - tmp = [] - for row in data: - i = 0 - tmp1 = {} - for key in field: - tmp1[key] = row[i] - i += 1 - tmp.append(tmp1) - del(tmp1) - data = tmp - del(tmp) - else: - # 将元组转换成列表 - tmp = map(list, data) - data = tmp - del(tmp) - self.__close() - return data - except Exception as ex: - return "error: " + str(ex) - - def inquiry(self, input_field=''): - # 查询数据集 - # 不清空查询参数 - self.__getConn() - try: - sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ - self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - - if os.path.exists('data/debug.pl'): - print(sql, self.__OPT_PARAM) - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - data = result.fetchall() - # 构造字曲系列 - if self.__OPT_FIELD != "*": - - if input_field != "": - field = input_field.split(',') - else: - field = self.__OPT_FIELD.split(',') - - tmp = [] - for row in data: - i = 0 - tmp1 = {} - for key in field: - tmp1[key] = row[i] - i += 1 - tmp.append(tmp1) - del(tmp1) - data = tmp - del(tmp) - else: - # 将元组转换成列表 - tmp = map(list, data) - data = tmp - del(tmp) - return data - except Exception as ex: - return "error: " + str(ex) - - def getField(self, keyName): - # 取回指定字段 - result = self.field(keyName).select() - # print(result) - if len(result) == 1: - return result[0][keyName] - return result - - def setField(self, keyName, keyValue): - # 更新指定字段 - return self.save(keyName, (keyValue,)) - - def find(self): - # 取一行数据 - result = self.limit("1").select() - if len(result) == 1: - return result[0] - return result - - def count(self): - # 取行数 - key = "COUNT(*)" - data = self.field(key).select() - try: - return int(data[0][key]) - except: - return 0 - - def add(self, keys, param): - # 插入数据 - self.__getConn() - try: - values = "" - for key in keys.split(','): - values += "?," - values = self.checkInput(values[0:len(values) - 1]) - sql = "INSERT INTO " + self.__DB_TABLE + \ - "(" + keys + ") " + "VALUES(" + values + ")" - result = self.__DB_CONN.execute(sql, param) - last_id = result.lastrowid - self.__close() - self.__DB_CONN.commit() - return last_id - except Exception as ex: - return "error: " + str(ex) - - # 插入数据 - def insert(self, pdata): - if not pdata: - return False - keys, param = self.__format_pdata(pdata) - return self.add(keys, param) - - # 更新数据 - def update(self, pdata): - if not pdata: - return False - keys, param = self.__format_pdata(pdata) - return self.save(keys, param) - - # 构造数据 - def __format_pdata(self, pdata): - keys = pdata.keys() - keys_str = ','.join(keys) - param = [] - for k in keys: - param.append(pdata[k]) - return keys_str, tuple(param) - - def checkInput(self, data): - if not data: - return data - if type(data) != str: - return data - checkList = [ - {'d': '<', 'r': '<'}, - {'d': '>', 'r': '>'}, - {'d': '\'', 'r': '‘'}, - {'d': '"', 'r': '“'}, - {'d': '&', 'r': '&'}, - {'d': '#', 'r': '#'}, - {'d': '<', 'r': '<'} - ] - for v in checkList: - data = data.replace(v['d'], v['r']) - return data - - def addAll(self, keys, param): - # 插入数据 - self.__getConn() - try: - values = "" - for key in keys.split(','): - values += "?," - values = values[0:len(values) - 1] - sql = "INSERT INTO " + self.__DB_TABLE + \ - "(" + keys + ") " + "VALUES(" + values + ")" - result = self.__DB_CONN.execute(sql, param) - return True - except Exception as ex: - return "error: " + str(ex) - - def commit(self): - self.__close() - self.__DB_CONN.commit() - - def save(self, keys, param): - # 更新数据 - self.__getConn() - try: - opt = "" - for key in keys.split(','): - opt += key + "=?," - opt = opt[0:len(opt) - 1] - sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE - - # import mw - # mw.writeFile('/tmp/test.pl', sql) - - # 处理拼接WHERE与UPDATE参数 - tmp = list(param) - for arg in self.__OPT_PARAM: - tmp.append(arg) - self.__OPT_PARAM = tuple(tmp) - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - self.__close() - self.__DB_CONN.commit() - return result.rowcount - except Exception as ex: - return "error: " + str(ex) - - def delete(self, id=None): - # 删除数据 - self.__getConn() - try: - if id: - self.__OPT_WHERE = " WHERE id=?" - self.__OPT_PARAM = (id,) - sql = "DELETE FROM " + self.__DB_TABLE + self.__OPT_WHERE - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - self.__close() - self.__DB_CONN.commit() - return result.rowcount - except Exception as ex: - return "error: " + str(ex) - - def originExecute(self, sql, param=()): - self.__getConn() - try: - result = self.__DB_CONN.execute(sql, param) - self.__DB_CONN.commit() - return result - except Exception as ex: - return "error: " + str(ex) - - def execute(self, sql, param=()): - # 执行SQL语句返回受影响行 - self.__getConn() - # print sql, param - try: - result = self.__DB_CONN.execute(sql, param) - self.__DB_CONN.commit() - return result.rowcount - except Exception as ex: - return "error: " + str(ex) - - def query(self, sql, param=()): - # 执行SQL语句返回数据集 - self.__getConn() - try: - result = self.__DB_CONN.execute(sql, param) - # 将元组转换成列表 - # data = map(list, result) - return result - except Exception as ex: - return "error: " + str(ex) - - def create(self, name): - # 创建数据表 - self.__getConn() - import mw - script = mw.readFile('data/' + name + '.sql') - result = self.__DB_CONN.executescript(script) - self.__DB_CONN.commit() - return result.rowcount - - def fofile(self, filename): - # 执行脚本 - self.__getConn() - import mw - script = mw.readFile(filename) - result = self.__DB_CONN.executescript(script) - self.__DB_CONN.commit() - return result.rowcount - - def __close(self): - # 清理条件属性 - self.__OPT_WHERE = "" - self.__OPT_FIELD = "*" - self.__OPT_ORDER = "" - self.__OPT_LIMIT = "" - self.__OPT_PARAM = () - - def close(self): - # 释放资源 - try: - self.__DB_CONN.close() - self.__DB_CONN = None - except: - pass diff --git a/class/core/files_api.py b/class/core/files_api.py deleted file mode 100755 index 02aadf1be..000000000 --- a/class/core/files_api.py +++ /dev/null @@ -1,1160 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 文件类操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd -import shutil - -from flask import request -from flask import send_file, send_from_directory -from flask import make_response -from flask import session - - -class files_api: - - rPath = None - - def __init__(self): - self.rPath = mw.getRootDir() + '/recycle_bin/' - - ##### ----- start ----- ### - def getBodyApi(self): - path = request.form.get('path', '') - return self.getBody(path) - - def getLastBodyApi(self): - path = request.form.get('path', '') - line = request.form.get('line', '100') - - if not os.path.exists(path): - return mw.returnJson(False, '文件不存在', (path,)) - - try: - data = mw.getLastLine(path, int(line)) - return mw.returnJson(True, 'OK', data) - except Exception as ex: - return mw.returnJson(False, '无法正确读取文件!' + str(ex)) - - def saveBodyApi(self): - path = request.form.get('path', '') - data = request.form.get('data', '') - encoding = request.form.get('encoding', '') - return self.saveBody(path, data, encoding) - - def downloadApi(self): - filename = request.args.get('filename', '') - if not os.path.exists(filename): - return '' - - is_attachment = True - if filename.endswith(".svg"): - is_attachment = False - - response = make_response(send_from_directory( - os.path.dirname(filename), os.path.basename(filename), as_attachment=is_attachment)) - return response - - def zipApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - stype = request.form.get('type', '') - path = request.form.get('path', '') - return self.zip(sfile, dfile, stype, path) - - def unzipApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - stype = request.form.get('type', '') - path = request.form.get('path', '') - return self.unzip(sfile, dfile, stype, path) - - def uncompressApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - path = request.form.get('path', '') - return self.uncompress(sfile, dfile, path) - - # 移动文件或目录 - def mvFileApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - if not self.checkFileName(dfile): - return mw.returnJson(False, '文件名中不能包含特殊字符!') - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - if not self.checkDir(sfile): - return mw.returnJson(False, 'FILE_DANGER') - - import shutil - try: - shutil.move(sfile, dfile) - msg = mw.getInfo('移动或重名命文件[{1}]到[{2}]成功!', (sfile, dfile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '移动或重名命文件成功!') - except: - return mw.returnJson(False, '移动或重名命文件失败!') - - def deleteApi(self): - path = request.form.get('path', '') - return self.delete(path) - - def fileAccessApi(self): - filename = request.form.get('filename', '') - data = self.getAccess(filename) - data['sys_users'] = self.getSysUserList() - return mw.getJson(data) - - def setFileAccessApi(self): - - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不设置!') - - filename = request.form.get('filename', '') - user = request.form.get('user', '') - access = request.form.get('access', '755') - sall = '-R' - try: - if not self.checkDir(filename): - return mw.returnJson(False, '请不要花样作死') - - if not os.path.exists(filename): - return mw.returnJson(False, '指定文件不存在!') - - os.system('chmod ' + sall + ' ' + access + " '" + filename + "'") - os.system('chown ' + sall + ' ' + user + - ':' + user + " '" + filename + "'") - msg = mw.getInfo( - '设置[{1}]权限为[{2}]所有者为[{3}]', (filename, access, user,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '设置成功!') - except: - return mw.returnJson(False, '设置失败!') - - def getDirSizeApi(self): - path = request.form.get('path', '') - tmp = self.getDirSize(path) - return mw.returnJson(True, tmp[0].split()[0]) - - def getDirApi(self): - path = request.form.get('path', '') - if not os.path.exists(path): - path = mw.getRootDir() + "/wwwroot" - search = request.args.get('search', '').strip().lower() - search_all = request.args.get('all', '').strip().lower() - page = request.args.get('p', '1').strip().lower() - row = request.args.get('row', '10') - order = request.form.get('order', '') - - if search_all == 'yes' and search != '': - return self.getAllDir(path, int(page), int(row), order, search) - return self.getDir(path, int(page), int(row), order, search) - - def createFileApi(self): - file = request.form.get('path', '') - try: - if not self.checkFileName(file): - return mw.returnJson(False, '文件名中不能包含特殊字符!') - if os.path.exists(file): - return mw.returnJson(False, '指定文件已存在!') - _path = os.path.dirname(file) - if not os.path.exists(_path): - os.makedirs(_path) - open(file, 'w+').close() - self.setFileAccept(file) - msg = mw.getInfo('创建文件[{1}]成功!', (file,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '文件创建成功!') - except Exception as e: - return mw.returnJson(True, '文件创建失败!') - - def createDirApi(self): - path = request.form.get('path', '') - try: - if not self.checkFileName(path): - return mw.returnJson(False, '目录名中不能包含特殊字符!') - if os.path.exists(path): - return mw.returnJson(False, '指定目录已存在!') - os.makedirs(path) - self.setFileAccept(path) - msg = mw.getInfo('创建目录[{1}]成功!', (path,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '目录创建成功!') - except Exception as e: - return mw.returnJson(False, '目录创建失败!') - - def downloadFileApi(self): - import db - import time - url = request.form.get('url', '') - path = request.form.get('path', '') - filename = request.form.get('filename', '') - execstr = url + '|mw|' + path + '/' + filename - execstr = execstr.strip() - mw.M('tasks').add('name,type,status,addtime,execstr', - ('下载文件[' + filename + ']', 'download', '-1', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)) - - # self.setFileAccept(path + '/' + filename) - mw.triggerTask() - return mw.returnJson(True, '已将下载任务添加到队列!') - - # 删除进程下的所有进程 - def removeTaskRecursion(self, pid): - cmd = "ps -ef|grep " + pid + \ - " | grep -v grep |sed -n '2,1p' | awk '{print $2}'" - sub_pid = mw.execShell(cmd) - - if sub_pid[0].strip() == '': - return 'ok' - - self.removeTaskRecursion(sub_pid[0].strip()) - mw.execShell('kill -9 ' + sub_pid[0].strip()) - return sub_pid[0].strip() - - def removeTaskApi(self): - import system_api - mid = request.form.get('id', '') - try: - name = mw.M('tasks').where('id=?', (mid,)).getField('name') - status = mw.M('tasks').where('id=?', (mid,)).getField('status') - mw.M('tasks').delete(mid) - if status == '-1': - task_pid = mw.execShell( - "ps aux | grep 'task.py' | grep -v grep |awk '{print $2}'") - - task_list = task_pid[0].strip().split("\n") - for i in range(len(task_list)): - self.removeTaskRecursion(task_list[i]) - - mw.triggerTask() - system_api.system_api().restartTask() - except: - system_api.system_api().restartTask() - - # 删除日志 - task_log = mw.getRunDir() + "/tmp/panelTask.pl" - if os.path.exists(task_log): - os.remove(task_log) - return mw.returnJson(True, '任务已删除!') - - def uploadFileApi(self): - # 上传文件 - from werkzeug.utils import secure_filename - from flask import request - - path = request.args.get('path', '') - - if not os.path.exists(path): - os.makedirs(path) - f = request.files['zunfile'] - filename = os.path.join(path, f.filename) - - s_path = path - if os.path.exists(filename): - s_path = filename - p_stat = os.stat(s_path) - - # print(filename) - f.save(filename) - os.chown(filename, p_stat.st_uid, p_stat.st_gid) - os.chmod(filename, p_stat.st_mode) - - msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (filename, path)) - mw.writeLog('文件管理', msg) - return mw.returnMsg(True, '上传成功!') - - # 设置文件和目录权限 - def setMode(self, path): - s_path = os.path.dirname(path) - p_stat = os.stat(s_path) - os.chown(path, p_stat.st_uid, p_stat.st_gid) - os.chmod(path, p_stat.st_mode) - - def uploadSegmentApi(self): - # 分段上传 - path = request.form.get('path', '') - name = request.form.get('name', '') - size = request.form.get('size') - start = request.form.get('start') - dir_mode = request.form.get('dir_mode', '') - file_mode = request.form.get('file_mode', '') - - if not mw.fileNameCheck(name): - return mw.returnJson(False, '文件名中不能包含特殊字符!') - - if path == '/': - return mw.returnJson(False, '不能直接上传文件到系统根目录!') - - if name.find('./') != -1 or path.find('./') != -1: - return mw.returnJson(False, '错误的参数') - - if not os.path.exists(path): - os.makedirs(path, 493) - if not dir_mode != '' or not file_mode != '': - mw.setMode(path) - - save_path = os.path.join( - path, name + '.' + str(int(size)) + '.upload.tmp') - d_size = 0 - if os.path.exists(save_path): - d_size = os.path.getsize(save_path) - - if d_size != int(start): - return str(d_size) - - f = open(save_path, 'ab') - b64_data = request.form.get('b64_data', '0') - if b64_data == '1': - import base64 - b64_data = base64.b64decode(args.b64_data) - f.write(b64_data) - else: - upload_files = request.files.getlist("blob") - for tmp_f in upload_files: - f.write(tmp_f.read()) - - f.close() - f_size = os.path.getsize(save_path) - if f_size != int(size): - return str(f_size) - - new_name = os.path.join(path, name) - if os.path.exists(new_name): - if new_name.find('.user.ini') != -1: - mw.execShell("chattr -i " + new_name) - try: - os.remove(new_name) - except: - mw.execShell("rm -f %s" % new_name) - - os.renames(save_path, new_name) - - if dir_mode != '' and dir_mode != '': - mode_tmp1 = dir_mode.split(',') - mw.setMode(path, mode_tmp1[0]) - mw.setOwn(path, mode_tmp1[1]) - mode_tmp2 = file_mode.split(',') - mw.setMode(new_name, mode_tmp2[0]) - mw.setOwn(new_name, mode_tmp2[1]) - else: - self.setMode(new_name) - - msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (new_name, path)) - mw.writeLog('文件管理', msg) - return mw.returnMsg(True, '上传成功!') - - def getRecycleBinApi(self): - rPath = self.rPath - if not os.path.exists(rPath): - os.system('mkdir -p ' + rPath) - data = {} - data['dirs'] = [] - data['files'] = [] - data['status'] = os.path.exists('data/recycle_bin.pl') - data['status_db'] = os.path.exists('data/recycle_bin_db.pl') - for file in os.listdir(rPath): - try: - tmp = {} - fname = rPath + file - tmp1 = file.split('_mw_') - tmp2 = tmp1[len(tmp1) - 1].split('_t_') - tmp['rname'] = file - tmp['dname'] = file.replace('_mw_', '/').split('_t_')[0] - tmp['name'] = tmp2[0] - tmp['time'] = int(float(tmp2[1])) - if os.path.islink(fname): - filePath = os.readlink(fname) - link = ' -> ' + filePath - if os.path.exists(filePath): - tmp['size'] = os.path.getsize(filePath) - else: - tmp['size'] = 0 - else: - tmp['size'] = os.path.getsize(fname) - if os.path.isdir(fname): - data['dirs'].append(tmp) - else: - data['files'].append(tmp) - except Exception as e: - continue - return mw.returnJson(True, 'OK', data) - - # 回收站开关 - def recycleBinApi(self): - c = 'data/recycle_bin.pl' - db = request.form.get('db', '') - if db != '': - c = 'data/recycle_bin_db.pl' - if os.path.exists(c): - os.remove(c) - mw.writeLog('文件管理', '已关闭回收站功能!') - return mw.returnJson(True, '已关闭回收站功能!') - else: - mw.writeFile(c, 'True') - mw.writeLog('文件管理', '已开启回收站功能!') - return mw.returnJson(True, '已开启回收站功能!') - - def reRecycleBinApi(self): - rPath = self.rPath - path = request.form.get('path', '') - dFile = path.replace('_mw_', '/').split('_t_')[0] - try: - import shutil - shutil.move(rPath + path, dFile) - msg = mw.getInfo('移动文件[{1}]到回收站成功!', (dFile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '恢复成功!') - except Exception as e: - msg = mw.getInfo('从回收站恢复[{1}]失败!', (dFile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(False, '恢复失败!') - - def delRecycleBinApi(self): - rPath = self.rPath - path = request.form.get('path', '') - empty = request.form.get('empty', '') - dFile = path.split('_t_')[0] - - if not self.checkDir(path): - return mw.returnJson(False, '敏感目录,请不要花样作死!') - - mw.execShell('which chattr && chattr -R -i ' + rPath + path) - if os.path.isdir(rPath + path): - import shutil - shutil.rmtree(rPath + path) - else: - os.remove(rPath + path) - - tfile = path.replace('_mw_', '/').split('_t_')[0] - msg = mw.getInfo('已彻底从回收站删除{1}!', (tfile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, msg) - - # 获取进度 - def getSpeedApi(self): - data = mw.getSpeed() - return mw.returnJson(True, '已清空回收站!', data) - - def closeRecycleBinApi(self): - rPath = self.rPath - mw.execShell('which chattr && chattr -R -i ' + rPath) - rlist = os.listdir(rPath) - i = 0 - l = len(rlist) - for name in rlist: - i += 1 - path = rPath + name - mw.writeSpeed(name, i, l) - if os.path.isdir(path): - shutil.rmtree(path) - else: - os.remove(path) - mw.writeSpeed(None, 0, 0) - mw.writeLog('文件管理', '已清空回收站!') - return mw.returnJson(True, '已清空回收站!') - - def deleteDirApi(self): - path = request.form.get('path', '') - if not os.path.exists(path): - return mw.returnJson(False, '指定文件不存在!') - - # 检查是否为.user.ini - if path.find('.user.ini'): - os.system("which chattr && chattr -i '" + path + "'") - try: - if os.path.exists('data/recycle_bin.pl'): - if self.mvRecycleBin(path): - return mw.returnJson(True, '已将文件移动到回收站!') - mw.execShell('rm -rf ' + path) - mw.writeLog('文件管理', '删除文件成功!', (path,)) - return mw.returnJson(True, '删除文件成功!') - except: - return mw.returnJson(False, '删除文件失败!') - - def closeLogsApi(self): - logPath = mw.getLogsDir() - os.system('rm -f ' + logPath + '/*') - os.system('kill -USR1 `cat ' + mw.getServerDir() + - 'openresty/nginx/logs/nginx.pid`') - mw.writeLog('文件管理', '网站日志已被清空!') - tmp = self.getDirSize(logPath) - return mw.returnJson(True, tmp[0].split()[0]) - - def setBatchDataApi(self): - path = request.form.get('path', '') - stype = request.form.get('type', '') - access = request.form.get('access', '') - user = request.form.get('user', '') - data = request.form.get('data') - if stype == '1' or stype == '2': - session['selected'] = { - 'path': path, - 'type': stype, - 'access': access, - 'user': user, - 'data': data - } - return mw.returnJson(True, '标记成功,请在目标目录点击粘贴所有按钮!') - elif stype == '3': - for key in json.loads(data): - try: - filename = path + '/' + key - if not self.checkDir(filename): - return mw.returnJson(False, 'FILE_DANGER') - os.system('chmod -R ' + access + " '" + filename + "'") - os.system('chown -R ' + user + ':' + - user + " '" + filename + "'") - except: - continue - mw.writeLog('文件管理', '批量设置权限成功!') - return mw.returnJson(True, '批量设置权限成功!') - else: - import shutil - isRecyle = os.path.exists('data/recycle_bin.pl') - data = json.loads(data) - l = len(data) - i = 0 - for key in data: - try: - filename = path + '/' + key - topath = filename - if not os.path.exists(filename): - continue - - i += 1 - mw.writeSpeed(key, i, l) - if os.path.isdir(filename): - if not self.checkDir(filename): - return mw.returnJson(False, '请不要花样作死!') - if isRecyle: - self.mvRecycleBin(topath) - else: - shutil.rmtree(filename) - else: - if key == '.user.ini': - os.system('which chattr && chattr -i ' + filename) - if isRecyle: - self.mvRecycleBin(topath) - else: - os.remove(filename) - except: - continue - mw.writeSpeed(None, 0, 0) - mw.writeLog('文件管理', '批量删除成功!') - return mw.returnJson(True, '批量删除成功!') - - def checkExistsFilesApi(self): - dfile = request.form.get('dfile', '') - filename = request.form.get('filename', '') - data = [] - filesx = [] - if filename == '': - filesx = json.loads(session['selected']['data']) - else: - filesx.append(filename) - - for fn in filesx: - if fn == '.': - continue - filename = dfile + '/' + fn - if os.path.exists(filename): - tmp = {} - stat = os.stat(filename) - tmp['filename'] = fn - tmp['size'] = os.path.getsize(filename) - tmp['mtime'] = str(int(stat.st_mtime)) - data.append(tmp) - return mw.returnJson(True, 'ok', data) - - def batchPasteApi(self): - path = request.form.get('path', '') - stype = request.form.get('type', '') - # filename = request.form.get('filename', '') - import shutil - if not self.checkDir(path): - return mw.returnJson(False, '请不要花样作死!') - i = 0 - myfiles = json.loads(session['selected']['data']) - l = len(myfiles) - if stype == '1': - for key in myfiles: - i += 1 - mw.writeSpeed(key, i, l) - try: - - sfile = session['selected'][ - 'path'] + '/' + key - dfile = path + '/' + key - - if os.path.isdir(sfile): - shutil.copytree(sfile, dfile) - else: - shutil.copyfile(sfile, dfile) - stat = os.stat(sfile) - os.chown(dfile, stat.st_uid, stat.st_gid) - except: - continue - msg = mw.getInfo('从[{1}]批量复制到[{2}]成功', - (session['selected']['path'], path,)) - mw.writeLog('文件管理', msg) - else: - for key in myfiles: - try: - i += 1 - mw.writeSpeed(key, i, l) - - sfile = session['selected'][ - 'path'] + '/' + key - dfile = path + '/' + key - - shutil.move(sfile, dfile) - except: - continue - msg = mw.getInfo('从[{1}]批量移动到[{2}]成功', - (session['selected']['path'], path,)) - mw.writeLog('文件管理', msg) - mw.writeSpeed(None, 0, 0) - errorCount = len(myfiles) - i - del(session['selected']) - msg = mw.getInfo('批量操作成功[{1}],失败[{2}]', (str(i), str(errorCount))) - return mw.returnJson(True, msg) - - def copyFileApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - - if sfile == dfile: - return mw.returnJson(False, '源与目的一致!') - - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - if os.path.isdir(sfile): - return self.copyDir(sfile, dfile) - - try: - import shutil - shutil.copyfile(sfile, dfile) - msg = mw.getInfo('复制文件[{1}]到[{2}]成功!', (sfile, dfile,)) - mw.writeLog('文件管理', msg) - stat = os.stat(sfile) - os.chown(dfile, stat.st_uid, stat.st_gid) - return mw.returnJson(True, '文件复制成功!') - except: - return mw.returnJson(False, '文件复制失败!') - - ##### ----- end ----- ### - - def copyDir(self, sfile, dfile): - - if not os.path.exists(sfile): - return mw.returnJson(False, '指定目录不存在!') - - if os.path.exists(dfile): - return mw.returnJson(False, '指定目录已存在!') - import shutil - try: - shutil.copytree(sfile, dfile) - stat = os.stat(sfile) - os.chown(dfile, stat.st_uid, stat.st_gid) - msg = mw.getInfo('复制目录[{1}]到[{2}]成功!', (sfile, dfile)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '目录复制成功!') - except: - return mw.returnJson(False, '目录复制失败!') - - # 检查敏感目录 - def checkDir(self, path): - # path = str(path, encoding='utf-8') - path = path.replace('//', '/') - if path[-1:] == '/': - path = path[:-1] - - nDirs = ('', - '/', - '/*', - '/www', - '/root', - '/boot', - '/bin', - '/etc', - '/home', - '/dev', - '/sbin', - '/var', - '/usr', - '/tmp', - '/sys', - '/proc', - '/media', - '/mnt', - '/opt', - '/lib', - '/srv', - '/selinux', - '/www/server', - mw.getRootDir()) - - return not path in nDirs - - def getDirSize(self, path): - tmp = mw.execShell('du -sh ' + path) - return tmp - - def checkFileName(self, filename): - # 检测文件名 - nots = ['\\', '&', '*', '|', ';'] - if filename.find('/') != -1: - filename = filename.split('/')[-1] - for n in nots: - if n in filename: - return False - return True - - def setFileAccept(self, filename): - auth = 'www:www' - if mw.getOs() == 'darwin': - user = mw.execShell( - "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() - auth = user + ':staff' - - os.system('chown -R ' + auth + ' ' + filename) - os.system('chmod -R 755 ' + filename) - - # 移动到回收站 - def mvRecycleBin(self, path): - rPath = self.rPath - if not os.path.exists(rPath): - os.system('mkdir -p ' + rPath) - - rFile = rPath + path.replace('/', '_mw_') + '_t_' + str(time.time()) - try: - import shutil - shutil.move(path, rFile) - mw.writeLog('文件管理', mw.getInfo( - '移动文件[{1}]到回收站成功!', (path))) - return True - except: - mw.writeLog('文件管理', mw.getInfo( - '移动文件[{1}]到回收站失败!', (path))) - return False - - def getBody(self, path): - if not os.path.exists(path): - return mw.returnJson(False, '文件不存在', (path,)) - - if os.path.getsize(path) > 2097152: - return mw.returnJson(False, '不能在线编辑大于2MB的文件!') - - if os.path.isdir(path): - return mw.returnJson(False, '这不是一个文件!') - - fp = open(path, 'rb') - data = {} - data['status'] = True - if fp: - srcBody = fp.read() - fp.close() - - encoding_list = ['utf-8', 'GBK', 'BIG5'] - for el in encoding_list: - try: - data['encoding'] = el - data['data'] = srcBody.decode(data['encoding']) - break - except Exception as ex: - if el == 'BIG5': - return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex)) - else: - return mw.returnJson(False, '文件未正常打开!') - - return mw.returnJson(True, 'OK', data) - - def saveBody(self, path, data, encoding='utf-8'): - if not os.path.exists(path): - return mw.returnJson(False, '文件不存在') - try: - if encoding == 'ascii': - encoding = 'utf-8' - - data = data.encode( - encoding, errors='ignore').decode(encoding) - fp = open(path, 'w+', encoding=encoding) - fp.write(data) - fp.close() - - if path.find("web_conf") > 0: - mw.restartWeb() - - mw.writeLog('文件管理', '文件[{1}]保存成功', (path,)) - return mw.returnJson(True, '文件保存成功') - except Exception as ex: - return mw.returnJson(False, '文件保存错误:' + str(ex)) - - def zip(self, sfile, dfile, stype, path): - if sfile.find(',') == -1: - if not os.path.exists(path + '/' + sfile): - return mw.returnMsg(False, '指定文件不存在!') - - try: - tmps = mw.getRunDir() + '/tmp/panelExec.log' - if stype == 'zip': - mw.execShell("cd '" + path + "' && zip '" + dfile + - "' -r '" + sfile + "' > " + tmps + " 2>&1") - else: - sfiles = '' - for sfile in sfile.split(','): - if not sfile: - continue - sfiles += " '" + sfile + "'" - mw.execShell("cd '" + path + "' && tar -zcvf '" + - dfile + "' " + sfiles + " > " + tmps + " 2>&1") - self.setFileAccept(dfile) - mw.writeLog("文件管理", '文件[{1}]压缩[{2}]成功!', (sfile, dfile)) - return mw.returnJson(True, '文件压缩成功!') - except: - return mw.returnJson(False, '文件压缩失败!') - - def uncompress(self, sfile, dfile, path): - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - filename = os.path.basename(sfile) - extension = os.path.splitext(filename)[-1] - extension = extension.strip('.') - - tar_gz = 'tar.gz' - tar_gz_len = len(tar_gz) - suffix_gz = sfile[-tar_gz_len:] - if suffix_gz == tar_gz: - extension = suffix_gz - - if not extension in ['tar.gz', 'gz', 'zip', 'rar']: - return mw.returnJson(False, '现在仅支持gz,zip,rar格式解压!') - - if mw.isAppleSystem() and extension == 'rar': - return mw.returnJson(False, 'macosx暂时不支持rar格式解压') - - try: - tmps = mw.getRunDir() + '/tmp/panelExec.log' - if extension == 'zip': - cmd = "cd " + path + " && unzip -o -d '" + dfile + \ - "' '" + sfile + "' > " + tmps + " 2>&1 &" - mw.execShell(cmd) - if extension == 'tar.gz': - cmd = "cd " + path + " && tar -zxvf " + sfile + \ - " -C " + dfile + " > " + tmps + " 2>&1 &" - mw.execShell(cmd) - if extension == 'gz': - cmd = "cd " + path + " && gunzip -k " + sfile + " > " + tmps + " 2>&1 &" - mw.execShell(cmd) - if extension == 'rar': - cmd = "cd " + path + " && unrar x " + sfile + \ - " " + dfile + " > " + tmps + " 2>&1 &" - mw.execShell(cmd) - - if os.path.exists(dfile): - self.setFileAccept(dfile) - mw.writeLog("文件管理", '文件[{1}]解压[{2}]成功!', (sfile, dfile)) - return mw.returnJson(True, '文件解压成功!') - except Exception as e: - return mw.returnJson(False, '文件解压失败!:' + str(e)) - - def unzip(self, sfile, dfile, stype, path): - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - try: - tmps = mw.getRunDir() + '/tmp/panelExec.log' - if stype == 'zip': - mw.execShell("cd " + path + " && unzip -o -d '" + dfile + - "' '" + sfile + "' > " + tmps + " 2>&1 &") - else: - sfiles = '' - for sfile in sfile.split(','): - if not sfile: - continue - sfiles += " '" + sfile + "'" - mw.execShell("cd " + path + " && tar -zxvf " + sfiles + - " -C " + dfile + " > " + tmps + " 2>&1 &") - - self.setFileAccept(dfile) - mw.writeLog("文件管理", '文件[{1}]解压[{2}]成功!', (sfile, dfile)) - return mw.returnJson(True, '文件解压成功!') - except: - return mw.returnJson(False, '文件解压失败!') - - def delete(self, path): - - if not os.path.exists(path): - return mw.returnJson(False, '指定文件不存在!') - - # 检查是否为.user.ini - if path.find('.user.ini') >= 0: - mw.execShell("which chattr && chattr -i '" + path + "'") - - try: - if os.path.exists('data/recycle_bin.pl'): - if self.mvRecycleBin(path): - return mw.returnJson(True, '已将文件移动到回收站!') - os.remove(path) - mw.writeLog('文件管理', mw.getInfo( - '删除文件[{1}]成功!', (path))) - return mw.returnJson(True, '删除文件成功!') - except: - return mw.returnJson(False, '删除文件失败!') - - def getAccess(self, filename): - data = {} - try: - stat = os.stat(filename) - data['chmod'] = str(oct(stat.st_mode)[-3:]) - data['chown'] = pwd.getpwuid(stat.st_uid).pw_name - except: - data['chmod'] = 755 - data['chown'] = 'www' - return data - - def getSysUserList(self): - pwd_file = '/etc/passwd' - if os.path.exists(pwd_file): - content = mw.readFile(pwd_file) - clist = content.split('\n') - sys_users = [] - for line in clist: - if line.find(":")<0: - continue - lines = line.split(":",1) - sys_users.append(lines[0]) - return sys_users - return ['root','mysql','www'] - - # 计算文件数量 - def getCount(self, path, search): - i = 0 - for name in os.listdir(path): - if search: - if name.lower().find(search) == -1: - continue - if name == '.': - continue - i += 1 - return i - - def getAllDir(self, path, page=1, page_size=10, order='', search=None): - # print("search:", search) - data = {} - dirnames = [] - filenames = [] - - info = {} - - i = 0 - n = 0 - count = 0 - max_limit = 3000 - order_arr = order.split(' ') - if len(order_arr) < 2: - plist = mw.sortAllFileList(path, order_arr[0],'',search, max_limit) - else: - plist = mw.sortAllFileList(path, order_arr[0], order_arr[1], search,max_limit) - - info['count'] = len(plist) - info['row'] = page_size - info['p'] = page - info['tojs'] = 'getFiles' - pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8') - data['PAGE'] = pageObj[0] - - for dst_file in plist: - - if not os.path.exists(dst_file): - continue - - i += 1 - if n >= pageObj[1].ROW: - break - if i < pageObj[1].SHIFT: - continue - - if os.path.isdir(dst_file): - dirnames.append(self.__get_stats(dst_file, path)) - else: - filenames.append(self.__get_stats(dst_file, path)) - n += 1 - - data['DIR'] = dirnames - data['FILES'] = filenames - data['PATH'] = path.replace('//', '/') - - return mw.getJson(data) - - #备份 - def getAllDirBk(self, path, page=1, page_size=10, order='', search=None): - data = {} - dirnames = [] - filenames = [] - - count = 0 - max_limit = 3000 - for d_list in os.walk(path): - - if count >= max_limit: - break - - for d in d_list[1]: - if count >= max_limit: - break - if d.lower().find(search) != -1: - filename = d_list[0] + '/' + d - if not os.path.exists(filename): - continue - dirnames.append(self.__get_stats(filename, path)) - count += 1 - - for f in d_list[2]: - if count >= max_limit: - break - - if f.lower().find(search) != -1: - filename = d_list[0] + '/' + f - if not os.path.exists(filename): - continue - filenames.append(self.__get_stats(filename, path)) - count += 1 - - data['DIR'] = sorted(dirnames) - data['FILES'] = sorted(filenames) - data['PATH'] = path.replace('//', '/') - - info = {} - info['count'] = len(dirnames) + len(filenames) - info['row'] = page_size - info['p'] = page - info['tojs'] = 'getFiles' - pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8') - data['PAGE'] = pageObj[0] - - return mw.getJson(data) - - def getDir(self, path, page=1, page_size=10, order = '', search=None): - data = {} - dirnames = [] - filenames = [] - - info = {} - info['count'] = self.getCount(path, search) - info['row'] = page_size - info['p'] = page - info['tojs'] = 'getFiles' - pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8') - data['PAGE'] = pageObj[0] - - i = 0 - n = 0 - - order_arr = order.split(' ') - if len(order_arr) < 2: - plist = mw.sortFileList(path, order_arr[0], '') - else: - plist = mw.sortFileList(path, order_arr[0], order_arr[1]) - - for filename in plist: - if search: - if filename.lower().find(search) == -1: - continue - i += 1 - if n >= pageObj[1].ROW: - break - if i < pageObj[1].SHIFT: - continue - try: - filePath = path + '/' + filename - if not os.path.exists(filePath): - continue - - file_stats = self.__get_stats(filePath, path) - if os.path.isdir(filePath): - dirnames.append(file_stats) - else: - filenames.append(file_stats) - n += 1 - except Exception as e: - continue - data['DIR'] = dirnames - data['FILES'] = filenames - data['PATH'] = path.replace('//', '/') - - return mw.getJson(data) - - def execShellApi(self): - # 执行SHELL命令 - shell = request.form.get('shell', '').strip() - path = request.form.get('path', '').strip() - disabled = ['vi', 'vim', 'top', 'passwd', 'su'] - tmp = shell.split(' ') - if tmp[0] in disabled: - return mw.returnJson(False, '禁止执行[{}]'.format(tmp[0])) - shellStr = '''#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin -export PATH -cd %s -%s -''' % (path, shell) - mw.writeFile('/tmp/panelShell.sh', shellStr) - mw.execShell( - 'nohup bash /tmp/panelShell.sh > /tmp/panelShell.pl 2>&1 &') - return mw.returnJson(True, 'ok') - - def getExecShellMsgApi(self): - # 取SHELL执行结果 - fileName = '/tmp/panelShell.pl' - if not os.path.exists(fileName): - return '' - status = not mw.processExists('bash', None, '/tmp/panelShell.sh') - return mw.returnJson(status, mw.getNumLines(fileName, 200)) - - def __get_stats(self, filename, path=None): - # print(filename,path) - filename = filename.replace('//', '/') - try: - stat = os.stat(filename) - accept = str(oct(stat.st_mode)[-3:]) - mtime = str(int(stat.st_mtime)) - user = '' - try: - user = str(pwd.getpwuid(stat.st_uid).pw_name) - except: - user = str(stat.st_uid) - size = str(stat.st_size) - link = '' - if os.path.islink(filename): - link = ' -> ' + os.readlink(filename) - - if path: - tmp_path = (path + '/').replace('//', '/') - filename = filename.replace(tmp_path, '', 1) - - return filename + ';' + size + ';' + mtime + ';' + accept + ';' + user + ';' + link - except Exception as e: - # print(e) - return ';;;;;' diff --git a/class/core/firewall_api.py b/class/core/firewall_api.py deleted file mode 100755 index 04d48f06b..000000000 --- a/class/core/firewall_api.py +++ /dev/null @@ -1,513 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 防火墙操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - -from flask import request - - -class firewall_api: - - __isFirewalld = False - __isIptables = False - __isUfw = False - __isMac = False - - def __init__(self): - iptables_file = mw.systemdCfgDir() + '/iptables.service' - if os.path.exists('/usr/sbin/firewalld'): - self.__isFirewalld = True - elif os.path.exists('/usr/sbin/ufw'): - self.__isUfw = True - elif os.path.exists(iptables_file): - self.__isIptables = True - elif mw.isAppleSystem(): - self.__isMac = True - - ##### ----- start ----- ### - # 添加屏蔽IP - def addDropAddressApi(self): - import re - port = request.form.get('port', '').strip() - ps = request.form.get('ps', '').strip() - - rep = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$" - if not re.search(rep, port): - return mw.returnJson(False, '您输入的IP地址不合法!') - address = port - if mw.M('firewall').where("port=?", (address,)).count() > 0: - return mw.returnJson(False, '您要放屏蔽的IP已存在屏蔽列表,无需重复处理!') - if self.__isUfw: - mw.execShell('ufw deny from ' + address + ' to any') - else: - if self.__isIptables: - cmd = 'iptables -I INPUT -s ' + address + ' -j DROP' - mw.execShell(cmd) - elif self.__isFirewalld: - cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \ - address + '" drop\'' - mw.execShell(cmd) - else: - pass - - msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,ps,addtime', (address, ps, addtime)) - self.firewallReload() - return mw.returnJson(True, '添加成功!') - - # 添加放行端口 - def addAcceptPortApi(self): - if not self.getFwStatus(): - return mw.returnJson(False, '防火墙启动时,才能添加规则!') - - port = request.form.get('port', '').strip() - ps = request.form.get('ps', '').strip() - stype = request.form.get('type', '').strip() - protocol = request.form.get('protocol', '').strip() - - data = self.addAcceptPortArgs(port, ps, stype, protocol) - return mw.getJson(data) - - # 添加放行端口 - def addAcceptPortArgs(self, port, ps, stype, protocol='tcp'): - import re - import time - - if not self.getFwStatus(): - self.setFw(0) - - rep = r"^\d{1,5}(:\d{1,5})?$" - if not re.search(rep, port): - return mw.returnData(False, '端口范围不正确!') - - if mw.M('firewall').where("port=?", (port,)).count() > 0: - return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') - - msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,protocol,ps,addtime', - (port, protocol, ps, addtime)) - - self.addAcceptPort(port, protocol) - self.firewallReload() - return mw.returnData(True, '添加放行(' + port + ')端口成功!') - - # 删除IP屏蔽 - def delDropAddressApi(self): - if not self.getFwStatus(): - return mw.returnJson(False, '防火墙启动时,才能删除规则!') - - port = request.form.get('port', '').strip() - ps = request.form.get('ps', '').strip() - sid = request.form.get('id', '').strip() - address = port - if self.__isUfw: - mw.execShell('ufw delete deny from ' + address + ' to any') - elif self.__isFirewalld: - mw.execShell( - 'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'') - elif self.__isIptables: - cmd = 'iptables -D INPUT -s ' + address + ' -j DROP' - mw.execShell(cmd) - else: - pass - - msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,)) - mw.writeLog("防火墙管理", msg) - mw.M('firewall').where("id=?", (sid,)).delete() - - self.firewallReload() - return mw.returnJson(True, '删除成功!') - - def delAcceptPortArgs(self, port, protocol='tcp'): - if self.__isUfw: - if protocol == 'tcp': - mw.execShell('ufw delete allow ' + port + '/tcp') - if protocol == 'udp': - mw.execShell('ufw delete allow ' + port + '/udp') - if protocol == 'tcp/udp': - mw.execShell('ufw delete allow ' + port + '/tcp') - mw.execShell('ufw delete allow ' + port + '/udp') - elif self.__isFirewalld: - port = port.replace(':', '-') - if protocol == 'tcp': - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') - if protocol == 'udp': - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') - if protocol == 'tcp/udp': - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') - elif self.__isIptables: - if protocol == 'tcp': - mw.execShell( - 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') - if protocol == 'udp': - mw.execShell( - 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') - if protocol == 'tcp/udp': - mw.execShell( - 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') - mw.execShell( - 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') - else: - pass - - mw.M('firewall').where("port=?", (port,)).delete() - - msg = mw.getInfo('删除防火墙放行端口[{1}][{2}]成功!', (port, protocol,)) - mw.writeLog("防火墙管理", msg) - - self.firewallReload() - return True - - # 删除放行端口 - def delAcceptPortApi(self): - port = request.form.get('port', '').strip() - protocol = request.form.get('protocol', 'tcp').strip() - sid = request.form.get('id', '').strip() - mw_port = mw.readFile('data/port.pl') - - if(port == mw_port): - return mw.returnJson(False, '失败,不能删除当前面板端口!') - try: - self.delAcceptPortArgs(port, protocol) - mw.M('firewall').where("id=?", (sid,)).delete() - - return mw.returnJson(True, '删除成功!') - except Exception as e: - return mw.returnJson(False, '删除失败!:' + str(e)) - - def getWwwPathApi(self): - path = mw.getLogsDir() - return mw.getJson({'path': path}) - - def getListApi(self): - p = request.form.get('p', '1').strip() - limit = request.form.get('limit', '10').strip() - return self.getList(int(p), int(limit)) - - def getSshInfoApi(self): - data = {} - - file = '/etc/ssh/sshd_config' - conf = mw.readFile(file) - rep = r"#*Port\s+([0-9]+)\s*\n" - port = re.search(rep, conf).groups(0)[0] - - isPing = True - try: - if mw.isAppleSystem(): - isPing = True - else: - file = '/etc/sysctl.conf' - sys_conf = mw.readFile(file) - rep = r"#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)" - tmp = re.search(rep, sys_conf).groups(0)[0] - if tmp == '1': - isPing = False - except: - isPing = True - - # sshd 检测 - status = True - cmd = "service sshd status | grep -P '(dead|stop)'|grep -v grep" - ssh_status = mw.execShell(cmd) - if ssh_status[0] != '': - status = False - - cmd = "systemctl status sshd.service | grep 'dead'|grep -v grep" - ssh_status = mw.execShell(cmd) - if ssh_status[0] != '': - status = False - - data['pass_prohibit_status'] = False - # 密码登陆配置检查 - pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" - pass_status = re.search(pass_rep, conf) - if pass_status: - if pass_status and pass_status.groups(0)[0].strip() == 'no': - data['pass_prohibit_status'] = True - else: - data['pass_prohibit_status'] = True - - data['port'] = port - data['status'] = status - data['ping'] = isPing - if mw.isAppleSystem(): - data['firewall_status'] = False - else: - data['firewall_status'] = self.getFwStatus() - return mw.getJson(data) - - def setSshPortApi(self): - port = request.form.get('port', '1').strip() - if int(port) < 22 or int(port) > 65535: - return mw.returnJson(False, '端口范围必需在22-65535之间!') - - ports = ['21', '25', '80', '443', '888'] - if port in ports: - return mw.returnJson(False, '(' + port + ')' + '特殊端口不可设置!') - - file = '/etc/ssh/sshd_config' - conf = mw.readFile(file) - - rep = r"#*Port\s+([0-9]+)\s*\n" - conf = re.sub(rep, "Port " + port + "\n", conf) - mw.writeFile(file, conf) - - self.addAcceptPortArgs(port, 'SSH端口修改', 'port') - if self.__isUfw: - mw.execShell("service ssh restart") - elif self.__isIptables: - mw.execShell("/etc/init.d/sshd restart") - elif self.__isFirewalld: - mw.execShell("systemctl restart sshd.service") - else: - return mw.returnJson(False, '修改失败!') - return mw.returnJson(True, '修改成功!') - - def setSshStatusApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能操作!') - - status = request.form.get('status', '1').strip() - msg = 'SSH服务已启用' - act = 'start' - if status == "1": - msg = 'SSH服务已停用' - act = 'stop' - - ssh_service = mw.systemdCfgDir() + '/sshd.service' - if os.path.exists(ssh_service): - mw.execShell("systemctl " + act + " sshd.service") - else: - mw.execShell('service sshd ' + act) - - if os.path.exists('/etc/init.d/sshd'): - mw.execShell('/etc/init.d/sshd ' + act) - - mw.writeLog("防火墙管理", msg) - return mw.returnJson(True, '操作成功!') - - def setSshPassStatusApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能操作!') - - status = request.form.get('status', '1').strip() - msg = '禁止密码登陆成功' - if status == "1": - msg = '开启密码登陆成功' - - file = '/etc/ssh/sshd_config' - if not os.path.exists(file): - return mw.returnJson(False, '无法设置!') - - conf = mw.readFile(file) - - pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" - pass_status = re.search(pass_rep, conf) - if not pass_status: - rep = r"(#)?PasswordAuthentication\s+(\w*)\s*\n" - conf = re.sub(rep, "PasswordAuthentication yes\n", conf) - - if status == '1': - rep = r"PasswordAuthentication\s+(\w*)\s*\n" - conf = re.sub(rep, "PasswordAuthentication yes\n", conf) - else: - rep = r"PasswordAuthentication\s+(\w*)\s*\n" - conf = re.sub(rep, "PasswordAuthentication no\n", conf) - mw.writeFile(file, conf) - mw.execShell("systemctl restart sshd.service") - mw.writeLog("SSH管理", msg) - return mw.returnJson(True, msg) - - def setPingApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能操作!') - - status = request.form.get('status') - filename = '/etc/sysctl.conf' - conf = mw.readFile(filename) - if conf.find('net.ipv4.icmp_echo') != -1: - rep = r"net\.ipv4\.icmp_echo.*" - conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + status, conf) - else: - conf += "\nnet.ipv4.icmp_echo_ignore_all=" + status - - mw.writeFile(filename, conf) - mw.execShell('sysctl -p') - return mw.returnJson(True, '设置成功!') - - def setFwApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能设置!') - - status = request.form.get('status', '1') - return mw.getJson(self.setFw(status)) - - def setFwIptables(self, status): - # iptables特殊处理 - if status == '1': - mw.execShell('service iptables save') - mw.execShell('service iptables stop') - else: - # 重新导入数据 - _list = mw.M('firewall').field('id,port,ps,addtime').limit( - '0,1000').order('id desc').select() - - mw.execShell('iptables -P INPUT DROP') - mw.execShell('iptables -P OUTPUT ACCEPT') - for x in _list: - port = x['port'] - if mw.isIpAddr(port): - cmd = 'iptables -I INPUT -s ' + port + ' -j DROP' - mw.execShell(cmd) - else: - self.addAcceptPort(port) - - mw.execShell('service iptables save') - mw.execShell('service iptables start') - - def setFw(self, status): - - if self.__isIptables: - self.setFwIptables(status) - return mw.returnData(True, '设置成功!') - - if status == '1': - if self.__isUfw: - mw.execShell('/usr/sbin/ufw disable') - - elif self.__isFirewalld: - mw.execShell('systemctl stop firewalld.service') - mw.execShell('systemctl disable firewalld.service') - else: - pass - else: - if self.__isUfw: - mw.execShell("echo 'y'| ufw enable") - elif self.__isFirewalld: - mw.execShell('systemctl start firewalld.service') - mw.execShell('systemctl enable firewalld.service') - else: - pass - - return mw.returnData(True, '设置成功!') - - ##### ----- start ----- ### - - def getList(self, page, limit): - - start = (page - 1) * limit - - _list = mw.M('firewall').field('id,port,protocol,ps,addtime').limit( - str(start) + ',' + str(limit)).order('id desc').select() - data = {} - data['data'] = _list - - count = mw.M('firewall').count() - _page = {} - _page['count'] = count - _page['tojs'] = 'showAccept' - _page['p'] = page - - data['page'] = mw.getPage(_page) - return mw.getJson(data) - - def addAcceptPort(self, port, protocol='tcp'): - if self.__isUfw: - if protocol == 'tcp': - mw.execShell('ufw allow ' + port + '/tcp') - if protocol == 'udp': - mw.execShell('ufw allow ' + port + '/udp') - if protocol == 'tcp/udp': - mw.execShell('ufw allow ' + port + '/tcp') - mw.execShell('ufw allow ' + port + '/udp') - elif self.__isFirewalld: - port = port.replace(':', '-') - if protocol == 'tcp': - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' - mw.execShell(cmd) - if protocol == 'udp': - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' - mw.execShell(cmd) - if protocol == 'tcp/udp': - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' - mw.execShell(cmd) - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' - mw.execShell(cmd) - elif self.__isIptables: - if protocol == 'tcp': - cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - if protocol == 'udp': - cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - if protocol == 'tcp/udp': - cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - else: - pass - return True - - def firewallReload(self): - if self.__isUfw: - mw.execShell('/usr/sbin/ufw reload') - return - elif self.__isIptables: - mw.execShell('service iptables save') - mw.execShell('service iptables restart') - elif self.__isFirewalld: - mw.execShell('firewall-cmd --reload') - else: - pass - - def getFwStatus(self): - if self.__isUfw: - cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'" - data = mw.execShell(cmd) - if data[0].strip() == 'inactive': - return False - return True - elif self.__isIptables: - cmd = "systemctl status iptables | grep 'inactive'" - data = mw.execShell(cmd) - if data[0] != '': - return False - return True - elif self.__isFirewalld: - cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'" - data = mw.execShell(cmd) - if data[0] == '': - return False - return True - else: - return False diff --git a/class/core/logs_api.py b/class/core/logs_api.py deleted file mode 100644 index 128d9a5dd..000000000 --- a/class/core/logs_api.py +++ /dev/null @@ -1,355 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 日志类操作 -# --------------------------------------------------------------------------------- - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - -from datetime import datetime -from flask import request - - -class logs_api: - - __months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', - 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'} - - def __init__(self): - pass - - def delPanelLogsApi(self): - mw.M('logs').where('id>?', (0,)).delete() - mw.writeLog('面板设置', '面板操作日志已清空!') - return mw.returnJson(True, '面板操作日志已清空!') - - def getLogListApi(self): - p = request.form.get('p', '1').strip() - limit = request.form.get('limit', '10').strip() - search = request.form.get('search', '').strip() - return self.getLogList(int(p), int(limit), search) - - def getLogList(self, page, limit, search=''): - find_search = '' - if search != '': - find_search = "type like '%" + search + "%' or log like '%" + \ - search + "%' or addtime like '%" + search + "%'" - - start = (page - 1) * limit - - _list = mw.M('logs').where(find_search, ()).field( - 'id,type,log,addtime').limit(str(start) + ',' + str(limit)).order('id desc').select() - data = {} - data['data'] = _list - - count = mw.M('logs').where(find_search, ()).count() - _page = {} - _page['count'] = count - _page['tojs'] = 'getLogs' - _page['p'] = page - - data['page'] = mw.getPage(_page) - return mw.getJson(data) - - def getLogsTitle(self, log_name): - log_name = log_name.replace('.1', '') - if log_name in ['mw-update.log']: - return '面板更新日志' - if log_name in ['mw-install.log']: - return '面板安装日志' - if log_name in ['auth.log', 'secure'] or log_name.find('auth.') == 0: - return '授权日志' - if log_name in ['dmesg'] or log_name.find('dmesg') == 0: - return '内核缓冲区日志' - if log_name in ['syslog'] or log_name.find('syslog') == 0: - return '系统日志' - if log_name in ['rsyncd.log']: - return '远程同步日志' - if log_name in ['btmp']: - return '失败的登录记录' - if log_name in ['utmp', 'wtmp']: - return '登录和重启记录' - if log_name in ['lastlog']: - return '用户最后登录' - if log_name in ['yum.log']: - return 'yum包管理器日志' - if log_name in ['anaconda.log']: - return 'Anaconda日志' - if log_name in ['dpkg.log']: - return 'dpkg日志' - if log_name in ['daemon.log']: - return '系统后台守护进程日志' - if log_name in ['boot.log']: - return '启动日志' - if log_name in ['kern.log']: - return '内核日志' - if log_name in ['maillog', 'mail.log']: - return '邮件日志' - if log_name.find('Xorg') == 0: - return 'Xorg日志' - if log_name in ['cron.log']: - return '定时任务日志' - if log_name in ['alternatives.log']: - return '更新替代信息' - if log_name in ['debug']: - return '调试信息' - if log_name.find('apt') == 0: - return 'apt-get相关日志' - if log_name.find('installer') == 0: - return '系统安装相关日志' - if log_name in ['messages']: - return '综合日志' - return '{}日志'.format(log_name.split('.')[0]) - - def getAuditLogsFilesApi(self): - log_dir = '/var/log' - log_files = [] - for log_file in os.listdir(log_dir): - log_suffix = log_file.split('.')[-1:] - if log_suffix[0] in ['gz', 'xz', 'bz2', 'asl']: - continue - - if log_file in ['.', '..']: - continue - - filename = os.path.join(log_dir, log_file) - if not os.path.exists(filename): - continue - - if os.path.isfile(filename): - file_size = os.path.getsize(filename) - if not file_size: - continue - - tmp = { - 'name': log_file, - 'size': file_size, - 'log_file': filename, - 'title': self.getLogsTitle(log_file), - 'uptime': os.path.getmtime(filename) - } - - log_files.append(tmp) - else: - for next_name in os.listdir(filename): - if next_name[-3:] in ['.gz', '.xz']: - continue - next_file = os.path.join(filename, next_name) - if not os.path.isfile(next_file): - continue - file_size = os.path.getsize(next_file) - if not file_size: - continue - log_name = '{}/{}'.format(log_file, next_name) - tmp = { - 'name': log_name, - 'size': file_size, - 'log_file': next_file, - 'title': self.getLogsTitle(log_name), - 'uptime': os.path.getmtime(next_file) - } - log_files.append(tmp) - log_files = sorted(log_files, key=lambda x: x['name'], reverse=True) - return mw.getJson(log_files) - - def getAuditFileApi(self): - log_name = request.form.get('log_name', '').strip() - return self.getAuditLogsName(log_name) - - def __to_date2(self, date_str): - tmp = date_str.split() - s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] - return s_date - - def __to_date3(self, date_str): - tmp = date_str.split() - s_date = str(datetime.now().year) + '-' + self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] - return s_date - - def __to_date4(self, date_str): - tmp = date_str.split() - s_date = str(datetime.now().year) + '-' + \ - self.__months.get(tmp[0], tmp[0]) + '-' + tmp[1] + ' ' + tmp[2] - return s_date - - def getAuditLast(self, log_name): - # 获取日志 - cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format( - '/var/log/' + log_name) - result = mw.execShell(cmd) - lastlog_list = [] - for _line in result[0].split("\n"): - if not _line: - continue - tmp = {} - sp_arr = _line.split() - tmp['用户'] = sp_arr[0] - if sp_arr[0] == 'runlevel': - tmp['来源'] = sp_arr[4] - tmp['端口'] = ' '.join(sp_arr[1:4]) - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[5:])) + ' ' + ' '.join(sp_arr[-2:]) - elif sp_arr[0] in ['reboot', 'shutdown']: - tmp['来源'] = sp_arr[3] - tmp['端口'] = ' '.join(sp_arr[1:3]) - if sp_arr[-3] == '-': - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-3:]) - else: - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-2:]) - elif sp_arr[1] in ['tty1', 'tty', 'tty2', 'tty3', 'hvc0', 'hvc1', 'hvc2'] or len(sp_arr) == 9: - tmp['来源'] = '' - tmp['端口'] = sp_arr[1] - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[2:])) + ' ' + ' '.join(sp_arr[-3:]) - else: - tmp['来源'] = sp_arr[2] - tmp['端口'] = sp_arr[1] - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[3:])) + ' ' + ' '.join(sp_arr[-3:]) - - # tmp['_line'] = _line - lastlog_list.append(tmp) - # lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True) - return mw.returnData(True, 'ok!', lastlog_list) - - def getAuditLastLog(self): - cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username''' - result = mw.execShell(cmd) - lastlog_list = [] - for _line in result[0].split("\n"): - if not _line: - continue - tmp = {} - sp_arr = _line.split() - tmp['用户'] = sp_arr[0] - # tmp['_line'] = _line - if _line.find('Never logged in') != -1: - tmp['最后登录时间'] = '0' - tmp['最后登录来源'] = '-' - tmp['最后登录端口'] = '-' - lastlog_list.append(tmp) - continue - tmp['最后登录来源'] = sp_arr[2] - tmp['最后登录端口'] = sp_arr[1] - tmp['最后登录时间'] = self.__to_date2(' '.join(sp_arr[3:])) - - lastlog_list.append(tmp) - lastlog_list = sorted(lastlog_list, key=lambda x: x[ - '最后登录时间'], reverse=True) - for i in range(len(lastlog_list)): - if lastlog_list[i]['最后登录时间'] == '0': - lastlog_list[i]['最后登录时间'] = '从未登录过' - return mw.returnData(True, 'ok!', lastlog_list) - - def parseAuditFile(self, log_name, result): - log_list = [] - is_string = True - for _line in result.split("\n"): - if not _line.strip(): - continue - if log_name.find('sa/sa') == -1: - if _line[:3] in self.__months: - _msg = _line[16:] - _tmp = _msg.split(": ") - _act = '' - if len(_tmp) > 1: - _act = _tmp[0] - _msg = _tmp[1] - else: - _msg = _tmp[0] - _line = { - "时间": self.__to_date4(_line[:16].strip()), - "角色": _act, - "事件": _msg - } - is_string = False - elif _line[:2] in ['19', '20', '21', '22', '23', '24']: - _msg = _line[19:] - _tmp = _msg.split(" ") - _act = _tmp[1] - _msg = ' '.join(_tmp[2:]) - _line = { - "时间": _line[:19].strip(), - "角色": _act, - "事件": _msg - } - is_string = False - elif log_name.find('alternatives') == 0: - _tmp = _line.split(": ") - _last = _tmp[0].split(" ") - _act = _last[0] - _msg = ' '.join(_tmp[1:]) - _line = { - "时间": ' '.join(_last[1:]).strip(), - "角色": _act, - "事件": _msg - } - is_string = False - else: - if not is_string: - if type(_line) != dict: - continue - - log_list.append(_line) - - return log_list - - def getAuditLogsName(self, log_name): - # print(log_name) - if log_name in ['wtmp', 'btmp', 'utmp'] or log_name.find('wtmp') == 0 or log_name.find('btmp') == 0 or log_name.find('utmp') == 0: - return self.getAuditLast(log_name) - - if log_name.find('lastlog') == 0: - return self.getAuditLastLog() - - if log_name.find('sa/sa') == 0: - if log_name.find('sa/sar') == -1: - return mw.execShell("sar -f /var/log/{}".format(log_name))[0] - log_dir = '/var/log' - log_file = log_dir + '/' + log_name - if not os.path.exists(log_file): - return mw.returnData(False, '日志文件不存在!') - result = mw.getLastLine(log_file, 100) - try: - log_list = self.parseAuditFile(log_name, result) - _string = [] - _dict = [] - _list = [] - for _line in log_list: - if isinstance(_line, str): - _string.append(_line.strip()) - elif isinstance(_line, dict): - _dict.append(_line) - elif isinstance(_line, list): - _list.append(_line) - else: - continue - _str_len = len(_string) - _dict_len = len(_dict) - _list_len = len(_list) - if _str_len > _dict_len + _list_len: - return "\n".join(_string) - elif _dict_len > _str_len + _list_len: - return mw.returnData(True, 'ok!', _dict) - else: - return mw.returnData(True, 'ok!', _list) - - except: - return mw.returnData(True, 'ok!', result) diff --git a/class/core/mw.py b/class/core/mw.py deleted file mode 100755 index 40bc1771f..000000000 --- a/class/core/mw.py +++ /dev/null @@ -1,2286 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 核心方法库 -# --------------------------------------------------------------------------------- - - -import os -import sys -import time -import string -import json -import hashlib -import shlex -import datetime -import subprocess -import glob -import base64 -import re - -from random import Random - -import db - - -def execShell(cmdstring, cwd=None, timeout=None, shell=True): - - if shell: - cmdstring_list = cmdstring - else: - cmdstring_list = shlex.split(cmdstring) - if timeout: - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) - - sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE, - shell=shell, bufsize=4096, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - while sub.poll() is None: - time.sleep(0.1) - if timeout: - if end_time <= datetime.datetime.now(): - raise Exception("Timeout:%s" % cmdstring) - - if sys.version_info[0] == 2: - return sub.communicate() - - data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) - - -def getTracebackInfo(): - import traceback - errorMsg = traceback.format_exc() - return errorMsg - - -def getRunDir(): - return os.getcwd() - - -def getRootDir(): - return os.path.dirname(os.path.dirname(getRunDir())) - - -def getPluginDir(): - return getRunDir() + '/plugins' - -def getPanelDataDir(): - return getRunDir() + '/data' - -def getMWLogs(): - return getRunDir() + '/logs' - -def getPanelTmp(): - return getRunDir() + '/tmp' - - -def getServerDir(): - return getRootDir() + '/server' - - -def getLogsDir(): - return getRootDir() + '/wwwlogs' - - -def getWwwDir(): - file = getRunDir() + '/data/site.pl' - if os.path.exists(file): - return readFile(file).strip() - return getRootDir() + '/wwwroot' - - -def setWwwDir(wdir): - file = getRunDir() + '/data/site.pl' - return writeFile(file, wdir) - - -def getBackupDir(): - file = getRunDir() + '/data/backup.pl' - if os.path.exists(file): - return readFile(file).strip() - return getRootDir() + '/backup' - - -def setBackupDir(bdir): - file = getRunDir() + '/data/backup.pl' - return writeFile(file, bdir) - - -def getAcmeDir(): - acme = '/root/.acme.sh' - if isAppleSystem(): - cmd = "who | sed -n '2, 1p' |awk '{print $1}'" - user = execShell(cmd)[0].strip() - acme = '/Users/' + user + '/.acme.sh' - if not os.path.exists(acme): - acme = '/.acme.sh' - return acme - - -def getAcmeDomainDir(domain): - acme_dir = getAcmeDir() - acme_domain = acme_dir + '/' + domain - acme_domain_ecc = acme_domain + '_ecc' - if os.path.exists(acme_domain_ecc): - acme_domain = acme_domain_ecc - return acme_domain - - -def fileNameCheck(filename): - f_strs = [';', '&', '<', '>'] - for fs in f_strs: - if filename.find(fs) != -1: - return False - return True - - -def triggerTask(): - isTask = getRunDir() + '/tmp/panelTask.pl' - writeFile(isTask, 'True') - - -def systemdCfgDir(): - # ubuntu - cfg_dir = '/lib/systemd/system' - if os.path.exists(cfg_dir): - return cfg_dir - - # debian,centos - cfg_dir = '/usr/lib/systemd/system' - if os.path.exists(cfg_dir): - return cfg_dir - - # local test - return "/tmp" - - -def getSslCrt(): - if os.path.exists('/etc/ssl/certs/ca-certificates.crt'): - return '/etc/ssl/certs/ca-certificates.crt' - if os.path.exists('/etc/pki/tls/certs/ca-bundle.crt'): - return '/etc/pki/tls/certs/ca-bundle.crt' - return '' - - -def getOs(): - return sys.platform - - -def getOsName(): - cmd = "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'" - data = execShell(cmd) - return data[0].strip().lower() - - -def getOsID(): - cmd = "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'" - data = execShell(cmd) - return data[0].strip() - - -def getFileSuffix(file): - tmp = file.split('.') - ext = tmp[len(tmp) - 1] - return ext - - - -def isAppleSystem(): - if getOs() == 'darwin': - return True - return False - -def isDocker(): - return os.path.exists('/.dockerenv') - - -def isSupportSystemctl(): - if isAppleSystem(): - return False - if isDocker(): - return False - - current_os = getOs() - if current_os.startswith("freebsd"): - return False - return True - - -def isDebugMode(): - if isAppleSystem(): - return True - - debugPath = getRunDir() + "/data/debug.pl" - if os.path.exists(debugPath): - return True - - return False - - -def isNumber(s): - try: - float(s) - return True - except ValueError: - pass - - try: - import unicodedata - unicodedata.numeric(s) - return True - except (TypeError, ValueError): - pass - - return False - - -def deleteFile(file): - if os.path.exists(file): - os.remove(file) - - -def isInstalledWeb(): - path = getServerDir() + '/openresty/nginx/sbin/nginx' - if os.path.exists(path): - return True - return False - - -def isIpAddr(ip): - check_ip = re.compile( - '^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$') - if check_ip.match(ip): - return True - else: - return False - - -def getWebStatus(): - pid = getServerDir() + '/openresty/nginx/logs/nginx.pid' - if os.path.exists(pid): - return True - return False - - -# ------------------------------ openresty start ----------------------------- -def restartWeb(): - return opWeb("reload") - - -def opWeb(method): - if not isInstalledWeb(): - return False - - # systemd - systemd = '/lib/systemd/system/openresty.service' - if os.path.exists(systemd): - execShell('systemctl ' + method + ' openresty') - return True - - # initd - initd = getServerDir() + '/openresty/init.d/openresty' - - if os.path.exists(initd): - execShell(initd + ' ' + method) - return True - - return False - - -def opLuaMake(cmd_name): - path = getServerDir() + '/web_conf/nginx/lua/lua.conf' - root_dir = getServerDir() + '/web_conf/nginx/lua/' + cmd_name - dst_path = getServerDir() + '/web_conf/nginx/lua/' + cmd_name + '.lua' - def_path = getServerDir() + '/web_conf/nginx/lua/empty.lua' - - if not os.path.exists(root_dir): - execShell('mkdir -p ' + root_dir) - - files = [] - for fl in os.listdir(root_dir): - suffix = getFileSuffix(fl) - if suffix != 'lua': - continue - flpath = os.path.join(root_dir, fl) - files.append(flpath) - - if len(files) > 0: - def_path = dst_path - content = '' - for f in files: - t = readFile(f) - f_base = os.path.basename(f) - content += '-- ' + '*' * 20 + ' ' + f_base + ' start ' + '*' * 20 + "\n" - content += t - content += "\n" + '-- ' + '*' * 20 + ' ' + f_base + ' end ' + '*' * 20 + "\n" - writeFile(dst_path, content) - else: - if os.path.exists(dst_path): - os.remove(dst_path) - - conf = readFile(path) - conf = re.sub(cmd_name + ' (.*);', - cmd_name + " " + def_path + ";", conf) - writeFile(path, conf) - - -def opLuaInitFile(): - opLuaMake('init_by_lua_file') - - -def opLuaInitWorkerFile(): - opLuaMake('init_worker_by_lua_file') - - -def opLuaInitAccessFile(): - opLuaMake('access_by_lua_file') - - -def opLuaMakeAll(): - opLuaInitFile() - opLuaInitWorkerFile() - opLuaInitAccessFile() - -# ------------------------------ openresty end ----------------------------- - - -def restartMw(): - import system_api - system_api.system_api().restartMw() - -def restartNginx(self): - writeFile('data/restart_nginx.pl', 'True') - return True - -def checkWebConfig(): - op_dir = getServerDir() + '/openresty/nginx' - # "ulimit -n 10240 && " + - cmd = op_dir + \ - "/sbin/nginx -t -c " + op_dir + "/conf/nginx.conf" - result = execShell(cmd) - searchStr = 'test is successful' - if result[1].find(searchStr) == -1: - msg = getInfo('配置文件错误: {1}', (result[1],)) - writeLog("软件管理", msg) - return result[1] - return True - - -def M(table): - sql = db.Sql() - return sql.table(table) - - -def getPage(args, result='1,2,3,4,5,8'): - data = getPageObject(args, result) - return data[0] - - -def getPageObject(args, result='1,2,3,4,5,8'): - # 取分页 - import page - # 实例化分页类 - page = page.Page() - info = {} - - info['count'] = 0 - if 'count' in args: - info['count'] = int(args['count']) - - info['row'] = 10 - if 'row' in args: - info['row'] = int(args['row']) - - info['p'] = 1 - if 'p' in args: - info['p'] = int(args['p']) - info['uri'] = {} - info['return_js'] = '' - if 'tojs' in args: - info['return_js'] = args['tojs'] - - if 'args_tpl' in args: - info['args_tpl'] = args['args_tpl'] - - return (page.GetPage(info, result), page) - - -def md5(content): - # 生成MD5 - try: - m = hashlib.md5() - m.update(content.encode("utf-8")) - return m.hexdigest() - except Exception as ex: - return False - - -def getFileMd5(filename): - # 文件的MD5值 - if not os.path.isfile(filename): - return False - - myhash = hashlib.md5() - f = file(filename, 'rb') - while True: - b = f.read(8096) - if not b: - break - myhash.update(b) - f.close() - return myhash.hexdigest() - - -def getRandomString(length): - # 取随机字符串 - rnd_str = '' - chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' - chrlen = len(chars) - 1 - random = Random() - for i in range(length): - rnd_str += chars[random.randint(0, chrlen)] - return rnd_str - - -def getUniqueId(): - """ - 根据时间生成唯一ID - :return: - """ - current_time = datetime.datetime.now() - str_time = current_time.strftime('%Y%m%d%H%M%S%f')[:-3] - unique_id = "{0}".format(str_time) - return unique_id - - -def getJson(data): - import json - return json.dumps(data) - -def getObjectByJson(data): - import json - return json.loads(data) - -def returnData(status, msg, data=None): - return {'status': status, 'msg': msg, 'data': data} - - -def returnJson(status, msg, data=None): - # if data == None: - # return {'status': status, 'msg': msg} - # return {'status': status, 'msg': msg, 'data': data} - if data == None: - return getJson({'status': status, 'msg': msg}) - return getJson({'status': status, 'msg': msg, 'data': data}) - - -def getLanguage(): - path = 'data/language.pl' - if not os.path.exists(path): - return 'Simplified_Chinese' - return readFile(path).strip() - - -def getStaticJson(name="public"): - file = 'static/language/' + getLanguage() + '/' + name + '.json' - if not os.path.exists(file): - file = 'route/static/language/' + getLanguage() + '/' + name + '.json' - return file - - -def returnMsg(status, msg, args=()): - # 取通用字曲返回 - pjson = getStaticJson('public') - logMessage = json.loads(readFile(pjson)) - keys = logMessage.keys() - - if msg in keys: - msg = logMessage[msg] - for i in range(len(args)): - rep = '{' + str(i + 1) + '}' - msg = msg.replace(rep, args[i]) - return {'status': status, 'msg': msg, 'data': args} - - -def getInfo(msg, args=()): - # 取提示消息 - for i in range(len(args)): - rep = '{' + str(i + 1) + '}' - msg = msg.replace(rep, args[i]) - return msg - - -def getMsg(key, args=()): - # 取提示消息 - try: - pjson = getStaticJson('public') - logMessage = json.loads(pjson) - keys = logMessage.keys() - msg = None - if key in keys: - msg = logMessage[key] - for i in range(len(args)): - rep = '{' + str(i + 1) + '}' - msg = msg.replace(rep, args[i]) - return msg - except: - return key - - -def getLan(key): - # 取提示消息 - pjson = getStaticJson('public') - logMessage = json.loads(pjson) - keys = logMessage.keys() - msg = None - if key in keys: - msg = logMessage[key] - return msg - - -def readFile(filename): - # 读文件内容 - try: - fp = open(filename, 'r') - fBody = fp.read() - fp.close() - return fBody - except Exception as e: - # print(e) - return False - - -def getDate(): - # 取格式时间 - import time - return time.strftime('%Y-%m-%d %X', time.localtime()) - - -def getDateFromNow(tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"): - # 取格式时间 - import time - os.environ['TZ'] = time_zone - time.tzset() - return time.strftime(tf_format, time.localtime()) - - -def getDataFromInt(val): - time_format = '%Y-%m-%d %H:%M:%S' - time_str = time.localtime(val) - return time.strftime(time_format, time_str) - - -def writeLog(stype, msg, args=()): - # 写日志 - uid = 1 - try: - from flask import session - if 'uid' in session: - uid = session['uid'] - except Exception as e: - print('writeL:',e) - # pass - # writeFileLog(getTracebackInfo()) - - print(stype, msg) - return writeDbLog(stype, msg, args, uid) - - -def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3): - log_file = getPanelDir() + '/logs/debug.log' - if path != None: - log_file = path - - if os.path.exists(log_file): - size = os.path.getsize(log_file) - if size > limit_size: - log_file_rename = log_file + "_" + \ - time.strftime("%Y-%m-%d_%H%M%S") + '.log' - os.rename(log_file, log_file_rename) - logs = sorted(glob.glob(log_file + "_*")) - count = len(logs) - save_limit = count - save_limit - for i in range(count): - if i > save_limit: - break - os.remove(logs[i]) - # print('|---多余日志[' + logs[i] + ']已删除!') - - f = open(log_file, 'ab+') - msg += "\n" - if __name__ == '__main__': - print(msg) - f.write(msg.encode('utf-8')) - f.close() - return True - - -def writeDbLog(stype, msg, args=(), uid=1): - try: - add_time = formatDate() - format_msg = getInfo(msg, args) - add_data = { - 'type':stype, - 'msg':format_msg, - 'uid':uid, - 'add_time':add_time, - } - result = M('logs').insert(add_data) - return True - except Exception as e: - return False - - -def writeFile(filename, content, mode='w+'): - # 写文件内容 - try: - fp = open(filename, mode) - fp.write(content) - fp.close() - return True - except Exception as e: - return False - - -def backFile(file, act=None): - """ - @name 备份配置文件 - @param file 需要备份的文件 - @param act 如果存在,则备份一份作为默认配置 - """ - file_type = "_bak" - if act: - file_type = "_def" - - # print("cp -p {0} {1}".format(file, file + file_type)) - execShell("cp -p {0} {1}".format(file, file + file_type)) - - -def removeBackFile(file, act=None): - """ - @name 删除备份配置文件 - @param file 需要删除备份文件 - @param act 如果存在,则还原默认配置 - """ - file_type = "_bak" - if act: - file_type = "_def" - execShell("rm -rf {0}".format(file + file_type)) - - -def restoreFile(file, act=None): - """ - @name 还原配置文件 - @param file 需要还原的文件 - @param act 如果存在,则还原默认配置 - """ - file_type = "_bak" - if act: - file_type = "_def" - execShell("cp -p {1} {0}".format(file, file + file_type)) - -def base64StrEncode(content): - content = bytes(content,'utf-8') - content = base64.b64encode(content) - return content.decode('utf8') - -def base64StrDecode(content): - content = bytes(content,'utf-8') - content = base64.urlsafe_b64decode(content) - return content.decode('utf8') - -def enPunycode(domain): - if sys.version_info[0] == 2: - domain = domain.encode('utf8') - tmp = domain.split('.') - newdomain = '' - for dkey in tmp: - if dkey == '*': - continue - # 匹配非ascii字符 - match = re.search(u"[\x80-\xff]+", dkey) - if not match: - match = re.search(u"[\u4e00-\u9fa5]+", dkey) - if not match: - newdomain += dkey + '.' - else: - if sys.version_info[0] == 2: - newdomain += 'xn--' + dkey.decode('utf-8').encode('punycode') + '.' - else: - newdomain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.' - if tmp[0] == '*': - newdomain = "*." + newdomain - return newdomain[0:-1] - - -def dePunycode(domain): - # punycode 转中文 - tmp = domain.split('.') - newdomain = '' - for dkey in tmp: - if dkey.find('xn--') >= 0: - newdomain += dkey.replace('xn--','').encode('utf-8').decode('punycode') + '.' - else: - newdomain += dkey + '.' - return newdomain[0:-1] - - -def enCrypt(key, strings): - # 加密字符串 - try: - import base64 - _key = key.encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - import cryptography - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.encrypt(strings) - return result.decode('utf-8') - except: - writeFileLog(getTracebackInfo()) - return strings - - -def deCrypt(key, strings): - - # 解密字符串 - try: - import base64 - _key = key.encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.decrypt(strings).decode('utf-8') - return result - except: - writeFileLog(getTracebackInfo()) - return strings - - -def enDoubleCrypt(key, strings): - # 加密字符串 - try: - import base64 - _key = md5(key).encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - import cryptography - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.encrypt(strings) - return result.decode('utf-8') - except: - writeFileLog(getTracebackInfo()) - return strings - - -def deDoubleCrypt(key, strings): - # 解密字符串 - try: - import base64 - _key = md5(key).encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.decrypt(strings).decode('utf-8') - return result - except: - writeFileLog(getTracebackInfo()) - return strings - - -def aesEncrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): - # aes加密 - # @param data 被加密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from cryptography.hazmat.primitives import padding - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - from cryptography.hazmat.backends import default_backend - - if not isinstance(data, bytes): - data = data.encode() - - # AES_CBC_KEY = os.urandom(32) - # AES_CBC_IV = os.urandom(16) - - AES_CBC_KEY = key.encode() - AES_CBC_IV = vi.encode() - - # print("AES_CBC_KEY:", AES_CBC_KEY) - # print("AES_CBC_IV:", AES_CBC_IV) - - padder = padding.PKCS7(algorithms.AES.block_size).padder() - padded_data = padder.update(data) + padder.finalize() - - cipher = Cipher(algorithms.AES(AES_CBC_KEY), - modes.CBC(AES_CBC_IV), - backend=default_backend()) - encryptor = cipher.encryptor() - - edata = encryptor.update(padded_data) - - # print(edata) - # print(str(edata)) - # print(edata.decode()) - return edata - - -def aesDecrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): - # aes加密 - # @param data 被解密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from cryptography.hazmat.primitives import padding - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - from cryptography.hazmat.backends import default_backend - - from cryptography.hazmat.primitives import padding - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - from cryptography.hazmat.backends import default_backend - - if not isinstance(data, bytes): - data = data.encode() - - AES_CBC_KEY = key.encode() - AES_CBC_IV = vi.encode() - - cipher = Cipher(algorithms.AES(AES_CBC_KEY), - modes.CBC(AES_CBC_IV), - backend=default_backend()) - decryptor = cipher.decryptor() - - ddata = decryptor.update(data) - - unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() - data = unpadder.update(ddata) - - try: - uppadded_data = data + unpadder.finalize() - except ValueError: - raise Exception('无效的加密信息!') - - return uppadded_data - - -def aesEncrypt_Crypto(data, key, vi): - # 该方法保留,暂时不使用 - # aes加密 - # @param data 被加密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from Crypto.Cipher import AES - cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) - # 判断是否含有中文 - zhmodel = re.compile(u'[\u4e00-\u9fff]') - match = zhmodel.search(data) - if match == None: - # 无中文时 - add = 16 - len(data) % 16 - pad = lambda s: s + add * chr(add) - data = pad(data) - enctext = cryptor.encrypt(data.encode('utf8')) - else: - # 含有中文时 - data = data.encode() - add = 16 - len(data) % 16 - data = data + add * (chr(add)).encode() - enctext = cryptor.encrypt(data) - encodestrs = base64.b64encode(enctext).decode('utf8') - return encodestrs - - -def aesDecrypt_Crypto(data, key, vi): - # 该方法保留,暂时不使用 - # aes加密 - # @param data 被加密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from crypto.Cipher import AES - data = data.encode('utf8') - encodebytes = base64.urlsafe_b64decode(data) - cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) - text_decrypted = cipher.decrypt(encodebytes) - # 判断是否含有中文 - zhmodel = re.compile(u'[\u4e00-\u9fff]') - match = zhmodel.search(text_decrypted) - if match == False: - # 无中文时补位 - unpad = lambda s: s[0:-s[-1]] - text_decrypted = unpad(text_decrypted) - text_decrypted = text_decrypted.decode('utf8').rstrip() # 去掉补位的右侧空格 - return text_decrypted - -def getDefault(data,val,def_val=''): - if val in data: - return data[val] - return def_val - -def encodeImage(imgsrc, newsrc): - # 图片加密 - import struct - old_fp = open(imgsrc, 'rb') - imgFile = old_fp.read() - old_fp.close() - - new_fp = open(newsrc,"wb") - for x in imgFile: - value = x ^ 86 - value = hex(value) - s = struct.pack('B',int(value,16)) - new_fp.write(s) - new_fp.close() - return True - -def buildSoftLink(src, dst, force=False): - ''' - 建立软连接 - ''' - if not os.path.exists(src): - return False - - if os.path.exists(dst) and force: - os.remove(dst) - - if not os.path.exists(dst): - execShell('ln -sf "' + src + '" "' + dst + '"') - return True - return False - - -def HttpGet(url, timeout=10): - """ - 发送GET请求 - @url 被请求的URL地址(必需) - @timeout 超时时间默认60秒 - return string - """ - if sys.version_info[0] == 2: - try: - import urllib2 - import ssl - if sys.version_info[0] == 2: - reload(urllib2) - reload(ssl) - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - response = urllib2.urlopen(url, timeout=timeout) - return response.read() - except Exception as ex: - return str(ex) - else: - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - response = urllib.request.urlopen(url, timeout=timeout) - result = response.read() - if type(result) == bytes: - result = result.decode('utf-8') - return result - except Exception as ex: - return str(ex) - - -def HttpGet2(url, timeout): - import urllib.request - - try: - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - req = urllib.request.urlopen(url, timeout=timeout) - result = req.read().decode('utf-8') - return result - - except Exception as e: - return str(e) - - -def httpGet(url, timeout=10): - return HttpGet2(url, timeout) - - -def HttpPost(url, data, timeout=10): - """ - 发送POST请求 - @url 被请求的URL地址(必需) - @data POST参数,可以是字符串或字典(必需) - @timeout 超时时间默认60秒 - return string - """ - if sys.version_info[0] == 2: - try: - import urllib - import urllib2 - import ssl - ssl._create_default_https_context = ssl._create_unverified_context - data = urllib.urlencode(data) - req = urllib2.Request(url, data) - response = urllib2.urlopen(req, timeout=timeout) - return response.read() - except Exception as ex: - return str(ex) - else: - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - data = urllib.parse.urlencode(data).encode('utf-8') - req = urllib.request.Request(url, data) - response = urllib.request.urlopen(req, timeout=timeout) - result = response.read() - if type(result) == bytes: - result = result.decode('utf-8') - return result - except Exception as ex: - return str(ex) - - -def httpPost(url, data, timeout=10): - return HttpPost(url, data, timeout) - - -def writeSpeed(title, used, total, speed=0): - # 写进度 - if not title: - data = {'title': None, 'progress': 0, - 'total': 0, 'used': 0, 'speed': 0} - else: - progress = int((100.0 * used / total)) - data = {'title': title, 'progress': progress, - 'total': total, 'used': used, 'speed': speed} - writeFile('/tmp/panelSpeed.pl', json.dumps(data)) - return True - - -def getSpeed(): - # 取进度 - path = getRootDir() - data = readFile(path + '/tmp/panelSpeed.pl') - if not data: - data = json.dumps({'title': None, 'progress': 0, - 'total': 0, 'used': 0, 'speed': 0}) - writeFile(path + '/tmp/panelSpeed.pl', data) - return json.loads(data) - - -def getLastLineBk(inputfile, lineNum): - # 读文件指定倒数行数 - try: - fp = open(inputfile, 'rb') - lastLine = "" - lines = fp.readlines() - count = len(lines) - if count > lineNum: - num = lineNum - else: - num = count - i = 1 - lastre = [] - for i in range(1, (num + 1)): - n = -i - try: - lastLine = lines[n].decode("utf-8", "ignore").strip() - except Exception as e: - lastLine = "" - lastre.append(lastLine) - - fp.close() - result = '' - num -= 1 - while num >= 0: - result += lastre[num] + "\n" - num -= 1 - return result - except Exception as e: - return str(e) - # return getMsg('TASK_SLEEP') - - -def getLastLine(path, num, p=1): - pyVersion = sys.version_info[0] - try: - import html - if not os.path.exists(path): - return "" - start_line = (p - 1) * num - count = start_line + num - fp = open(path, 'rb') - buf = "" - - fp.seek(0, 2) - if fp.read(1) == "\n": - fp.seek(0, 2) - data = [] - b = True - n = 0 - - for i in range(count): - while True: - newline_pos = str.rfind(str(buf), "\n") - pos = fp.tell() - if newline_pos != -1: - if n >= start_line: - line = buf[newline_pos + 1:] - try: - data.insert(0, html.escape(line)) - except Exception as e: - pass - buf = buf[:newline_pos] - n += 1 - break - else: - if pos == 0: - b = False - break - to_read = min(4096, pos) - fp.seek(-to_read, 1) - t_buf = fp.read(to_read) - if pyVersion == 3: - if type(t_buf) == bytes: - t_buf = t_buf.decode("utf-8", "ignore").strip() - buf = t_buf + buf - fp.seek(-to_read, 1) - if pos - to_read == 0: - buf = "\n" + buf - if not b: - break - fp.close() - except Exception as e: - return str(e) - - return "\n".join(data) - - -def downloadFile(url, filename): - import urllib - urllib.urlretrieve(url, filename=filename, reporthook=downloadHook) - - -def downloadHook(count, blockSize, totalSize): - speed = {'total': totalSize, 'block': blockSize, 'count': count} - print('%02d%%' % (100.0 * count * blockSize / totalSize)) - - -def getLocalIpBack(): - # 取本地外网IP - try: - import re - filename = 'data/iplist.txt' - ipaddress = readFile(filename) - if not ipaddress or ipaddress == '127.0.0.1': - import urllib - url = 'http://pv.sohu.com/cityjson?ie=utf-8' - req = urllib.request.urlopen(url, timeout=10) - content = req.read().decode('utf-8') - ipaddress = re.search('\\d+.\\d+.\\d+.\\d+', content).group(0) - writeFile(filename, ipaddress) - - ipaddress = re.search('\\d+.\\d+.\\d+.\\d+', ipaddress).group(0) - return ipaddress - except Exception as ex: - # print(ex) - return '127.0.0.1' - - -def getClientIp(): - from flask import request - return request.remote_addr.replace('::ffff:', '') - - -def getLocalIp(): - filename = 'data/iplist.txt' - try: - ipaddress = readFile(filename) - if not ipaddress or ipaddress == '127.0.0.1': - cmd = "curl --insecure -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" - ip = execShell(cmd) - result = ip[0].strip() - if result == '': - raise Exception("ipv4 is empty!") - writeFile(filename, result) - return result - return ipaddress - except Exception as e: - cmd = "curl --insecure -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" - ip = execShell(cmd) - result = ip[0].strip() - if result == '': - return '127.0.0.1' - writeFile(filename, result) - return result - finally: - pass - return '127.0.0.1' - - -def inArray(arrays, searchStr): - # 搜索数据中是否存在 - for key in arrays: - if key == searchStr: - return True - - return False - - -def formatDate(format="%Y-%m-%d %H:%M:%S", times=None): - # 格式化指定时间戳 - if not times: - times = int(time.time()) - time_local = time.localtime(times) - return time.strftime(format, time_local) - - -def strfToTime(sdate): - # 转换时间 - import time - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%b %d %H:%M:%S %Y %Z')) - - -def checkIp(ip): - # 检查是否为IPv4地址 - import re - p = re.compile( - '^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$') - if p.match(ip): - return True - else: - return False - - -def getHost(port=False): - from flask import request - host_tmp = request.headers.get('host') - if not host_tmp: - if request.url_root: - tmp = re.findall(r"(https|http)://([\w:\.-]+)", request.url_root) - if tmp: - host_tmp = tmp[0][1] - if not host_tmp: - host_tmp = getLocalIp() + ':' + readFile('data/port.pl').strip() - try: - if host_tmp.find(':') == -1: - host_tmp += ':80' - except: - host_tmp = "127.0.0.1:8888" - h = host_tmp.split(':') - if port: - return h[-1] - return ':'.join(h[0:-1]) - - -def getClientIp(): - from flask import request - return request.remote_addr.replace('::ffff:', '') - - -def checkDomainPanel(): - tmp = getHost() - domain = readFile('data/bind_domain.pl') - - port = 7200 - if os.path.exists('data/port.pl'): - port = readFile('data/port.pl').strip() - - scheme = 'http' - - choose_file = getRunDir()+'/ssl/choose.pl' - if os.path.exists(choose_file): - choose = readFile(choose_file).strip() - if not inArray(['local','nginx'], choose): - return False - else: - return False - - local_ssl = getRunDir()+'/ssl/local' - if choose == 'local': - scheme = 'https' - - if choose == 'nginx': - # print(port) - npid = getServerDir() + "/openresty/nginx/logs/nginx.pid" - if not os.path.exists(npid): - return False - - nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf" - if os.path.exists(nconf): - port = "80" - if not domain: - return False - - client_ip = getClientIp() - if client_ip in ['127.0.0.1', 'localhost', '::1']: - return False - - if tmp.strip().lower() != domain.strip().lower(): - from flask import Flask, redirect, request, url_for - to = scheme + "://" + domain + ":" + str(port) - # print(to) - return redirect(to, code=302) - - return False - - - -def createLinuxUser(user, group): - execShell("groupadd {}".format(group)) - execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group)) - return True - - -def setOwn(filename, user, group=None): - if isAppleSystem(): - return True - - # 设置用户组 - if not os.path.exists(filename): - return False - from pwd import getpwnam - try: - user_info = getpwnam(user) - user = user_info.pw_uid - if group: - user_info = getpwnam(group) - group = user_info.pw_gid - except: - if user == 'www': - createLinuxUser(user) - # 如果指定用户或组不存在,则使用www - try: - user_info = getpwnam('www') - except: - createLinuxUser(user) - user_info = getpwnam('www') - user = user_info.pw_uid - group = user_info.pw_gid - os.chown(filename, user, group) - return True - - -def setMode(filename, mode): - # 设置文件权限 - if not os.path.exists(filename): - return False - mode = int(str(mode), 8) - os.chmod(filename, mode) - return True - - -def checkPort(port): - # 检查端口是否合法 - ports = ['21', '443', '888'] - if port in ports: - return False - intport = int(port) - if intport < 1 or intport > 65535: - return False - return True - - -def getStrBetween(startStr, endStr, srcStr): - # 字符串取中间 - start = srcStr.find(startStr) - if start == -1: - return None - end = srcStr.find(endStr) - if end == -1: - return None - return srcStr[start + 1:end] - - -def getCpuType(): - cpuType = '' - if isAppleSystem(): - cmd = "system_profiler SPHardwareDataType | grep 'Processor Name' | awk -F ':' '{print $2}'" - cpuinfo = execShell(cmd) - return cpuinfo[0].strip() - - current_os = getOs() - if current_os.startswith('freebsd'): - cmd = "sysctl -a | egrep -i 'hw.model' | awk -F ':' '{print $2}'" - cpuinfo = execShell(cmd) - return cpuinfo[0].strip() - - # 取CPU类型 - cpuinfo = open('/proc/cpuinfo', 'r').read() - rep = "model\\s+name\\s+:\\s+(.+)" - tmp = re.search(rep, cpuinfo, re.I) - if tmp: - cpuType = tmp.groups()[0] - else: - cpuinfo = execShell('LANG="en_US.UTF-8" && lscpu')[0] - rep = "Model\\s+name:\\s+(.+)" - tmp = re.search(rep, cpuinfo, re.I) - if tmp: - cpuType = tmp.groups()[0] - return cpuType - - -def isRestart(): - # 检查是否允许重启 - num = M('tasks').where('status!=?', ('1',)).count() - if num > 0: - return False - return True - - -def isUpdateLocalSoft(): - num = M('tasks').where('status!=?', ('1',)).count() - if os.path.exists('mdserver-web.zip'): - return True - - if num > 0: - data = M('tasks').where('status!=?', ('1',)).field( - 'id,type,execstr').limit('1').select() - argv = data[0]['execstr'].split('|dl|') - if data[0]['type'] == 'download' and argv[1] == 'mdserver-web.zip': - return True - - return False - - -def hasPwd(password): - # 加密密码字符 - import crypt - return crypt.crypt(password, password) - - -def getTimeout(url): - start = time.time() - result = httpGet(url) - if result != 'True': - return False - return int((time.time() - start) * 1000) - - -def makeConf(): - file = getRunDir() + '/data/json/config.json' - if not os.path.exists(file): - c = {} - c['title'] = '后羿面板' - c['home'] = 'http://github/midoks/mdserver-web' - c['recycle_bin'] = True - c['template'] = 'default' - writeFile(file, json.dumps(c)) - return c - c = readFile(file) - return json.loads(c) - - -def getConfig(k): - c = makeConf() - return c[k] - - -def setConfig(k, v): - c = makeConf() - c[k] = v - file = getRunDir() + '/data/json/config.json' - return writeFile(file, json.dumps(c)) - - -def getHostAddr(): - if os.path.exists('data/iplist.txt'): - return readFile('data/iplist.txt').strip() - return '127.0.0.1' - - -def setHostAddr(addr): - file = getRunDir() + '/data/iplist.txt' - return writeFile(file, addr) - - -def getHostPort(): - if os.path.exists('data/port.pl'): - return readFile('data/port.pl').strip() - return '7200' - - -def setHostPort(port): - file = getRunDir() + '/data/port.pl' - return writeFile(file, port) - - -def auth_decode(data): - # 解密数据 - token = GetToken() - # 是否有生成Token - if not token: - return returnMsg(False, 'REQUEST_ERR') - - # 校验access_key是否正确 - if token['access_key'] != data['btauth_key']: - return returnMsg(False, 'REQUEST_ERR') - - # 解码数据 - import binascii - import hashlib - import urllib - import hmac - import json - tdata = binascii.unhexlify(data['data']) - - # 校验signature是否正确 - signature = binascii.hexlify( - hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest()) - if signature != data['signature']: - return returnMsg(False, 'REQUEST_ERR') - - # 返回 - return json.loads(urllib.unquote(tdata)) - - -# 数据加密 -def auth_encode(data): - token = GetToken() - pdata = {} - - # 是否有生成Token - if not token: - return returnMsg(False, 'REQUEST_ERR') - - # 生成signature - import binascii - import hashlib - import urllib - import hmac - import json - tdata = urllib.quote(json.dumps(data)) - # 公式 hex(hmac_sha256(data)) - pdata['signature'] = binascii.hexlify( - hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest()) - - # 加密数据 - pdata['btauth_key'] = token['access_key'] - pdata['data'] = binascii.hexlify(tdata) - pdata['timestamp'] = time.time() - - # 返回 - return pdata - - -def checkToken(get): - # 检查Token - tempFile = 'data/tempToken.json' - if not os.path.exists(tempFile): - return False - import json - import time - tempToken = json.loads(readFile(tempFile)) - if time.time() > tempToken['timeout']: - return False - if get.token != tempToken['token']: - return False - return True - - -def checkInput(data): - # 过滤输入 - if not data: - return data - if type(data) != str: - return data - checkList = [ - {'d': '<', 'r': '<'}, - {'d': '>', 'r': '>'}, - {'d': '\'', 'r': '‘'}, - {'d': '"', 'r': '“'}, - {'d': '&', 'r': '&'}, - {'d': '#', 'r': '#'}, - {'d': '<', 'r': '<'} - ] - for v in checkList: - data = data.replace(v['d'], v['r']) - return data - - -def checkCert(certPath='ssl/certificate.pem'): - # 验证证书 - openssl = '/usr/bin/openssl' - if not os.path.exists(openssl): - openssl = '/usr/local/openssl/bin/openssl' - if not os.path.exists(openssl): - openssl = 'openssl' - certPem = readFile(certPath) - s = "\n-----BEGIN CERTIFICATE-----" - tmp = certPem.strip().split(s) - for tmp1 in tmp: - if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: - tmp1 = s + tmp1 - writeFile(certPath, tmp1) - result = execShell(openssl + " x509 -in " + - certPath + " -noout -subject") - if result[1].find('-bash:') != -1: - return True - if len(result[1]) > 2: - return False - if result[0].find('error:') != -1: - return False - return True - - -def sortFileList(path, ftype = 'mtime', sort = 'desc'): - flist = os.listdir(path) - if ftype == 'mtime': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=False) - - if ftype == 'size': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=False) - - if ftype == 'fname': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=False) - return flist - - -def sortAllFileList(path, ftype = 'mtime', sort = 'desc', search = '',limit = 3000): - count = 0 - flist = [] - for d_list in os.walk(path): - if count >= limit: - break - - for d in d_list[1]: - if count >= limit: - break - if d.lower().find(search) != -1: - filename = d_list[0] + '/' + d - if not os.path.exists(filename): - continue - count += 1 - flist.append(filename) - - for f in d_list[2]: - if count >= limit: - break - - if f.lower().find(search) != -1: - filename = d_list[0] + '/' + f - if not os.path.exists(filename): - continue - count += 1 - flist.append(filename) - - if ftype == 'mtime': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=False) - - if ftype == 'size': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=False) - return flist - -def getPathSize(path): - # 取文件或目录大小 - if not os.path.exists(path): - return 0 - if not os.path.isdir(path): - return os.path.getsize(path) - size_total = 0 - for nf in os.walk(path): - for f in nf[2]: - filename = nf[0] + '/' + f - size_total += os.path.getsize(filename) - return size_total - - -def toSize(size): - # 字节单位转换 - d = ('b', 'KB', 'MB', 'GB', 'TB') - s = d[0] - for b in d: - if size < 1024: - return str(round(size, 2)) + ' ' + b - size = float(size) / 1024.0 - s = b - return str(round(size, 2)) + ' ' + b - - -def getPathSuffix(path): - return os.path.splitext(path)[-1] - - -def getMacAddress(): - # 获取mac - import uuid - mac = uuid.UUID(int=uuid.getnode()).hex[-12:] - return ":".join([mac[e:e + 2] for e in range(0, 11, 2)]) - - -def get_string(t): - if t != -1: - max = 126 - m_types = [{'m': 122, 'n': 97}, {'m': 90, 'n': 65}, {'m': 57, 'n': 48}, { - 'm': 47, 'n': 32}, {'m': 64, 'n': 58}, {'m': 96, 'n': 91}, {'m': 125, 'n': 123}] - else: - max = 256 - t = 0 - m_types = [{'m': 255, 'n': 0}] - arr = [] - for i in range(max): - if i < m_types[t]['n'] or i > m_types[t]['m']: - continue - arr.append(chr(i)) - return arr - - -def get_string_find(t): - if type(t) != list: - t = [t] - return_str = '' - for s1 in t: - return_str += get_string(int(s1[0]))[int(s1[1:])] - return return_str - - -def get_string_arr(t): - s_arr = {} - t_arr = [] - for s1 in t: - for i in range(6): - if not i in s_arr: - s_arr[i] = get_string(i) - for j in range(len(s_arr[i])): - if s1 == s_arr[i][j]: - t_arr.append(str(i) + str(j)) - return t_arr - - # 转换时间 - - -def strfDate(sdate): - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) - - -# 获取证书名称 -def getCertName(certPath): - if not os.path.exists(certPath): - return None - try: - import OpenSSL - result = {} - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, readFile(certPath)) - # 取产品名称 - issuer = x509.get_issuer() - result['issuer'] = '' - if hasattr(issuer, 'CN'): - result['issuer'] = issuer.CN - if not result['issuer']: - is_key = [b'0', '0'] - issue_comp = issuer.get_components() - if len(issue_comp) == 1: - is_key = [b'CN', 'CN'] - for iss in issue_comp: - if iss[0] in is_key: - result['issuer'] = iss[1].decode() - break - if not result['issuer']: - if hasattr(issuer, 'O'): - result['issuer'] = issuer.O - # 取到期时间 - result['notAfter'] = strfDate(bytes.decode(x509.get_notAfter())[:-1]) - # 取申请时间 - result['notBefore'] = strfDate(bytes.decode(x509.get_notBefore())[:-1]) - # 取可选名称 - result['dns'] = [] - for i in range(x509.get_extension_count()): - s_name = x509.get_extension(i) - if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: - s_dns = str(s_name).split(',') - for d in s_dns: - result['dns'].append(d.split(':')[1]) - subject = x509.get_subject().get_components() - # 取主要认证名称 - if len(subject) == 1: - result['subject'] = subject[0][1].decode() - else: - if not result['dns']: - for sub in subject: - if sub[0] == b'CN': - result['subject'] = sub[1].decode() - break - if 'subject' in result: - result['dns'].append(result['subject']) - else: - result['subject'] = result['dns'][0] - result['endtime'] = int(int(time.mktime(time.strptime( - result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400) - return result - except Exception as e: - writeFileLog(getTracebackInfo()) - return None - - -def createLocalSSL(): - if not os.path.exists('ssl/local'): - execShell('mkdir -p ssl/local') - - - # 自签证书 - # if os.path.exists('ssl/local/input.pl'): - # return True - - client_ip = getClientIp() - - import OpenSSL - key = OpenSSL.crypto.PKey() - key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) - cert = OpenSSL.crypto.X509() - cert.set_serial_number(0) - - if client_ip == '127.0.0.1': - cert.get_subject().CN = '127.0.0.1' - else: - cert.get_subject().CN = getLocalIp() - - cert.set_issuer(cert.get_subject()) - cert.gmtime_adj_notBefore(0) - cert.gmtime_adj_notAfter(86400 * 3650) - cert.set_pubkey(key) - cert.sign(key, 'md5') - cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) - private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) - if len(cert_ca) > 100 and len(private_key) > 100: - writeFile('ssl/local/cert.pem', cert_ca, 'wb+') - writeFile('ssl/local/private.pem', private_key, 'wb+') - return True - return False - - -def getSSHPort(): - try: - file = '/etc/ssh/sshd_config' - conf = readFile(file) - rep = "(#*)?Port\\s+([0-9]+)\\s*\n" - port = re.search(rep, conf).groups(0)[1] - return int(port) - except: - return 22 - - -def getSSHStatus(): - if os.path.exists('/usr/bin/apt-get'): - status = execShell("service ssh status | grep -P '(dead|stop)'") - else: - import system_api - version = system_api.system_api().getSystemVersion() - if version.find(' Mac ') != -1: - return True - if version.find(' 7.') != -1: - status = execShell("systemctl status sshd.service | grep 'dead'") - else: - status = execShell( - "/etc/init.d/sshd status | grep -e 'stopped' -e '已停'") - if len(status[0]) > 3: - status = False - else: - status = True - return status - - -def requestFcgiPHP(sock, uri, document_root='/tmp', method='GET', pdata=b''): - # 直接请求到PHP-FPM - # version php版本 - # uri 请求uri - # filename 要执行的php文件 - # args 请求参数 - # method 请求方式 - sys.path.append(os.getcwd() + "/class/plugin") - - import fpm - p = fpm.fpm(sock, document_root) - - if type(pdata) == dict: - pdata = url_encode(pdata) - result = p.load_url_public(uri, pdata, method) - return result - - -def getMyORM(): - ''' - 获取MySQL资源的ORM - ''' - sys.path.append(os.getcwd() + "/class/plugin") - import orm - o = orm.ORM() - return o - - -def getMyORMDb(): - ''' - 获取MySQL资源的ORM pip install mysqlclient==2.0.3 | pip install mysql-python - ''' - sys.path.append(os.getcwd() + "/class/plugin") - import ormDb - o = ormDb.ORM() - return o - -##################### notify start ######################################### - - -def initNotifyConfig(): - p = getNotifyPath() - if not os.path.exists(p): - writeFile(p, '{}') - return True - - -def getNotifyPath(): - path = 'data/notify.json' - return path - - -def getNotifyData(is_parse=False): - initNotifyConfig() - notify_file = getNotifyPath() - notify_data = readFile(notify_file) - - data = json.loads(notify_data) - - if is_parse: - tag_list = ['tgbot', 'email'] - for t in tag_list: - if t in data and 'cfg' in data[t]: - data[t]['data'] = json.loads(deDoubleCrypt(t, data[t]['cfg'])) - return data - - -def writeNotify(data): - p = getNotifyPath() - return writeFile(p, json.dumps(data)) - - -def tgbotNotifyChatID(): - data = getNotifyData(True) - if 'tgbot' in data and 'enable' in data['tgbot']: - if data['tgbot']['enable']: - t = data['tgbot']['data'] - return t['chat_id'] - return '' - - -def tgbotNotifyObject(): - data = getNotifyData(True) - if 'tgbot' in data and 'enable' in data['tgbot']: - if data['tgbot']['enable']: - t = data['tgbot']['data'] - import telebot - bot = telebot.TeleBot(app_token) - return True, bot - return False, None - - -def tgbotNotifyMessage(app_token, chat_id, msg): - import telebot - bot = telebot.TeleBot(app_token) - try: - data = bot.send_message(chat_id, msg) - return True - except Exception as e: - writeFileLog(str(e)) - return False - - -def tgbotNotifyHttpPost(app_token, chat_id, msg): - try: - url = 'https://api.telegram.org/bot' + app_token + '/sendMessage' - post_data = { - 'chat_id': chat_id, - 'text': msg, - } - rdata = httpPost(url, post_data) - return True - except Exception as e: - writeFileLog(str(e)) - return False - - -def tgbotNotifyTest(app_token, chat_id): - msg = 'MW-通知验证测试OK' - return tgbotNotifyHttpPost(app_token, chat_id, msg) - - -def emailNotifyMessage(data): - ''' - 邮件通知 - ''' - sys.path.append(os.getcwd() + "/class/plugin") - import memail - try: - if data['smtp_ssl'] == 'ssl': - memail.sendSSL(data['smtp_host'], data['smtp_port'], - data['username'], data['password'], - data['to_mail_addr'], data['subject'], data['content']) - else: - memail.send(data['smtp_host'], data['smtp_port'], - data['username'], data['password'], - data['to_mail_addr'], data['subject'], data['content']) - return True - except Exception as e: - print(getTracebackInfo()) - return False - - -def emailNotifyTest(data): - # print(data) - data['subject'] = 'MW通知测试' - data['content'] = data['mail_test'] - return emailNotifyMessage(data) - - -def notifyMessageTry(msg, stype='common', trigger_time=300, is_write_log=True): - - lock_file = getPanelTmp() + '/notify_lock.json' - if not os.path.exists(lock_file): - writeFile(lock_file, '{}') - - lock_data = json.loads(readFile(lock_file)) - if stype in lock_data: - diff_time = time.time() - lock_data[stype]['do_time'] - if diff_time >= trigger_time: - lock_data[stype]['do_time'] = time.time() - else: - return False - else: - lock_data[stype] = {'do_time': time.time()} - - writeFile(lock_file, json.dumps(lock_data)) - - if is_write_log: - writeLog("通知管理[" + stype + "]", msg) - - data = getNotifyData(True) - # tag_list = ['tgbot', 'email'] - # tagbot - do_notify = False - if 'tgbot' in data and 'enable' in data['tgbot']: - if data['tgbot']['enable']: - t = data['tgbot']['data'] - i = sys.version_info - - # telebot 在python小于3.7无法使用 - if i[0] < 3 or i[1] < 7: - do_notify = tgbotNotifyHttpPost( - t['app_token'], t['chat_id'], msg) - else: - do_notify = tgbotNotifyMessage( - t['app_token'], t['chat_id'], msg) - - if 'email' in data and 'enable' in data['email']: - if data['email']['enable']: - t = data['email']['data'] - t['subject'] = 'MW通知' - t['content'] = msg - do_notify = emailNotifyMessage(t) - return do_notify - - -def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True): - try: - return notifyMessageTry(msg, stype, trigger_time, is_write_log) - except Exception as e: - writeFileLog(getTracebackInfo()) - return False - - -##################### notify end ######################################### - - -def getGlibcVersion(): - try: - cmd_result = execShell("ldd --version")[0] - if not cmd_result: return '' - glibc_version = cmd_result.split("\n")[0].split()[-1] - except: - return '' - return glibc_version - -##################### ssh start ######################################### -def getSshDir(): - if isAppleSystem(): - user = execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() - return '/Users/' + user + '/.ssh' - return '/root/.ssh' - - -def processExists(pname, exe=None, cmdline=None): - # 进程是否存在 - try: - import psutil - pids = psutil.pids() - for pid in pids: - try: - p = psutil.Process(pid) - if p.name() == pname: - if not exe and not cmdline: - return True - else: - if exe: - if p.exe() == exe: - return True - if cmdline: - if cmdline in p.cmdline(): - return True - except: - pass - return False - except: - return True - - -def createRsa(): - # ssh-keygen -t rsa -P "" -C "midoks@163.com" - ssh_dir = getSshDir() - # mw.execShell("rm -f /root/.ssh/*") - if not os.path.exists(ssh_dir + '/authorized_keys'): - execShell('touch ' + ssh_dir + '/authorized_keys') - - if not os.path.exists(ssh_dir + '/id_rsa.pub') and os.path.exists(ssh_dir + '/id_rsa'): - execShell('echo y | ssh-keygen -q -t rsa -P "" -f ' + - ssh_dir + '/id_rsa') - else: - execShell('ssh-keygen -q -t rsa -P "" -f ' + ssh_dir + '/id_rsa') - - execShell('cat ' + ssh_dir + '/id_rsa.pub >> ' + - ssh_dir + '/authorized_keys') - execShell('chmod 600 ' + ssh_dir + '/authorized_keys') - - -def createSshInfo(): - ssh_dir = getSshDir() - if not os.path.exists(ssh_dir + '/id_rsa') or not os.path.exists(ssh_dir + '/id_rsa.pub'): - createRsa() - - # 检查是否写入authorized_keys - data = execShell("cat " + ssh_dir + "/id_rsa.pub | awk '{print $3}'") - if data[0] != "": - cmd = "cat " + ssh_dir + "/authorized_keys | grep " + data[0] - ak_data = execShell(cmd) - if ak_data[0] == "": - cmd = 'cat ' + ssh_dir + '/id_rsa.pub >> ' + ssh_dir + '/authorized_keys' - execShell(cmd) - execShell('chmod 600 ' + ssh_dir + '/authorized_keys') - - -def connectSsh(): - import paramiko - ssh = paramiko.SSHClient() - createSshInfo() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - port = getSSHPort() - try: - ssh.connect('127.0.0.1', port, timeout=5) - except Exception as e: - ssh.connect('localhost', port, timeout=5) - except Exception as e: - ssh.connect(getHostAddr(), port, timeout=30) - except Exception as e: - return False - - shell = ssh.invoke_shell(term='xterm', width=83, height=21) - shell.setblocking(0) - return shell - - -def clearSsh(): - # 服务器IP - ip = getHostAddr() - sh = ''' -#!/bin/bash -PLIST=`who | grep localhost | awk '{print $2}'` -for i in $PLIST -do - ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9 -done - -# getHostAddr -PLIST=`who | grep "${ip}" | awk '{print $2}'` -for i in $PLIST -do - ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9 -done -''' - if not isAppleSystem(): - info = execShell(sh) - print(info[0], info[1]) -##################### ssh end ######################################### - -# --------------------------------------------------------------------------------- -# 打印相关 START -# --------------------------------------------------------------------------------- - - -def echoStart(tag): - print("=" * 89) - print("★开始{}[{}]".format(tag, formatDate())) - print("=" * 89) - - -def echoEnd(tag): - print("=" * 89) - print("☆{}完成[{}]".format(tag, formatDate())) - print("=" * 89) - print("\n") - - -def echoInfo(msg): - print("|-{}".format(msg)) - -# --------------------------------------------------------------------------------- -# 打印相关 END -# --------------------------------------------------------------------------------- diff --git a/class/core/page.py b/class/core/page.py deleted file mode 100755 index e2ccdb8a1..000000000 --- a/class/core/page.py +++ /dev/null @@ -1,245 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 分页库操作 -# --------------------------------------------------------------------------------- - - -import math -import string - - -class Page(): - #-------------------------- - # 分页类 - JS回调版 - #-------------------------- - __PREV = '上一页' - __NEXT = '下一页' - __START = '首页' - __END = '尾页' - __COUNT_START = '共' - __COUNT_END = '条数据' - __FO = '从' - __LINE = '条' - __LIST_NUM = 4 - SHIFT = None # 偏移量 - ROW = None # 每页行数 - __C_PAGE = None # 当前页 - __COUNT_PAGE = None # 总页数 - __COUNT_ROW = None # 总行数 - __URI = None # URI - __RTURN_JS = False # 是否返回JS回调 - __START_NUM = None # 起始行 - __END_NUM = None # 结束行 - __ARGS_TPL = '' - - def __init__(self): - if False: - self.__PREV = tmp['PREV'] - self.__NEXT = tmp['NEXT'] - self.__START = tmp['START'] - self.__END = tmp['END'] - self.__COUNT_START = tmp['COUNT_START'] - self.__COUNT_END = tmp['COUNT_END'] - self.__FO = tmp['FO'] - self.__LINE = tmp['LINE'] - - def getPageNum(self, num): - return str(num) + self.__ARGS_TPL - - def GetPage(self, pageInfo, limit='1,2,3,4,5,6,7,8'): - # 取分页信息 - # @param pageInfo 传入分页参数字典 - # @param limit 返回系列 - self.__RTURN_JS = pageInfo['return_js'] - self.__COUNT_ROW = pageInfo['count'] - - if 'args_tpl' in pageInfo: - self.__ARGS_TPL = pageInfo['args_tpl'] - - self.ROW = pageInfo['row'] - self.__C_PAGE = self.__GetCpage(pageInfo['p']) - self.__START_NUM = self.__StartRow() - self.__END_NUM = self.__EndRow() - self.__COUNT_PAGE = self.__GetCountPage() - self.__URI = self.__SetUri(pageInfo['uri']) - self.SHIFT = self.__START_NUM - 1 - - keys = limit.split(',') - pages = {} - # 起始页 - pages['1'] = self.__GetStart() - # 上一页 - pages['2'] = self.__GetPrev() - # 分页 - pages['3'] = self.__GetPages() - # 下一页 - pages['4'] = self.__GetNext() - # 尾页 - pages['5'] = self.__GetEnd() - - # 当前显示页与总页数 - pages['6'] = "" + \ - str(self.__C_PAGE) + "/" + \ - str(self.__COUNT_PAGE) + "" - - # 本页显示开始与结束行 - pages['7'] = "" + str(self.__FO) + \ - str(self.__START_NUM) + "-" + \ - str(self.__END_NUM) + str(self.__LINE) + "" - # 行数 - pages['8'] = "" + str(self.__COUNT_START) + \ - str(self.__COUNT_ROW) + str(self.__COUNT_END) + "" - - # 构造返回数据 - retuls = '
                    ' - for value in keys: - retuls += pages[value] - retuls += '
                    ' - - # 返回分页数据 - return retuls - - def __GetEnd(self): - # 构造尾页 - endStr = "" - if self.__C_PAGE >= self.__COUNT_PAGE: - endStr = '' - else: - if self.__RTURN_JS == "": - endStr = "" + str(self.__END) + "" - else: - endStr = "" + \ - str(self.__END) + "" - return endStr - - def __GetNext(self): - # 构造下一页 - nextStr = "" - if self.__C_PAGE >= self.__COUNT_PAGE: - nextStr = '' - else: - if self.__RTURN_JS == "": - nextStr = "" + str(self.__NEXT) + "" - else: - nextStr = "" + \ - str(self.__NEXT) + "" - - return nextStr - - def __GetPages(self): - # 构造分页 - pages = '' - num = 0 - # 当前页之前 - if (self.__COUNT_PAGE - self.__C_PAGE) < self.__LIST_NUM: - num = self.__LIST_NUM + \ - (self.__LIST_NUM - (self.__COUNT_PAGE - self.__C_PAGE)) - else: - num = self.__LIST_NUM - n = 0 - for i in range(num): - n = num - i - page = self.__C_PAGE - n - if page > 0: - if self.__RTURN_JS == "": - pages += "" + str(page) + "" - else: - pages += "" + str(page) + "" - - # 当前页 - if self.__C_PAGE > 0: - pages += "" + \ - str(self.__C_PAGE) + "" - - # 当前页之后 - if self.__C_PAGE <= self.__LIST_NUM: - num = self.__LIST_NUM + (self.__LIST_NUM - self.__C_PAGE) + 1 - else: - num = self.__LIST_NUM - for i in range(num): - if i == 0: - continue - page = self.__C_PAGE + i - if page > self.__COUNT_PAGE: - break - if self.__RTURN_JS == "": - pages += "" + str(page) + "" - else: - pages += "" + str(page) + "" - - return pages - - def __GetPrev(self): - # 构造上一页 - startStr = '' - if self.__C_PAGE == 1: - startStr = '' - else: - if self.__RTURN_JS == "": - startStr = "" + str(self.__PREV) + "" - else: - startStr = "" + \ - str(self.__PREV) + "" - return startStr - - def __GetStart(self): - # 构造起始分页 - startStr = '' - if self.__C_PAGE == 1: - startStr = '' - else: - if self.__RTURN_JS == "": - startStr = "" + str(self.__START) + "" - else: - startStr = "" + str(self.__START) + "" - return startStr - - def __GetCpage(self, p): - # 取当前页 - if p: - return p - return 1 - - def __StartRow(self): - # 从多少行开始 - return (self.__C_PAGE - 1) * self.ROW + 1 - - def __EndRow(self): - # 从多少行结束 - if self.ROW > self.__COUNT_ROW: - return self.__COUNT_ROW - return self.__C_PAGE * self.ROW - - def __GetCountPage(self): - # 取总页数 - return int(math.ceil(self.__COUNT_ROW / float(self.ROW))) - - def __SetUri(self, sinput): - # 构造URI - uri = '?' - for key in sinput: - if key == 'p': - continue - uri += key + '=' + sinput[key] + '&' - return str(uri) diff --git a/class/core/plugins_api.py b/class/core/plugins_api.py deleted file mode 100755 index a08779624..000000000 --- a/class/core/plugins_api.py +++ /dev/null @@ -1,1105 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 插件操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import mw -import re -import json - -import sys -# reload(sys) -# sys.setdefaultencoding('utf8') - -import threading -import multiprocessing - -from flask import render_template -from flask import request - - -class pa_thread(threading.Thread): - - def __init__(self, func, args, name=''): - threading.Thread.__init__(self) - self.name = name - self.func = func - self.args = args - self.result = self.func(*self.args) - - def getResult(self): - try: - return self.result - except Exception: - return None - - -class plugins_api: - __tasks = None - __plugin_dir = 'plugins' - __type = 'data/json/type.json' - __index = 'data/json/index.json' - __index_data = None - setupPath = None - - def __init__(self): - self.setupPath = 'server' - self.__plugin_dir = mw.getRunDir() + '/plugins' - self.__type = mw.getRunDir() + '/data/json/type.json' - self.__index = mw.getRunDir() + '/data/json/index.json' - - self.initIndexData() - - def initIndexData(self): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - self.__index_data = json.loads(mw.readFile(self.__index)) - - ##### ----- start ----- ### - def listApi(self): - sType = request.args.get('type', '0') - sPage = request.args.get('p', '1') - - if not mw.isNumber(sPage): - sPage = 1 - - if not mw.isNumber(sType): - sType = 0 - - # print(sPage, sType) - search = request.args.get('search', '').lower() - data = self.getPluginList(sType, search, int(sPage)) - return mw.getJson(data) - - def menuGetAbsPath(self, tag, path): - if path[0:1] == '/': - return path - else: - return mw.getPluginDir() + '/' + tag + '/' + path - - def menuApi(self): - import config_api - data = config_api.config_api().get() - tag = request.args.get('tag', '') - menu_file = 'data/hook_menu.json' - content = '' - if os.path.exists(menu_file): - t = mw.readFile(menu_file) - tlist = json.loads(t) - for menu_data in tlist: - if tag == menu_data['name'] and 'path' in menu_data: - tpath = self.menuGetAbsPath(tag, menu_data['path']) - content = mw.readFile(tpath) - data['plugin_content'] = content - return render_template('plugin_menu.html', data=data) - - def fileApi(self): - name = request.args.get('name', '') - if name.strip() == '': - return '' - - f = request.args.get('f', '') - - if f.strip() == '': - return '' - - file = mw.getPluginDir() + '/' + name + '/' + f - if not os.path.exists(file): - return '' - - suffix = mw.getPathSuffix(file) - if suffix == '.css': - content = mw.readFile(file) - from flask import Response - from flask import make_response - v = Response(content, headers={ - 'Content-Type': 'text/css; charset="utf-8"'}) - return make_response(v) - content = open(file, 'rb').read() - return content - - def indexListApi(self): - data = self.getIndexList() - return mw.getJson(data) - - def indexSortApi(self): - sort = request.form.get('ssort', '') - if sort.strip() == '': - return mw.returnJson(False, '排序数据不能为空!') - data = self.setIndexSort(sort) - if data: - return mw.returnJson(True, '成功!') - return mw.returnJson(False, '失败!') - - def initApi(self): - - plugin_names = { - 'openresty': '1.25.3', - 'php': '56', - 'swap': '1.1', - 'mysql': '5.7', - 'phpmyadmin': '4.4.15', - } - - pn_dir = mw.getPluginDir() - pn_server_dir = mw.getServerDir() - pn_list = [] - for pn in plugin_names: - info = {} - pn_json = pn_dir + '/' + pn + '/info.json' - pn_server = pn_server_dir + '/' + pn - if not os.path.exists(pn_server): - - tmp = mw.readFile(pn_json) - tmp = json.loads(tmp) - - info['title'] = tmp['title'] - info['name'] = tmp['name'] - info['versions'] = tmp['versions'] - info['default_ver'] = plugin_names[pn] - pn_list.append(info) - else: - return mw.returnJson(False, 'ok') - - return mw.returnJson(True, 'ok', pn_list) - - def initInstallApi(self): - pn_list = request.form.get('list', '') - try: - pn_list = json.loads(pn_list) - - for pn in pn_list: - name = pn['name'] - version = pn['version'] - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - self.hookInstall(pluginInfo) - execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \ - pluginInfo['shell'] + ' install ' + version - - taskAdd = ('安装[' + name + '-' + version + ']', - 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr) - - mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd) - os.mkdir(mw.getServerDir() + '/php') - # 任务执行相关 - mw.triggerTask() - return mw.returnJson(True, '添加成功') - except Exception as e: - return mw.returnJson(False, mw.getTracebackInfo()) - - def installApi(self): - rundir = mw.getRunDir() - name = request.form.get('name', '') - version = request.form.get('version', '') - - mmsg = '安装' - if hasattr(request.form, 'upgrade'): - mtype = 'update' - mmsg = 'upgrade' - - if name.strip() == '': - return mw.returnJson(False, '缺少插件名称!', ()) - - if version.strip() == '': - return mw.returnJson(False, '缺少版本信息!', ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - # print infoJsonPos - - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, '配置文件不存在!', ()) - - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - self.hookInstall(pluginInfo) - - execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \ - pluginInfo['shell'] + ' install ' + version - - if mw.isAppleSystem(): - print(execstr) - - taskAdd = (mmsg + '[' + name + '-' + version + ']', - 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr) - - mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd) - - # 任务执行相关 - mw.triggerTask() - return mw.returnJson(True, '已将安装任务添加到队列!') - - def hookInstallFile(self, hook_name, info): - hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json" - data = [] - if os.path.exists(hookPath): - t = mw.readFile(hookPath) - data = json.loads(t) - - isNeedAdd = True - for x in range(len(data)): - if data[x]['title'] == info['title'] and data[x]['name'] == info['name']: - isNeedAdd = False - - if isNeedAdd: - data.append(info) - mw.writeFile(hookPath, json.dumps(data)) - - def hookUninstallFile(self, hook_name, info): - hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json" - data = [] - if os.path.exists(hookPath): - t = mw.readFile(hookPath) - data = json.loads(t) - - for idx in range(len(data)): - if data[idx]['name'] == info['name']: - data.remove(data[idx]) - break - mw.writeFile(hookPath, json.dumps(data)) - - def hookInstall(self, info): - valid_hook = ['backup', 'database'] - valid_list_hook = ['menu', 'global_static', 'site_cb'] - if 'hook' in info: - hooks = info['hook'] - for h in hooks: - hooks_type = type(h) - if hooks_type == dict: - tag = h['tag'] - if tag in valid_list_hook: - self.hookInstallFile(tag, h[tag]) - elif hooks_type == str: - for x in hooks: - if x in valid_hook: - self.hookInstallFile(x, info) - return True - return False - - def hookUninstall(self, info): - valid_hook = ['backup', 'database'] - valid_list_hook = ['menu', 'global_static', 'site_cb'] - if 'hook' in info: - hooks = info['hook'] - for h in hooks: - hooks_type = type(h) - if hooks_type == dict: - tag = h['tag'] - if tag in valid_list_hook: - self.hookUninstallFile(tag, h[tag]) - elif hooks_type == str: - for x in hooks: - if x in valid_hook: - self.hookUninstallFile(x, info) - return True - return False - - def uninstallOldApi(self): - rundir = mw.getRunDir() - name = request.form.get('name', '') - version = request.form.get('version', '') - if name.strip() == '': - return mw.returnJson(False, "缺少插件名称!", ()) - - if version.strip() == '': - return mw.returnJson(False, "缺少版本信息!", ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, "配置文件不存在!", ()) - - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - execstr = "cd " + os.getcwd() + "/plugins/" + \ - name + " && /bin/bash " + pluginInfo["shell"] \ - + " uninstall " + version - - taskAdd = (None, '卸载[' + name + '-' + version + ']', - 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr) - - mw.M('tasks').add('id,name,type,status,addtime, execstr', taskAdd) - return mw.returnJson(True, '已将卸载任务添加到队列!') - - # 卸载时间短,不加入任务中... - def uninstallApi(self): - rundir = mw.getRunDir() - name = request.form.get('name', '') - version = request.form.get('version', '') - if name.strip() == '': - return mw.returnJson(False, "缺少插件名称!", ()) - - if version.strip() == '': - return mw.returnJson(False, "缺少版本信息!", ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, "配置文件不存在!", ()) - - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - self.hookUninstall(pluginInfo) - execstr = "cd " + os.getcwd() + "/plugins/" + \ - name + " && /bin/bash " + pluginInfo["shell"] \ - + " uninstall " + version - - data = mw.execShell(execstr) - if mw.isDebugMode(): - print(execstr) - print(data[0], data[1]) - self.removeIndex(name, version) - return mw.returnJson(True, '卸载执行成功!') - # if data[1] == '': - # return mw.returnJson(True, '已将卸载成功!') - # else: - # return mw.returnJson(False, '卸载出现错误信息!' + data[1]) - - def checkApi(self): - name = request.form.get('name', '') - if name.strip() == '': - return mw.returnJson(False, "缺少插件名称!", ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, "配置文件不存在!", ()) - return mw.returnJson(True, "插件存在!", ()) - - def setIndexApi(self): - name = request.form.get('name', '') - status = request.form.get('status', '0') - version = request.form.get('version', '') - if status == '1': - return self.addIndex(name, version) - return self.removeIndex(name, version) - - def settingApi(self): - name = request.args.get('name', '') - html = self.__plugin_dir + '/' + name + '/index.html' - return mw.readFile(html) - - def runApi(self): - name = request.form.get('name', '') - func = request.form.get('func', '') - version = request.form.get('version', '') - args = request.form.get('args', '') - script = request.form.get('script', 'index') - - data = self.run(name, func, version, args, script) - if data[1] == '': - r = mw.returnJson(True, "OK", data[0].strip()) - else: - r = mw.returnJson(False, data[1].strip()) - return r - - def callbackApi(self): - name = request.form.get('name', '') - func = request.form.get('func', '') - args = request.form.get('args', '') - script = request.form.get('script', 'index') - - data = self.callback(name, func, args, script) - if data[0]: - return mw.returnJson(True, "OK", data[1]) - return mw.returnJson(False, data[1]) - - def updateZipApi(self): - tmp_path = mw.getRootDir() + '/temp' - if not os.path.exists(tmp_path): - os.makedirs(tmp_path) - mw.execShell("rm -rf " + tmp_path + '/*') - - tmp_file = tmp_path + '/plugin_tmp.zip' - from werkzeug.utils import secure_filename - from flask import request - f = request.files['plugin_zip'] - if f.filename[-4:] != '.zip': - return mw.returnJson(False, '仅支持zip文件!') - f.save(tmp_file) - mw.execShell('cd ' + tmp_path + ' && unzip ' + tmp_file) - os.remove(tmp_file) - - p_info = tmp_path + '/info.json' - if not os.path.exists(p_info): - d_path = None - for df in os.walk(tmp_path): - if len(df[2]) < 3: - continue - if not 'info.json' in df[2]: - continue - if not 'install.sh' in df[2]: - continue - if not os.path.exists(df[0] + '/info.json'): - continue - d_path = df[0] - if d_path: - tmp_path = d_path - p_info = tmp_path + '/info.json' - try: - data = json.loads(mw.readFile(p_info)) - data['size'] = mw.getPathSize(tmp_path) - if not 'author' in data: - data['author'] = '未知' - if not 'home' in data: - data['home'] = 'https://github.com/midoks/mdserver-web' - plugin_path = mw.getPluginDir() + data['name'] + '/info.json' - data['old_version'] = '0' - data['tmp_path'] = tmp_path - if os.path.exists(plugin_path): - try: - old_info = json.loads(mw.ReadFile(plugin_path)) - data['old_version'] = old_info['versions'] - except: - pass - except: - mw.execShell("rm -rf " + tmp_path) - return mw.returnJson(False, '在压缩包中没有找到插件信息,请检查插件包!') - protectPlist = ('openresty', 'mysql', 'php', 'redis', 'memcached' - 'mongodb', 'swap', 'gogs', 'pureftp') - if data['name'] in protectPlist: - return mw.returnJson(False, '[' + data['name'] + '],重要插件不可修改!') - return mw.getJson(data) - - def inputZipApi(self): - plugin_name = request.form.get('plugin_name', '') - tmp_path = request.form.get('tmp_path', '') - - if not os.path.exists(tmp_path): - return mw.returnJson(False, '临时文件不存在,请重新上传!') - plugin_path = mw.getPluginDir() + '/' + plugin_name - if not os.path.exists(plugin_path): - print(mw.execShell('mkdir -p ' + plugin_path)) - mw.execShell("cp -rf " + tmp_path + '/* ' + plugin_path + '/') - mw.execShell('chmod -R 755 ' + plugin_path) - p_info = mw.readFile(plugin_path + '/info.json') - if p_info: - mw.writeLog('软件管理', '安装第三方插件[%s]' % - json.loads(p_info)['title']) - return mw.returnJson(True, '安装成功!') - mw.execShell("rm -rf " + plugin_path) - return mw.returnJson(False, '安装失败!') - ##### ----- end ----- ### - - # 进程是否存在 - def processExists(self, pname, exe=None): - try: - if not self.pids: - self.pids = psutil.pids() - for pid in self.pids: - try: - p = psutil.Process(pid) - if p.name() == pname: - if not exe: - return True - else: - if p.exe() == exe: - return True - except: - pass - return False - except: - return True - - # 检查是否正在安装 - def checkSetupTask(self, sName, sVer, sCoexist): - if not self.__tasks: - self.__tasks = mw.M('tasks').where( - "status!=?", ('1',)).field('status,name').select() - isTask = '1' - for task in self.__tasks: - tmpt = mw.getStrBetween('[', ']', task['name']) - if not tmpt: - continue - task_sign = tmpt.split('-') - task_len = len(task_sign) - - task_name = task_sign[0].lower() - task_ver = task_sign[1] - if task_len > 2: - nameArr = task_sign[0:task_len - 1] - task_name = '-'.join(nameArr).lower() - task_ver = task_sign[task_len - 1] - if sCoexist: - if task_name == sName and task_ver == sVer: - isTask = task['status'] - else: - if task_name == sName: - isTask = task['status'] - return isTask - - def checkStatus(self, info): - if not info['setup']: - return False - - data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return True - return False - - def checkStatusProcess(self, info, i, return_dict): - if not info['setup']: - return_dict[i] = False - return - - data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return_dict[i] = True - else: - return_dict[i] = False - - def checkStatusThreads(self, info, i): - if not info['setup']: - return False - data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return True - else: - return False - - # 还无法正常运行,先保留 - def checkStatusThreadsCallback(self, info, i): - if not info['setup']: - return False - - try: - data = self.callback(info['name'], 'status', info['setup_version']) - except Exception as e: - data = self.callback(info['name'], 'status') - - # data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return True - else: - return False - - def checkStatusMThreads(self, plugins_info): - try: - threads = [] - ntmp_list = range(len(plugins_info)) - for i in ntmp_list: - t = pa_thread(self.checkStatusThreads, - (plugins_info[i], i)) - threads.append(t) - - for i in ntmp_list: - threads[i].start() - for i in ntmp_list: - threads[i].join() - - for i in ntmp_list: - t = threads[i].getResult() - plugins_info[i]['status'] = t - except Exception as e: - print('checkStatusMThreads:', str(e)) - - return plugins_info - - def checkStatusMProcess(self, plugins_info): - manager = multiprocessing.Manager() - return_dict = manager.dict() - jobs = [] - for i in range(len(plugins_info)): - p = multiprocessing.Process( - target=self.checkStatusProcess, args=(plugins_info[i], i, return_dict)) - jobs.append(p) - p.start() - - for proc in jobs: - proc.join() - - returnData = return_dict.values() - for i in ntmp_list: - plugins_info[i]['status'] = returnData[i] - - return plugins_info - - def checkIndexList(self, name, version): - indexList = self.__index_data - for i in indexList: - nv = i.split('-') - if nv[0] == name: - return True - return False - - def checkDisplayIndex(self, name, version, coexist): - # if not os.path.exists(self.__index): - # mw.writeFile(self.__index, '[]') - # indexList = json.loads(mw.readFile(self.__index)) - - indexList = self.__index_data - if coexist: - if type(version) == list: - for index in range(len(version)): - vname = name + '-' + version[index] - if vname in indexList: - return True - else: - vname = name + '-' + version - if vname in indexList: - return True - - else: - if type(version) == list: - for index in range(len(version)): - return self.checkIndexList(name, version) - else: - return self.checkIndexList(name, version) - return False - - def getVersion(self, path): - version_f = path + '/version.pl' - if os.path.exists(version_f): - return mw.readFile(version_f).strip() - return '' - - # 构造本地插件信息 - def getPluginInfo(self, info): - checks = '' - path = '' - coexist = False - - if info["checks"][0:1] == '/': - checks = info["checks"] - else: - checks = mw.getRootDir() + '/' + info['checks'] - - if 'path' in info: - path = info['path'] - - if path[0:1] != '/': - path = mw.getRootDir() + '/' + path - - if 'coexist' in info and info['coexist']: - coexist = True - - pInfo = { - "id": 10000, - "pid": info['pid'], - "type": 1000, - "name": info['name'], - "title": info['title'], - "ps": info['ps'], - "dependnet": "", - "mutex": "", - "icon": "", - "path": path, - "install_checks": checks, - "uninsatll_checks": checks, - "coexist": coexist, - "versions": info['versions'], - # "updates": info['updates'], - "display": False, - "setup": False, - "setup_version": "", - "status": False, - "install_pre_inspection": False, - "uninstall_pre_inspection": False, - } - - if 'icon' in info: - pInfo['icon'] = info['icon'] - - if checks.find('VERSION') > -1: - pInfo['install_checks'] = checks.replace( - 'VERSION', info['versions']) - - if path.find('VERSION') > -1: - pInfo['path'] = path.replace( - 'VERSION', info['versions']) - - pInfo['task'] = self.checkSetupTask( - pInfo['name'], info['versions'], coexist) - pInfo['display'] = self.checkDisplayIndex( - info['name'], pInfo['versions'], coexist) - - pInfo['setup'] = os.path.exists(pInfo['install_checks']) - - if coexist and pInfo['setup']: - pInfo['setup_version'] = info['versions'] - else: - pInfo['setup_version'] = self.getVersion(pInfo['install_checks']) - # pluginInfo['status'] = self.checkStatus(pluginInfo) - - if 'install_pre_inspection' in info: - pInfo['install_pre_inspection'] = info['install_pre_inspection'] - if 'uninstall_pre_inspection' in info: - pInfo['uninstall_pre_inspection'] = info[ - 'uninstall_pre_inspection'] - - pInfo['status'] = False - return pInfo - - def makeCoexist(self, data): - plugins_info = [] - for index in range(len(data['versions'])): - tmp = data.copy() - tmp['title'] = tmp['title'] + \ - '-' + data['versions'][index] - tmp['versions'] = data['versions'][index] - pg = self.getPluginInfo(tmp) - plugins_info.append(pg) - - return plugins_info - - def makeList(self, data, sType='0'): - plugins_info = [] - - # 相应类型 - if (data['pid'] == sType): - if type(data['versions']) == list and 'coexist' in data and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - return plugins_info - - # 全部 - if sType == '0': - if type(data['versions']) == list and 'coexist' in data and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - - # 已经安装 - if sType == '-1': - if type(data['versions']) == list and 'coexist' in data and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - if tmp_data[index]['setup']: - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - if pg['setup']: - plugins_info.append(pg) - - # print plugins_info, data - return plugins_info - - def getAllList(self, sType='0'): - plugins_info = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - json_file = path + '/info.json' - if os.path.exists(json_file): - try: - data = json.loads(mw.readFile(json_file)) - tmp_data = self.makeList(data, sType) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - except Exception as e: - print(e) - return plugins_info - - def searchKey(self, info, kw): - try: - if info['title'].lower().find(kw) > -1: - return True - if info['ps'].lower().find(kw) > -1: - return True - if info['name'].lower().find(kw) > -1: - return True - except Exception as e: - return False - - def getAllListPage(self, sType='0', kw='', page=1, pageSize=10): - plugins_info = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - if os.path.isdir(path): - info_file = path + '/info.json' - if os.path.exists(info_file): - try: - data = json.loads(mw.readFile(info_file)) - # 判断是否搜索 - if kw != '' and not self.searchKey(data, kw): - continue - tmp_data = self.makeList(data, sType) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - except Exception as e: - print(mw.getTracebackInfo()) - - start = (page - 1) * pageSize - end = start + pageSize - _plugins_info = plugins_info[start:end] - - _plugins_info = self.checkStatusMThreads(_plugins_info) - return (_plugins_info, len(plugins_info)) - - def makeListThread(self, data, sType='0'): - plugins_info = [] - - if (data['pid'] == sType): - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - return plugins_info - - if sType == '0': - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - - # print plugins_info, data - return plugins_info - - def getAllListThread(self, sType='0'): - plugins_info = [] - tmp_list = [] - threads = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - if os.path.isdir(path): - json_file = path + '/info.json' - if os.path.exists(json_file): - data = json.loads(mw.readFile(json_file)) - if sType == '0': - tmp_list.append(data) - - if (data['pid'] == sType): - tmp_list.append(data) - - ntmp_list = range(len(tmp_list)) - for i in ntmp_list: - t = pa_thread(self.makeListThread, (tmp_list[i], sType)) - threads.append(t) - for i in ntmp_list: - threads[i].start() - for i in ntmp_list: - threads[i].join() - - for i in ntmp_list: - t = threads[i].getResult() - for index in range(len(t)): - plugins_info.append(t[index]) - - return plugins_info - - def makeListProcess(self, data, sType, i, return_dict): - plugins_info = [] - - if (data['pid'] == sType): - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - # return plugins_info - - if sType == '0': - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - - return_dict[i] = plugins_info - # return plugins_info - - def getAllListProcess(self, sType='0'): - plugins_info = [] - tmp_list = [] - manager = multiprocessing.Manager() - return_dict = manager.dict() - jobs = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - if os.path.isdir(path): - json_file = path + '/info.json' - if os.path.exists(json_file): - data = json.loads(mw.readFile(json_file)) - if sType == '0': - tmp_list.append(data) - - if (data['pid'] == sType): - tmp_list.append(data) - - ntmp_list = range(len(tmp_list)) - for i in ntmp_list: - p = multiprocessing.Process( - target=self.makeListProcess, args=(tmp_list[i], sType, i, return_dict)) - jobs.append(p) - p.start() - - for proc in jobs: - proc.join() - - returnData = return_dict.values() - for i in ntmp_list: - for index in range(len(returnData[i])): - plugins_info.append(returnData[i][index]) - - return plugins_info - - def getPluginList(self, sType, kw='', sPage=1, sPageSize=10): - # print(sType, kw, sPage, sPageSize) - - ret = {} - ret['type'] = json.loads(mw.readFile(self.__type)) - # plugins_info = self.getAllListThread(sType) - # plugins_info = self.getAllListProcess(sType) - data = self.getAllListPage(sType, kw, sPage, sPageSize) - ret['data'] = data[0] - - args = {} - args['count'] = data[1] - args['p'] = sPage - args['tojs'] = 'getSList' - args['row'] = sPageSize - - ret['list'] = mw.getPage(args) - return ret - - def getIndexList(self): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - indexList = json.loads(mw.readFile(self.__index)) - plist = [] - for i in indexList: - tmp = i.split('-') - tmp_len = len(tmp) - plugin_name = tmp[0] - plugin_ver = tmp[1] - if tmp_len > 2: - tmpArr = tmp[0:tmp_len - 1] - plugin_name = '-'.join(tmpArr) - plugin_ver = tmp[tmp_len - 1] - - read_json_file = self.__plugin_dir + '/' + plugin_name + '/info.json' - if os.path.exists(read_json_file): - content = mw.readFile(read_json_file) - try: - data = json.loads(content) - data = self.makeList(data) - for index in range(len(data)): - if data[index]['coexist']: - if data[index]['versions'] == plugin_ver or plugin_ver in data[index]['versions']: - data[index]['display'] = True - plist.append(data[index]) - continue - else: - data[index]['display'] = True - plist.append(data[index]) - - except Exception as e: - print('getIndexList:', mw.getTracebackInfo()) - - # 使用gevent模式时,无法使用多进程 - # plist = self.checkStatusMProcess(plist) - plist = self.checkStatusMThreads(plist) - return plist - - def setIndexSort(self, sort): - data = sort.split('|') - mw.writeFile(self.__index, json.dumps(data)) - return True - - def addIndex(self, name, version): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - indexList = json.loads(mw.readFile(self.__index)) - vname = name + '-' + version - - if vname in indexList: - return mw.returnJson(False, '请不要重复添加!') - if len(indexList) > 12: - return mw.returnJson(False, '首页最多只能显示12个软件!') - - indexList.append(vname) - mw.writeFile(self.__index, json.dumps(indexList)) - return mw.returnJson(True, '添加成功!') - - def removeIndex(self, name, version): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - indexList = json.loads(mw.readFile(self.__index)) - vname = name + '-' + version - if not vname in indexList: - return mw.returnJson(True, '删除成功!') - indexList.remove(vname) - mw.writeFile(self.__index, json.dumps(indexList)) - return mw.returnJson(True, '删除成功!') - - # shell 调用 - def run(self, name, func, version='', args='', script='index'): - - path = self.__plugin_dir + '/' + name + '/' + script + '.py' - if not os.path.exists(path): - path = self.__plugin_dir + '/' + name + '/' + name + '.py' - - py = 'python3 ' + path - - if args == '': - py_cmd = py + ' ' + func + ' ' + version - else: - py_cmd = py + ' ' + func + ' ' + version + ' ' + args - - if not os.path.exists(path): - return ('', '') - data = mw.execShell(py_cmd) - - if mw.isDebugMode(): - print('run', py_cmd) - print(data) - # print os.path.exists(py_cmd) - - return (data[0].strip(), data[1].strip()) - - # 映射包调用 - def callback(self, name, func, args='', script='index'): - package = self.__plugin_dir + '/' + name - if not os.path.exists(package): - return (False, "插件不存在!") - - if not package in sys.path: - sys.path.append(package) - eval_str = "__import__('" + script + "')." + func + '(' + args + ')' - newRet = None - try: - newRet = eval(eval_str) - except Exception as e: - print(mw.getTracebackInfo()) - - if mw.isDebugMode(): - print('callback', eval_str) - - return (True, newRet) diff --git a/class/core/site_api.py b/class/core/site_api.py deleted file mode 100755 index 3375146db..000000000 --- a/class/core/site_api.py +++ /dev/null @@ -1,2909 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 站点操作 -# --------------------------------------------------------------------------------- - -import time -import os -import sys -from urllib.parse import urlparse -import mw -import re -import json -import shutil - - -from flask import request - - -class site_api: - siteName = None # 网站名称 - sitePath = None # 根目录 - sitePort = None # 端口 - phpVersion = None # PHP版本 - - setupPath = None # 安装路径 - vhostPath = None - logsPath = None - passPath = None - rewritePath = None - redirectPath = None - sslDir = None # ssl目录 - sslLetsDir = None # lets ssl目录 - - def __init__(self): - # nginx conf - self.setupPath = mw.getServerDir() + '/web_conf' - - self.vhostPath = vhost = self.setupPath + '/nginx/vhost' - if not os.path.exists(vhost): - mw.execShell("mkdir -p " + vhost + " && chmod -R 755 " + vhost) - self.rewritePath = rewrite = self.setupPath + '/nginx/rewrite' - if not os.path.exists(rewrite): - mw.execShell("mkdir -p " + rewrite + " && chmod -R 755 " + rewrite) - - self.passPath = passwd = self.setupPath + '/nginx/pass' - if not os.path.exists(passwd): - mw.execShell("mkdir -p " + passwd + " && chmod -R 755 " + passwd) - - self.redirectPath = redirect = self.setupPath + '/nginx/redirect' - if not os.path.exists(redirect): - mw.execShell("mkdir -p " + redirect + - " && chmod -R 755 " + redirect) - - self.proxyPath = proxy = self.setupPath + '/nginx/proxy' - if not os.path.exists(proxy): - mw.execShell("mkdir -p " + proxy + " && chmod -R 755 " + proxy) - - self.logsPath = mw.getRootDir() + '/wwwlogs' - # ssl conf - self.sslDir = self.setupPath + '/ssl' - self.sslLetsDir = self.setupPath + '/letsencrypt' - if not os.path.exists(self.sslLetsDir): - mw.execShell("mkdir -p " + self.sslLetsDir + - " && chmod -R 755 " + self.sslLetsDir) - - def runHook(self, hook_name, func_name): - # 站点操作Hook - hook_file = 'data/hook_site_cb.json' - hook_cfg = [] - if os.path.exists(hook_file): - t = mw.readFile(hook_file) - hook_cfg = json.loads(t) - - hook_num = len(hook_cfg) - if hook_num == 0: - return - - import plugins_api - pa = plugins_api.plugins_api() - - for x in range(hook_num): - hook_data = hook_cfg[x] - if func_name in hook_data: - app_name = hook_data["name"] - run_func = hook_data[func_name]['func'] - # print(app_name, run_func) - pa.run(app_name, run_func) - return True - - ##### ----- start ----- ### - def listApi(self): - limit = request.form.get('limit', '10') - p = request.form.get('p', '1') - type_id = request.form.get('type_id', '0').strip() - search = request.form.get('search', '').strip() - - start = (int(p) - 1) * (int(limit)) - - siteM = siteC = mw.M('sites').field('id,name,path,status,ps,addtime,edate,type_id') - - sql_where = '' - if search != '': - sql_where = " name like '%" + search + "%' or ps like '%" + search + "%' " - - if type_id != '' and int(type_id) >= 0 and search != '': - sql_where = sql_where + " and type_id=" + type_id + "" - if type_id != '' and int(type_id) >= 0: - sql_where = " type_id=" + type_id - - if sql_where != '': - siteM.where(sql_where) - - _list = siteM.limit((str(start)) + ',' +limit).order('id desc').select() - if _list != None: - for i in range(len(_list)): - _list[i]['backup_count'] = mw.M('backup').where("pid=? AND type=?", (_list[i]['id'], 0)).count() - - _ret = {} - _ret['data'] = _list - - if sql_where != '': - count = siteC.where(sql_where).count() - else: - count = siteC.count() - - _page = {} - _page['count'] = count - _page['tojs'] = 'getWeb' - _page['p'] = p - _page['row'] = limit - - page_args_tpl = '' - page_args_tpl += ','+type_id+'' - if search != '': - page_args_tpl += ',"'+search+'"' - _page['args_tpl'] = page_args_tpl - - _ret['page'] = mw.getPage(_page) - return mw.getJson(_ret) - - def setDefaultSiteApi(self): - name = request.form.get('name', '') - import time - # 清理旧的 - default_site = mw.readFile('data/default_site.pl') - if default_site: - path = self.getHostConf(default_site) - if os.path.exists(path): - conf = mw.readFile(path) - rep = "listen\s+80.+;" - conf = re.sub(rep, 'listen 80;', conf, 1) - rep = "listen\s+443.+;" - conf = re.sub(rep, 'listen 443 ssl;', conf, 1) - mw.writeFile(path, conf) - - path = self.getHostConf(name) - if os.path.exists(path): - conf = mw.readFile(path) - rep = "listen\s+80\s*;" - conf = re.sub(rep, 'listen 80 default_server;', conf, 1) - rep = "listen\s+443\s*ssl\s*\w*\s*;" - conf = re.sub(rep, 'listen 443 ssl default_server;', conf, 1) - mw.writeFile(path, conf) - - mw.writeFile('data/default_site.pl', name) - mw.restartWeb() - return mw.returnJson(True, '设置成功!') - - def getDefaultSiteApi(self): - data = {} - data['sites'] = mw.M('sites').field( - 'name').order('id desc').select() - data['default_site'] = mw.readFile('data/default_site.pl') - return mw.getJson(data) - - def getCliPhpVersionApi(self): - php_dir = mw.getServerDir() + '/php' - if not os.path.exists(php_dir): - return mw.returnJson(False, '未安装PHP,无法设置') - - php_bin = '/usr/bin/php' - php_versions = self.getPhpVersion() - php_versions = php_versions[1:] - - if len(php_versions) < 1: - return mw.returnJson(False, '未安装PHP,无法设置') - - if os.path.exists(php_bin) and os.path.islink(php_bin): - link_re = os.readlink(php_bin) - for v in php_versions: - if link_re.find(v['version']) != -1: - return mw.getJson({"select": v, "versions": php_versions}) - - return mw.getJson({ - "select": php_versions[0], - "versions": php_versions}) - - def setCliPhpVersionApi(self): - if mw.isAppleSystem(): - return mw.returnJson(False, "开发机不可设置!") - - version = request.form.get('version', '') - - php_bin = '/usr/bin/php' - php_bin_src = "/www/server/php/%s/bin/php" % version - php_ize = '/usr/bin/phpize' - php_ize_src = "/www/server/php/%s/bin/phpize" % version - php_fpm = '/usr/bin/php-fpm' - php_fpm_src = "/www/server/php/%s/sbin/php-fpm" % version - php_pecl = '/usr/bin/pecl' - php_pecl_src = "/www/server/php/%s/bin/pecl" % version - php_pear = '/usr/bin/pear' - php_pear_src = "/www/server/php/%s/bin/pear" % version - if not os.path.exists(php_bin_src): - return mw.returnJson(False, '指定PHP版本未安装!') - - is_chattr = mw.execShell('lsattr /usr|grep /usr/bin')[0].find('-i-') - if is_chattr != -1: - mw.execShell('chattr -i /usr/bin') - mw.execShell("rm -f " + php_bin + ' ' + php_ize + ' ' + - php_fpm + ' ' + php_pecl + ' ' + php_pear) - mw.execShell("ln -sf %s %s" % (php_bin_src, php_bin)) - mw.execShell("ln -sf %s %s" % (php_ize_src, php_ize)) - mw.execShell("ln -sf %s %s" % (php_fpm_src, php_fpm)) - mw.execShell("ln -sf %s %s" % (php_pecl_src, php_pecl)) - mw.execShell("ln -sf %s %s" % (php_pear_src, php_pear)) - if is_chattr != -1: - mw.execShell('chattr +i /usr/bin') - mw.writeLog('面板设置', '设置PHP-CLI版本为: %s' % version) - return mw.returnJson(True, '设置成功!') - - def setPsApi(self): - mid = request.form.get('id', '') - ps = request.form.get('ps', '') - if mw.M('sites').where("id=?", (mid,)).setField('ps', ps): - return mw.returnJson(True, '修改成功!') - return mw.returnJson(False, '修改失败!') - - def stopApi(self): - mid = request.form.get('id', '') - name = request.form.get('name', '') - - return self.stop(mid, name) - - def stop(self, mid, name): - path = self.setupPath + '/stop' - if not os.path.exists(path): - os.makedirs(path) - default_text = 'The website has been closed!!!' - mw.writeFile(path + '/index.html', default_text) - - binding = mw.M('binding').where('pid=?', (mid,)).field( - 'id,pid,domain,path,port,addtime').select() - for b in binding: - bpath = path + '/' + b['path'] - if not os.path.exists(bpath): - mw.execShell('mkdir -p ' + bpath) - mw.execShell('ln -sf ' + path + - '/index.html ' + bpath + '/index.html') - - sitePath = mw.M('sites').where("id=?", (mid,)).getField('path') - - # nginx - file = self.getHostConf(name) - conf = mw.readFile(file) - if conf: - conf = conf.replace(sitePath, path) - mw.writeFile(file, conf) - - mw.M('sites').where("id=?", (mid,)).setField('status', '0') - mw.restartWeb() - msg = mw.getInfo('网站[{1}]已被停用!', (name,)) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, '站点已停用!') - - def startApi(self): - mid = request.form.get('id', '') - name = request.form.get('name', '') - path = self.setupPath + '/stop' - sitePath = mw.M('sites').where("id=?", (mid,)).getField('path') - - # nginx - file = self.getHostConf(name) - conf = mw.readFile(file) - if conf: - conf = conf.replace(path, sitePath) - mw.writeFile(file, conf) - - mw.M('sites').where("id=?", (mid,)).setField('status', '1') - mw.restartWeb() - msg = mw.getInfo('网站[{1}]已被启用!', (name,)) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, '站点已启用!') - - def getBackupApi(self): - limit = request.form.get('limit', '') - p = request.form.get('p', '') - mid = request.form.get('search', '') - - find = mw.M('sites').where("id=?", (mid,)).field( - "id,name,path,status,ps,addtime,edate").find() - - start = (int(p) - 1) * (int(limit)) - _list = mw.M('backup').where('pid=?', (mid,)).field('id,type,name,pid,filename,size,addtime').limit( - (str(start)) + ',' + limit).order('id desc').select() - _ret = {} - _ret['data'] = _list - - count = mw.M('backup').where("id=?", (mid,)).count() - info = {} - info['count'] = count - info['tojs'] = 'getBackup' - info['p'] = p - info['row'] = limit - _ret['page'] = mw.getPage(info) - _ret['site'] = find - return mw.getJson(_ret) - - def toBackupApi(self): - mid = request.form.get('id', '') - find = mw.M('sites').where( - "id=?", (mid,)).field('name,path,id').find() - fileName = find['name'] + '_' + \ - time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.zip' - backupPath = mw.getBackupDir() + '/site' - zipName = backupPath + '/' + fileName - if not (os.path.exists(backupPath)): - os.makedirs(backupPath) - tmps = mw.getRunDir() + '/tmp/panelExec.log' - execStr = "cd '" + find['path'] + "' && zip '" + \ - zipName + "' -r ./* > " + tmps + " 2>&1" - # print execStr - mw.execShell(execStr) - - if os.path.exists(zipName): - fsize = os.path.getsize(zipName) - else: - fsize = 0 - sql = mw.M('backup').add('type,name,pid,filename,size,addtime', - (0, fileName, find['id'], zipName, fsize, mw.getDate())) - - msg = mw.getInfo('备份网站[{1}]成功!', (find['name'],)) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, '备份成功!') - - def delBackupApi(self): - mid = request.form.get('id', '') - filename = mw.M('backup').where( - "id=?", (mid,)).getField('filename') - if os.path.exists(filename): - os.remove(filename) - name = mw.M('backup').where("id=?", (mid,)).getField('name') - msg = mw.getInfo('删除网站[{1}]的备份[{2}]成功!', (name, filename)) - mw.writeLog('网站管理', msg) - mw.M('backup').where("id=?", (mid,)).delete() - return mw.returnJson(True, '站点删除成功!') - - def getPhpVersionApi(self): - data = self.getPhpVersion() - return mw.getJson(data) - - def setPhpVersionApi(self): - siteName = request.form.get('siteName', '') - version = request.form.get('version', '') - - # nginx - file = self.getHostConf(siteName) - conf = mw.readFile(file) - if conf: - rep = "enable-php-(.*)\.conf" - tmp = re.search(rep, conf).group() - conf = conf.replace(tmp, 'enable-php-' + version + '.conf') - mw.writeFile(file, conf) - - msg = mw.getInfo('成功切换网站[{1}]的PHP版本为PHP-{2}', (siteName, version)) - mw.writeLog("网站管理", msg) - mw.restartWeb() - return mw.returnJson(True, msg) - - def getDomainApi(self): - pid = request.form.get('pid', '') - return self.getDomain(pid) - - # 获取站点所有域名 - def getSiteDomainsApi(self): - pid = request.form.get('id', '') - - data = {} - domains = mw.M('domain').where( - 'pid=?', (pid,)).field('name,id').select() - binding = mw.M('binding').where( - 'pid=?', (pid,)).field('domain,id').select() - if type(binding) == str: - return binding - for b in binding: - tmp = {} - tmp['name'] = b['domain'] - tmp['id'] = b['id'] - domains.append(tmp) - data['domains'] = domains - data['email'] = mw.M('users').getField('email') - if data['email'] == 'midoks@163.com': - data['email'] = '' - return mw.returnJson(True, 'OK', data) - - def getDirBindingApi(self): - mid = request.form.get('id', '') - - path = mw.M('sites').where('id=?', (mid,)).getField('path') - if not os.path.exists(path): - checks = ['/', '/usr', '/etc'] - if path in checks: - data = {} - data['dirs'] = [] - data['binding'] = [] - return mw.returnJson(True, 'OK', data) - os.system('mkdir -p ' + path) - os.system('chmod 755 ' + path) - os.system('chown www:www ' + path) - siteName = mw.M('sites').where( - 'id=?', (get.id,)).getField('name') - mw.writeLog( - '网站管理', '站点[' + siteName + '],根目录[' + path + ']不存在,已重新创建!') - - dirnames = [] - for filename in os.listdir(path): - try: - filePath = path + '/' + filename - if os.path.islink(filePath): - continue - if os.path.isdir(filePath): - dirnames.append(filename) - except: - pass - - data = {} - data['dirs'] = dirnames - data['binding'] = mw.M('binding').where('pid=?', (mid,)).field( - 'id,pid,domain,path,port,addtime').select() - return mw.returnJson(True, 'OK', data) - - def getDirUserIniApi(self): - mid = request.form.get('id', '') - - path = mw.M('sites').where('id=?', (mid,)).getField('path') - name = mw.M('sites').where("id=?", (mid,)).getField('name') - data = {} - data['logs'] = self.getLogsStatus(name) - data['runPath'] = self.getSiteRunPath(mid) - - data['userini'] = False - if os.path.exists(path + '/.user.ini'): - data['userini'] = True - - if data['runPath']['runPath'] != '/': - if os.path.exists(path + data['runPath']['runPath'] + '/.user.ini'): - data['userini'] = True - - data['pass'] = self.getHasPwd(name) - data['path'] = path - data['name'] = name - return mw.returnJson(True, 'OK', data) - - def setDirUserIniApi(self): - path = request.form.get('path', '') - runPath = request.form.get('runPath', '') - filename = path + '/.user.ini' - - if os.path.exists(filename): - self.delUserInI(path) - mw.execShell("which chattr && chattr -i " + filename) - os.remove(filename) - return mw.returnJson(True, '已清除防跨站设置!') - - self.setDirUserINI(path, runPath) - mw.execShell("which chattr && chattr +i " + filename) - - return mw.returnJson(True, '已打开防跨站设置!') - - def setRewriteApi(self): - data = request.form.get('data', '') - path = request.form.get('path', '') - encoding = request.form.get('encoding', '') - if not os.path.exists(path): - mw.writeFile(path, '') - - mw.backFile(path) - mw.writeFile(path, data) - isError = mw.checkWebConfig() - if(type(isError) == str): - mw.restoreFile(path) - return mw.returnJson(False, 'ERROR:
                    ' + 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 + '

                    响应状态:' + str(emsg['status']) + '

                    错误类型:' + emsg[ - 'type'] + '

                    错误代码:' + emsg['detail'] + '

                    ' - return mw.returnJson(data['status'], msg, data['msg']) - - mw.execShell('mkdir -p ' + dst_letpath) - mw.buildSoftLink(src_csrpath, dst_csrpath, True) - mw.buildSoftLink(src_keypath, dst_keypath, True) - mw.execShell('echo "lets" > "' + dst_letpath + '/README"') - - # 写入配置文件 - result = self.setSslConf(siteName) - if not result['status']: - return mw.getJson(result) - - result['csr'] = mw.readFile(src_csrpath) - result['key'] = mw.readFile(src_keypath) - - mw.restartWeb() - return mw.returnJson(data['status'], data['msg'], result) - - def getAcmeLogsApi(self): - log_file = mw.getRunDir() + '/logs/acme.log' - if not os.path.exists(log_file): - mw.execShell('touch ' + log_file) - return mw.returnJson(True, 'OK', log_file) - - def createAcmeApi(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, '请选择域名') - - file = self.getHostConf(siteName) - if os.path.exists(file): - siteConf = mw.readFile(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, '检测到您的站点做了反向代理设置,请先关闭反向代理!') - - siteInfo = mw.M('sites').where( - 'name=?', (siteName,)).field('id,name,path').find() - path = self.getSitePath(siteName) - if path == '': - return mw.returnJson(False, '【'+siteName+'】配置文件,异常!') - - srcPath = siteInfo['path'] - - # 检测acme是否安装 - acme_dir = mw.getAcmeDir() - if not os.path.exists(acme_dir): - try: - mw.execShell("curl -sS curl https://get.acme.sh | sh") - except: - pass - if not os.path.exists(acme_dir): - return mw.returnJson(False, '尝试自动安装ACME失败,请通过以下命令尝试手动安装

                    安装命令: curl https://get.acme.sh | sh

                    ') - - # 避免频繁执行 - checkAcmeRun = mw.execShell('ps -ef|grep acme.sh |grep -v grep') - if checkAcmeRun[0] != '': - return mw.returnJson(False, '正在申请或更新SSL中...') - - if force == 'true': - force_bool = True - - if renew == 'true': - execStr = acme_dir + "/acme.sh --renew --yes-I-know-dns-manual-mode-enough-go-ahead-please" - else: - execStr = acme_dir + "/acme.sh --issue --force" - - # 确定主域名顺序 - domainsTmp = [] - if siteName in domains: - domainsTmp.append(siteName) - for domainTmp in domains: - if domainTmp == siteName: - continue - domainsTmp.append(domainTmp) - domains = domainsTmp - - domainCount = 0 - for domain in domains: - if mw.checkIp(domain): - continue - if domain.find('*.') != -1: - return mw.returnJson(False, '泛域名不能使用【文件验证】的方式申请证书!') - execStr += ' -w ' + path - execStr += ' -d ' + domain - domainCount += 1 - if domainCount == 0: - return mw.returnJson(False, '请选择域名(不包括IP地址与泛域名)!') - - log_file = mw.getRunDir() + '/logs/acme.log' - mw.writeFile(log_file, "开始ACME申请...\n", "wb+") - cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + \ - execStr + ' >> ' + log_file - # print(domains) - # print(cmd) - result = mw.execShell(cmd) - - src_path = mw.getAcmeDomainDir(domains[0]) - src_cert = src_path + '/fullchain.cer' - src_key = src_path + '/' + domains[0] + '.key' - src_cert.replace("\\*", "*") - - msg = '签发失败,您尝试申请证书的失败次数已达上限!

                    1、检查域名是否绑定到对应站点

                    \ -

                    2、检查域名是否正确解析到本服务器,或解析还未完全生效

                    \ -

                    3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭

                    \ -

                    4、如果您的站点设置了301重定向,请先将其关闭

                    \ -

                    5、如果以上检查都确认没有问题,请尝试更换DNS服务商

                    ' - if not os.path.exists(src_cert): - data = {} - data['err'] = result - data['out'] = result[0] - data['msg'] = msg - data['result'] = {} - if result[1].find('new-authz error:') != -1: - data['result'] = json.loads( - re.search("{.+}", result[1]).group()) - if data['result']['status'] == 429: - data['msg'] = msg - data['status'] = False - return mw.getJson(data) - - dst_path = self.sslDir + '/' + siteName - dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 - dst_key = dst_path + "/privkey.pem" # 密钥文件路径 - - if not os.path.exists(dst_path): - mw.execShell("mkdir -p " + dst_path) - - mw.buildSoftLink(src_cert, dst_cert, True) - mw.buildSoftLink(src_key, dst_key, True) - mw.execShell('echo "acme" > "' + dst_path + '/README"') - - # 写入配置文件 - result = self.setSslConf(siteName) - if not result['status']: - return mw.getJson(result) - result['csr'] = mw.readFile(src_cert) - result['key'] = mw.readFile(src_key) - - mw.restartWeb() - return mw.returnJson(True, '证书已更新!', result) - - def httpToHttpsApi(self): - siteName = request.form.get('siteName', '') - return self.httpToHttps(siteName) - - def httpToHttps(self, site_name): - file = self.getHostConf(site_name) - conf = mw.readFile(file) - 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(file, conf) - - mw.restartWeb() - return mw.returnJson(True, '设置成功!') - - def closeToHttpsApi(self): - siteName = request.form.get('siteName', '') - return self.closeToHttps(siteName) - - def closeToHttps(self, site_name): - file = self.getHostConf(site_name) - conf = mw.readFile(file) - if conf: - rep = r"\n\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" - conf = re.sub(rep, '', conf) - rep = r"\s+if.+server_port.+\n.+\n\s+\s*}" - conf = re.sub(rep, '', conf) - mw.writeFile(file, conf) - - mw.restartWeb() - return mw.returnJson(True, '关闭HTTPS跳转成功!') - - def getIndexApi(self): - sid = request.form.get('id', '') - data = {} - index = self.getIndex(sid) - data['index'] = index - return mw.getJson(data) - - def setIndexApi(self): - sid = request.form.get('id', '') - index = request.form.get('index', '') - return self.setIndex(sid, index) - - def getLimitNetApi(self): - sid = request.form.get('id', '') - return self.getLimitNet(sid) - - def saveLimitNetApi(self): - sid = request.form.get('id', '') - perserver = request.form.get('perserver', '') - perip = request.form.get('perip', '') - limit_rate = request.form.get('limit_rate', '') - return self.saveLimitNet(sid, perserver, perip, limit_rate) - - def closeLimitNetApi(self): - sid = request.form.get('id', '') - return self.closeLimitNet(sid) - - def getSecurityApi(self): - sid = request.form.get('id', '') - name = request.form.get('name', '') - return self.getSecurity(sid, name) - - def setSecurityApi(self): - fix = request.form.get('fix', '') - domains = request.form.get('domains', '') - status = request.form.get('status', '') - name = request.form.get('name', '') - none = request.form.get('none', '') - sid = request.form.get('id', '') - return self.setSecurity(sid, name, fix, domains, status, none) - - def getLogsApi(self): - siteName = request.form.get('siteName', '') - return self.getLogs(siteName) - - def getErrorLogsApi(self): - siteName = request.form.get('siteName', '') - return self.getErrorLogs(siteName) - - def getSitePhpVersionApi(self): - siteName = request.form.get('siteName', '') - return self.getSitePhpVersion(siteName) - - def getHostConfApi(self): - siteName = request.form.get('siteName', '') - host = self.getHostConf(siteName) - return mw.getJson({'host': host}) - - def saveHostConfApi(self): - path = request.form.get('path', '') - data = request.form.get('data', '') - encoding = request.form.get('encoding', '') - - import files_api - - mw.backFile(path) - save_ret_data = files_api.files_api().saveBody(path, data, encoding) - rdata = json.loads(save_ret_data) - - if rdata['status']: - isError = mw.checkWebConfig() - if isError != True: - mw.restoreFile(path) - return mw.returnJson(False, 'ERROR: 检测到配置文件有错误,请先排除后再操作

                    ' + 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/ssh_local.py b/class/core/ssh_local.py deleted file mode 100644 index 275ff1254..000000000 --- a/class/core/ssh_local.py +++ /dev/null @@ -1,120 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# SSH终端操作 -# --------------------------------------------------------------------------------- - -import json -import time -import os -import sys -import socket -import threading -import re - -from io import BytesIO, StringIO - -import mw -import paramiko - -from flask_socketio import SocketIO, emit, send - - -class ssh_local(object): - - __debug_file = 'logs/ssh_local.log' - __log_type = 'SSH终端' - - __ssh = None - - # lock - _instance_lock = threading.Lock() - - def __init__(self): - pass - - @classmethod - def instance(cls, *args, **kwargs): - if not hasattr(ssh_local, "_instance"): - with ssh_local._instance_lock: - if not hasattr(ssh_local, "_instance"): - ssh_local._instance = ssh_local(*args, **kwargs) - return ssh_local._instance - - def debug(self, msg): - msg = "{} - {}:{} => {} \n".format(mw.formatDate(), - self.__host, self.__port, msg) - if not mw.isDebugMode(): - return - mw.writeFile(self.__debug_file, msg, 'a+') - - def returnMsg(self, status, msg): - return {'status': status, 'msg': msg} - - - def connectSsh(self): - import paramiko - ssh = paramiko.SSHClient() - mw.createSshInfo() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - port = mw.getSSHPort() - try: - ssh.connect('127.0.0.1', port, timeout=5) - except Exception as e: - ssh.connect('localhost', port, timeout=5) - except Exception as e: - ssh.connect(mw.getHostAddr(), port, timeout=30) - except Exception as e: - return False - - shell = ssh.invoke_shell(term='xterm', width=83, height=21) - shell.setblocking(0) - return shell - - def send(self): - pass - - def close(self): - try: - if self.__ssh: - self.__ssh.close() - except: - pass - - def wsSend(self, recv): - try: - t = recv.decode("utf-8") - return emit('server_response', {'data': t}) - except Exception as e: - return emit('server_response', {'data': recv}) - - def wsSendConnect(self): - return emit('connect', {'data': 'ok'}) - - def wsSendReConnect(self): - return emit('reconnect', {'data': 'ok'}) - - - def run(self, info): - if not self.__ssh: - self.__ssh = self.connectSsh() - - if self.__ssh.exit_status_ready(): - self.__ssh = self.connectSsh() - - self.__ssh.send(info) - try: - time.sleep(0.005) - recv = self.__ssh.recv(8192) - return self.wsSend(recv) - except Exception as ex: - return self.wsSend('') diff --git a/class/core/ssh_terminal.py b/class/core/ssh_terminal.py deleted file mode 100644 index 315289fb7..000000000 --- a/class/core/ssh_terminal.py +++ /dev/null @@ -1,467 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# SSH终端操作 -# --------------------------------------------------------------------------------- - -import json -import time -import os -import sys -import socket -import threading -import re - -from io import BytesIO, StringIO - -import mw -import paramiko - -from flask_socketio import SocketIO, emit, send - - -class ssh_terminal(object): - - __debug_file = 'logs/ssh_terminal.log' - __log_type = 'SSH终端' - - # websocketio 唯一标识 - __sid = '' - - __host = None - __type = '0' - __port = 22 - __user = None - __pass = None - __pkey = None - __key_passwd = None - - __rep_ssh_config = False - __rep_ssh_service = False - __sshd_config_backup = None - - __ssh = None - __tp = None - __ps = None - - __ssh_list = {} - __ssh_last_request_time = {} - - # lock - _instance_lock = threading.Lock() - - def __init__(self): - ht = threading.Thread(target=self.heartbeat) - ht.start() - - @classmethod - def instance(cls, *args, **kwargs): - if not hasattr(ssh_terminal, "_instance"): - with ssh_terminal._instance_lock: - if not hasattr(ssh_terminal, "_instance"): - ssh_terminal._instance = ssh_terminal(*args, **kwargs) - return ssh_terminal._instance - - def debug(self, msg): - msg = "{} - {}:{} => {} \n".format(mw.formatDate(), - self.__host, self.__port, msg) - if not mw.isDebugMode(): - return - mw.writeFile(self.__debug_file, msg, 'a+') - - def returnMsg(self, status, msg): - return {'status': status, 'msg': msg} - - def restartSsh(self, act='reload'): - ''' - 重启ssh 无参数传递 - ''' - version = mw.readFile('/etc/redhat-release') - if not os.path.exists('/etc/redhat-release'): - mw.execShell('service ssh ' + act) - elif version.find(' 7.') != -1 or version.find(' 8.') != -1: - mw.execShell("systemctl " + act + " sshd.service") - else: - mw.execShell("/etc/init.d/sshd " + act) - - def isRunning(self, rep=False): - try: - if rep and self.__rep_ssh_service: - self.restartSsh('stop') - return True - - status = self.getSshStatus() - if not status: - self.restartSsh('start') - self.__rep_ssh_service = True - return True - return False - except: - return False - - def setSshdConfig(self, rep=False): - self.isRunning(rep) - if rep and not self.__rep_ssh_config: - return False - - try: - sshd_config_file = '/etc/ssh/sshd_config' - if not os.path.exists(sshd_config_file): - return False - - sshd_config = mw.readFile(sshd_config_file) - if not sshd_config: - return False - - if rep: - if self.__sshd_config_backup: - mw.writeFile(sshd_config_file, self.__sshd_config_backup) - self.restartSsh() - return True - - pin = r'^\s*PubkeyAuthentication\s+(yes|no)' - pubkey_status = re.findall(pin, sshd_config, re.I) - if pubkey_status: - if pubkey_status[0] == 'yes': - pubkey_status = True - else: - pubkey_status = False - - pin = r'^\s*RSAAuthentication\s+(yes|no)' - rsa_status = re.findall(pin, sshd_config, re.I) - if rsa_status: - if rsa_status[0] == 'yes': - rsa_status = True - else: - rsa_status = False - - self._sshd_config_backup = sshd_config - is_write = False - if not pubkey_status: - sshd_config = re.sub( - r'\n#?PubkeyAuthentication\s\w+', '\nPubkeyAuthentication yes', sshd_config) - is_write = True - if not rsa_status: - sshd_config = re.sub( - r'\n#?RSAAuthentication\s\w+', '\nRSAAuthentication yes', sshd_config) - is_write = True - - if is_write: - mw.writeFile(sshd_config_file, sshd_config) - self.__rep_ssh_config = True - self.restartSsh() - else: - self.__sshd_config_backup = None - return True - except: - return False - - def setSid(self, sid): - self.__sid = sid - - def connect(self, sid): - # self.connectBySocket() - if self.__host in ['127.0.0.1', 'localhost']: - return self.connectLocalSsh(sid) - else: - return self.connectBySocket(sid) - - def connectLocalSsh(self, sid): - mw.createSshInfo() - self.__ps = paramiko.SSHClient() - self.__ps.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - self.__port = mw.getSSHPort() - try: - self.__ps.connect(self.__host, self.__port, timeout=60) - except Exception as e: - self.__ps.connect('127.0.0.1', self.__port) - except Exception as e: - self.__ps.connect('localhost', self.__port) - except Exception as e: - self.setSshdConfig(True) - self.__ps.close() - e = str(e) - if e.find('websocket error!') != -1: - return self.returnMsg(True, '连接成功') - if e.find('Authentication timeout') != -1: - self.debug("认证超时{}".format(e)) - return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e)) - if e.find('Connection reset by peer') != -1: - self.debug('目标服务器主动拒绝连接') - return self.returnMsg(False, '目标服务器主动拒绝连接') - if e.find('Error reading SSH protocol banner') != -1: - self.debug('协议头响应超时') - return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e) - if not e: - self.debug('SSH协议握手超时') - return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕") - err = mw.getTracebackInfo() - self.debug(err) - return self.returnMsg(False, "未知错误: {}".format(err)) - - self.debug('local-ssh:认证成功,正在构建会话通道') - ssh = self.__ps.invoke_shell( - term='xterm', width=83, height=21) - ssh.setblocking(0) - self.__ssh_list[sid] = ssh - mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format( - self.__host, self.__port)) - self.debug('local-ssh:通道已构建') - return self.returnMsg(True, '连接成功!') - - def connectBySocket(self, sid): - if not self.__host: - return self.returnMsg(False, '错误的连接地址') - if not self.__user: - self.__user = 'root' - if not self.__port: - self.__port = 22 - - self.setSshdConfig(True) - num = 0 - while num < 5: - num += 1 - try: - self.debug('正在尝试第{}次连接'.format(num)) - if self.__rep_ssh_config: - time.sleep(0.1) - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(2 + num) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192) - sock.connect((self.__host, self.__port)) - break - except Exception as e: - if num == 5: - self.setSshdConfig(True) - self.debug('重试连接失败,{}'.format(e)) - return self.returnMsg(False, '连接目标服务器失败, {}:{}'.format(self.__host, self.__port)) - else: - time.sleep(0.2) - - # print(self.__host, sock) - self.__tp = paramiko.Transport(sock) - try: - self.__tp.start_client() - self.__tp.banner_timeout = 60 - if not self.__pass and not self.__pkey: - return self.returnMsg(False, '密码或私钥不能都为空: {}:{}'.format(self.__host, self.__port)) - - if self.__pkey != '' and self.__type != '0': - self.debug('正在认证私钥') - p_file = StringIO(str(self.__pkey.replace('\\n', '\n'))) - # p_file = "/tmp/t_ssh_pkey.txt" - # mw.writeFile(p_file, self.__pkey.replace('\\n', '\n')) - # mw.execShell('chmod 600 ' + p_file) - try: - p_file.seek(0) - pkey = paramiko.RSAKey.from_private_key(p_file) - except: - try: - p_file.seek(0) # 重置游标 - pkey = paramiko.Ed25519Key.from_private_key( - p_file) - except: - try: - p_file.seek(0) - pkey = paramiko.ECDSAKey.from_private_key(p_file) - except: - p_file.seek(0) - pkey = paramiko.DSSKey.from_private_key(p_file) - - self.__tp.auth_publickey(username=self.__user, key=pkey) - else: - try: - self.__tp.auth_none(self.__user) - except Exception as e: - e = str(e) - if e.find('keyboard-interactive') >= 0: - self._auth_interactive() - else: - self.debug('正在认证密码') - self.__tp.auth_password( - username=self.__user, password=self.__pass) - except Exception as e: - self.setSshdConfig(True) - self.__tp.close() - e = str(e) - # print(e) - if e.find('Authentication timeout') != -1: - self.debug("认证超时{}".format(e)) - return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e)) - if e.find('Authentication failed') != -1: - self.debug('认证失败{}'.format(e)) - return self.returnMsg(False, '帐号或密码错误: {}'.format(e + "," + self.__user + "@" + self.__host + ":" + str(self.__port))) - if e.find('Bad authentication type; allowed types') != -1: - self.debug('认证失败{}'.format(e)) - if self.__host in ['127.0.0.1', 'localhost'] and self.__pass == 'none': - return self.returnMsg(False, '帐号或密码错误: {}'.format("Authentication failed ," + self.__user + "@" + self.__host + ":" + str(self.__port))) - return self.returnMsg(False, '不支持的身份验证类型: {}'.format(e)) - if e.find('Connection reset by peer') != -1: - self.debug('目标服务器主动拒绝连接') - return self.returnMsg(False, '目标服务器主动拒绝连接') - if e.find('Error reading SSH protocol banner') != -1: - self.debug('协议头响应超时') - return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e) - if not e: - self.debug('SSH协议握手超时') - return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕") - err = mw.getTracebackInfo() - self.debug(err) - return self.returnMsg(False, "未知错误: {}".format(err)) - - self.debug('认证成功,正在构建会话通道') - - ssh = self.__tp.open_session() - ssh.get_pty(term='xterm', width=100, height=34) - ssh.invoke_shell() - self.__ssh_list[sid] = ssh - mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format( - self.__host, self.__port)) - self.debug('通道已构建') - return self.returnMsg(True, '连接成功.') - - def getSshInfo(self, file): - rdata = mw.readFile(file) - destr = mw.deDoubleCrypt('mdserver-web', rdata) - return json.loads(destr) - - def setAttr(self, sid, info): - self.__host = info['host'].strip() - - # 外部连接获取 - if not self.__host in ['127.0.0.1', 'localhost']: - dst_info = mw.getServerDir() + '/webssh/host/' + self.__host + '/info.json' - if os.path.exists(dst_info): - info = self.getSshInfo(dst_info) - - if 'type' in info: - self.__type = info['type'] - - if 'port' in info: - self.__port = int(info['port']) - if 'username' in info: - self.__user = info['username'] - if 'pkey' in info: - self.__pkey = info['pkey'] - if 'password' in info: - self.__pass = info['password'] - if 'pkey_passwd' in info: - self.__key_passwd = info['pkey_passwd'] - - # print(self.__host, self.__pass, self.__key_passwd) - try: - result = self.connect(sid) - # print(result) - except Exception as ex: - if str(ex).find("NoneType") == -1: - raise ex - return result - - def send(self): - pass - - def close(self): - try: - if self.__ssh: - self.__ssh.close() - if self.__tp: # 关闭宿主服务 - self.__tp.close() - if self.__ps: - self.__ps.close() - except: - pass - - def resize(self, sid, data): - try: - self.__ssh_list[sid].resize_pty( - width=data['cols'], height=data['rows']) - return True - except: - return False - - def wsSend(self, recv): - try: - t = recv.decode("utf-8") - return emit('server_response', {'data': t}) - except Exception as e: - return emit('server_response', {'data': recv}) - - def wsSendConnect(self): - return emit('connect', {'data': 'ok'}) - - def wsSendReConnect(self): - return emit('reconnect', {'data': 'ok'}) - - def heartbeat(self): - # limit_cos = 10 - while True: - time.sleep(3) - cur_time = time.time() - for x in list(self.__ssh_list.keys()): - ssh_last_time = self.__ssh_last_request_time[x] - sid_off_cos = cur_time - ssh_last_time - - # print("heartbeat off cos :", x, sid_off_cos) - if sid_off_cos > 3: - cur_ssh = self.__ssh_list[x] - if not cur_ssh: - del(self.__ssh_list[x]) - del(self.__ssh_last_request_time[x]) - continue - - if cur_ssh.exit_status_ready(): - del(self.__ssh_list[x]) - del(self.__ssh_last_request_time[x]) - continue - - cur_ssh.send("exit\r\n") - del(self.__ssh_list[x]) - del(self.__ssh_last_request_time[x]) - - def run(self, sid, info): - self.__sid = sid - if not self.__sid: - return self.wsSend('WebSocketIO无效') - - self.__ssh_last_request_time[sid] = time.time() - if not sid in self.__ssh_list: - if type(info) == dict and 'host' in info: - result = self.setAttr(sid, info) - if result['status']: - return self.wsSendConnect() - else: - return self.wsSend(result['msg']) - else: - return self.wsSendReConnect() - - result = self.returnMsg(False, '') - if sid in self.__ssh_list: - if 'resize' in info: - self.resize(sid, info) - result = self.returnMsg(True, '已连接') - if result['status']: - if type(info) == str: - time.sleep(0.1) - cur_ssh = self.__ssh_list[sid] - if cur_ssh.exit_status_ready(): - self.wsSend("logout\r\n") - del(self.__ssh_list[sid]) - return - cur_ssh.send(info) - try: - time.sleep(0.005) - recv = cur_ssh.recv(8192) - return self.wsSend(recv) - except Exception as ex: - return self.wsSend('') - else: - return self.wsSend(result['msg']) 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 -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 系统信息操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import re -import math -import sys -import json - -from flask import Flask, session -from flask import request - -import db -import mw - -import config_api -import requests - -from threading import Thread -from time import sleep - - -def mw_async(f): - def wrapper(*args, **kwargs): - thr = Thread(target=f, args=args, kwargs=kwargs) - thr.start() - return wrapper - - -class system_api: - setupPath = None - pids = None - cache = {} - def __init__(self): - self.setupPath = mw.getServerDir() - - - ##### ----- start ----- ### - def networkApi(self): - data = self.getNetWork() - return mw.getJson(data) - - def updateServerApi(self): - stype = request.args.get('type', 'check') - version = request.args.get('version', '') - return self.updateServer(stype, version) - - def systemTotalApi(self): - data = self.getSystemTotal() - return mw.getJson(data) - - def diskInfoApi(self): - diskInfo = self.getDiskInfo() - return mw.getJson(diskInfo) - - def setControlApi(self): - stype = request.form.get('type', '') - day = request.form.get('day', '') - data = self.setControl(stype, day) - return data - - def getLoadAverageApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getLoadAverageData(start, end) - return mw.getJson(data) - - def getCpuIoApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getCpuIoData(start, end) - return mw.getJson(data) - - def getDiskIoApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getDiskIoData(start, end) - return mw.getJson(data) - - def getNetworkIoApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getNetWorkIoData(start, end) - return mw.getJson(data) - - def rememoryApi(self): - os.system('sync') - scriptFile = mw.getRunDir() + '/script/rememory.sh' - mw.execShell("/bin/bash " + scriptFile) - data = self.getMemInfo() - return mw.getJson(data) - - # 重启面板 - def restartApi(self): - self.restartMw() - return mw.returnJson(True, '面板已重启!') - - def restartServerApi(self): - if mw.isAppleSystem(): - return mw.returnJson(False, "开发环境不可重起") - self.restartServer() - return mw.returnJson(True, '正在重启服务器!') - ##### ----- end ----- ### - - def restartTask(self): - initd = mw.getRunDir() + '/scripts/init.d/mw' - if os.path.exists(initd): - os.system(initd + ' ' + 'restart_task') - return True - - def restartMw(self): - mw.writeFile('data/restart.pl', 'True') - return True - - @mw_async - def restartServer(self): - if not mw.isRestart(): - return mw.returnJson(False, '请等待所有安装任务完成再执行!') - mw.execShell("sync && init 6 &") - return mw.returnJson(True, '命令发送成功!') - - # 名取PID - def getPid(self, pname): - try: - if not self.pids: - self.pids = psutil.pids() - for pid in self.pids: - if psutil.Process(pid).name() == pname: - return True - return False - except: - return False - - # 检查端口是否占用 - def isOpen(self, port): - import socket - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - s.connect(('127.0.0.1', int(port))) - s.shutdown(2) - return True - except: - return False - - # 检测指定进程是否存活 - def checkProcess(self, pid): - try: - if not self.pids: - self.pids = psutil.pids() - if int(pid) in self.pids: - return True - return False - except: - return False - - def getEnvInfoApi(self, get=None): - serverInfo = {} - serverInfo['status'] = True - sdir = mw.getServerDir() - - serverInfo['webserver'] = '未安装' - if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'): - serverInfo['webserver'] = 'OpenResty' - serverInfo['php'] = [] - phpversions = ['52', '53', '54', '55', '56', '70', '71', - '72', '73', '74', '80', '81', '82', '83', '84'] - phpPath = sdir + '/php/' - for pv in phpversions: - if not os.path.exists(phpPath + pv + '/bin/php'): - continue - serverInfo['php'].append(pv) - serverInfo['mysql'] = False - if os.path.exists(sdir + '/mysql/bin/mysql'): - serverInfo['mysql'] = True - import psutil - try: - diskInfo = psutil.disk_usage('/www') - except: - diskInfo = psutil.disk_usage('/') - serverInfo['disk'] = diskInfo[2] - return mw.returnJson(True, 'ok', serverInfo) - - def getPanelInfo(self, get=None): - # 取面板配置 - address = mw.GetLocalIp() - try: - try: - port = web.ctx.host.split(':')[1] - except: - port = mw.readFile('data/port.pl') - except: - port = '7200' - domain = '' - if os.path.exists('data/domain.conf'): - domain = mw.readFile('data/domain.conf') - - autoUpdate = '' - if os.path.exists('data/autoUpdate.pl'): - autoUpdate = 'checked' - limitip = '' - if os.path.exists('data/limitip.conf'): - limitip = mw.readFile('data/limitip.conf') - - templates = [] - for template in os.listdir('templates/'): - if os.path.isdir('templates/' + template): - templates.append(template) - template = mw.readFile('data/templates.pl') - - check502 = '' - if os.path.exists('data/502Task.pl'): - check502 = 'checked' - return {'port': port, 'address': address, 'domain': domain, 'auto': autoUpdate, '502': check502, 'limitip': limitip, 'templates': templates, 'template': template} - - def getSystemTotal(self, interval=1): - # 取系统统计信息 - data = self.getMemInfo() - cpu = self.getCpuInfo(interval) - data['cpuNum'] = cpu[1] - data['cpuRealUsed'] = cpu[0] - data['time'] = self.getBootTime() - data['system'] = self.getSystemVersion() - data['isuser'] = mw.M('users').where( - 'username=?', ('admin',)).count() - data['version'] = '0.0.1' - return data - - def getLoadAverage(self): - c = os.getloadavg() - data = {} - data['one'] = round(float(c[0]), 2) - data['five'] = round(float(c[1]), 2) - data['fifteen'] = round(float(c[2]), 2) - data['max'] = psutil.cpu_count() * 2 - data['limit'] = data['max'] - data['safe'] = data['max'] * 0.75 - return data - - def getAllInfo(self, get): - data = {} - data['load_average'] = self.GetLoadAverage(get) - data['title'] = self.GetTitle() - data['network'] = self.GetNetWorkApi(get) - data['panel_status'] = not os.path.exists('/www/server/mdserver-web/data/close.pl') - import firewalls - ssh_info = firewalls.firewalls().GetSshInfo(None) - data['enable_ssh_status'] = ssh_info['status'] - data['disable_ping_status'] = not ssh_info['ping'] - data['time'] = self.GetBootTime() - # data['system'] = self.GetSystemVersion(); - # data['mem'] = self.GetMemInfo(); - data['version'] = web.ctx.session.version - return data - - def getTitle(self): - titlePl = 'data/title.pl' - title = 'Linux面板' - if os.path.exists(titlePl): - title = mw.readFile(titlePl).strip() - return title - - def getSystemDeviceTemperature(self): - if not hasattr(psutil, "sensors_temperatures"): - return False, "platform not supported" - temps = psutil.sensors_temperatures() - if not temps: - return False, "can't read any temperature" - for name, entries in temps.items(): - for entry in entries: - return True, entry.label - # print("%-20s %s °C (high = %s °C, critical = %s °C)" % ( - # entry.label or name, entry.current, entry.high, - # entry.critical)) - return False, "" - - def getSystemVersion(self): - # - # 取操作系统版本 - current_os = mw.getOs() - # sys_temper = self.getSystemDeviceTemperature() - # print(sys_temper) - # mac - if current_os == 'darwin': - data = mw.execShell('sw_vers')[0] - data_list = data.strip().split("\n") - mac_version = '' - for x in data_list: - xlist = x.split("\t") - mac_version += xlist[len(xlist)-1] + ' ' - - arch_ver = mw.execShell("arch") - return mac_version + " (" + arch_ver[0].strip() + ")" - - # freebsd - if current_os.startswith('freebsd'): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - arch_ver = mw.execShell( - "sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - - redhat_series = '/etc/redhat-release' - if os.path.exists(redhat_series): - version = mw.readFile('/etc/redhat-release') - version = version.replace('release ', '').strip() - - arch_ver = mw.execShell("arch") - return version + " (" + arch_ver[0].strip() + ")" - - os_series = '/etc/os-release' - if os.path.exists(os_series): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - - arch_ver = mw.execShell("arch") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - - return '未识别系统信息' - - def getBootTime(self): - # 取系统启动时间 - uptime = mw.readFile('/proc/uptime') - if uptime == False: - start_time = psutil.boot_time() - run_time = time.time() - start_time - else: - run_time = uptime.split()[0] - tStr = float(run_time) - min = tStr / 60 - hours = min / 60 - days = math.floor(hours / 24) - hours = math.floor(hours - (days * 24)) - min = math.floor(min - (days * 60 * 24) - (hours * 60)) - return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min)))) - - def getCpuInfo(self, interval=1): - # 取CPU信息 - cpuCount = psutil.cpu_count() - cpuLogicalNum = psutil.cpu_count(logical=False) - used = psutil.cpu_percent(interval=interval) - cpuLogicalNum = 0 - if os.path.exists('/proc/cpuinfo'): - c_tmp = mw.readFile('/proc/cpuinfo') - d_tmp = re.findall("physical id.+", c_tmp) - cpuLogicalNum = len(set(d_tmp)) - - used_all = psutil.cpu_percent(percpu=True) - cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum) - return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum - - def getMemInfo(self): - # 取内存信息 - mem = psutil.virtual_memory() - if mw.getOs() == 'darwin': - memInfo = { - 'memTotal': mem.total, - } - memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) - else: - memInfo = { - 'memTotal': mem.total, - 'memFree': mem.free, - 'memBuffers': mem.buffers, - 'memCached': mem.cached - } - - memInfo['memRealUsed'] = memInfo['memTotal'] - \ - memInfo['memFree'] - memInfo['memBuffers'] - \ - memInfo['memCached'] - return memInfo - - def getMemUsed(self): - # 取内存使用率 - try: - import psutil - mem = psutil.virtual_memory() - - if mw.getOs() == 'darwin': - return mem.percent - - memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024, - 'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024} - tmp = memInfo['memTotal'] - memInfo['memFree'] - \ - memInfo['memBuffers'] - memInfo['memCached'] - tmp1 = memInfo['memTotal'] / 100 - return (tmp / tmp1) - except Exception as ex: - return 1 - - def getDiskInfo(self): - info = self.getDiskInfo2() - if len(info) != 0: - return info - - # 取磁盘分区信息 - diskIo = psutil.disk_partitions() - diskInfo = [] - - for disk in diskIo: - if disk[1] == '/mnt/cdrom': - continue - if disk[1] == '/boot': - continue - tmp = {} - tmp['path'] = disk[1] - size_tmp = psutil.disk_usage(disk[1]) - tmp['size'] = [mw.toSize(size_tmp[0]), mw.toSize( - size_tmp[1]), mw.toSize(size_tmp[2]), str(size_tmp[3]) + '%'] - diskInfo.append(tmp) - return diskInfo - - def getDiskInfo2(self): - # 取磁盘分区信息 - temp = mw.execShell( - "df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - tempInodes = mw.execShell( - "df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - temp1 = temp.split('\n') - tempInodes1 = tempInodes.split('\n') - diskInfo = [] - n = 0 - cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', - '/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user'] - for tmp in temp1: - n += 1 - inodes = tempInodes1[n - 1].split() - disk = tmp.split() - if len(disk) < 5: - continue - if disk[1].find('M') != -1: - continue - if disk[1].find('K') != -1: - continue - if len(disk[5].split('/')) > 4: - continue - if disk[5] in cuts: - continue - arr = {} - arr['path'] = disk[5] - tmp1 = [disk[1], disk[2], disk[3], disk[4]] - arr['size'] = tmp1 - arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]] - diskInfo.append(arr) - return diskInfo - - # 清理系统垃圾 - def clearSystem(self, get): - count = total = 0 - tmp_total, tmp_count = self.ClearMail() - count += tmp_count - total += tmp_total - tmp_total, tmp_count = self.ClearOther() - count += tmp_count - total += tmp_total - return count, total - - # 清理邮件日志 - def clearMail(self): - rpath = '/var/spool' - total = count = 0 - import shutil - con = ['cron', 'anacron', 'mail'] - for d in os.listdir(rpath): - if d in con: - continue - dpath = rpath + '/' + d - time.sleep(0.2) - num = size = 0 - for n in os.listdir(dpath): - filename = dpath + '/' + n - fsize = os.path.getsize(filename) - size += fsize - if os.path.isdir(filename): - shutil.rmtree(filename) - else: - os.remove(filename) - # print('mail clear ok') - num += 1 - total += size - count += num - return total, count - - # 清理其它 - def clearOther(self): - clearPath = [ - {'path': '/www/server/mdserver-web', 'find': 'testDisk_'}, - {'path': '/www/wwwlogs', 'find': 'log'}, - {'path': '/tmp', 'find': 'panelBoot.pl'}, - {'path': '/www/server/mdserver-web/install', 'find': '.rpm'} - ] - - total = count = 0 - for c in clearPath: - for d in os.listdir(c['path']): - if d.find(c['find']) == -1: - continue - filename = c['path'] + '/' + d - fsize = os.path.getsize(filename) - total += fsize - if os.path.isdir(filename): - shutil.rmtree(filename) - else: - os.remove(filename) - count += 1 - mw.restartWeb() - os.system('echo > /tmp/panelBoot.pl') - return total, count - - def psutilNetIoCounters(self): - ''' - 统计网卡流量 - ''' - stat_pl = 'data/only_netio_counters.pl' - if os.path.exists(stat_pl): - local_lo = (0, 0, 0, 0) - ioName = psutil.net_io_counters(pernic=True).keys() - for x in ioName: - - if x.find("lo") > -1: - local_lo = psutil.net_io_counters( - pernic=True).get(x)[:4] - - all_io = psutil.net_io_counters()[:4] - result_io = tuple([all_io[i] - local_lo[i] - for i in range(0, len(all_io))]) - - # print(local_lo) - # print(all_io) - # print(result_io) - return result_io - return psutil.net_io_counters()[:4] - - def getNetWork(self): - # 取网络流量信息 - try: - networkInfo = {} - # 取网络流量信息 - # networkIo = self.psutilNetIoCounters() - # if not "otime" in session: - # session['up'] = networkIo[0] - # session['down'] = networkIo[1] - # session['otime'] = time.time() - - # ntime = time.time() - # print("source",ntime - session['otime']) - - # networkInfo['upTotal'] = networkIo[0] - # networkInfo['downTotal'] = networkIo[1] - # networkInfo['up'] = round(float( - # networkIo[0] - session['up']) / (ntime - session['otime']), 2) - # networkInfo['down'] = round( - # float(networkIo[1] - session['down']) / (ntime - session['otime']), 2) - # networkInfo['downPackets'] = networkIo[3] - # networkInfo['upPackets'] = networkIo[2] - - # # print networkIo[1], session['down'], ntime, session['otime'] - # session['up'] = networkIo[0] - # session['down'] = networkIo[1] - # session['otime'] = time.time() - - networkInfo['cpu'] = self.getCpuInfo() - networkInfo['load'] = self.getLoadAverage() - networkInfo['mem'] = self.getMemInfo() - networkInfo['iostat'] = self.getDiskIostat() - networkInfo['network'] = self.getNetWorkIostat() - - return networkInfo - except Exception as e: - print(mw.getTracebackInfo()) - return None - - def getNetWorkIostat(self): - netInfo = {} - - netInfo['ALL'] = {} - netInfo['ALL']['up'] = 0 - netInfo['ALL']['down'] = 0 - netInfo['ALL']['upTotal'] = 0 - netInfo['ALL']['downTotal'] = 0 - netInfo['ALL']['upPackets'] = 0 - netInfo['ALL']['downPackets'] = 0 - - mtime = time.time() - iokey = 'netstat' - netio = None - if iokey in self.cache: - netio = self.cache[iokey] - - if not netio: - netio = {} - netio['info'] = None - netio['all_io'] = None - netio['time'] = mtime - - stime = mtime - netio['time'] - if not stime: stime = 1 - - # print("new:",stime) - netio_group = psutil.net_io_counters(pernic=True).keys() - - netio_cache = netio['info'] - allio_cache = netio['all_io'] - if not netio_cache: - netio_cache = {} - - netio_group_t = {} - for name in netio_group: - netInfo[name] = {} - - io_data = psutil.net_io_counters(pernic=True).get(name) - if not name in netio_cache: - netio_cache[name] = io_data - - netio_group_t[name] = io_data - - netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2) - netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2) - - netInfo[name]['upTotal'] = io_data[0] - netInfo[name]['downTotal'] = io_data[1] - netInfo[name]['upPackets'] = io_data[2] - netInfo[name]['downPackets'] = io_data[3] - - all_io = psutil.net_io_counters()[:4] - if not allio_cache: - allio_cache = all_io - - netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2) - netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2) - netInfo['ALL']['upTotal'] = all_io[0] - netInfo['ALL']['downTotal'] = all_io[1] - netInfo['ALL']['upPackets'] = all_io[2] - netInfo['ALL']['downPackets'] = all_io[3] - - self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime} - return netInfo - - - def getDiskIostat(self): - iokey = 'iostat' - - diskInfo = {} - diskInfo['ALL'] = {} - diskInfo['ALL']['read_count'] = 0 - diskInfo['ALL']['write_count'] = 0 - diskInfo['ALL']['read_bytes'] = 0 - diskInfo['ALL']['write_bytes'] = 0 - diskInfo['ALL']['read_time'] = 0 - diskInfo['ALL']['write_time'] = 0 - diskInfo['ALL']['read_merged_count'] = 0 - diskInfo['ALL']['write_merged_count'] = 0 - - try: - diskio = None - if iokey in self.cache: - diskio = self.cache[iokey] - - mtime = int(time.time()) - if not diskio: - diskio = {} - diskio['info'] = None - diskio['time'] = mtime - - diskio_cache = diskio['info'] - stime = mtime - diskio['time'] - if not stime: stime = 1 - - diskio_group = psutil.disk_io_counters(perdisk=True) - if not diskio_cache: - diskio_cache = diskio_group - - for disk_name in diskio_group.keys(): - diskInfo[disk_name] = {} - # print('disk_name',disk_name) - # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time) - # print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count) - - diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime) - diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime) - diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime) - diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime) - diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime) - diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime) - - if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime) - if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime) - - diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count'] - diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count'] - diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes'] - diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes'] - if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']: - diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time'] - if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']: - diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time'] - - if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count'] - if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count'] - - self.cache[iokey] = {'info':diskio_group,'time':mtime} - except Exception as e: - pass - - return diskInfo - - def getNetWorkIoData(self, start, end): - # 取指定时间段的网络Io - data = mw.M('network').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field( - 'id,up,down,total_up,total_down,down_packets,up_packets,addtime').order('id asc').select() - return self.toAddtime(data) - - def getDiskIoData(self, start, end): - # 取指定时间段的磁盘Io - data = mw.M('diskio').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field( - 'id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime').order('id asc').select() - return self.toAddtime(data) - - def getCpuIoData(self, start, end): - # 取指定时间段的CpuIo - data = mw.M('cpuio').dbfile('system').where("addtime>=? AND addtime<=?", - (start, end)).field('id,pro,mem,addtime').order('id asc').select() - return self.toAddtime(data, True) - - def getLoadAverageData(self, start, end): - data = mw.M('load_average').dbfile('system').where("addtime>=? AND addtime<=?", ( - start, end)).field('id,pro,one,five,fifteen,addtime').order('id asc').select() - return self.toAddtime(data) - - # 格式化addtime列 - def toAddtime(self, data, tomem=False): - import time - if tomem: - import psutil - mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100 - length = len(data) - he = 1 - if length > 100: - he = 1 - if length > 1000: - he = 3 - if length > 10000: - he = 15 - if he == 1: - for i in range(length): - data[i]['addtime'] = time.strftime( - '%m/%d %H:%M', time.localtime(float(data[i]['addtime']))) - if tomem and data[i]['mem'] > 100: - data[i]['mem'] = data[i]['mem'] / mPre - - return data - else: - count = 0 - tmp = [] - for value in data: - if count < he: - count += 1 - continue - value['addtime'] = time.strftime( - '%m/%d %H:%M', time.localtime(float(value['addtime']))) - if tomem and value['mem'] > 100: - value['mem'] = value['mem'] / mPre - tmp.append(value) - count = 0 - return tmp - - def setControl(self, stype, day): - - filename = 'data/control.conf' - stat_pl = 'data/only_netio_counters.pl' - - if stype == '0': - mw.execShell("rm -rf " + filename) - elif stype == '1': - _day = int(day) - if _day < 1: - return mw.returnJson(False, "设置失败!") - mw.writeFile(filename, day) - elif stype == '2': - mw.execShell("rm -rf " + stat_pl) - elif stype == '3': - mw.execShell("echo 'True' > " + stat_pl) - elif stype == 'del': - if not mw.isRestart(): - return mw.returnJson(False, '请等待所有安装任务完成再执行') - os.remove("data/system.db") - - sql = db.Sql().dbfile('system') - csql = mw.readFile('data/sql/system.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - return mw.returnJson(True, "监控服务已关闭") - else: - data = {} - if os.path.exists(filename): - try: - data['day'] = int(mw.readFile(filename)) - except: - data['day'] = 30 - data['status'] = True - else: - data['day'] = 30 - data['status'] = False - - if os.path.exists(stat_pl): - data['stat_all_status'] = True - else: - data['stat_all_status'] = False - - return mw.getJson(data) - - return mw.returnJson(True, "设置成功!") - - def versionDiff(self, old, new): - ''' - test 测试 - new 有新版本 - none 没有新版本 - ''' - new_list = new.split('.') - if len(new_list) > 3: - return 'test' - - old_list = old.split('.') - ret = 'none' - - isHasNew = True - if int(new_list[0]) == int(old_list[0]) and int(new_list[1]) == int(old_list[1]) and int(new_list[2]) == int(old_list[2]): - isHasNew = False - - if isHasNew: - return 'new' - return ret - - def getServerInfo(self): - import urllib.request - import ssl - upAddr = 'https://api.github.com/repos/midoks/mdserver-web/releases/latest' - try: - context = ssl._create_unverified_context() - req = urllib.request.urlopen(upAddr, context=context, timeout=3) - result = req.read().decode('utf-8') - version = json.loads(result) - return version - except Exception as e: - print('getServerInfo', e) - return {} - - def updateServer(self, stype, version=''): - return mw.returnJson(False, '与后续版本差异太大,不再提供更新') - # 更新服务 - try: - if not mw.isRestart(): - return mw.returnJson(False, '请等待所有安装任务完成再执行!') - - version_new_info = self.getServerInfo() - version_now = config_api.config_api().getVersion() - - new_ver = version_new_info['name'] - - if stype == 'check': - if not 'name' in version_new_info: - return mw.returnJson(False, '服务器数据或网络有问题!') - - diff = self.versionDiff(version_now, new_ver) - if diff == 'new': - return mw.returnJson(True, '有新版本!', new_ver) - elif diff == 'test': - return mw.returnJson(True, '有测试版本!', new_ver) - else: - return mw.returnJson(False, '已经是最新,无需更新!') - - if stype == 'info': - if not 'name' in version_new_info: - return mw.returnJson(False, '服务器数据有问题!') - diff = self.versionDiff(version_now, new_ver) - data = {} - data['version'] = new_ver - data['content'] = version_new_info[ - 'body'].replace("\n", "
                    ") - 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 -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 任务页操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - - -from flask import request - - -class task_api: - - def __init__(self): - pass - - def countApi(self): - c = mw.M('tasks').where("status!=?", ('1',)).count() - return str(c) - - def listApi(self): - - p = request.form.get('p', '1') - limit = request.form.get('limit', '10').strip() - search = request.form.get('search', '').strip() - - start = (int(p) - 1) * int(limit) - limit_str = str(start) + ',' + str(limit) - - _list = mw.M('tasks').where('', ()).field( - 'id,name,type,status,addtime,start,end').limit(limit_str).order('id desc').select() - _ret = {} - _ret['data'] = _list - - count = mw.M('tasks').where('', ()).count() - _page = {} - _page['count'] = count - _page['tojs'] = 'remind' - _page['p'] = p - - # return data - _ret['count'] = count - _ret['page'] = mw.getPage(_page) - return mw.getJson(_ret) - - def getExecLogApi(self): - file = os.getcwd() + "/tmp/panelExec.log" - v = mw.getLastLine(file, 100) - return v - - def getTaskSpeedApi(self): - tempFile = mw.getRunDir() + '/tmp/panelExec.log' - freshFile = mw.getRunDir() + '/tmp/panelFresh' - - find = mw.M('tasks').where('status=? OR status=?', - ('-1', '0')).field('id,type,name,execstr').find() - if not len(find): - return mw.returnJson(False, '当前没有任务队列在执行-2!') - - mw.triggerTask() - - data = {} - data['name'] = find['name'] - data['execstr'] = find['execstr'] - if find['type'] == 'download': - readLine = "" - for i in range(3): - try: - readLine = mw.readFile(tempFile) - if len(readLine) > 10: - data['msg'] = json.loads(readLine) - data['isDownload'] = True - break - except Exception as e: - if i == 2: - mw.M('tasks').where("id=?", (find['id'],)).save( - 'status', ('0',)) - return mw.returnJson(False, '当前没有任务队列在执行-4:' + str(e)) - time.sleep(0.5) - else: - data['msg'] = mw.getLastLine(tempFile, 10) - data['isDownload'] = False - - data['task'] = mw.M('tasks').where("status!=?", ('1',)).field( - 'id,status,name,type').order("id asc").select() - return mw.getJson(data) diff --git a/class/core/vilidate.py b/class/core/vilidate.py deleted file mode 100755 index 14cf6abfb..000000000 --- a/class/core/vilidate.py +++ /dev/null @@ -1,143 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 验证码操作 -# --------------------------------------------------------------------------------- - - -import random -import math -from PIL import Image, ImageDraw, ImageFont, ImageFilter - - -class vieCode: - __fontSize = 20 # 字体大小 - __width = 120 # 画布宽度 - __heigth = 45 # 画布高度 - __length = 4 # 验证码长度 - __draw = None # 画布 - __img = None # 图片资源 - __code = None # 验证码字符 - __str = None # 自定义验证码字符集 - __inCurve = True # 是否画干扰线 - __inNoise = True # 是否画干扰点 - __type = 2 # 验证码类型 1、纯字母 2、数字字母混合 - __fontPatn = 'class/fonts/2.ttf' # 字体 - - def GetCodeImage(self, size=80, length=4): - '''获取验证码图片 - @param int size 验证码大小 - @param int length 验证码长度 - ''' - # 准备基础数据 - self.__length = length - self.__fontSize = size - self.__width = self.__fontSize * self.__length - self.__heigth = int(self.__fontSize * 1.5) - - # 生成验证码图片 - self.__createCode() - self.__createImage() - self.__createNoise() - self.__printString() - self.__cerateFilter() - - return self.__img, self.__code - - def __cerateFilter(self): - '''模糊处理''' - self.__img = self.__img.filter(ImageFilter.BLUR) - filter = ImageFilter.ModeFilter(8) - self.__img = self.__img.filter(filter) - - def __createCode(self): - '''创建验证码字符''' - # 是否自定义字符集合 - if not self.__str: - # 源文本 - number = "3456789" - srcLetter = "qwertyuipasdfghjkzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM" - srcUpper = srcLetter.upper() - if self.__type == 1: - self.__str = number - else: - self.__str = srcLetter + srcUpper + number - - # 构造验证码 - self.__code = random.sample(self.__str, self.__length) - - def __createImage(self): - '''创建画布''' - bgColor = (random.randint(200, 255), random.randint( - 200, 255), random.randint(200, 255)) - self.__img = Image.new('RGB', (self.__width, self.__heigth), bgColor) - self.__draw = ImageDraw.Draw(self.__img) - - def __createNoise(self): - '''画干扰点''' - if not self.__inNoise: - return - font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5)) - for i in range(5): - # 杂点颜色 - noiseColor = (random.randint(150, 200), random.randint( - 150, 200), random.randint(150, 200)) - putStr = random.sample(self.__str, 2) - for j in range(2): - # 绘杂点 - size = (random.randint(-10, self.__width), - random.randint(-10, self.__heigth)) - self.__draw.text(size, putStr[j], font=font, fill=noiseColor) - pass - - def __createCurve(self): - '''画干扰线''' - if not self.__inCurve: - return - x = y = 0 - - # 计算曲线系数 - a = random.uniform(1, self.__heigth / 2) - b = random.uniform(-self.__width / 4, self.__heigth / 4) - f = random.uniform(-self.__heigth / 4, self.__heigth / 4) - t = random.uniform(self.__heigth, self.__width * 2) - xend = random.randint(self.__width / 2, self.__width * 2) - w = (2 * math.pi) / t - - # 画曲线 - color = (random.randint(30, 150), random.randint( - 30, 150), random.randint(30, 150)) - for x in range(xend): - if w != 0: - for k in range(int(self.__heigth / 10)): - y = a * math.sin(w * x + f) + b + self.__heigth / 2 - i = int(self.__fontSize / 5) - while i > 0: - px = x + i - py = y + i + k - self.__draw.point((px, py), color) - i -= i - - def __printString(self): - '''打印验证码字符串''' - font = ImageFont.truetype(self.__fontPatn, self.__fontSize) - x = 0 - # 打印字符到画板 - for i in range(self.__length): - # 设置字体随机颜色 - color = (random.randint(30, 150), random.randint( - 30, 150), random.randint(30, 150)) - # 计算座标 - x = random.uniform(self.__fontSize * i * 0.95, - self.__fontSize * i * 1.1) - y = self.__fontSize * random.uniform(0.3, 0.5) - # 打印字符 - self.__draw.text((x, y), self.__code[i], font=font, fill=color) diff --git a/class/core/vip_api.py b/class/core/vip_api.py deleted file mode 100644 index df4a3061b..000000000 --- a/class/core/vip_api.py +++ /dev/null @@ -1,48 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# VIP用户管理 -# --------------------------------------------------------------------------------- - -import mw - -from flask import request -import requests - - -class vip_api: - - api_url = 'https://wo.midoks.icu/api/wp-json/vip' - - def __init__(self): - pass - - def loginApi(self): - username = request.form.get('username', '') - password = request.form.get('password', '') - password = mw.aesEncrypt(password) - - headers = { - "Content-Type": "application/x-www-form-urlencoded", - } - - print("name:", str(username)) - print("pwd:", str(password)) - args = { - 'name': username, - 'pass': password - } - data = requests.post(self.api_url + '/v1/login', - data=args, headers=headers) - - print(data.text) - - return mw.returnJson(False, "测试中!") diff --git a/class/fonts/2.ttf b/class/fonts/2.ttf deleted file mode 100755 index 3a452b68f3baa612539ad43cd427725e96f44100..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28328 zcmdUY2YegHmG{hIv5VdtNss^tq7Wbnk{}6EB*CQ!l9EU@s&`Sv>Yb7$OD>Wu%d#v> z?!79mk>VyhF0rH7j_nkuJI%SX&xsw|Nt}9#&$fW?KZ`<8l5^kPcfb4nz6BT9T`YIr zym{}<|GjxLAe0bdB=tl=PEVZ?Yus;_+6dWj2d>VZ)-yP>@#$CkasD@)w=CZo&$53xPRIp6Aw+n6^~&KDKfmiID+$?H!=Aqy7sM09j`I~buUfrfWapVFe>yGC)md0 z`0eZ=QIZO3&K(OxN1JoUA{n8(3F9!^*weGel!)XjxnmB;FXxUGIQ~QKn8)$YbH_?z zq+IS;AOel$jzuz#cBADeug0?({n_Iz{n_Iz{n_Iz{n_Iz{n_Iz{n_Iz{n=xg{)Zdd z+FHHyhc~YD&fIk2#%(J%uJB$ok~@-CZyMP!yk>pf@=Y7O7hZT_-K>pUal^{3>;ie1 zG>|sDT8Wp;C&OeTS&5^WWD~g%$J=nWk*vU#i$MF>YZ6(FD71@sG4U?_s zz4N>m8|#`Iz4cA8#(1o;zP^DpqOfMXd!aT>B!;87{9cdqhK6{d{9Nh(e=!h(BvDec zqu2YGBGBs}UhVR)UYe@SXz8omN<|mxCC`#Ra**6XcA?_W(Ki;1`{EaL#DwQrbvmt9 zqfx6>DusgMD1{;%d4^^V^sY{Q(chCwzK=wo6a~CL&hxnKaY4X4=p!6~4%4Ry(0Vo5 zD%DesM$)2SmB3S8!7EWkp5qiMQKeR?1yNK9f~XQjK`nqVDrAZ2$`V5l)~Q}meXe?i zeH!pd>#&i!x*TBw(G(M=VT2|8)WV4VF-on8P~0hYQTEPF(ch@QV~hMw-*|T3yl3b$cw_venT%o* zH$uvYhlZs4sMjm`Y@YJc5{KPpHtDpWT)|b8S1`U(&ZG9YaVw~IxoCNL1z7I(xRq+9 zV9*=1TAfC%0nd#_lOPyPMw3}>ww(Kg#cZ*vtv0*eVYAuGR3fiX5>f52Tg(QfUes2g zaE($eQjgo^)G4*1(PlL%jDnJJE!ToEX9xaFXU6Vh8Wc^NedQXs_?#WfOqH9TofTNh z*vhuNbPB{<%vvvFZ9w!V{A^Feap`Ev3!^gLWhM-GA<}vO@?q(7qH85#UN)u^Qlhj;V zAFBy_%60X%k#MEA)a5klH7ccoBQdI`Tus=n*Et+cj&pdN4yRXLbU`IjrvvJ)EY?7o zMTS=jFEGcDmH)yu)#vQna~YE5OI|sYq^X!MO^h#2@FdvW)I|lm&B=}uEs2&c8W$l; zM%of$pOAXNitjx%7!vXIN5Qp$UB-t6eb**?VA{l)wzc-nng=!7w*8Lq?5T|jT}Ww7 zwAwU^$&;+hsu~O~qrObyY1r_5$Ds1=YkTH8J|s|!5Elhs ztzaxM84Y?$4S5E_Y(VB>c$v-=WUYu%d6_Yg)?(hYGT!k1O2&_PBV&QmPvbbi55C~oTS{esoP6uJ-WR#dgiHXxHqppb3e|*2WRdmjsAzi;0@T#-atwE z$?TzCYmF6)>F=PHbjF}UboAjYg4L1qODy7u%xOSGh#9mVrOzv!8Arb z%vmHANfxsp$ylHah6I0QC@U8^>4-NvvqIr)NjM=IqQj|>8G&=WLO~`kGF!MvqH^0s zN5V!++j9nkf8OA{WmPsq=vJ@Adn{t|d#V~=u-i)=VZkeCOu8S;bE%}p@yfDbTX%`u z>))F4RaCh9LcWNzyrg`JG|>_)Z?FVP0wwewmDZvQT6nYGq7gr-tT$>y9&|F9G<}1- z!MzPB8j>ogAV}!pV8bb=h85?PFo>|(P(;e*SpwlG7K^+%a6~BPg}``WjZxU;e&e{C z+Pbn98701#8R4wlb?CQ~l7$+L5@b@TP^$DgL0Motgpe|}=bx@3#^R?N+0!kIYfit+ z<3L>aM@-Txgv*`D)l|9v7P@HRW&1)aCQiO&_xDd9V>NBhjL>x`*GxJkGqqSGD-=Ns zy+D_#$O}J;WeuD!RzWPq#adXg2|@JB&6a3sZVJWGu#YXg;lQefRGZs5$nA3vZP>D; zW#a6b+MDn5H&34OEmrn#>CN;Q_abqT>5`k4lt`s|n^muHT1>EW98rl54X5O&GfPHS zS1d<|ObdI<37nIZ6w^~8GdUPaI3P%IL4+{L!X<_TG1M}*+dDt*GdM4@R+;O^>&7>i z84o(DrAghKL#JJAF;~|Hnw19ga*bGB)-vdgB}1U}@8k$KoohkAzE>KT>(|vPzzTHV zxCqEWRg@Ey0we>SAjgRswV+UnRLd)XlmJ6ujQLS^=6kQK$gsW&#<-|2%5$10=XUNi z*ZZ(uhYn4;XVTYF&$IU)>bdT^1NZ~*;?Sxd0B{|dA(d0TUNQiN;7@^0TB`!l41B0Z zTNsS}0wAOy`eNSd8E>JiA=cLD14ZGb)=Sm3MxUj-=R}RGAz9w15p=Et^O&dPqa)ld z`d8SRHpxhNUIO|7NE(X_AzAs_!nZ3fi)ObpJ>-?-;Ngl1$286x1*>Wg@OX40eaFbhC@LBL!)ha zKJ};PsZjzQz_LU1ldCDWouM!RZzJ3W!~%SzS8~$IO3AM)RSFiR&Mk5_QKKn<*I3NN zx*ygH<#{HM%3_{XGM)wUrq+_}U=&bE*2UyI8FVvSmTubO({$bO>8%~J1Drn^s%&Vl ziiSK5O_J1SE9<+MHt(3U;}Ke~Q+Z?6%SOT#vA~iQ;D1>r!>PDsL_=l+{cE+7POYH$ zBe+6U8V!MP;Z0SLEe5#1Sh%#s#A{{YvdS6@&86XQ&EJ!nFl}<;Ncy{!9<3=qXbx7j zpSe@E(4gbbput5347&O z7Gk}0uAc$}jSvzp-q&;AC7xtUt;gwpVA3-yw}kB$|D4th^PK$WDLSpSd2X+>F6=3> z#thr$G?rF+s`{kJxOyXZ5R?-!bA~(2tp(0oh(4F$yl{D$ttMPiVpEH}Nm*wR?V?Du zA_6g7-e`2mEgxfT&kP^iSf86Sfc*IYncUmTcr4zSuTR2}tC+06KqR@E<-plft7^*3 zk7`;1DT^jvGJf_(k1{I7Z)vPn>2#mxp~drZP*o#&0u*412kD zow{tAv)fngu(rC~r4@a3u9kBD+_n}7cjhy^eS=#L3DJ{VqzTkukc^^Mr&Tlj&uR5q zom!`dv!+xEdYwM&ZD>_m9gGx{D!5A`)kDr;!k9Vbs3?pw2I2*G0N%hkwzNp^Wrl%c z1%4Td<<1~04a}-9LF0Gi4OTX1Y&m8&c-0q7X-!w8Zav|NBzJ4DS5f-Dw zl=+auz8w zbMTl#%PKv_{*Gx~fm9V!TUD&$x%SPssujjkM6=56NuSw%`I=~%;Yn5Vta@X6Q}6!2 z(ZjvzPs5?A{+T7gDMNMcYERVQG)I<}+p41`I_b(*jg+Wvm<+3=gMJ<1))Nnjl6jJk z*3?L~daui2ij+CfKUXPvqo@vw#3rg?5%XP}ETwXF@*FP~(wgFG)X2)7g@kz>gQFm; zU9JWOYApcc=B6h`g1W?v-|m?cbm|`0geUfQwD(SN)qgWO7%?zBE^qIK`)>-n z!sQWTnZ9(ryQ{gnVaW)s8*)2q=X-1cw3`aD_8^dK7W^41(LTggly z%CxMdQIWQGnTCn+AYB)QA^#5C12HgdP~JvZ<%()P*pYFq)SW>n`oJ*)gM__ z?+leyOs@^qmUznj15tNi)?aq>;T1Do?esRA08fEZH2X zU)G+O==An(yq_+Z<8=gvD(tn{9-AJc&!S~TvIW{N$G{NeVO|w4@&zae;D>;eh})>3 z6!8r}U|v;naVQoPlkHRi1CBx3{CVPI`5@Gt{U`d+{%ypL?(Bo0MSa_@$ zn=;kTHHJvc#f@QRjlt+s6eGl&C!Cn@^tj7}8ne$LO*m0sLVJ%MO`ja^9ncDflA}xF z%$^dmjXTaw0~%tuP81~-Vu_p_HB;xKW}J}k&||S;v{j5C#bSCyMvplW5cO0X$E}%| zzL$~-G(2+Z+J#Hu{cxE-ldo{EazVKz2c(kGmdv*q`WgfmM?s!!vB!Et=WipQsDxSA zBympZ%sTc?AH4L^{{Vyqw!9y8oxn{ks;iPkb#neC?hpiwa{PDd8cNjCgi>5EkuFVV zCeSCxZ9BGP(Xmt8m?o+Lj+b&5LK7>=YAHhfeyOS)0STYSF8aK_N>!!D>-Bj&UX!xi z=M_B`v81HZLp_DcvyO`SpJV#fA{62w{$l3)8S`Nw*-Lf}lQiE=0ghx16bq(uTJPey zMy2~6rBGfUxZUU<7#_TF+=k&zd+N*1JPg^PTT2@1^vWi^u`T2o)VSQ$%Zw|VV$Ghw zr1X@dM_D_5nwiGeavMpC%pm)uRyuR0G|SR6z9!^$_4d?Mhr;S`b)>pF(xDU@m6pj$ zk0f#}qOM;IOchFXTy>ZR3h~7*sHPm@C}3??WdmHaTx2X8?8vKtKxP)#dM2y2Qt*4_ zSVxjpv5MyLBO)MUtWzQmMS)pjdryreoRYl z+&;L##85L}%m11N$eH+3){Iqqjv5CLGh-=4DO}MchjUcf5Q1(cRB$ zy`8CX9%IdvIpN?%Enl@-2WIYkZMi=@WVLnlte>-?s;VUxs9iCAWze1ep2248+8C)C ztaRNyIMmv(DCAkYk=|-+k0;BmmBY~i!XsXxqso> zaXHnL*kNK=H=uSnV~BaH>*`cRms~(eLEgYAH4$Z4Q(eQmBh^lYL!=FeJX101@e5`% zf#>DUS*9+;;4pr`ng)muj!;Jj4)YDftkh^oGwV|qg)?&vmv1@uc~CP2^kWSMxs+QC z%@snh60uFzo-Gs?fu@_AwA7!VHYW?$3Ebivu1UL>U%qF*@lm=xHE}St>%rS^x#0A7 zHh1?9(B>-+UAH!wdW(BSapui8>736VO{VB=w{ucT+HCsn6_*~mAT_vh(Jkw~w|~!p z6{&$U$ph2oq>@iinjYEDl|A-3o&S>*;=_4*@6A(vW*nTtC>} zToVeq3|g+ezr9n_nbd2w`u@It=BpCgu9_4Y7#Iu&2lR%*VFT|Lm5%Ys#00*2ieLnc z69)$@3Ujj4pwNoJfWN;FsHjfxc!*I!tH-tgW4s)QKf~gptR>`^jAB_h8Okzxob5ld zN;D?kUSvJ0$@F49nl4)pMAhY9J5`!7ZV z4yL*`aeQzmD;=Q7uxCD$;?` zyI9pO@CsTvreO+g zkp(yHVROQ` zGX_(a!zS+iDiti|pYonP6Q&HNR$h6@f$m95+9!2iG4N7z=duO2ZruIvk0evQ(#AXa zh4UYLXW78dE_&yVWa@y^>EllRm|p9wP+oQ5=$h2vgr03?e{}AQHLqU-&3InwP>kEP_>ng8`oY^GOmg@HYl|T-rXti(Flv$mAG1(#rAOkGd{s$VXQ8; zLhw1^BFLjqIq4sj_v{c0llQ{VVxb{&$jMEl;x>?SZBQiR7Q0Q5tmAvi`PWG6=e>)8VIBUctD|)z#K1R1J>C#<<A8!QrE6Q1Gcs+K7t*%zAiB<~mlh7KjF@m3q0%T(WIW5Sy zNMWE?woKVRf_3O)Y>})exz=%=XA}n-!dk~C`irz9YkQy<>~hus1w+gU_Z4go^k`w>EUBv9r&BKl19hR{MD_=)U2ud(!Yt(Ro&@Z(jQ%YRhn6u zJzA}Ud-=V6H{5v7NNTXBYXdF+^FvUCeg4XQ52uoAjvYL(cptsz&^M_GZJ{X0zmtTK zhH(!@5gQw&xH@Vvn`(mrm(`+FghOGMsmfnvR+%kPYfVk9#Zu$p)z!Q`hA@L)iSTu8 z4N?WdA#?z4L8pUHL+$5d^m$m!#J-57vfars)J__U#l4Y^~MY_oVxlQV&gM-p!b81|hY>fNsYoigbo>WIv zk!W3QZCx{O<^sx!aUDv{c)oI?z}MD@(MVEMH8SI@61Zaf+yU^ChcB|p=!n6DjK!7( zGr&rj{Srv%kA- z-T2wvdk0=<9Y1-#>;7)K;IbKOe-EDabgkb{S1+Hp@E3~*{`I1F?@Xo+IUMEOgKyGn zNqR+b7v zC7J_0lHXDjt+A@Cwun6%t+Cmn9$p*fopFJLf?{2*TQC@yNEgPG#xzK=y~ql4EY)3ANJf^AGNxr?Dq@}pMTC%c!9#A<3w((%NUo!W)75}z(*Z$$u;Hb!d z{ygl(BY#gHe0|QOH|c#$jGV=2RKY-%hm8UT9YzCEJuvj) zP#FUTL!gXTd3kGCATB|xWxTYGnumf>kb6Mb*8;Z+AS5T$*7JmMU=T)OcVHBA=j|#w zI!6x3jl4u1cQo9-bk$($rSykiN!+w>*#N!$latBRqS=SfY@ao4-){_mdXVws!{J-* zdgP+i;O{ovbld$qfy(~v`>EuHTdz6v(3)!x9-yzhQY?K+5`g9GB-cnC@@TlZCFTND zXbXDuI?|?TWl`yvzFw!RkMd?->2Boxo(i$FT@b=d;dNS0P*8uKA324Won{7)Wk666 zrL$R+V~qbiqvz;);1?>+bxZ&dkXaDVIVXIcQ3yveR<$Hp*iWE-tA)FAIk15C$=+kL z(E+Wxdf&lGOOID=hF}fQBa}RuOwE{l+0L%CVg8IOeN|7VA4{e#vf5OFi+iP4+TuKP z!wswp8tB{USykJ=_oDsFz{o$ml}fHUdimuaU(7YGzwzpW#tC%w&x*NO0&Z3VhkaXG zND~RERSTu9sSXGHIc=_w)rBEuvOZUbtE;L4e&k08L3KTkp@3LS8H?3hpyy1TYwXZ( zo2?e=9O0c!0y&Q+7o?BLUABK`9a3Ja85YZHb&(Di17ul^NA);StjCEWJysS$fLyOd z3w@Sog(`=#nv9l{6?x-Lhp$=pkMt)8r_DJ>lPCA@ymnLig6RvI(g&f-JE-N-JqL!F z?9Q0FU%KtsHMeX!N0*mhdCA^c>FZ9Qg{kvL4{Yz9gkEeE1=uk*QA4i8cy(>9 zRHq7q5cKP%E?J-b4pqQu;gQ-{2AEdvzW{U(@Izj0L-A8|UOEPuI zj+1A8{_RJP|K%aZrr#^>ec@Zzqz3=+gZ+E1SdRXtgYJ7UmE8Nx$!n)P^6zy1Yh!x% zGGx3U8;EM8y)sf&Lkw#p)euBKJV&z$-AqG$ED{3#aXKt!Q3yps)tc%E64ophTLWJo zt7l1X?qJXztBcjE>Km#fk&xLO3E2Q&5cA+ zSTBm^IZLh;ek-J&1wD{wl<%J|y#KbF7XIU&2eO#Z>-61!JUBFOO#3;B7v6FMc|%%~#kY}ajA;429v5&f8ZYYi0qTkZEJ{QhiB?tl z{Qjya9Eu1-3m(zybqRvkmq)=NYdBnOv4qQcbwEbJrRSpF^BR_Xi~N(eD0F(ccF3v6 z`N41&AQZg5v)K=uqL7(y%`<-|nBV&F$nTgzdHBxB4`2PY0s7rvK|5Z1KK*3xFRG& zSNgt>4$hcgY{wZcXd^vjy;Mt6DXCZ25ewO^U2RcsS&1gV>*7k)IKI4FAk89QDHx6L z@c;|T^U}>?b^yLPFFD7GENgNMDJQ97dy&f7iy|Fxv3}`t z?&Fcv4|lWJkD}{mN?*;w_JMz#NDcN%n?zr9Vb8p`ZUxKUcs`Z9?nlavw|*%c!7z{EvY&Jo zbGegonHC~!LE5N<8349Ew{IKu$PmoWVpJJeeAd}L+*+G#HKg0%nO$79EU-!H9ZX&J z!~=KqpStSn180uhe*cbS>W6*Drhh&C@eRlR#9T7^^L-Xqy$6A?Rqu)RY4yqdyDwj! z8r;14{%fe^A17hHciy&aZ7O;4)2)?O`ps*;OWknKMx#3(r2IFN6q!OUmYV6*snRrE ze|uYNQ+-_xq>`J|(o`R<&PisMQq{|s4+^9^Cz-`^c|Ng(bdFKlf`CpfX5G~Pih#1b zMiBrm#H18aHfMq%GdXZp?1hayO_kM=mC}&^WytCkdoHi3S=BwO{-y!?7<0Pr8Gn(t z?$~tXs0|!_BGhUG(?GNC2-SFoASx(z#V>@@LO2w(BS5+=`R8@8Nx6YhBhtA&h&oPsi|lM2p5dR|Af)w>>%OItX^9U9Q+uC!_|Ab-ES4raV6RTwbmsDxnLUo`4*MAwG`ckM$r5>*UDv-c+L2Us0yxNN>L!%ABRtgn3g; zsTojqcJtl}v7`ne;yD5z5Uf@fEc52AaV`cSrxImj_NJ_3k{{k0W1w-8<2EJ|6?1z& z<92b-nDMg6PP0^SP98BSI3DHsSesfV_f4@~Gd^Ov6}Xso5Nx_Qbr&(u9D zE`9s9Wa>(T!ODH*2XrLZc-0k0*QO9u-flinolsN#^U#Z5eVav-ckC8QOyz2&+E7|* zDbf47W_7o63m*NPYi;IDj>-~qiJ`K4b#I&IM6t(Q0wmc$Zb0kEakgp}XNyKkS)8pD zbOl+Et=bxiL@kyGz+Z@WBFGkv1OrNCF3grkg2mwoIhpAT5J$mnk&RTYWt``atzk|= zQIniIgvDSU05`HYv$3^rQ2Qt2BS+^f8cc1z?btE@so@>H69;JD<@=9ZkW9^-y7#^_ zXMVo(&GB^J9}!7bbp3YU^*7!Pr*Q7jp}x}xFS}|LEO+fC14F6g>SI?NcDB)K15bmwL1P@HD|b0Wp`9Pc;8~Rjq#UT^wbguSmq0u$TvJzz zR9c28O7k-8%zD-`qWA)*den~RxKv8U7t8PXtO?J8t|C4GVw?>y(C0x;cYs+vdb^p*D&tg&L;-wQy&6fd@tHDZv2@K4)#nu6^kvj z`uu9mP_(RO>w$yUu4WuuJm=V|-|X8BXL>LtZ4`G^ANa!q5XtX8mr9=a$IP9becYBS zPVc>D{(>jkswzVzbKc!IbyhJ)nSb3#wjl#09+#T5Q2^mUHcnO(DYau{AE7U0G^(@( zd1pOyJI;xH<;0BjgjuyO?)(bk6)$F7{0kTd0n6(9Xz*5c=M;HPZN2l_>jqDs*fSAh zLaPt&zq0HJ!*2)ZB|Eq6S&#&(+;ArRpyg!`b?@JIHRMgvHGk&ep{Fno1eG1=+g1K9 zwSNQ5`E*U^cxdb;J9huaEpO3ydK+!Icwlddw4D6jC7$KcThFLp{rvq zUPZYZ43g(HH8LL$^Ns8_mdMRKIc3OSmF9D{;g&ifR>5Qg}&cA148tq^bAM&%(=7GuB574qNp)iaL_rglGa z_bto5I=Fj)`*mfY=6)7<)e-{q`L_ZFCiNF4=b+X*f$*#Cd{exMIo}}>P8))5^>!PYMBUKnwpgvUXw-_ z?El=y9`&n7o1}>C#o9316PA#UVuk4VVugT>C{7xH#TBi778wEvRKzh&DrZCPyZMGo zfAZO`zQMuNw!3e*e(f(m+&y9P0G+UZKU0PAoon~5d@GTj(%Zf9+PQDW=3a5|+Cr%J z^p*RsM!bE|tfNDZA3AW&8W`ayQ;6h(J==CIPrvpx`tD*3mTsi}lfC<=BH%lQ>tW0g zehg!zEFVz|C8^D^yfIcMGd#rNaY&v)pc4_fh6bhEf)OMZkIQQc7+%enwET}SyqMiN z<5NP;&2`L0D~bWfd3!hIIgH+T20?Axi`q?ZL zxl=3T(_=Y@V)9*#d__~0&&%6wyw6*ekl&l-D}SYTBiSxM?@kY=-@Z3I_aRdZiLCj# zYRE~F=g1XkL+U^=ZE{K|MR1eGtu)9#c#3Y_S3x%&KTZeJr%=w1GTq$l%+<&U{qN6N z5;5Xh7vvTPzdkU)a$6OdHpPD^T!^BrlEU)nS|vKPG6T<5y7d|*Pr^!THJj)rnk&SB z28^afDW}XyV-x4|5UmiP$jLu{o{==A4|&ziaH|qN1NG4SOj8!yMUH*eEe6s>s)E2> zoK7uW99y)4((m`(#eJgaY1tAltJIVs9XsC9 zv32~C$rWZSDe$zpKHAbcyRZ8?Xz)jwe{e(e71+F5GFS3KQ>BN1xTLZ;A)u87HMve8S2-`KD{j3yCRvyyP|rjkquTQ99H!ZF-lRW)I^~?m z7Lm=TWzhYYpwPyuTo(5YeaF^6<@X@x;&E>N%y?CUF?L|lZ=B5D})*&HGuc>fO z?;ePCwYH`@40?Z!$x-X7Sm?LiSh`TH!a^fl=5*%Yxvkt%WZ{({{?tNCOQkX`o5q5O zGmkm^m>H@RFhvyorl`sux|CX@sI*}|j~aR4MKf2jY3@)Z*?hxzv(K?fFY1qeID?l{%M5tfGpjMzhxC2_CZY7n@+7 z_=?H%<;aswZC2QFGdpr5%F})CnH1|dyzjc9a6<8{&?K$vjCh{Eg4fy{jNZ6maVdS@$%2~{1Ws=*drzJ*8klJW4A~gt05ZIKW zLo^Yys8tvZoOU#`9l5=5b~>2-I-A*r?xd`^E@iAPzEQzKbucqck!3b`O-pUFXC$lZ z_U^f2Ah_gY`i)^}b7yjEFy{7l9N4%v{KA>L5n7Y#c6VXsK;{n+p$5z?b|OP<7WBj8 zk-T=HR4iB8Ii=o7#IjOeRJ%khoF1;oU z`FI@^$1s1xj+qV()Zvhvc7k;Wv{+UHQ%F@BtxktbQynK-voo{=t;Wh34M^LfRGX^+ z+;uhqk1g0RHf^8DP_8ao;wY|*gViNF8J;b!fWaqBHibPyZRo#7%gYR+@q&(351e>l zuw!yuXyA7g} zD=kx%xy#+|a=+3R5HW9%ctsa+mr)mTmh)XTlb2j~o%P$s#@2Jxb0ktseI%;@KpIZL zo*kPwOM(<^Zj;gn9KesyhCdgsNfIkKg*Zev%B^^i|tvcv*z1zKtY#%WkfS@n7y=G>`t zm|%y-Vrz70w7e1%EVXuxT7k8Vkgz~CxvHX(e;0VB#0*iT%}O{rlsS2)F(OPF6H2=e?IHn*n8vdZ&43$n)2kF(mIO+-Em zK%RdB2PRizpz~+RlgadwU!C|BeSgoMw3|MWp2BEenrY%ht{=o%Wy!MHB)cs2Mgg;I z3`TsR2c*FzBgi&!oC#AqK_4dE@Zy_*o7$lQhN64XbU9e zElgqlK5VF#9G9lbv)QrYidLgW560$)Bddoc?IRr=T^JJ_4ThuGq-Ql&`oZLODo=DT z^mOtRceU!Qu8pN*p4$L&O-CC;uI<>437(EKgbOF^b#*8BP9hC_rPCOI6-reGCF1Sxqij#R*6mh+6| zAb>h({6zWEO@jl6?9N?3o;dMH zi9zt&l0T!*RfStyjrJNT{Uc^sfQz}g^kwu~E2V1LFR&Vo1|7jt0$Lp=lA#Nbt%}~M z(<+FWvV~K0Utm>o)hpP2-FaMxyhUNtt#dV!F&qD^pC(bOMq*PzzKbli!z*`vnlx!rOqJ|(-)zdybSQ< zToeGnRfK35;>u2_hXp@1xhZwi!K0I#$KP_LS8D6%>)Js}{k?})E_7G*A6U77&S-2- zHm^!HgR=FRS=@U1bs52|gg?M2!|Y%{xMKWRqSPbxLHzJPG% z@8oTB0V{&=4<`kOPtf>r>&%(SaBSZtYH4F<=O#zw>)ddluFTu9ck>3iXiInF&W?J< z)sHi4xLYy5kfASzezWLHA;T|Rq^d0ZIva^GAO>7!pkt93#D0w6=V5Tc2<8+(JYQp+ z)u9$avgejgoh${Sm+d(?(`&sq{f183HZFPL%`b+_JRMhTTvt~GNcPvAQthrzx!?R1 zDpn1YU?P*?xw9FQCM+3*+39Q%4;5(EP^uo4W~QrS0}$%-OoGfo7tDOiX(UWlNkdDp zrnKbb+xHoh?eQ25m-IF+NVL*tD?;VavJW$9E(M9LC37SnijZO@Zl&L>beAhjfL651 zh#IRJh=5=p#~OhMbn~0jnUa>Fu6=l6>k=d zunW$*;daOkMK8(Gp8-Xdkj)saU_O)HX?L1s9LZ?`B2_x9l*SfAlc6KDQ_IPCaBj>tF2{xW1uT|28$z7r!pv&oKXvC5*7q1=XtL*CR3jTE3;?Bh@ zsLtFO9pF0rBU0C{$YYBP0nY+@+1msw$7Uu_ty^8#ZCl+{dME3 z9ib&B-{wN~a8zU0&6zWC;?)*Ut!v8H(*5AN1CqUzK7*La0;wuHrxp+j>!hd^e71)d z0I_&Qc6qgejRDE>LVcr*E?8@7FBBI{is{0;NxZXTL(P z@pt+h)}UdY4qJmprx7_U6UPi47VJa#hAjcX!%@gZJG$fvWyS59_YU~;tU`kXVQ5!E z1Q>!xcGWeNU%t8aTF;9|15e*c-+6~VLx21J`|pE}n?c93^m(A273jU#ntWNbgNie7 z!xqF*sexOK7Z^v~yWBin!dvkNZx(QiT3(o8FSlLugRhNYlvb#xkqj$`Dg(W#2L@0<49oGXf%{Nk1-N+SkW9eTjOj-Zhs^eYAi{%p8Wi&^y&@4>91?9 zQg;C#{*fBfbGMeZg1#3};>B|7tpI(j^#rVJ0Za~12pkCU;W<&sKqDHMt(;Hiq78A=2yS=dYv+o{;oLT9Ufn55rt-1>p*{cgJMCv^9UNmpa; zG?&?#nasV(Jp#T}$}wU#i`nRNm067cJVt!()MOSTHqcy*xW$qU5gRx;Ld>Qr#~Zm@ z6Z04@zfergBemCxAda^141=ujCWVyrF~_9D=$6Vy`YNFE?t(^Gq_FXYD}_ z5n)F*B3u>3U@QEr3S2`sUI(%_f=W&%Hl6}+P_QK)F#CcD=8cKXlrA2|yi=+aW{yux zuJAQ=K=U*~Rdf56mLA0?^jkgjr)OUJs_-+#swIuz=EAYtNniPoE+rhIQ= zNl&72{3-gmDT0uk_C%t+n4U%)LW5UaqRl^`!k1nk}KfCl|F!;NRfU@d6+vcOIr-Hm=|MJ;7z~; zJ9bDH3W_9OIl@oMjyW{)b!+%p7cqV+M9Tn`O$N6B7Uh+yM))h5T>X zC^XU|D|3Fi?4e~HGptv{LLzt3U}50nm+57_bO&MtqXQpT9SP6ga@X;DpIE=PJTzzX z-SW}W(xt=q9=rbN$|+SPOP1XuADwW0@Z!Dq-LYjwwQJk<7w^6I&Mhmr!2Fd*uD$ll zYggF=^DkgWKfHb9zWYYlC<;bUR;|ANdOE*;@|1%&-gno=L}lSf@H8&J=GrSR>g$bq z8kQfu_KIEoy`OLK)o-BfM=wH_nYVt!9d{qw)z`DNp)W1s_U34b15z%=B z6U4Eayzo9D;#}-Mz|zxL%U<<&jLJNK#p`OYUxxhyLbMpt#k^Od!`k-x@q`$fuuwnN zjWXgp(`iD?IJdy9vGUmO!gBj~p6z>t*fEH0e~u8xO&AZw{UtjIDMPr>eFq^GCPF;; z^!|#FO5E>9V5RD(gaoSy3Ef9X82=Z!h>+;-38}3lqz=ci-w@KUf{-{*NK+#r&6i`T z{@Vy?#WUMpA*4M_NXJ4#Ivv=5Nk|gqCEp>W3-n191j%;TQP1(05z>>y{wG3GCkW}q zGx|`+egQk4GXZo>1icggM#!Xx2pK?~2hxNLg2u`737OJ@{qKZK#s5x=V1I&;=@YPj zK*$iDJpEhVx#0zJt$Kv0sXPC-z3{o3Z0Q1^$Qo1fMIgv*)q<*gfo7 z<1+8T8odWp^By4?J<5NLtI7O`KMj{-W9B37Xy!CGl=&U^(fGc^cWvC!s?oVQ>^%ZJA6?m)_E;K+^CGW zk)c-sRO}@_pm;x!OaO7z5RhIq#>k_XwN?wv7en3*Tk=1S^@*D?i!gz{rVT5fv|~<1 zCrLtzB#8KU%u7jOZTvo9i3wyPnS^=UgZTM@DP$^ENSTgb9+-jOJ(xvilR0EAa;fK& z1!N&v1l+NNEXA)894D8P1LRS%i|i&B%hrRTPkBQw1mg)5nBh@KP8ib03 zcEc!t?kLB%{1@kZW}i8hYcM+h%Upvg_JXo7vPsHR^#U_cxu7Jb&z@v8gL0D=}q!?uPKwkRWG?0XMfF|NT(8zE36JD9OhLdh+3?a7c)>3dvYUdPV3mI?n~dLQP+^ow z#1B+tPcYzw#ZS>$E42dt1L-|Vo)>sgiGM|`3TYMi!1r2>9!XW$GzKMZ#&0X+$`TZa z;IOhp_J_<2xMKp=JjU-Wus9FOVLyk#eh~xFM!e$@f}mqQ8~Z1$>kX>$!uTWmf2_oY zHr5)jrY$zDNe-CtYS4>yz>#`{7po+r9%br8A`Jqk7B2WLPJ#@G?A!@UZ;*ro>*@&*}1i6%tbFi_6Y81z~Lj_{|&s1`2a5o(nMH)$Dx zf~XNud1O7P1q~h|x2MHq$Am|0hz74Sy@`8R*74|9o8y3QD7xvJP;eQZ}4*pdb0^X|6wvU?F4&0qiTsQD-th(X(Yi09cctK$L|>vYFV167ZW1 z28-EjGNVc+lf`UgywPf%Aj|;i(u!(5gqB58wI~z+&&qPz$}k}co6W5u8r0pSM=mZj zQU@advM_9cR?$OAA#FOlnQbPs*a4KaSy>K`7EBP;7J~&t65xTL7jy=c#oC9Jr8nyJ zPKy-@^Y|?mlNkbRv#}Gi)r#rwtR3{aG7yGE_zils#-IVC4WJKYnzCh;xjYUBE6eHA zp)8f!Vzio-Q21~^Q!%G$x8FE^T4Stg@eYtot_p`Z`-V-ke#%S$U=E>;#ikXm{SsFvft z#$AQ;quEdTP11;PBQ~DBWw$);lg}FBBddW+z2xtTuJluYl3jEFUt}?#{COEW{Sf03 zWGTMow&#BM%A4OZ*b D -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $Id$ -# -# Copyright (c) 2011 Vladimir Rusinov - -__author__ = 'Allan Saddi ' -__version__ = '$Revision$' -import sys -import select -import struct -import socket -import errno -import types - - -__all__ = ['FCGIApp'] - -# Constants from the spec. -FCGI_LISTENSOCK_FILENO = 0 - -FCGI_HEADER_LEN = 8 - -FCGI_VERSION_1 = 1 - -FCGI_BEGIN_REQUEST = 1 -FCGI_ABORT_REQUEST = 2 -FCGI_END_REQUEST = 3 -FCGI_PARAMS = 4 -FCGI_STDIN = 5 -FCGI_STDOUT = 6 -FCGI_STDERR = 7 -FCGI_DATA = 8 -FCGI_GET_VALUES = 9 -FCGI_GET_VALUES_RESULT = 10 -FCGI_UNKNOWN_TYPE = 11 -FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE - -FCGI_NULL_REQUEST_ID = 0 - -FCGI_KEEP_CONN = 1 - -FCGI_RESPONDER = 1 -FCGI_AUTHORIZER = 2 -FCGI_FILTER = 3 - -FCGI_REQUEST_COMPLETE = 0 -FCGI_CANT_MPX_CONN = 1 -FCGI_OVERLOADED = 2 -FCGI_UNKNOWN_ROLE = 3 - -FCGI_MAX_CONNS = 'FCGI_MAX_CONNS' -FCGI_MAX_REQS = 'FCGI_MAX_REQS' -FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS' - -FCGI_Header = '!BBHHBx' -FCGI_BeginRequestBody = '!HB5x' -FCGI_EndRequestBody = '!LB3x' -FCGI_UnknownTypeBody = '!B7x' - -FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody) -FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody) -FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody) - -if __debug__: - import time - - # Set non-zero to write debug output to a file. - DEBUG = 0 - DEBUGLOG = '/www/server/mdserver-web/logs/fastcgi.log' - - def _debug(level, msg): - if DEBUG < level: - return - - try: - f = open(DEBUGLOG, 'a') - f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg)) - f.close() - except: - pass - - -def decode_pair(s, pos=0): - """ - Decodes a name/value pair. - - The number of bytes decoded as well as the name/value pair - are returned. - """ - nameLength = ord(s[pos]) - if nameLength & 128: - nameLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff - pos += 4 - else: - pos += 1 - - valueLength = ord(s[pos]) - if valueLength & 128: - valueLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff - pos += 4 - else: - pos += 1 - - name = s[pos:pos + nameLength] - pos += nameLength - value = s[pos:pos + valueLength] - pos += valueLength - - return (pos, (name, value)) - - -def encode_pair(name, value): - """ - Encodes a name/value pair. - - The encoded string is returned. - """ - nameLength = len(name) - if nameLength < 128: - s = chr(nameLength).encode() - else: - s = struct.pack('!L', nameLength | 0x80000000) - - valueLength = len(value) - if valueLength < 128: - s += chr(valueLength).encode() - else: - s += struct.pack('!L', valueLength | 0x80000000) - - return s + name + value - - -class Record(object): - """ - A FastCGI Record. - - Used for encoding/decoding records. - """ - - def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID): - self.version = FCGI_VERSION_1 - self.type = type - self.requestId = requestId - self.contentLength = 0 - self.paddingLength = 0 - self.contentData = '' - - def _recvall(sock, length): - """ - Attempts to receive length bytes from a socket, blocking if necessary. - (Socket may be blocking or non-blocking.) - """ - dataList = [] - recvLen = 0 - while length: - try: - data = sock.recv(length) - except socket.error as e: - if e[0] == errno.EAGAIN: - select.select([sock], [], []) - continue - else: - raise - if not data: # EOF - break - dataList.append(data) - dataLen = len(data) - recvLen += dataLen - length -= dataLen - return b''.join(dataList), recvLen - _recvall = staticmethod(_recvall) - - def read(self, sock): - """Read and decode a Record from a socket.""" - try: - header, length = self._recvall(sock, FCGI_HEADER_LEN) - except: - raise EOFError - - if length < FCGI_HEADER_LEN: - raise EOFError - - self.version, self.type, self.requestId, self.contentLength, \ - self.paddingLength = struct.unpack(FCGI_Header, header) - - if __debug__: - _debug(9, 'read: fd = %d, type = %d, requestId = %d, ' - 'contentLength = %d' % - (sock.fileno(), self.type, self.requestId, - self.contentLength)) - - if self.contentLength: - try: - self.contentData, length = self._recvall(sock, - self.contentLength) - except: - raise EOFError - - if length < self.contentLength: - raise EOFError - - if self.paddingLength: - try: - self._recvall(sock, self.paddingLength) - except: - raise EOFError - - def _sendall(sock, data): - """ - Writes data to a socket and does not return until all the data is sent. - """ - length = len(data) - while length: - try: - sent = sock.send(data) - except socket.error as e: - if e[0] == errno.EAGAIN: - select.select([], [sock], []) - continue - else: - raise - data = data[sent:] - length -= sent - _sendall = staticmethod(_sendall) - - def write(self, sock): - """Encode and write a Record to a socket.""" - self.paddingLength = -self.contentLength & 7 - - if __debug__: - _debug(9, 'write: fd = %d, type = %d, requestId = %d, ' - 'contentLength = %d' % - (sock.fileno(), self.type, self.requestId, - self.contentLength)) - - header = struct.pack(FCGI_Header, self.version, self.type, - self.requestId, self.contentLength, - self.paddingLength) - self._sendall(sock, header) - if self.contentLength: - self._sendall(sock, self.contentData) - if self.paddingLength: - self._sendall(sock, b'\x00' * self.paddingLength) - - -class FCGIApp(object): - - def __init__(self, connect=None, host=None, port=None, filterEnviron=True): - if host is not None: - assert port is not None - connect = (host, port) - - self._connect = connect - self._filterEnviron = filterEnviron - - def __call__(self, environ, io, start_response=None): - # For sanity's sake, we don't care about FCGI_MPXS_CONN - # (connection multiplexing). For every request, we obtain a new - # transport socket, perform the request, then discard the socket. - # This is, I believe, how mod_fastcgi does things... - - sock = self._getConnection() - - # Since this is going to be the only request on this connection, - # set the request ID to 1. - requestId = 1 - - # Begin the request - rec = Record(FCGI_BEGIN_REQUEST, requestId) - rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0) - rec.contentLength = FCGI_BeginRequestBody_LEN - rec.write(sock) - - # Filter WSGI environ and send it as FCGI_PARAMS - if self._filterEnviron: - params = self._defaultFilterEnviron(environ) - else: - params = self._lightFilterEnviron(environ) - # TODO: Anything not from environ that needs to be sent also? - # return '200 OK',[],str(params),'' - self._fcgiParams(sock, requestId, params) - self._fcgiParams(sock, requestId, {}) - - # Transfer wsgi.input to FCGI_STDIN - content_length = int(environ.get('CONTENT_LENGTH') or 0) - s = '' - #io = StringIO(stdin) - while True: - if not io: - break - chunk_size = min(content_length, 4096) - s = io.read(chunk_size) - content_length -= len(s) - rec = Record(FCGI_STDIN, requestId) - rec.contentData = s - rec.contentLength = len(s) - rec.write(sock) - if not s: - break - # Empty FCGI_DATA stream - rec = Record(FCGI_DATA, requestId) - rec.write(sock) - return sock - - def _getConnection(self): - if self._connect is not None: - # The simple case. Create a socket and connect to the - # application. - if isinstance(self._connect, str): - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(self._connect) - elif hasattr(socket, 'create_connection'): - sock = socket.create_connection(self._connect) - else: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.connect(self._connect) - return sock - - # To be done when I have more time... - # , 'Launching and managing FastCGI programs not yet implemented' - raise NotImplementedError - - def _fcgiGetValues(self, sock, vars): - # Construct FCGI_GET_VALUES record - outrec = Record(FCGI_GET_VALUES) - data = [] - for name in vars: - data.append(encode_pair(name, '')) - data = ''.join(data) - outrec.contentData = data - outrec.contentLength = len(data) - outrec.write(sock) - - # Await response - inrec = Record() - inrec.read(sock) - result = {} - if inrec.type == FCGI_GET_VALUES_RESULT: - pos = 0 - while pos < inrec.contentLength: - pos, (name, value) = decode_pair(inrec.contentData, pos) - result[name] = value - return result - - def _fcgiParams(self, sock, requestId, params): - rec = Record(FCGI_PARAMS, requestId) - data = [] - for name, value in params.items(): - data.append(encode_pair(name.encode( - 'latin-1'), value.encode('latin-1'))) - data = b''.join(data) - rec.contentData = data - rec.contentLength = len(data) - rec.write(sock) - - _environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_', - 'CONTENT_', 'DOCUMENT_', 'SCRIPT_'] - _environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE'] - _environRenames = [] - - def _defaultFilterEnviron(self, environ): - result = {} - for n in environ.keys(): - iv = False - for p in self._environPrefixes: - if n.startswith(p): - result[n] = environ[n] - iv = True - if n in self._environCopies: - result[n] = environ[n] - iv = True - if n in self._environRenames: - result[self._environRenames[n]] = environ[n] - iv = True - if not iv: - result[n] = environ[n] - - return result - - def _lightFilterEnviron(self, environ): - result = {} - for n in environ.keys(): - if n.upper() == n: - result[n] = environ[n] - return result diff --git a/class/plugin/fpm.py b/class/plugin/fpm.py deleted file mode 100644 index ed6e8058d..000000000 --- a/class/plugin/fpm.py +++ /dev/null @@ -1,208 +0,0 @@ -# coding:utf-8 - - -import json -import os -import time -import re -import sys -import time -import struct -import fcgi_client - -FCGI_Header = '!BBHHBx' - -if sys.version_info[0] == 2: - try: - from cStringIO import StringIO - except: - from StringIO import StringIO -else: - from io import BytesIO as StringIO - - -def get_header_data(sock): - ''' - @name 获取头部32KB数据 - @param sock socketobject(fastcgi套接字对象) - @return bytes - ''' - headers_data = b'' - total_len = 0 - header_len = 1024 * 128 - while True: - fastcgi_header = sock.recv(8) - if not fastcgi_header: - break - if len(fastcgi_header) != 8: - headers_data += fastcgi_header - break - fast_pack = struct.unpack(FCGI_Header, fastcgi_header) - if fast_pack[1] == 3: - break - - tlen = fast_pack[3] - while tlen > 0: - sd = sock.recv(tlen) - if not sd: - break - headers_data += sd - tlen -= len(sd) - - total_len += fast_pack[3] - if fast_pack[4]: - sock.recv(fast_pack[4]) - if total_len > header_len: - break - return headers_data - - -def format_header_data(headers_data): - ''' - @name 格式化响应头 - @param headers_data bytes(fastcgi头部32KB数据) - @return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据) - ''' - status = '200 OK' - headers = {} - pos = 0 - while True: - eolpos = headers_data.find(b'\n', pos) - if eolpos < 0: - break - line = headers_data[pos:eolpos - 1] - pos = eolpos + 1 - line = line.strip() - if len(line) < 2: - break - if line.find(b':') == -1: - continue - header, value = line.split(b':', 1) - header = header.strip() - value = value.strip() - if isinstance(header, bytes): - header = header.decode() - value = value.decode() - if header == 'Status': - status = value - if status.find(' ') < 0: - status += ' BTPanel' - else: - headers[header] = value - bdata = headers_data[pos:] - status = int(status.split(' ')[0]) - return status, headers, bdata - - -def resp_sock(sock, bdata): - ''' - @name 以流的方式发送剩余数据 - @param sock socketobject(fastcgi套接字对象) - @param bdata bytes(格式化响应头后的多余数据) - @return yield bytes - ''' - # 发送除响应头以外的多余头部数据 - yield bdata - while True: - fastcgi_header = sock.recv(8) - if not fastcgi_header: - break - if len(fastcgi_header) != 8: - yield fastcgi_header - break - fast_pack = struct.unpack(FCGI_Header, fastcgi_header) - if fast_pack[1] == 3: - break - tlen = fast_pack[3] - while tlen > 0: - sd = sock.recv(tlen) - if not sd: - break - tlen -= len(sd) - if sd: - yield sd - - if fast_pack[4]: - sock.recv(fast_pack[4]) - sock.close() - - -class fpm(object): - - def __init__(self, sock=None, document_root='', last_path=''): - ''' - @name 实例化FPM对象 - @param sock string(unixsocket路径) - @param document_root string(PHP文档根目录) - @return FPM - ''' - if sock: - self.fcgi_sock = sock - if document_root[-1:] != '/': - document_root += '/' - self.document_root = document_root - self.last_path = last_path - - def load_url_public(self, url, content=b'', method='GET', content_type='application/x-www-form-urlencoded'): - ''' - @name 转发URL到PHP-FPM 公共 - @param url string(URI地址) - @param content stream(POST数据io对象) - @return fastcgi-socket - ''' - fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock) - try: - script_name, query_string = url.split('?') - except ValueError: - script_name = url - query_string = '' - - content_length = len(content) - if content: - content = StringIO(content) - - env = { - 'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name), - 'QUERY_STRING': query_string, - 'REQUEST_METHOD': method, - 'SCRIPT_NAME': self.last_path + script_name, - 'REQUEST_URI': url, - 'GATEWAY_INTERFACE': 'CGI/1.1', - 'SERVER_SOFTWARE': 'MW-Panel', - 'REDIRECT_STATUS': '200', - 'CONTENT_TYPE': content_type, - 'CONTENT_LENGTH': str(content_length), - 'DOCUMENT_URI': script_name, - 'DOCUMENT_ROOT': self.document_root, - 'SERVER_PROTOCOL': 'HTTP/1.1', - 'REMOTE_ADDR': '127.0.0.1', - 'REMOTE_PORT': '7200', - 'SERVER_ADDR': '127.0.0.1', - 'SERVER_PORT': '80', - 'SERVER_NAME': 'MW-Panel' - } - - fpm_sock = fcgi(env, content) - - _data = b'' - while True: - fastcgi_header = fpm_sock.recv(8) - if not fastcgi_header: - break - if len(fastcgi_header) != 8: - _data += fastcgi_header - break - fast_pack = struct.unpack(FCGI_Header, fastcgi_header) - if fast_pack[1] == 3: - break - tlen = fast_pack[3] - while tlen > 0: - sd = fpm_sock.recv(tlen) - if not sd: - break - tlen -= len(sd) - _data += sd - if fast_pack[4]: - fpm_sock.recv(fast_pack[4]) - status, headers, data = format_header_data(_data) - return data diff --git a/class/plugin/memail.py b/class/plugin/memail.py deleted file mode 100644 index 6ba7b62d5..000000000 --- a/class/plugin/memail.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding: utf-8 - -import smtplib - -from email import encoders -from email.header import Header -from email.mime.text import MIMEText -from email.utils import parseaddr, formataddr - - -def _format_addr(s): - name, addr = parseaddr(s) - return formataddr((Header(name, 'utf-8').encode(), addr)) - - -def send(smtp_host, smtp_port, username, password, to_mail, subject, content): - - smtp = smtplib.SMTP() - smtp.connect(smtp_host, port=smtp_port) - smtp.login(user=username, password=password) - - msg = MIMEText(content, 'plain', 'utf-8') - msg['From'] = _format_addr(username) - msg['To'] = _format_addr(to_mail) - msg['Subject'] = Header(subject, 'utf-8').encode() - - smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string()) - return True - - -def sendSSL(smtp_host, smtp_port, username, password, to_mail, subject, content): - - smtp = smtplib.SMTP_SSL(smtp_host, port=smtp_port) - smtp.login(user=username, password=password) - - msg = MIMEText(content, 'plain', 'utf-8') - msg['From'] = _format_addr(username) - msg['To'] = _format_addr(to_mail) - msg['Subject'] = Header(subject, 'utf-8').encode() - - smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string()) - smtp.quit() - return True diff --git a/class/plugin/orm.py b/class/plugin/orm.py deleted file mode 100755 index 89ba623e9..000000000 --- a/class/plugin/orm.py +++ /dev/null @@ -1,140 +0,0 @@ -# coding: utf-8 - -import re -import os -import sys - -import pymysql.cursors - - -class ORM: - __DB_PASS = None - __DB_USER = 'root' - __DB_PORT = 3306 - __DB_NAME = '' - __DB_HOST = 'localhost' - __DB_CONN = None - __DB_CUR = None - __DB_ERR = None - __DB_CNF = '/etc/my.cnf' - __DB_TIMEOUT=1 - __DB_SOCKET = '/www/server/mysql/mysql.sock' - - __DB_CHARSET = "utf8" - - def __Conn(self): - # print(self.__DB_HOST, self.__DB_USER, self.__DB_PASS, self.__DB_SOCKET) - '''连接数据库''' - try: - - if self.__DB_HOST != 'localhost': - try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - except Exception as e: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - elif os.path.exists(self.__DB_SOCKET): - try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) - except Exception as e: - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) - else: - try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - except Exception as e: - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - - self.__DB_CUR = self.__DB_CONN.cursor() - return True - except Exception as e: - self.__DB_ERR = e - return False - - def setDbConf(self, conf): - self.__DB_CNF = conf - - def setSocket(self, sock): - self.__DB_SOCKET = sock - - def setCharset(self, charset): - self.__DB_CHARSET = charset - - def setHost(self, host): - self.__DB_HOST = host - - def setPort(self, port): - self.__DB_PORT = port - - def setUser(self, user): - self.__DB_USER = user - - def setPwd(self, pwd): - self.__DB_PASS = pwd - - def getPwd(self): - return self.__DB_PASS - - def setTimeout(self, timeout = 1): - self.__DB_TIMEOUT = timeout - return True - - def setDbName(self, name): - self.__DB_NAME = name - - def execute(self, sql): - # 执行SQL语句返回受影响行 - if not self.__Conn(): - return self.__DB_ERR - try: - result = self.__DB_CUR.execute(sql) - self.__DB_CONN.commit() - self.__Close() - return result - except Exception as ex: - return ex - - def ping(self): - try: - self.__DB_CONN.ping() - except Exception as e: - print(e) - return True - - def query(self, sql): - # 执行SQL语句返回数据集 - if not self.__Conn(): - return self.__DB_ERR - try: - self.__DB_CUR.execute(sql) - result = self.__DB_CUR.fetchall() - # print(result) - # 将元组转换成列表 - # data = map(list, result) - self.__Close() - return result - except Exception as ex: - return ex - - def __Close(self): - # 关闭连接 - self.__DB_CUR.close() - self.__DB_CONN.close() diff --git a/class/plugin/ormDb.py b/class/plugin/ormDb.py deleted file mode 100755 index 14624ccc0..000000000 --- a/class/plugin/ormDb.py +++ /dev/null @@ -1,115 +0,0 @@ -# coding: utf-8 - -import re -import os -import sys - - -class ORM: - __DB_PASS = None - __DB_USER = 'root' - __DB_PORT = 3306 - __DB_HOST = 'localhost' - __DB_CONN = None - __DB_CUR = None - __DB_ERR = None - __DB_CNF = '/etc/my.cnf' - - __DB_SOCKET = '/www/server/mysql/mysql.sock' - __DB_CHARSET = 'utf8' - - def __Conn(self): - '''连接MYSQL数据库''' - try: - - try: - import MySQLdb - except Exception as ex: - self.__DB_ERR = ex - return False - - # print(self.__DB_HOST) - # print(self.__DB_USER) - # print(self.__DB_PASS) - # print(self.__DB_PORT) - # print(self.__DB_CHARSET) - # print(self.__DB_SOCKET) - if os.path.exists(self.__DB_SOCKET): - try: - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET) - except Exception as e: - print(e) - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET) - else: - try: - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1) - except Exception as e: - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1) - - self.__DB_CUR = self.__DB_CONN.cursor() - - return True - except MySQLdb.Error as e: - self.__DB_ERR = e - return False - - def setDbConf(self, conf): - self.__DB_CNF = conf - - def setSocket(self, sock): - self.__DB_SOCKET = sock - - def setCharset(self, charset): - self.__DB_CHARSET = charset - - def setPort(self, port): - self.__DB_PORT = port - - def setPwd(self, pwd): - self.__DB_PASS = pwd - - def getPwd(self): - return self.__DB_PASS - - def setDbName(self, name): - self.__DB_NAME = name - - def setUser(self, user): - self.__DB_USER = user - - def execute(self, sql): - # 执行SQL语句返回受影响行 - if not self.__Conn(): - return self.__DB_ERR - try: - result = self.__DB_CUR.execute(sql) - self.__DB_CONN.commit() - self.__Close() - return result - except Exception as ex: - return ex - - def query(self, sql): - # 执行SQL语句返回数据集 - if not self.__Conn(): - return self.__DB_ERR - try: - self.__DB_CUR.execute(sql) - result = self.__DB_CUR.fetchall() - # 将元组转换成列表 - data = map(list, result) - self.__Close() - return data - except Exception as ex: - return ex - - # 关闭连接 - def __Close(self): - self.__DB_CUR.close() - self.__DB_CONN.close() From 371b6651f0ff5a1fc4adb1d69c34987e67598892 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 04:21:09 +0800 Subject: [PATCH 559/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index 2b3f1fe25..9163ad25d 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -37,8 +37,8 @@ def init_cmd(): # initd if os.path.exists('/etc/rc.d/init.d'): initd_bin = '/etc/rc.d/init.d/mw' - if not os.access(initd_bin, os.W_OK): - return False + # if not os.access(initd_bin, os.W_OK): + # return False shutil.copyfile(script_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) # 加入自启动 @@ -46,8 +46,8 @@ def init_cmd(): if os.path.exists('/etc/init.d'): initd_bin = '/etc/init.d/mw' - if not os.access(initd_bin, os.W_OK): - return False + # if not os.access(initd_bin, os.W_OK): + # return False shutil.copyfile(script_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) # 加入自启动 From 30e2ea56c8833f7ab5386df932554f5cd882fba4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 13:34:58 +0800 Subject: [PATCH 560/965] Update init_cmd.py --- web/admin/setup/init_cmd.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py index 9163ad25d..a307b494a 100644 --- a/web/admin/setup/init_cmd.py +++ b/web/admin/setup/init_cmd.py @@ -39,8 +39,9 @@ def init_cmd(): initd_bin = '/etc/rc.d/init.d/mw' # if not os.access(initd_bin, os.W_OK): # return False - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) + if not os.path.exists(initd_bin): + shutil.copyfile(script_bin, initd_bin) + mw.execShell('chmod +x ' + initd_bin) # 加入自启动 mw.execShell('which chkconfig && chkconfig --add mw') @@ -48,8 +49,9 @@ def init_cmd(): initd_bin = '/etc/init.d/mw' # if not os.access(initd_bin, os.W_OK): # return False - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) + if not os.path.exists(initd_bin): + shutil.copyfile(script_bin, initd_bin) + mw.execShell('chmod +x ' + initd_bin) # 加入自启动 mw.execShell('which update-rc.d && update-rc.d -f mw defaults') return True \ No newline at end of file From 485b0605b410a4d2d897418dcf83afaef81f96b0 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 13:52:36 +0800 Subject: [PATCH 561/965] Update python-app-op.yml --- .github/workflows/python-app-op.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app-op.yml b/.github/workflows/python-app-op.yml index a1818efc2..bab069fd1 100644 --- a/.github/workflows/python-app-op.yml +++ b/.github/workflows/python-app-op.yml @@ -35,4 +35,4 @@ jobs: - name: Start DEBUG run: | cd web && gunicorn -c setting.py app:app - python3 task.py & + python3 panel_task.py & From 37f424448489bf1b69ab626833a9dd73aec81a1e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 13:53:32 +0800 Subject: [PATCH 562/965] update --- .github/workflows/python-app-mysql55.yml | 4 ++-- .github/workflows/python-app-mysql56.yml | 4 ++-- .github/workflows/python-app-mysql57.yml | 4 ++-- .github/workflows/python-app-mysql80.yml | 4 ++-- .github/workflows/python-app-php.yml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) 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-php.yml b/.github/workflows/python-app-php.yml index 790544208..5e505ade1 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 & + cd web && gunicorn -c setting.py app:app + python3 panel_task.py & From 52e9ff38fad8bcb93dcd2c42b647eb5bd89ee5f1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 14:54:25 +0800 Subject: [PATCH 563/965] Update phplib.conf --- plugins/php-apt/versions/phplib.conf | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/php-apt/versions/phplib.conf b/plugins/php-apt/versions/phplib.conf index d64cde6b0..e9b22403a 100755 --- a/plugins/php-apt/versions/phplib.conf +++ b/plugins/php-apt/versions/phplib.conf @@ -1,4 +1,22 @@ [ + { + "name": "ionCube", + "versions": [ + "56", + "70", + "71", + "72", + "73", + "74", + "81", + "82", + "83" + ], + "type": "脚本解密", + "msg": "用于解密ionCube Encoder加密脚本!", + "shell": "ioncube.sh", + "check": "ioncube" + }, { "name": "pdo", "versions": [ @@ -126,24 +144,6 @@ "shell": "zend_optimizer.sh", "check": "ZendOptimizer" }, - { - "name": "ionCube", - "versions": [ - "56", - "70", - "71", - "72", - "73", - "74", - "81", - "82", - "83" - ], - "type": "脚本解密", - "msg": "用于解密ionCube Encoder加密脚本!", - "shell": "ioncube.sh", - "check": "ioncube" - }, { "name": "opcache", "versions": [ From afb9f430b35e8b8f27c5578fd7f69a6ccda37aa1 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 15:04:12 +0800 Subject: [PATCH 564/965] update --- plugins/php-apt/versions/common/sg11.sh | 138 ++++++++++++++++++++++++ plugins/php-apt/versions/lib.sh | 2 + plugins/php-apt/versions/phplib.conf | 19 ++++ 3 files changed, 159 insertions(+) create mode 100644 plugins/php-apt/versions/common/sg11.sh diff --git a/plugins/php-apt/versions/common/sg11.sh b/plugins/php-apt/versions/common/sg11.sh new file mode 100644 index 000000000..8bcf13f33 --- /dev/null +++ b/plugins/php-apt/versions/common/sg11.sh @@ -0,0 +1,138 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH=$PATH:/opt/homebrew/bin + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php + +# https://www.sourceguardian.com/loaders.html + +# support 52-81 + +LIBNAME=sg11 +LIBV=0 +sysName=`uname` +actionType=$1 +version=$2 +SG_VER=${version:0:1}.${version:1:2} + + +LIB_PATH_NAME=lib/php +if [ -d $serverPath/php/${version}/lib64 ];then + LIB_PATH_NAME=lib64 +fi + +NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts` +extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so + +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + + +Install_lib() +{ + bash ${rootPath}/scripts/getos.sh + OSNAME=`cat ${rootPath}/data/osname.pl` + if [ "$OSNAME" == 'macos' ];then + VERSION_ID=none + else + VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` + fi + + echo "${OSNAME}:${VERSION_ID}" + + DEFAULT_OSNAME=linux-x86_64 + SUFFIX_NAME=lin + if [ "$OSNAME" == 'macos' ];then + DEFAULT_OSNAME=macosx + SUFFIX_NAME=dar + fi + + isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + + if [ ! -f "$extFile" ];then + + php_lib=$sourcePath/php_lib + mkdir -p $php_lib + mkdir -p $php_lib/sg11 + if [ ! -f $php_lib/sg11_loaders.tar.bz2 ];then + curl -sSLo $php_lib/sg11_loaders.tar.bz2 https://www.sourceguardian.com/loaders/download/loaders.tar.bz2 + echo "cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11" + cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11 + fi + + + if [ ! -d $php_lib/sg11/macosx ];then + cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11 + fi + cd $php_lib/sg11 + # echo "mv $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.lin $extFile" + if [ -f $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} ];then + cp -rf $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} $extFile + else + echo 'Not supported temporarily' + exit + fi + + if [ "$OSNAME" == 'macos' ];then + xattr -c * $extFile + fi + fi + + if [ ! -f "$extFile" ];then + echo "ERROR!" + return + fi + + echo "" >> $serverPath/php/$version/etc/php.ini + echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini + echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini + + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ ! -f "$serverPath/php/$version/bin/php-config" ];then + echo "php$version 未安装,请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + echo "php$version 未安装${LIBNAME},请选择其它版本!" + echo "php-$vphp not install ${LIBNAME}, Plese select other version!" + return + fi + + sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini + sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini + + rm -f $extFile + + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===============================================' + echo 'successful!' +} + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file diff --git a/plugins/php-apt/versions/lib.sh b/plugins/php-apt/versions/lib.sh index fed6f11ee..0822ff7c4 100644 --- a/plugins/php-apt/versions/lib.sh +++ b/plugins/php-apt/versions/lib.sh @@ -30,4 +30,6 @@ elif [[ "$version" == '8.2' ]]; then echo '20220829' elif [[ "$version" == '8.3' ]]; then echo '20230831' +elif [[ "$version" == '8.4' ]]; then + echo '20240924' fi \ No newline at end of file diff --git a/plugins/php-apt/versions/phplib.conf b/plugins/php-apt/versions/phplib.conf index e9b22403a..6060939eb 100755 --- a/plugins/php-apt/versions/phplib.conf +++ b/plugins/php-apt/versions/phplib.conf @@ -1,4 +1,23 @@ [ + { + "name": "sg11", + "versions": [ + "56", + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83" + ], + "type": "脚本解密", + "msg": "用于解密SG11加密脚本!", + "shell": "sg11.sh", + "check": "sg11.so" + }, { "name": "ionCube", "versions": [ From 1972df1f2d37dfa7474a46d8a86ef93f395c6800 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 15:20:29 +0800 Subject: [PATCH 565/965] update --- plugins/php-apt/versions/common/phalcon.sh | 2 + plugins/php-apt/versions/common/sg11.sh | 46 +++++++++------------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/plugins/php-apt/versions/common/phalcon.sh b/plugins/php-apt/versions/common/phalcon.sh index 17d4b232c..758ee0961 100755 --- a/plugins/php-apt/versions/common/phalcon.sh +++ b/plugins/php-apt/versions/common/phalcon.sh @@ -50,6 +50,8 @@ elif [[ "$version" == '8.2' ]]; then CMD="${CMD}phalcon5" elif [[ "$version" == '8.3' ]]; then CMD="${CMD}phalcon5" +elif [[ "$version" == '8.4' ]]; then + CMD="${CMD}phalcon5" fi $CMD diff --git a/plugins/php-apt/versions/common/sg11.sh b/plugins/php-apt/versions/common/sg11.sh index 8bcf13f33..d1e7dbbd3 100644 --- a/plugins/php-apt/versions/common/sg11.sh +++ b/plugins/php-apt/versions/common/sg11.sh @@ -21,14 +21,8 @@ actionType=$1 version=$2 SG_VER=${version:0:1}.${version:1:2} - -LIB_PATH_NAME=lib/php -if [ -d $serverPath/php/${version}/lib64 ];then - LIB_PATH_NAME=lib64 -fi - -NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts` -extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so +extVer=`bash $curPath/lib.sh $version` +extFile=/usr/lib/php/${extVer}/${LIBNAME}.so if [ "$sysName" == "Darwin" ];then BAK='_bak' @@ -36,6 +30,7 @@ else BAK='' fi +SORT_LIBNAME="10-${LIBNAME}" Install_lib() { @@ -56,7 +51,7 @@ Install_lib() SUFFIX_NAME=dar fi - isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"` + isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"` if [ "${isInstall}" != "" ];then echo "php-$version 已安装${LIBNAME},请选择其它版本!" return @@ -97,11 +92,11 @@ Install_lib() return fi - echo "" >> $serverPath/php/$version/etc/php.ini - echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini - echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini - - cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo "" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + + systemctl restart php${version}-fpm echo '===========================================================' echo 'successful!' } @@ -109,23 +104,18 @@ Install_lib() Uninstall_lib() { - if [ ! -f "$serverPath/php/$version/bin/php-config" ];then - echo "php$version 未安装,请选择其它版本!" + if [ ! -f "/usr/bin/php-config${version}" ];then + echo "php-$version 未安装,请选择其它版本!" return fi - - if [ ! -f "$extFile" ];then - echo "php$version 未安装${LIBNAME},请选择其它版本!" - echo "php-$vphp not install ${LIBNAME}, Plese select other version!" - return - fi - - sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini - sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini - - rm -f $extFile - cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + + if [ -f /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini ];then + rm -rf /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + rm -rf $extFile + fi + + systemctl restart php${version}-fpm echo '===============================================' echo 'successful!' } From 9276dd1bb8d57d74dfa562eccbb3e212554926b8 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 15:23:03 +0800 Subject: [PATCH 566/965] Update sg11.sh --- plugins/php-apt/versions/common/sg11.sh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugins/php-apt/versions/common/sg11.sh b/plugins/php-apt/versions/common/sg11.sh index d1e7dbbd3..d2f98c6f0 100644 --- a/plugins/php-apt/versions/common/sg11.sh +++ b/plugins/php-apt/versions/common/sg11.sh @@ -34,15 +34,6 @@ SORT_LIBNAME="10-${LIBNAME}" Install_lib() { - bash ${rootPath}/scripts/getos.sh - OSNAME=`cat ${rootPath}/data/osname.pl` - if [ "$OSNAME" == 'macos' ];then - VERSION_ID=none - else - VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` - fi - - echo "${OSNAME}:${VERSION_ID}" DEFAULT_OSNAME=linux-x86_64 SUFFIX_NAME=lin From 446e39fb6866dd568ff8f679d4d1f45a1761b45c Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 15:42:57 +0800 Subject: [PATCH 567/965] Update sg11.sh --- plugins/php-apt/versions/common/sg11.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/php-apt/versions/common/sg11.sh b/plugins/php-apt/versions/common/sg11.sh index d2f98c6f0..a0ad40cee 100644 --- a/plugins/php-apt/versions/common/sg11.sh +++ b/plugins/php-apt/versions/common/sg11.sh @@ -6,7 +6,6 @@ curPath=`pwd` rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath") -rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source/php @@ -19,7 +18,8 @@ LIBV=0 sysName=`uname` actionType=$1 version=$2 -SG_VER=${version:0:1}.${version:1:2} +# SG_VER=${version:0:1}.${version:1:2} +SG_VER=${version} extVer=`bash $curPath/lib.sh $version` extFile=/usr/lib/php/${extVer}/${LIBNAME}.so @@ -34,6 +34,10 @@ SORT_LIBNAME="10-${LIBNAME}" Install_lib() { + bash ${rootPath}/scripts/getos.sh + OSNAME=`cat ${rootPath}/data/osname.pl` + + echo "${OSNAME}:${VERSION_ID}" DEFAULT_OSNAME=linux-x86_64 SUFFIX_NAME=lin From cb794a946aac02a409fcdf070865c5a8694fad16 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 15:48:17 +0800 Subject: [PATCH 568/965] Update sg11.sh --- plugins/php-apt/versions/common/sg11.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/php-apt/versions/common/sg11.sh b/plugins/php-apt/versions/common/sg11.sh index a0ad40cee..e10e2d3bf 100644 --- a/plugins/php-apt/versions/common/sg11.sh +++ b/plugins/php-apt/versions/common/sg11.sh @@ -37,7 +37,7 @@ Install_lib() bash ${rootPath}/scripts/getos.sh OSNAME=`cat ${rootPath}/data/osname.pl` - echo "${OSNAME}:${VERSION_ID}" + echo "${OSNAME}" DEFAULT_OSNAME=linux-x86_64 SUFFIX_NAME=lin From 5b0e8ad2da95dfc45901052bf42db6e03ed46bf4 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 16:32:05 +0800 Subject: [PATCH 569/965] update --- web/admin/setting/__init__.py | 1 + web/admin/setting/panel_bookmark.py | 69 +++++++++++++++++++++++++++++ web/admin/setting/setting.py | 6 --- web/static/app/public.js | 31 ++++++------- 4 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 web/admin/setting/panel_bookmark.py diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py index f6183dbaf..1dfb93200 100644 --- a/web/admin/setting/__init__.py +++ b/web/admin/setting/__init__.py @@ -19,4 +19,5 @@ from .notify_tgbot import * from .app import * from .panel_ssl import * +from .panel_bookmark import * diff --git a/web/admin/setting/panel_bookmark.py b/web/admin/setting/panel_bookmark.py new file mode 100644 index 000000000..cf8c83598 --- /dev/null +++ b/web/admin/setting/panel_bookmark.py @@ -0,0 +1,69 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from utils.setting import setting as MwSetting + +from .setting import blueprint +import thisdb + +# 添加面板书签 +@blueprint.route('/add_panel_info', endpoint='add_panel_info', methods=['POST']) +@panel_login_required +def add_panel_info(): + title = request.form.get('title', '') + url = request.form.get('url', '') + username = request.form.get('username', '') + password = request.form.get('password', '') + + # 校验是还是重复 + isAdd = mw.M('panel').where('title=? OR url=?', (title, url)).count() + if isAdd: + return mw.returnData(False, '备注或面板地址重复!') + isRe = mw.M('panel').add('title,url,username,password,click,add_time', + (title, url, username, password, 0, int(time.time()))) + if isRe: + return mw.returnData(True, '添加成功!') + return mw.returnData(False, '添加失败!') + +# 取面板书签列表 +@blueprint.route('/get_panel_list', endpoint='get_panel_list', methods=['GET','POST']) +@panel_login_required +def get_panel_list(): + data = mw.M('panel').field('id,title,url,username,password,click,add_time').order('click desc').select() + return data + +# 删除面板书签 +@blueprint.route('/del_panel_info', endpoint='del_panel_info', methods=['GET','POST']) +@panel_login_required +def del_panel_info(): + panel_id = request.form.get('id', '') + isExists = mw.M('panel').where('id=?', (panel_id,)).count() + if not isExists: + return mw.returnData(False, '指定面板资料不存在!') + mw.M('panel').where('id=?', (panel_id,)).delete() + return mw.returnData(True, '删除成功!') + + + + diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 6f07c3e60..df1eee5fa 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -32,12 +32,6 @@ def index(): name = thisdb.getOption('template', default='default') return render_template('%s/setting.html' % name) -@blueprint.route('/get_panel_list', endpoint='get_panel_list', methods=['POST']) -@panel_login_required -def get_panel_list(): - return [] - - # 设置面板名称 @blueprint.route('/set_webname', endpoint='set_webname', methods=['POST']) @panel_login_required diff --git a/web/static/app/public.js b/web/static/app/public.js index d0df5768d..942a33ec3 100755 --- a/web/static/app/public.js +++ b/web/static/app/public.js @@ -1138,7 +1138,7 @@ function listOrder(skey,type,obj){ //获取关联列表 function getPanelList(){ var con =''; - $.post("/config/get_panel_list",function(rdata){ + $.post("/setting/get_panel_list",function(rdata){ for(var i=0; i\ '+rdata[i].title+'\ @@ -1203,7 +1203,7 @@ function bindPanel(a,type,ip,btid,url,user,pw){ if(pw == undefined) pw=""; if(ip == undefined) ip=""; if(a == 1) { - var gurl = "/config/add_panel_info"; + var gurl = "/setting/add_panel_info"; var btaddress = $("#btaddress").val(); if(!btaddress.match(/^(http|https)+:\/\/([\w-]+\.)+[\w-]+:\d+/)){ layer.msg('面板地址格式不正确,示例:

                    http://192.168.0.1:8888

                    ',{icon:5,time:5000}); @@ -1218,7 +1218,7 @@ function bindPanel(a,type,ip,btid,url,user,pw){ return; } if(type=="c"){ - gurl = "/config/set_panel_info"; + gurl = "/setting/set_panel_info"; data = data+"&id="+btid; } $.post(gurl, data, function(b) { @@ -1276,7 +1276,7 @@ function bindPanel(a,type,ip,btid,url,user,pw){ } //删除快捷登录 function bindPaneldel(id){ - $.post("/config/del_panel_info","id="+id,function(rdata){ + $.post("/setting/del_panel_info","id="+id,function(rdata){ layer.closeAll(); layer.msg(rdata.msg,{icon:rdata.status?1:2}); getPanelList(); @@ -1309,6 +1309,7 @@ function getSpeed(sele){ },1000); },'json'); } + //消息盒子 function messageBox() { layer.open({ @@ -1318,17 +1319,17 @@ function messageBox() { closeBtn: 1, shadeClose: false, content: '
                    \ -
                    \ -
                    \ -

                    任务列表(0)

                    \ -

                    消息列表(0)

                    \ -

                    执行日志

                    \ -
                    \ -
                    \ -
                    \ -
                    \ -
                    \ -
                    ' +
                    \ +
                    \ +

                    任务列表(0)

                    \ +

                    消息列表(0)

                    \ +

                    执行日志

                    \ +
                    \ +
                    \ +
                    \ +
                    \ +
                    \ +
                    ' }); $(".bt-w-menu p").click(function(){ $(this).addClass("bgw").siblings().removeClass("bgw"); From b22f2407195e206a865af6389d9fbf451284ce1e Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Sun, 1 Dec 2024 16:57:29 +0800 Subject: [PATCH 570/965] Update public.js --- web/static/app/public.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/web/static/app/public.js b/web/static/app/public.js index 942a33ec3..941b95d4e 100755 --- a/web/static/app/public.js +++ b/web/static/app/public.js @@ -1152,10 +1152,13 @@ function getPanelList(){ },function(){ $(this).css("opacity",".6"); }).click(function(){ - $("#btpanelform").remove(); + // $("#btpanelform").remove(); var murl = $(this).attr("data-url"); var user = $(this).attr("data-user"); var pw = $(this).attr("data-pw"); + + const now_url = new URL(murl) + // console.log(murl,user,pw,now_url.origin); layer.open({ type: 2, title: false, @@ -1165,21 +1168,23 @@ function getPanelList(){ offset: 'rb', //右下角弹出 time: 5, //2秒后自动关闭 anim: 2, - content: [murl+'/login', 'no'] + content: [murl, 'no'] }); - var loginForm ='
                    From 33a83a6d8ee06bde9fe10628b1169a3a9356dd12 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Tue, 3 Dec 2024 18:03:40 +0800 Subject: [PATCH 720/965] Update index.html --- plugins/xui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/xui/index.html b/plugins/xui/index.html index 84ccf5b29..a2e634744 100755 --- a/plugins/xui/index.html +++ b/plugins/xui/index.html @@ -14,7 +14,7 @@ - +