diff --git a/package/ctcgfw/luci-app-openclash/Makefile b/package/ctcgfw/luci-app-openclash/Makefile index 58de6b1757..440adec83f 100644 --- a/package/ctcgfw/luci-app-openclash/Makefile +++ b/package/ctcgfw/luci-app-openclash/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-openclash -PKG_VERSION:=0.35.6 +PKG_VERSION:=0.36.1 PKG_RELEASE:=beta PKG_MAINTAINER:=vernesong @@ -30,6 +30,9 @@ define Build/Prepare po2lmo $(po) $(PKG_BUILD_DIR)/$(patsubst %.po,%.lmo,$(notdir $(po)));) chmod 0755 ${CURDIR}/files/etc/init.d/openclash chmod -R 0755 ${CURDIR}/files/usr/share/openclash/ + mkdir -p ${CURDIR}/files/etc/openclash/config >/dev/null 2>&1 + mkdir -p ${CURDIR}/files/etc/openclash/proxy_provider >/dev/null 2>&1 + mkdir -p ${CURDIR}/files/etc/openclash/backup >/dev/null 2>&1 endef define Build/Configure @@ -46,6 +49,11 @@ if [ -f "/etc/config/openclash" ]; then cp "/etc/config/openclash_custom_hosts.list" "/tmp/openclash_custom_hosts.list.bak" >/dev/null 2>&1 cp "/etc/config/openclash_custom_fake_black.conf" "/tmp/openclash_custom_fake_black.conf.bak" >/dev/null 2>&1 fi +if [ -f "/etc/openclash/custom/openclash_custom_rules.list" ]; then + cp "/etc/openclash/custom/openclash_custom_rules.list" "/tmp/openclash_custom_rules.list.bak" >/dev/null 2>&1 + cp "/etc/openclash/custom/openclash_custom_hosts.list" "/tmp/openclash_custom_hosts.list.bak" >/dev/null 2>&1 + cp "/etc/openclash/custom/openclash_custom_fake_black.conf" "/tmp/openclash_custom_fake_black.conf.bak" >/dev/null 2>&1 +fi endef define Package/$(PKG_NAME)/postinst @@ -60,17 +68,31 @@ uci set firewall.openclash.type=script >/dev/null 2>&1 uci set firewall.openclash.path=/var/etc/openclash.include >/dev/null 2>&1 uci set firewall.openclash.reload=1 >/dev/null 2>&1 uci commit firewall >/dev/null 2>&1 +mkdir -p /etc/openclash/config >/dev/null 2>&1 +mkdir -p /etc/openclash/proxy_provider >/dev/null 2>&1 +mkdir -p /etc/openclash/backup >/dev/null 2>&1 if [ -f "/tmp/openclash.bak" ]; then mv "/tmp/openclash.bak" "/etc/config/openclash" >/dev/null 2>&1 - mv "/tmp/openclash_custom_rules.list.bak" "/etc/config/openclash_custom_rules.list" >/dev/null 2>&1 - mv "/tmp/openclash_custom_hosts.list.bak" "/etc/config/openclash_custom_hosts.list" >/dev/null 2>&1 - mv "/tmp/openclash_custom_fake_black.conf.bak" "/etc/config/openclash_custom_fake_black.conf" >/dev/null 2>&1 + mv "/tmp/openclash_custom_rules.list.bak" "/etc/openclash/custom/openclash_custom_rules.list" >/dev/null 2>&1 + mv "/tmp/openclash_custom_hosts.list.bak" "/etc/openclash/custom/openclash_custom_hosts.list" >/dev/null 2>&1 + mv "/tmp/openclash_custom_fake_black.conf.bak" "/etc/openclash/custom/openclash_custom_fake_black.conf" >/dev/null 2>&1 fi if [ -f "/tmp/config.yaml" ]; then - mv "/tmp/config.yaml" "/etc/openclash/config.yaml" >/dev/null 2>&1 + mv "/tmp/config.yaml" "/etc/openclash/config/config.yaml" >/dev/null 2>&1 elif [ -f "/tmp/config.yml" ]; then - mv "/tmp/config.yml" "/etc/openclash/config.yaml" >/dev/null 2>&1 + mv "/tmp/config.yml" "/etc/openclash/config/config.yaml" >/dev/null 2>&1 +elif [ -d "/tmp/openclash_config/" ]; then + rm -rf "/etc/openclash/config" >/dev/null 2>&1 + mv "/tmp/openclash_config" "/etc/openclash/config" >/dev/null 2>&1 fi +if [ -d "/tmp/openclash_proxy_provider/" ]; then + rm -rf "/etc/openclash/proxy_provider" >/dev/null 2>&1 + mv "/tmp/openclash_proxy_provider" "/etc/openclash/proxy_provider" >/dev/null 2>&1 +fi +mkdir -p /lib/upgrade/keep.d >/dev/null 2>&1 +cat > "/lib/upgrade/keep.d/luci-app-openclash" <<-EOF +/etc/openclash/custom/ +EOF uci set openclash.config.enable=0 >/dev/null 2>&1 uci commit openclash >/dev/null 2>&1 chmod 0755 /etc/init.d/openclash >/dev/null 2>&1 @@ -83,22 +105,28 @@ define Package/$(PKG_NAME)/prerm if [ ! -f "/tmp/openclash_update.sh" ]; then rm -rf /etc/openclash/clash >/dev/null 2>&1 fi + mv "/etc/openclash/config.yaml" "/tmp/config.yaml" >/dev/null 2>&1 + mv "/etc/openclash/config" "/tmp/openclash_config" >/dev/null 2>&1 + mv "/etc/openclash/proxy_provider" "/tmp/openclash_proxy_provider" >/dev/null 2>&1 cp "/etc/config/openclash" "/tmp/openclash.bak" >/dev/null 2>&1 cp "/etc/config/openclash_custom_rules.list" "/tmp/openclash_custom_rules.list.bak" >/dev/null 2>&1 cp "/etc/config/openclash_custom_hosts.list" "/tmp/openclash_custom_hosts.list.bak" >/dev/null 2>&1 cp "/etc/config/openclash_custom_fake_black.conf" "/tmp/openclash_custom_fake_black.conf.bak" >/dev/null 2>&1 + cp "/etc/openclash/custom/openclash_custom_rules.list" "/tmp/openclash_custom_rules.list.bak" >/dev/null 2>&1 + cp "/etc/openclash/custom/openclash_custom_hosts.list" "/tmp/openclash_custom_hosts.list.bak" >/dev/null 2>&1 + cp "/etc/openclash/custom/openclash_custom_fake_black.conf" "/tmp/openclash_custom_fake_black.conf.bak" >/dev/null 2>&1 endef define Package/$(PKG_NAME)/postrm #!/bin/sh -if [ -f "/etc/openclash/config.yaml" ]; then - mv "/etc/openclash/config.yaml" "/tmp/config.yaml" -fi if [ -f "/etc/openclash/clash" ]; then - rm -rf /etc/openclash/config.* >/dev/null 2>&1 + rm -rf /etc/openclash/config >/dev/null 2>&1 + rm -rf /etc/openclash/backup >/dev/null 2>&1 + rm -rf /etc/openclash/proxy_provider >/dev/null 2>&1 else rm -rf /etc/openclash >/dev/null 2>&1 fi + rm -rf /lib/upgrade/keep.d/luci-app-openclash >/dev/null 2>&1 rm -rf /tmp/openclash.log >/dev/null 2>&1 rm -rf /tmp/openclash_start.log >/dev/null 2>&1 rm -rf /tmp/Proxy_Group >/dev/null 2>&1 diff --git a/package/ctcgfw/luci-app-openclash/files/etc/init.d/openclash b/package/ctcgfw/luci-app-openclash/files/etc/init.d/openclash index 36436bb077..54430d6c95 100755 --- a/package/ctcgfw/luci-app-openclash/files/etc/init.d/openclash +++ b/package/ctcgfw/luci-app-openclash/files/etc/init.d/openclash @@ -8,16 +8,18 @@ STOP=15 CLASH="/etc/openclash/clash" CLASH_CONFIG="/etc/openclash" CRON_FILE="/etc/crontabs/root" -CONFIG_FILE="/etc/openclash/config.yaml" +CONFIG_FILE=$(uci get openclash.config.config_path 2>/dev/null) LOG_FILE="/tmp/openclash.log" START_LOG="/tmp/openclash_start.log" -BACKUP_FILE="/etc/openclash/config.bak" +BACKUP_FILE="/etc/openclash/backup/$(uci get openclash.config.config_path |awk -F '/' '{print $5}' 2>/dev/null)" START_BACKUP="/tmp/config.sbak" CHANGE_FILE="/tmp/yaml_change.yaml" RULE_FILE="/tmp/yaml_rules.yaml" DNS_FILE="/tmp/yaml_dns.yaml" PROXY_FILE="/tmp/yaml_proxy.yaml" -HOSTS_FILE="/etc/config/openclash_custom_hosts.list" +HOSTS_FILE="/etc/openclash/custom/openclash_custom_hosts.list" +PROXY_FWMARK="0x162" +PROXY_ROUTE_TABLE="0x162" add_cron() { @@ -103,18 +105,7 @@ yml_check() #替换tab sed -i 's/\t/ /g' "$3" 2>/dev/null - - #自定义DNS还原 - if [ "$(grep -c '##Custom DNS##' "$3")" -gt 0 ] && [ "$2" = 0 ] && [ -f "$4" ]; then - awk '/^ {0,}nameserver:/,/Proxy:/{print}' "$4" | sed '/^Proxy:/d' >/tmp/bakdns.config 2>/dev/null - sed -i '/OpenClash-General-Settings/i\Custom DNS End' "$3" 2>/dev/null - sed -i '/^ \{0,\}nameserver:/,/^Custom DNS End$/d' "$3" 2>/dev/null - sed -i '/##Custom DNS##/r/tmp/bakdns.config' "$3" 2>/dev/null - rm -rf /tmp/bakdns.config 2>/dev/null - fi - yml_dns_check - #检查关键字避免后续操作出错 [ ! -z "$(grep "^ \{0,\}'Proxy':" "$3")" ] || [ ! -z "$(grep '^ \{0,\}"Proxy":' "$3")" ] && { @@ -122,6 +113,11 @@ yml_check() sed -i '/^ \{0,\}\"Proxy\":/c\Proxy:' "$3" } + [ ! -z "$(grep "^ \{0,\}'proxy-provider':" "$3")" ] || [ ! -z "$(grep '^ \{0,\}"proxy-provider":' "$3")" ] && { + sed -i "/^ \{0,\}\'proxy-provider\'/c\proxy-provider:" "$3" + sed -i '/^ \{0,\}\"proxy-provider\":/c\proxy-provider:' "$3" + } + [ ! -z "$(grep "^ \{0,\}'Proxy Group':" "$3")" ] || [ ! -z "$(grep '^ \{0,\}"Proxy Group":' "$3")" ] && { sed -i "/^ \{0,\}\'Proxy Group\'/c\Proxy Group:" "$3" sed -i '/^ \{0,\}\"Proxy Group\":/c\Proxy Group:' "$3" @@ -140,7 +136,11 @@ yml_check() [ ! -z "$(grep "^ \{1,\}Proxy:" "$3")" ] && { sed -i "/^ \{1,\}Proxy:/c\Proxy:" "$3" } - + + [ ! -z "$(grep "^ \{1,\}proxy-provider:" "$3")" ] && { + sed -i "/^ \{1,\}proxy-provider:/c\proxy-provider:" "$3" + } + [ ! -z "$(grep "^ \{1,\}Proxy Group:" "$3")" ] && { sed -i "/^ \{1,\}Proxy Group:/c\Proxy Group:" "$3" } @@ -153,6 +153,50 @@ yml_check() sed -i "/^ \{1,\}dns:/c\dns:" "$3" } + #处理provider位置 + proxy_len=$(sed -n '/^Proxy:/=' "$3" 2>/dev/null) + group_len=$(sed -n '/^ \{0,\}Proxy Group:/=' "$3" 2>/dev/null) + provider_len=$(sed -n '/^proxy-provider:/=' "$3" 2>/dev/null) + if [ ! -z "$provider_len" ]; then + if [ "$provider_len" -ge "$proxy_len" ] && [ "$provider_len" -le "$group_len" ]; then + awk '/^proxy-provider:/,/^Proxy Group:/{print}' "$3" | sed '/^Proxy Group:/d' >/tmp/backprovider.yaml 2>/dev/null + sed -i '/^Proxy Group:/i\proxy-provider-tag' "$3" 2>/dev/null + sed -i '/^proxy-provider:/,/proxy-provider-tag/d' "$3" 2>/dev/null + sed -i '/^Rule:/i\proxy-provider:' "$3" 2>/dev/null + sed -i '/^proxy-provider:/d' /tmp/backprovider.yaml 2>/dev/null + sed -i '/proxy-provider:/r/tmp/bakprovider.yaml' "$3" 2>/dev/null + rm -rf /tmp/backprovider.yaml 2>/dev/null + elif [ "$provider_len" -ge "$group_len" ]; then + awk '/^proxy-provider:/,/^Rule:/{print}' "$3" | sed '/^Rule:/d' >/tmp/backprovider.yaml 2>/dev/null + sed -i '/^Rule:/i\proxy-provider-tag' "$3" 2>/dev/null + sed -i '/^proxy-provider:/,/proxy-provider-tag/d' "$3" 2>/dev/null + sed -i '/^Rule:/i\proxy-provider:' "$3" 2>/dev/null + sed -i '/^proxy-provider:/d' /tmp/backprovider.yaml 2>/dev/null + sed -i '/proxy-provider:/r/tmp/bakprovider.yaml' "$3" 2>/dev/null + rm -rf /tmp/backprovider.yaml 2>/dev/null + fi + fi + + + if [ ! -z "$provider_len" ]; then + proxy_provider_mode=1 + fi + + #自定义DNS还原 + if [ "$(grep -c '##Custom DNS##' "$3")" -gt 0 ] && [ "$2" = 0 ] && [ -f "$4" ]; then + if [ "$proxy_provider_mode" -eq 1 ]; then + awk '/^ {0,}nameserver:/,/proxy-provider:/{print}' "$4" | sed '/^proxy-provider:/d' >/tmp/backdns.config 2>/dev/null + else + awk '/^ {0,}nameserver:/,/Proxy:/{print}' "$4" | sed '/^Proxy:/d' >/tmp/backdns.config 2>/dev/null + fi + sed -i '/OpenClash-General-Settings/i\Custom DNS End' "$3" 2>/dev/null + sed -i '/^ \{0,\}nameserver:/,/^Custom DNS End$/d' "$3" 2>/dev/null + sed -i '/##Custom DNS##/r/tmp/bakdns.config' "$3" 2>/dev/null + rm -rf /tmp/backdns.config 2>/dev/null + fi + + yml_dns_check + if [ ! -z "$(grep "^ \{1,\}port:" "$3")" ] || [ ! -z "$(grep "^ \{1,\}mode:" "$3")" ] || [ ! -z "$(grep "^ \{1,\}log-level:" "$3")" ]; then cp "$3" /tmp/config.check 2>/dev/null sed -i '/^dns:/,$d' /tmp/config.check 2>/dev/null @@ -163,22 +207,34 @@ yml_check() #添加标识 sed -i '/OpenClash-General/d' "$3" 2>/dev/null - sed -i '/^Proxy:/i\#===================== OpenClash-General-Settings =====================#' "$3" 2>/dev/null + if [ "$proxy_provider_mode" -eq 1 ]; then + sed -i '/^proxy-provider:/i\#===================== OpenClash-General-Settings =====================#' "$3" 2>/dev/null + else + sed -i '/^Proxy:/i\#===================== OpenClash-General-Settings =====================#' "$3" 2>/dev/null + fi } #检查DNS设置 yml_dns_check() { - [ -z "$(grep '^dns:' $CONFIG_FILE)" ] && { - sed -i '/^Proxy:/i\dns:' "$CONFIG_FILE" 2>/dev/null - } + if [ -z "$(grep '^dns:' $CONFIG_FILE)" ]; then + if [ "$proxy_provider_mode" -eq 1 ]; then + sed -i '/^proxy-provider:/i\dns:' "$CONFIG_FILE" 2>/dev/null + else + sed -i '/^Proxy:/i\dns:' "$CONFIG_FILE" 2>/dev/null + fi + fi - [ -z "$(grep '^ \{0,\}nameserver:' $CONFIG_FILE)" ] && { - sed -i '/^Proxy:/i\ nameserver:' "$CONFIG_FILE" 2>/dev/null + if [ -z "$(grep '^ \{0,\}nameserver:' $CONFIG_FILE)" ]; then + if [ "$proxy_provider_mode" -eq 1 ]; then + sed -i '/^proxy-provider:/i\ nameserver:' "$CONFIG_FILE" 2>/dev/null + else + sed -i '/^Proxy:/i\ nameserver:' "$CONFIG_FILE" 2>/dev/null + fi sed -i "/^ \{0,\}nameserver:/a\ - tls://8.8.8.8:853" "$CONFIG_FILE" 2>/dev/null sed -i "/^ \{0,\}nameserver:/a\ - 114.114.114.114" "$CONFIG_FILE" 2>/dev/null - } + fi dns_port=$(grep "^ \{0,\}listen:" $CONFIG_FILE |awk -F ':' '{print $3}' |awk -F '#' '{print $1}' |tr -cd "[0-9]" 2>/dev/null) if [ -z "$dns_port" ] || [ "$dns_port" -eq 53 ]; then @@ -201,11 +257,18 @@ yml_cut() cp "$4" "$1" sed -i '/^Rule:/,$d' "$1" sed -n '/^ \{0,\}nameserver:/,$p' "$1" >"$3" - sed -n '/^ \{0,\}Proxy:/,$p' "$3" >"$5" + if [ "$proxy_provider_mode" -eq 1 ]; then + sed -n '/^ \{0,\}proxy-provider:/,$p' "$3" >"$5" + else + sed -n '/^ \{0,\}Proxy:/,$p' "$3" >"$5" + fi sed -i '/^ \{0,\}nameserver:/,$d' "$1" - sed -i '/^ \{0,\}Proxy:/,$d' "$3" + if [ "$proxy_provider_mode" -eq 1 ]; then + sed -i '/^ \{0,\}proxy-provider:/,$d' "$3" + else + sed -i '/^ \{0,\}Proxy:/,$d' "$3" + fi sed -n '/^Rule:/,$p' "$4" >"$2" - } #获取自定义DNS设置 @@ -311,13 +374,15 @@ yml_dns_custom() if [ -z "$(grep "^ - " $2)" ]; then sed -i '/^ \{0,\}ipcidr:/,/OpenClash-General-Settings/ {s/^ \{0,\}- / - /}' "$2" 2>/dev/null #修改参数空格 fi - else #删除fallback-filter +#删除fallback-filter + else if [ ! -z "$(grep '^ \{0,\}fallback-filter:' $2)" ]; then sed -i '/fallback-filter:/,$d' "$2" 2>/dev/null echo "#===================== OpenClash-General-Settings =====================#" >>"$2" fi if [ ! -z "$(grep "^ \{0,1\}- " $2)" ]; then - sed -i "s/^ \{0,\}- / - /" "$2" 2>/dev/null #添加参数空格 + #添加参数空格 + sed -i "s/^ \{0,\}- / - /" "$2" 2>/dev/null fi fi @@ -362,11 +427,27 @@ start() status=$(ps|grep -c /etc/init.d/openclash) [ "$status" -gt "3" ] && exit 0 -if [ ! -f "$CONFIG_FILE" ] && [ "$(ls -l /etc/openclash/config.yml 2>/dev/null |awk '{print int($5/1024)}')" -gt 0 ]; then - mv "/etc/openclash/config.yml" "$CONFIG_FILE" -fi -if [ ! -f "$CONFIG_FILE" ] && [ -f "$BACKUP_FILE" ]; then +if [ -z "$CONFIG_FILE" ]; then + CONFIG_NAME=$(ls -lt /etc/openclash/config/ | grep -E '.yaml|.yml' | head -n 1 |awk '{print $9}') + if [ ! -z "$CONFIG_NAME" ]; then + uci set openclash.config.config_path="/etc/openclash/config/$CONFIG_NAME" + uci commit openclash + CONFIG_FILE="/etc/openclash/config/$CONFIG_NAME" + elif [ -f "/etc/openclash/config.yaml" ]; then + mv "/etc/openclash/config.yaml" "/etc/openclash/config/config.yaml" + uci set openclash.config.config_path="/etc/openclash/config/config.yaml" + uci commit openclash + CONFIG_FILE="/etc/openclash/config/config.yaml" + elif [ -f "/etc/openclash/config.yml" ]; then + mv "/etc/openclash/config.yml" "/etc/openclash/config/config.yaml" + uci set openclash.config.config_path="/etc/openclash/config/config.yaml" + uci commit openclash + CONFIG_FILE="/etc/openclash/config/config.yaml" + fi +else + if [ ! -f "$CONFIG_FILE" ] && [ -f "$BACKUP_FILE" ]; then cp $BACKUP_FILE $CONFIG_FILE + fi fi enable=$(uci get openclash.config.enable 2>/dev/null) @@ -381,6 +462,18 @@ if [ "$enable" -eq 1 ] && [ -f "$CONFIG_FILE" ]; then echo "OpenClash 开始启动..." >$START_LOG echo "第一步: 获取配置..." >$START_LOG en_mode=$(uci get openclash.config.en_mode 2>/dev/null) + if [ "$en_mode" = "fake-ip-tun" ]; then + en_mode_tun="1" + en_mode="fake-ip" + fi + if [ "$en_mode" = "redir-host-vpn" ]; then + en_mode_tun="2" + en_mode="redir-host" + fi + if [ "$en_mode" = "fake-ip-vpn" ]; then + en_mode_tun="2" + en_mode="fake-ip" + fi enable_custom_dns=$(uci get openclash.config.enable_custom_dns 2>/dev/null) rule_source=$(uci get openclash.config.rule_source 2>/dev/null) enable_custom_clash_rules=$(uci get openclash.config.enable_custom_clash_rules 2>/dev/null) @@ -392,6 +485,7 @@ if [ "$enable" -eq 1 ] && [ -f "$CONFIG_FILE" ]; then socks_port=$(uci get openclash.config.socks_port 2>/dev/null) enable_redirect_dns=$(uci get openclash.config.enable_redirect_dns 2>/dev/null) lan_ip=$(uci get network.lan.ipaddr 2>/dev/null |awk -F '/' '{print $1}' 2>/dev/null) + lan_ip6=$(uci get network.lan.ip6addr 2>/dev/null) direct_dns=$(uci get openclash.config.direct_dns 2>/dev/null) disable_masq_cache=$(uci get openclash.config.disable_masq_cache 2>/dev/null) log_level=$(uci get openclash.config.log_level 2>/dev/null) @@ -399,126 +493,173 @@ if [ "$enable" -eq 1 ] && [ -f "$CONFIG_FILE" ]; then intranet_allowed=$(uci get openclash.config.intranet_allowed 2>/dev/null) echo "第二步: 配置文件检查..." >$START_LOG yml_check "$en_mode" "$enable_custom_dns" "$CONFIG_FILE" "$BACKUP_FILE" "$START_BACKUP" - grep "^ \{0,\}Proxy:" $CONFIG_FILE >/dev/null 2>&1 && grep "^ \{0,\}Proxy Group:" $CONFIG_FILE >/dev/null 2>&1 && grep "^ \{0,\}Rule:" $CONFIG_FILE >/dev/null 2>&1 - if [ "$?" -ne "0" ]; then - nohup $CLASH -d "$CLASH_CONFIG" >> $LOG_FILE 2>&1 & - echo "错误: 配置文件完整性检查不通过,已自动还原配置文件,请根据日志信息对照模板格式检查修改配置文件!" >$START_LOG - mv $START_BACKUP $CONFIG_FILE - stop - sleep 5 - echo "" >$START_LOG - else - echo "第三步: 修改配置文件..." >$START_LOG - config_load "openclash" - config_foreach yml_auth_get "authentication" - yml_auth_custom "$CONFIG_FILE" - yml_cut "$CHANGE_FILE" "$RULE_FILE" "$DNS_FILE" "$CONFIG_FILE" "$PROXY_FILE" - yml_dns_custom "$enable_custom_dns" "$DNS_FILE" - sh /usr/share/openclash/yml_change.sh "$LOGTIME" "$en_mode" "$enable_custom_dns" "$da_password" "$cn_port" "$proxy_port" "$CHANGE_FILE" "$ipv6_enable" "$http_port" "$socks_port" "$lan_ip" "$log_level" "$proxy_mode" "$intranet_allowed" & - sh /usr/share/openclash/yml_rules_change.sh "$LOGTIME" "$rule_source" "$enable_custom_clash_rules" "$RULE_FILE" & - wait - cat "$CHANGE_FILE" "$DNS_FILE" "$PROXY_FILE" "$RULE_FILE" >$CONFIG_FILE 2>/dev/null - rm -rf /tmp/yaml_* 2>/dev/null - echo "第四步: DNS设置检查..." >$START_LOG - if [ ! -z "$(sed -n '/^ \{0,\}nameserver:/{n;p}' $CONFIG_FILE |grep '^ \{0,\}fallback:')" ] || [ ! -z "$(sed -n '/^ \{0,\}nameserver:/{n;p}' $CONFIG_FILE |grep 'OpenClash-General')" ]; then - echo "错误: 配置文件DNS选项下的Nameserver必须设置服务器,已自动还原配置文件,请重新设置!" >$START_LOG - echo "${LOGTIME} Nameserver Must Be Set, Please Change Your Configrations In Config.yaml" >>$LOG_FILE + grep "^ \{0,\}Proxy Group:" $CONFIG_FILE >/dev/null 2>&1 && grep "^ \{0,\}Rule:" $CONFIG_FILE >/dev/null 2>&1 + if [ "$?" -eq "0" ]; then + grep "^ \{0,\}Proxy:" $CONFIG_FILE >/dev/null 2>&1 || grep "^ \{0,\}proxy-provider:" $CONFIG_FILE >/dev/null 2>&1 + if [ "$?" -ne "0" ]; then + nohup $CLASH -d "$CLASH_CONFIG" -f "$CONFIG_FILE" >> $LOG_FILE 2>&1 & + echo "错误: 配置文件完整性检查不通过,已自动还原配置文件,请根据日志信息对照模板格式检查修改配置文件!" >$START_LOG mv $START_BACKUP $CONFIG_FILE - sleep 10 + stop + sleep 5 echo "" >$START_LOG else - echo "第五步: 启动 Clash 主程序..." >$START_LOG - nohup $CLASH -d "$CLASH_CONFIG" >> $LOG_FILE 2>&1 & + echo "第三步: 修改配置文件..." >$START_LOG + config_load "openclash" + config_foreach yml_auth_get "authentication" + yml_auth_custom "$CONFIG_FILE" + yml_cut "$CHANGE_FILE" "$RULE_FILE" "$DNS_FILE" "$CONFIG_FILE" "$PROXY_FILE" + yml_dns_custom "$enable_custom_dns" "$DNS_FILE" + sh /usr/share/openclash/yml_change.sh "$LOGTIME" "$en_mode" "$enable_custom_dns" "$da_password" "$cn_port" "$proxy_port" "$CHANGE_FILE" "$ipv6_enable" "$http_port" "$socks_port" "$lan_ip" "$log_level" "$proxy_mode" "$intranet_allowed" "$en_mode_tun" & + sh /usr/share/openclash/yml_rules_change.sh "$LOGTIME" "$rule_source" "$enable_custom_clash_rules" "$RULE_FILE" & + wait + cat "$CHANGE_FILE" "$DNS_FILE" "$PROXY_FILE" "$RULE_FILE" >$CONFIG_FILE 2>/dev/null + rm -rf /tmp/yaml_* 2>/dev/null + echo "第四步: DNS设置检查..." >$START_LOG + if [ ! -z "$(sed -n '/^ \{0,\}nameserver:/{n;p}' $CONFIG_FILE |grep '^ \{0,\}fallback:')" ] || [ ! -z "$(sed -n '/^ \{0,\}nameserver:/{n;p}' $CONFIG_FILE |grep 'OpenClash-General')" ]; then + echo "错误: 配置文件DNS选项下的Nameserver必须设置服务器,已自动还原配置文件,请重新设置!" >$START_LOG + echo "${LOGTIME} Nameserver Must Be Set, Please Change Your Configrations In Config.yaml" >>$LOG_FILE + mv $START_BACKUP $CONFIG_FILE + sleep 10 + echo "" >$START_LOG + else + echo "第五步: 启动 Clash 主程序..." >$START_LOG + nohup $CLASH -d "$CLASH_CONFIG" -f "$CONFIG_FILE" >> $LOG_FILE 2>&1 & - echo "第六步: 设置控制面板..." >$START_LOG - ln -s /usr/share/openclash/yacd /www/openclash 2>/dev/null + echo "第六步: 设置控制面板..." >$START_LOG + ln -s /usr/share/openclash/yacd /www/openclash 2>/dev/null - echo "第七步: 设置 OpenClash 防火墙规则..." >$START_LOG - if [ -z "$(uci get firewall.openclash 2>/dev/null)" ] || [ -z "$(uci get ucitrack.@openclash[-1].init 2>/dev/null)" ]; then - uci delete ucitrack.@openclash[-1] >/dev/null 2>&1 - uci add ucitrack openclash >/dev/null 2>&1 - uci set ucitrack.@openclash[-1].init=openclash >/dev/null 2>&1 - uci commit ucitrack >/dev/null 2>&1 - uci delete firewall.openclash >/dev/null 2>&1 - uci set firewall.openclash=include >/dev/null 2>&1 - uci set firewall.openclash.type=script >/dev/null 2>&1 - uci set firewall.openclash.path=/var/etc/openclash.include >/dev/null 2>&1 - uci set firewall.openclash.reload=1 >/dev/null 2>&1 - uci commit firewall >/dev/null 2>&1 - fi - + echo "第七步: 设置 OpenClash 防火墙规则..." >$START_LOG + if [ -z "$(uci get firewall.openclash 2>/dev/null)" ] || [ -z "$(uci get ucitrack.@openclash[-1].init 2>/dev/null)" ]; then + uci delete ucitrack.@openclash[-1] >/dev/null 2>&1 + uci add ucitrack openclash >/dev/null 2>&1 + uci set ucitrack.@openclash[-1].init=openclash >/dev/null 2>&1 + uci commit ucitrack >/dev/null 2>&1 + uci delete firewall.openclash >/dev/null 2>&1 + uci set firewall.openclash=include >/dev/null 2>&1 + uci set firewall.openclash.type=script >/dev/null 2>&1 + uci set firewall.openclash.path=/var/etc/openclash.include >/dev/null 2>&1 + uci set firewall.openclash.reload=1 >/dev/null 2>&1 + fi + + if [ ! -z "$en_mode_tun" ] && [ "$(uci get firewall.@defaults[0].forward)" != "ACCEPT" ]; then + uci set firewall.@defaults[0].forward=ACCEPT >/dev/null 2>&1 + uci commit firewall >/dev/null 2>&1 + /etc/init.d/firewall reload >/dev/null 2>&1 + fi + mkdir -p /var/etc cat > "/var/etc/openclash.include" <<-EOF /etc/init.d/openclash restart EOF + if [ "$en_mode_tun" != "2" ]; then + iptables -t nat -N openclash - iptables -t nat -N openclash - iptables -t nat -A openclash -d 0.0.0.0/8 -j RETURN - iptables -t nat -A openclash -d 10.0.0.0/8 -j RETURN - iptables -t nat -A openclash -d 127.0.0.0/8 -j RETURN - iptables -t nat -A openclash -d 169.254.0.0/16 -j RETURN - iptables -t nat -A openclash -d 172.16.0.0/12 -j RETURN - iptables -t nat -A openclash -d 192.168.0.0/16 -j RETURN - iptables -t nat -A openclash -d 224.0.0.0/4 -j RETURN - iptables -t nat -A openclash -d 240.0.0.0/4 -j RETURN - iptables -t nat -A openclash -p tcp -j REDIRECT --to-ports "$proxy_port" - iptables -t nat -A PREROUTING -i br-lan -p tcp -j openclash - iptables -t nat -A OUTPUT -p tcp -d 198.18.0.0/16 -j REDIRECT --to-ports "$proxy_port" + if [ "$en_mode_tun" = "1" ]; then + iptables -t nat -A openclash -d 198.18.0.0/16 -j RETURN + fi - if [ "$ipv6_enable" -eq 1 ]; then - ip6tables -t nat -N openclash - ip6tables -t nat -A openclash -p tcp -j REDIRECT --to-ports "$proxy_port" - ip6tables -t nat -A PREROUTING -i br-lan -p tcp -j openclash - fi - - echo "第八步: 重启 Dnsmasq 程序..." >$START_LOG - if [ "$(iptables -t nat -nL PREROUTING --line-number |grep dpt:53 |wc -l)" -gt 2 ]; then - echo "发现53端口被劫持,如连接异常请将OpenClash设置为劫持53端口程序的上游DNS服务器!" >$START_LOG - echo "${LOGTIME} Warring: OpenClash May Can Not Take Over DNS, Please Use OpenClash for Upstream DNS Resolve Server" >> $LOG_FILE - sleep 5 - fi - change_dns "$enable_redirect_dns" "$disable_masq_cache" - fake_block "$en_mode" "$direct_dns" - /etc/init.d/dnsmasq restart >/dev/null 2>&1 - if pidof clash >/dev/null; then - echo "第九步: 添加 OpenClash 计划任务,启动进程守护程序..." >$START_LOG - add_cron - if [ -z "$(uci get dhcp.lan.dhcpv6 2>/dev/null)" ]; then - echo "OpenClash 启动成功,请等待服务器上线!" >$START_LOG - echo "${LOGTIME} OpenClash Start Successful" >> $LOG_FILE - sleep 5 + iptables -t nat -A openclash -d 0.0.0.0/8 -j RETURN + iptables -t nat -A openclash -d 10.0.0.0/8 -j RETURN + iptables -t nat -A openclash -d 127.0.0.0/8 -j RETURN + iptables -t nat -A openclash -d 169.254.0.0/16 -j RETURN + iptables -t nat -A openclash -d 172.16.0.0/12 -j RETURN + iptables -t nat -A openclash -d 192.168.0.0/16 -j RETURN + iptables -t nat -A openclash -d 224.0.0.0/4 -j RETURN + iptables -t nat -A openclash -d 240.0.0.0/4 -j RETURN + iptables -t nat -A openclash -p tcp -j REDIRECT --to-ports "$proxy_port" + iptables -t nat -A zone_lan_prerouting -p tcp -j openclash + + if [ "$en_mode_tun" != 1 ]; then + iptables -t nat -A OUTPUT -p tcp -d 198.18.0.0/16 -j REDIRECT --to-ports "$proxy_port" + fi + + if [ "$ipv6_enable" -eq 1 ]; then + ip6tables -t nat -N openclash + ip6tables -t nat -A openclash -p tcp -j REDIRECT --to-ports "$proxy_port" + ip6tables -t nat -A zone_lan_prerouting -p tcp -j openclash + fi else - echo "OpenClash 启动成功,检测到您启用了IPV6的DHCP服务,可能会造成连接异常!" >$START_LOG - echo "${LOGTIME} OpenClash Start Successful, Please Note That Network May Abnormal With IPV6's DHCP Server" >> $LOG_FILE - sleep 10 + #TUN2模式 + ipset create localnetwork hash:net + ipset add localnetwork 127.0.0.0/8 + ipset add localnetwork 10.0.0.0/8 + ipset add localnetwork 169.254.0.0/16 + ipset add localnetwork 192.168.0.0/16 + ipset add localnetwork 224.0.0.0/4 + ipset add localnetwork 240.0.0.0/4 + ipset add localnetwork 172.16.0.0/12 + #启动TUN + ip tuntap add user root mode tun clash0 + ip link set clash0 up + ip route replace default dev clash0 table "$PROXY_ROUTE_TABLE" + ip rule add fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE" + #设置防火墙 + iptables -t mangle -N openclash + iptables -t mangle -F openclash + iptables -t mangle -A openclash -d 198.18.0.0/16 -j MARK --set-mark "$PROXY_FWMARK" + iptables -t mangle -A openclash -m set --match-set localnetwork dst -j RETURN + iptables -t mangle -I OUTPUT -j openclash + iptables -t mangle -I PREROUTING -m set ! --match-set localnetwork dst -j MARK --set-mark "$PROXY_FWMARK" + #ipv6 + ip6tables -t mangle -I PREROUTING -j MARK --set-mark "$PROXY_FWMARK" fi - echo "" >$START_LOG - else - if [ "$rule_source" != 0 ] || [ "$enable_custom_clash_rules" != 0 ]; then - echo "错误: OpenClash 启动失败,尝试还原规则并重新启动 Clash 主程序..." >$START_LOG - echo "${LOGTIME} OpenClash Can Not Start, Try Use Backup Rules Start Again" >> $LOG_FILE - mv "$BACKUP_FILE" /etc/openclash/configrules.bak - sed -i -n '/^Rule:/,$p' /etc/openclash/configrules.bak - sed -i '/^Rule:/,$d' "$CONFIG_FILE" - cat /etc/openclash/configrules.bak >> "$CONFIG_FILE" - rm -rf /etc/openclash/configrules.bak - nohup $CLASH -d "$CLASH_CONFIG" >> $LOG_FILE 2>&1 & - sleep 3 - if pidof clash >/dev/null; then - add_cron - if [ -z "$(uci get dhcp.lan.dhcpv6 2>/dev/null)" ]; then - echo "OpenClash 使用备份规则启动成功,请更新、检查变动的规则后重试!" >$START_LOG - echo "${LOGTIME} OpenClash Start Successful With Backup Rules Config, Please Check Or Update Other Rules And Retry" >> $LOG_FILE - sleep 10 + + echo "第八步: 重启 Dnsmasq 程序..." >$START_LOG + if [ "$(iptables -t nat -nL PREROUTING --line-number |grep dpt:53 |wc -l)" -gt 2 ]; then + echo "发现53端口被劫持,如连接异常请将OpenClash设置为劫持53端口程序的上游DNS服务器!" >$START_LOG + echo "${LOGTIME} Warring: OpenClash May Can Not Take Over DNS, Please Use OpenClash for Upstream DNS Resolve Server" >> $LOG_FILE + sleep 5 + fi + change_dns "$enable_redirect_dns" "$disable_masq_cache" + fake_block "$en_mode" "$direct_dns" + /etc/init.d/dnsmasq restart >/dev/null 2>&1 + if pidof clash >/dev/null; then + echo "第九步: 添加 OpenClash 计划任务,启动进程守护程序..." >$START_LOG + add_cron + if [ -z "$(uci get dhcp.lan.dhcpv6 2>/dev/null)" ]; then + echo "OpenClash 启动成功,请等待服务器上线!" >$START_LOG + echo "${LOGTIME} OpenClash Start Successful" >> $LOG_FILE + sleep 5 + else + echo "OpenClash 启动成功,检测到您启用了IPV6的DHCP服务,可能会造成连接异常!" >$START_LOG + echo "${LOGTIME} OpenClash Start Successful, Please Note That Network May Abnormal With IPV6's DHCP Server" >> $LOG_FILE + sleep 10 + fi + echo "" >$START_LOG + else + if [ "$rule_source" != 0 ] || [ "$enable_custom_clash_rules" != 0 ]; then + echo "错误: OpenClash 启动失败,尝试还原规则并重新启动 Clash 主程序..." >$START_LOG + echo "${LOGTIME} OpenClash Can Not Start, Try Use Backup Rules Start Again" >> $LOG_FILE + mv "$BACKUP_FILE" /etc/openclash/configrules.bak + sed -i -n '/^Rule:/,$p' /etc/openclash/configrules.bak + sed -i '/^Rule:/,$d' "$CONFIG_FILE" + cat /etc/openclash/configrules.bak >> "$CONFIG_FILE" + rm -rf /etc/openclash/configrules.bak + nohup $CLASH -d "$CLASH_CONFIG" -f "$CONFIG_FILE" >> $LOG_FILE 2>&1 & + sleep 3 + if pidof clash >/dev/null; then + add_cron + if [ -z "$(uci get dhcp.lan.dhcpv6 2>/dev/null)" ]; then + echo "OpenClash 使用备份规则启动成功,请更新、检查变动的规则后重试!" >$START_LOG + echo "${LOGTIME} OpenClash Start Successful With Backup Rules Config, Please Check Or Update Other Rules And Retry" >> $LOG_FILE + sleep 10 + else + echo "OpenClash 使用备份规则启动成功,请更新、检查变动的规则后重试!" >$START_LOG + echo "${LOGTIME} OpenClash Start Successful With Backup Rules Config, Please Check Or Update Other Rules And Retry" >> $LOG_FILE + sleep 10 + echo "OpenClash 启动成功,检测到您启用了IPV6的DHCP服务,可能会造成连接异常!" >$START_LOG + echo "${LOGTIME} OpenClash Start Successful, Please Note That Network May Abnormal With IPV6's DHCP Server" >> $LOG_FILE + sleep 10 + fi + echo "" >$START_LOG else - echo "OpenClash 使用备份规则启动成功,请更新、检查变动的规则后重试!" >$START_LOG - echo "${LOGTIME} OpenClash Start Successful With Backup Rules Config, Please Check Or Update Other Rules And Retry" >> $LOG_FILE - sleep 10 - echo "OpenClash 启动成功,检测到您启用了IPV6的DHCP服务,可能会造成连接异常!" >$START_LOG - echo "${LOGTIME} OpenClash Start Successful, Please Note That Network May Abnormal With IPV6's DHCP Server" >> $LOG_FILE + echo "错误: OpenClash 启动失败,请到日志页面查看详细错误信息!" >$START_LOG + echo "${LOGTIME} OpenClash Can Not Start, Please Check The Error Info And Try Again" >> $LOG_FILE sleep 10 + echo "" >$START_LOG + stop && echo "" >$START_LOG fi - echo "" >$START_LOG else echo "错误: OpenClash 启动失败,请到日志页面查看详细错误信息!" >$START_LOG echo "${LOGTIME} OpenClash Can Not Start, Please Check The Error Info And Try Again" >> $LOG_FILE @@ -526,15 +667,16 @@ EOF echo "" >$START_LOG stop && echo "" >$START_LOG fi - else - echo "错误: OpenClash 启动失败,请到日志页面查看详细错误信息!" >$START_LOG - echo "${LOGTIME} OpenClash Can Not Start, Please Check The Error Info And Try Again" >> $LOG_FILE - sleep 10 - echo "" >$START_LOG - stop && echo "" >$START_LOG fi fi fi + else + nohup $CLASH -d "$CLASH_CONFIG" -f "$CONFIG_FILE" >> $LOG_FILE 2>&1 & + echo "错误: 配置文件完整性检查不通过,已自动还原配置文件,请根据日志信息对照模板格式检查修改配置文件!" >$START_LOG + mv $START_BACKUP $CONFIG_FILE + stop + sleep 5 + echo "" >$START_LOG fi rm -rf $START_BACKUP 2>/dev/null else @@ -564,9 +706,9 @@ stop() #ipv4 iptables -t nat -F openclash >/dev/null 2>&1 - nat_clashs=$(iptables -nvL PREROUTING -t nat |sed 1,2d |sed -n '/openclash/=' |sort -rn) + nat_clashs=$(iptables -nvL zone_lan_prerouting -t nat |sed 1,2d |sed -n '/openclash/=' |sort -rn) for nat_clash in $nat_clashs; do - iptables -t nat -D PREROUTING "$nat_clash" >/dev/null 2>&1 + iptables -t nat -D zone_lan_prerouting "$nat_clash" >/dev/null 2>&1 done iptables -t nat -X openclash >/dev/null 2>&1 @@ -579,12 +721,24 @@ stop() #ipv6 ip6tables -t nat -F openclash >/dev/null 2>&1 - nat6_clashs=$(ip6tables -nvL PREROUTING -t nat 2>/dev/null | sed 1,2d | sed -n '/openclash/=' |sort -r) + nat6_clashs=$(ip6tables -nvL zone_lan_prerouting -t nat 2>/dev/null | sed 1,2d | sed -n '/openclash/=' |sort -r) for nat6_clash in $nat6_clashs; do - ip6tables -t nat -D PREROUTING "$nat6_clash" >/dev/null 2>&1 + ip6tables -t nat -D zone_lan_prerouting "$nat6_clash" >/dev/null 2>&1 done ip6tables -t nat -X openclash >/dev/null 2>&1 +#TUN + ip link set dev clash0 down >/dev/null 2>&1 + ip tuntap del clash0 mode tun >/dev/null 2>&1 + ip route del default dev clash0 table "$PROXY_ROUTE_TABLE" >/dev/null 2>&1 + ip rule del fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE" >/dev/null 2>&1 + + iptables -t mangle -D OUTPUT -j openclash >/dev/null 2>&1 + iptables -t mangle -D PREROUTING -m set ! --match-set localnetwork dst -j MARK --set-mark "$PROXY_FWMARK" >/dev/null 2>&1 + ip6tables -t mangle -D PREROUTING -j MARK --set-mark "$PROXY_FWMARK" >/dev/null 2>&1 + iptables -t mangle -F openclash >/dev/null 2>&1 + iptables -t mangle -X openclash >/dev/null 2>&1 + ipset destroy localnetwork >/dev/null 2>&1 echo "第二步: 关闭 OpenClash 守护程序..." >$START_LOG watchdog_pids=$(ps |grep openclash_watchdog.sh |grep -v grep |awk '{print $1}' 2>/dev/null) diff --git a/package/ctcgfw/luci-app-openclash/files/etc/config/openclash_custom_fake_black.conf b/package/ctcgfw/luci-app-openclash/files/etc/openclash/custom/openclash_custom_fake_black.conf similarity index 99% rename from package/ctcgfw/luci-app-openclash/files/etc/config/openclash_custom_fake_black.conf rename to package/ctcgfw/luci-app-openclash/files/etc/openclash/custom/openclash_custom_fake_black.conf index 74bce50e2d..cbf2b90256 100644 --- a/package/ctcgfw/luci-app-openclash/files/etc/config/openclash_custom_fake_black.conf +++ b/package/ctcgfw/luci-app-openclash/files/etc/openclash/custom/openclash_custom_fake_black.conf @@ -86,4 +86,4 @@ music.migu.cn msftconnecttest.com msftncsi.com #QQ快捷登录 -qq.com \ No newline at end of file +ptlogin2.qq.com \ No newline at end of file diff --git a/package/ctcgfw/luci-app-openclash/files/etc/config/openclash_custom_hosts.list b/package/ctcgfw/luci-app-openclash/files/etc/openclash/custom/openclash_custom_hosts.list similarity index 100% rename from package/ctcgfw/luci-app-openclash/files/etc/config/openclash_custom_hosts.list rename to package/ctcgfw/luci-app-openclash/files/etc/openclash/custom/openclash_custom_hosts.list diff --git a/package/ctcgfw/luci-app-openclash/files/etc/config/openclash_custom_rules.list b/package/ctcgfw/luci-app-openclash/files/etc/openclash/custom/openclash_custom_rules.list similarity index 100% rename from package/ctcgfw/luci-app-openclash/files/etc/config/openclash_custom_rules.list rename to package/ctcgfw/luci-app-openclash/files/etc/openclash/custom/openclash_custom_rules.list diff --git a/package/ctcgfw/luci-app-openclash/files/etc/openclash/lhie1.yaml b/package/ctcgfw/luci-app-openclash/files/etc/openclash/lhie1.yaml index 0a60633c33..0d0a815a77 100644 --- a/package/ctcgfw/luci-app-openclash/files/etc/openclash/lhie1.yaml +++ b/package/ctcgfw/luci-app-openclash/files/etc/openclash/lhie1.yaml @@ -1,4 +1,5 @@ Rule: +##updated ##source:lhie1 # (GlobalTV) # > ABC @@ -37,6 +38,14 @@ Rule: # > CHOCO TV - DOMAIN-SUFFIX,chocotv.com.tw,GlobalTV +# > Disney Plus +- DOMAIN,cdn.registerdisney.go.com,GlobalTV +- DOMAIN-SUFFIX,disneyplus.com,GlobalTV +- DOMAIN-SUFFIX,disney-plus.net,GlobalTV +- DOMAIN-SUFFIX,dssott.com,GlobalTV +- DOMAIN-SUFFIX,bamgrid.com,GlobalTV +- DOMAIN-SUFFIX,execute-api.us-east-1.amazonaws.com,GlobalTV + # > Epicgames - DOMAIN-KEYWORD,epicgames,GlobalTV - DOMAIN-SUFFIX,helpshift.com,GlobalTV @@ -133,143 +142,32 @@ Rule: - DOMAIN-SUFFIX,api.mob.app.letv.com,AsianTV # > NeteaseMusic -- DOMAIN-SUFFIX,163yun.com,AsianTV -- DOMAIN-SUFFIX,music.126.net,AsianTV -- DOMAIN-SUFFIX,music.163.com,AsianTV +- DOMAIN-SUFFIX,music.126.net,Netease Music +- DOMAIN-SUFFIX,music.163.com,Netease Music +- IP-CIDR,39.105.63.80/32,Netease Music +- IP-CIDR,45.254.48.1/32,Netease Music +- IP-CIDR,47.100.127.239/32,Netease Music +- IP-CIDR,59.111.160.195/32,Netease Music +- IP-CIDR,59.111.160.197/32,Netease Music +- IP-CIDR,59.111.181.35/32,Netease Music +- IP-CIDR,59.111.181.38/32,Netease Music +- IP-CIDR,59.111.181.60/32,Netease Music +- IP-CIDR,101.71.154.241/32,Netease Music +- IP-CIDR,103.126.92.132/32,Netease Music +- IP-CIDR,103.126.92.133/32,Netease Music +- IP-CIDR,112.13.119.17/32,Netease Music +- IP-CIDR,112.13.122.1/32,Netease Music +- IP-CIDR,115.236.118.33/32,Netease Music +- IP-CIDR,115.236.121.1/32,Netease Music +- IP-CIDR,118.24.63.156/32,Netease Music +- IP-CIDR,193.112.159.225/32,Netease Music +- IP-CIDR,223.252.199.66/32,Netease Music +- IP-CIDR,223.252.199.67/32,Netease Music # > Tencent Video - DOMAIN-SUFFIX,vv.video.qq.com,AsianTV -# > 国内网站 -- DOMAIN-SUFFIX,cn,Domestic -- DOMAIN-KEYWORD,-cn,Domestic - -- DOMAIN-SUFFIX,126.com,Domestic -- DOMAIN-SUFFIX,126.net,Domestic -- DOMAIN-SUFFIX,127.net,Domestic -- DOMAIN-SUFFIX,163.com,Domestic -- DOMAIN-SUFFIX,360buyimg.com,Domestic -- DOMAIN-SUFFIX,36kr.com,Domestic -- DOMAIN-SUFFIX,acfun.tv,Domestic -- DOMAIN-SUFFIX,air-matters.com,Domestic -- DOMAIN-SUFFIX,aixifan.com,Domestic -- DOMAIN-SUFFIX,akamaized.net,Domestic -- DOMAIN-KEYWORD,alicdn,Domestic -- DOMAIN-KEYWORD,alipay,Domestic -- DOMAIN-KEYWORD,taobao,Domestic -- DOMAIN-SUFFIX,amap.com,Domestic -- DOMAIN-SUFFIX,autonavi.com,Domestic -- DOMAIN-KEYWORD,baidu,Domestic -- DOMAIN-SUFFIX,bdimg.com,Domestic -- DOMAIN-SUFFIX,bdstatic.com,Domestic -- DOMAIN-SUFFIX,bilibili.com,Domestic -- DOMAIN-SUFFIX,caiyunapp.com,Domestic -- DOMAIN-SUFFIX,clouddn.com,Domestic -- DOMAIN-SUFFIX,cnbeta.com,Domestic -- DOMAIN-SUFFIX,cnbetacdn.com,Domestic -- DOMAIN-SUFFIX,cootekservice.com,Domestic -- DOMAIN-SUFFIX,csdn.net,Domestic -- DOMAIN-SUFFIX,ctrip.com,Domestic -- DOMAIN-SUFFIX,dgtle.com,Domestic -- DOMAIN-SUFFIX,dianping.com,Domestic -- DOMAIN-SUFFIX,douban.com,Domestic -- DOMAIN-SUFFIX,doubanio.com,Domestic -- DOMAIN-SUFFIX,duokan.com,Domestic -- DOMAIN-SUFFIX,easou.com,Domestic -- DOMAIN-SUFFIX,ele.me,Domestic -- DOMAIN-SUFFIX,feng.com,Domestic -- DOMAIN-SUFFIX,fir.im,Domestic -- DOMAIN-SUFFIX,frdic.com,Domestic -- DOMAIN-SUFFIX,g-cores.com,Domestic -- DOMAIN-SUFFIX,godic.net,Domestic -- DOMAIN-SUFFIX,gtimg.com,Domestic -- DOMAIN,cdn.hockeyapp.net,Domestic -- DOMAIN-SUFFIX,hongxiu.com,Domestic -- DOMAIN-SUFFIX,hxcdn.net,Domestic -- DOMAIN-SUFFIX,iciba.com,Domestic -- DOMAIN-SUFFIX,ifeng.com,Domestic -- DOMAIN-SUFFIX,ifengimg.com,Domestic -- DOMAIN-SUFFIX,ipip.net,Domestic -- DOMAIN-SUFFIX,iqiyi.com,Domestic -- DOMAIN-SUFFIX,jd.com,Domestic -- DOMAIN-SUFFIX,jianshu.com,Domestic -- DOMAIN-SUFFIX,knewone.com,Domestic -- DOMAIN-SUFFIX,le.com,Domestic -- DOMAIN-SUFFIX,lecloud.com,Domestic -- DOMAIN-SUFFIX,lemicp.com,Domestic -- DOMAIN-SUFFIX,licdn.com,Domestic -- DOMAIN-SUFFIX,linkedin.com,Domestic -- DOMAIN-SUFFIX,luoo.net,Domestic -- DOMAIN-SUFFIX,meituan.com,Domestic -- DOMAIN-SUFFIX,meituan.net,Domestic -- DOMAIN-SUFFIX,mi.com,Domestic -- DOMAIN-SUFFIX,miaopai.com,Domestic -- DOMAIN-SUFFIX,microsoft.com,Domestic -- DOMAIN-SUFFIX,microsoftonline.com,Domestic -- DOMAIN-SUFFIX,miui.com,Domestic -- DOMAIN-SUFFIX,miwifi.com,Domestic -- DOMAIN-SUFFIX,mob.com,Domestic -- DOMAIN-SUFFIX,netease.com,Domestic -- DOMAIN-SUFFIX,office.com,Domestic -- DOMAIN-SUFFIX,office365.com,Domestic -- DOMAIN-KEYWORD,officecdn,Domestic -- DOMAIN-SUFFIX,oschina.net,Domestic -- DOMAIN-SUFFIX,ppsimg.com,Domestic -- DOMAIN-SUFFIX,pstatp.com,Domestic -- DOMAIN-SUFFIX,qcloud.com,Domestic -- DOMAIN-SUFFIX,qdaily.com,Domestic -- DOMAIN-SUFFIX,qdmm.com,Domestic -- DOMAIN-SUFFIX,qhimg.com,Domestic -- DOMAIN-SUFFIX,qhres.com,Domestic -- DOMAIN-SUFFIX,qidian.com,Domestic -- DOMAIN-SUFFIX,qihucdn.com,Domestic -- DOMAIN-SUFFIX,qiniu.com,Domestic -- DOMAIN-SUFFIX,qiniucdn.com,Domestic -- DOMAIN-SUFFIX,qiyipic.com,Domestic -- DOMAIN-SUFFIX,qq.com,Domestic -- DOMAIN-SUFFIX,qqurl.com,Domestic -- DOMAIN-SUFFIX,rarbg.to,Domestic -- DOMAIN-SUFFIX,ruguoapp.com,Domestic -- DOMAIN-SUFFIX,segmentfault.com,Domestic -- DOMAIN-SUFFIX,sinaapp.com,Domestic -- DOMAIN-SUFFIX,smzdm.com,Domestic -- DOMAIN-SUFFIX,snapdrop.net,Domestic -- DOMAIN-SUFFIX,sogou.com,Domestic -- DOMAIN-SUFFIX,sogoucdn.com,Domestic -- DOMAIN-SUFFIX,sohu.com,Domestic -- DOMAIN-SUFFIX,soku.com,Domestic -- DOMAIN-SUFFIX,sspai.com,Domestic -- DOMAIN-SUFFIX,suning.com,Domestic -- DOMAIN-SUFFIX,taobao.com,Domestic -- DOMAIN-SUFFIX,tencent.com,Domestic -- DOMAIN-SUFFIX,tenpay.com,Domestic -- DOMAIN-SUFFIX,tianyancha.com,Domestic -- DOMAIN-SUFFIX,tmall.com,Domestic -- DOMAIN-SUFFIX,tudou.com,Domestic -- DOMAIN-SUFFIX,umetrip.com,Domestic -- DOMAIN-SUFFIX,upaiyun.com,Domestic -- DOMAIN-SUFFIX,upyun.com,Domestic -- DOMAIN-SUFFIX,veryzhun.com,Domestic -- DOMAIN-SUFFIX,weather.com,Domestic -- DOMAIN-SUFFIX,weibo.com,Domestic -- DOMAIN-SUFFIX,xiami.com,Domestic -- DOMAIN-SUFFIX,xiami.net,Domestic -- DOMAIN-SUFFIX,xiaomicp.com,Domestic -- DOMAIN-SUFFIX,ximalaya.com,Domestic -- DOMAIN-SUFFIX,xmcdn.com,Domestic -- DOMAIN-SUFFIX,xunlei.com,Domestic -- DOMAIN-SUFFIX,yhd.com,Domestic -- DOMAIN-SUFFIX,yihaodianimg.com,Domestic -- DOMAIN-SUFFIX,yinxiang.com,Domestic -- DOMAIN-SUFFIX,ykimg.com,Domestic -- DOMAIN-SUFFIX,youdao.com,Domestic -- DOMAIN-SUFFIX,youku.com,Domestic -- DOMAIN-SUFFIX,zealer.com,Domestic -- DOMAIN-SUFFIX,zhihu.com,Domestic -- DOMAIN-SUFFIX,zhimg.com,Domestic -- DOMAIN-SUFFIX,zimuzu.tv,Domestic - -# > 抗 DNS 污染 +# > 抗 DNS 污染 - DOMAIN-KEYWORD,amazon,Proxy - DOMAIN-KEYWORD,google,Proxy - DOMAIN-KEYWORD,gmail,Proxy @@ -285,9 +183,12 @@ Rule: - DOMAIN-SUFFIX,youtu.be,Proxy - DOMAIN-KEYWORD,whatsapp,Proxy +# > Fast +- DOMAIN-SUFFIX,fast.com,Speedtest + # > Speedtest -- DOMAIN-KEYWORD,speedtest,Proxy -- DOMAIN-SUFFIX,ooklaserver.net,Proxy +- DOMAIN-KEYWORD,speedtest,Speedtest +- DOMAIN-SUFFIX,ooklaserver.net,Speedtest # > 国外网站 - DOMAIN-SUFFIX,9to5mac.com,Proxy @@ -363,7 +264,6 @@ Rule: - DOMAIN-SUFFIX,eurekavpt.com,Proxy - DOMAIN-SUFFIX,evernote.com,Proxy - DOMAIN-SUFFIX,fabric.io,Proxy -- DOMAIN-SUFFIX,fast.com,Proxy - DOMAIN-SUFFIX,fastly.net,Proxy - DOMAIN-SUFFIX,fc2.com,Proxy - DOMAIN-SUFFIX,feedburner.com,Proxy @@ -498,7 +398,6 @@ Rule: - DOMAIN-SUFFIX,symcd.com,Proxy - DOMAIN-SUFFIX,tapbots.com,Proxy - DOMAIN-SUFFIX,tapbots.net,Proxy -- DOMAIN-SUFFIX,tdesktop.com,Proxy - DOMAIN-SUFFIX,techcrunch.com,Proxy - DOMAIN-SUFFIX,techsmith.com,Proxy - DOMAIN-SUFFIX,thepiratebay.org,Proxy @@ -559,15 +458,151 @@ Rule: - DOMAIN-SUFFIX,ytimg.com,Proxy # > Telegram -- DOMAIN-SUFFIX,telegra.ph,Proxy -- DOMAIN-SUFFIX,telegram.org,Proxy +- DOMAIN-SUFFIX,t.me,Telegram +- DOMAIN-SUFFIX,tdesktop.com,Telegram +- DOMAIN-SUFFIX,telegra.ph,Telegram +- DOMAIN-SUFFIX,telegram.me,Telegram +- DOMAIN-SUFFIX,telegram.org,Telegram -- IP-CIDR,91.108.56.0/22,Proxy -- IP-CIDR,91.108.4.0/22,Proxy -- IP-CIDR,91.108.8.0/22,Proxy -- IP-CIDR,109.239.140.0/24,Proxy -- IP-CIDR,149.154.160.0/20,Proxy -- IP-CIDR,149.154.164.0/22,Proxy +# > 国内网站 +- DOMAIN-KEYWORD,-cn,Domestic + +- DOMAIN-SUFFIX,126.com,Domestic +- DOMAIN-SUFFIX,126.net,Domestic +- DOMAIN-SUFFIX,127.net,Domestic +- DOMAIN-SUFFIX,163.com,Domestic +- DOMAIN-SUFFIX,360buyimg.com,Domestic +- DOMAIN-SUFFIX,36kr.com,Domestic +- DOMAIN-SUFFIX,acfun.tv,Domestic +- DOMAIN-SUFFIX,air-matters.com,Domestic +- DOMAIN-SUFFIX,aixifan.com,Domestic +- DOMAIN-SUFFIX,akamaized.net,Domestic +- DOMAIN-KEYWORD,alicdn,Domestic +- DOMAIN-KEYWORD,alipay,Domestic +- DOMAIN-KEYWORD,taobao,Domestic +- DOMAIN-SUFFIX,amap.com,Domestic +- DOMAIN-SUFFIX,autonavi.com,Domestic +- DOMAIN-KEYWORD,baidu,Domestic +- DOMAIN-SUFFIX,bdimg.com,Domestic +- DOMAIN-SUFFIX,bdstatic.com,Domestic +- DOMAIN-SUFFIX,bilibili.com,Domestic +- DOMAIN-SUFFIX,caiyunapp.com,Domestic +- DOMAIN-SUFFIX,clouddn.com,Domestic +- DOMAIN-SUFFIX,cnbeta.com,Domestic +- DOMAIN-SUFFIX,cnbetacdn.com,Domestic +- DOMAIN-SUFFIX,cootekservice.com,Domestic +- DOMAIN-SUFFIX,csdn.net,Domestic +- DOMAIN-SUFFIX,ctrip.com,Domestic +- DOMAIN-SUFFIX,dgtle.com,Domestic +- DOMAIN-SUFFIX,dianping.com,Domestic +- DOMAIN-SUFFIX,douban.com,Domestic +- DOMAIN-SUFFIX,doubanio.com,Domestic +- DOMAIN-SUFFIX,duokan.com,Domestic +- DOMAIN-SUFFIX,easou.com,Domestic +- DOMAIN-SUFFIX,ele.me,Domestic +- DOMAIN-SUFFIX,feng.com,Domestic +- DOMAIN-SUFFIX,fir.im,Domestic +- DOMAIN-SUFFIX,frdic.com,Domestic +- DOMAIN-SUFFIX,g-cores.com,Domestic +- DOMAIN-SUFFIX,godic.net,Domestic +- DOMAIN-SUFFIX,gtimg.com,Domestic +- DOMAIN,cdn.hockeyapp.net,Domestic +- DOMAIN-SUFFIX,hongxiu.com,Domestic +- DOMAIN-SUFFIX,hxcdn.net,Domestic +- DOMAIN-SUFFIX,iciba.com,Domestic +- DOMAIN-SUFFIX,ifeng.com,Domestic +- DOMAIN-SUFFIX,ifengimg.com,Domestic +- DOMAIN-SUFFIX,ipip.net,Domestic +- DOMAIN-SUFFIX,iqiyi.com,Domestic +- DOMAIN-SUFFIX,jd.com,Domestic +- DOMAIN-SUFFIX,jianshu.com,Domestic +- DOMAIN-SUFFIX,knewone.com,Domestic +- DOMAIN-SUFFIX,le.com,Domestic +- DOMAIN-SUFFIX,lecloud.com,Domestic +- DOMAIN-SUFFIX,lemicp.com,Domestic +- DOMAIN-SUFFIX,licdn.com,Domestic +- DOMAIN-SUFFIX,linkedin.com,Domestic +- DOMAIN-SUFFIX,luoo.net,Domestic +- DOMAIN-SUFFIX,meituan.com,Domestic +- DOMAIN-SUFFIX,meituan.net,Domestic +- DOMAIN-SUFFIX,mi.com,Domestic +- DOMAIN-SUFFIX,miaopai.com,Domestic +- DOMAIN-SUFFIX,microsoft.com,Domestic +- DOMAIN-SUFFIX,microsoftonline.com,Domestic +- DOMAIN-SUFFIX,miui.com,Domestic +- DOMAIN-SUFFIX,miwifi.com,Domestic +- DOMAIN-SUFFIX,mob.com,Domestic +- DOMAIN-SUFFIX,netease.com,Domestic +- DOMAIN-SUFFIX,office.com,Domestic +- DOMAIN-SUFFIX,office365.com,Domestic +- DOMAIN-KEYWORD,officecdn,Domestic +- DOMAIN-SUFFIX,oschina.net,Domestic +- DOMAIN-SUFFIX,ppsimg.com,Domestic +- DOMAIN-SUFFIX,pstatp.com,Domestic +- DOMAIN-SUFFIX,qcloud.com,Domestic +- DOMAIN-SUFFIX,qdaily.com,Domestic +- DOMAIN-SUFFIX,qdmm.com,Domestic +- DOMAIN-SUFFIX,qhimg.com,Domestic +- DOMAIN-SUFFIX,qhres.com,Domestic +- DOMAIN-SUFFIX,qidian.com,Domestic +- DOMAIN-SUFFIX,qihucdn.com,Domestic +- DOMAIN-SUFFIX,qiniu.com,Domestic +- DOMAIN-SUFFIX,qiniucdn.com,Domestic +- DOMAIN-SUFFIX,qiyipic.com,Domestic +- DOMAIN-SUFFIX,qq.com,Domestic +- DOMAIN-SUFFIX,qqurl.com,Domestic +- DOMAIN-SUFFIX,rarbg.to,Domestic +- DOMAIN-SUFFIX,ruguoapp.com,Domestic +- DOMAIN-SUFFIX,segmentfault.com,Domestic +- DOMAIN-SUFFIX,sinaapp.com,Domestic +- DOMAIN-SUFFIX,smzdm.com,Domestic +- DOMAIN-SUFFIX,snapdrop.net,Domestic +- DOMAIN-SUFFIX,sogou.com,Domestic +- DOMAIN-SUFFIX,sogoucdn.com,Domestic +- DOMAIN-SUFFIX,sohu.com,Domestic +- DOMAIN-SUFFIX,soku.com,Domestic +- DOMAIN-SUFFIX,sspai.com,Domestic +- DOMAIN-SUFFIX,suning.com,Domestic +- DOMAIN-SUFFIX,taobao.com,Domestic +- DOMAIN-SUFFIX,tencent.com,Domestic +- DOMAIN-SUFFIX,tenpay.com,Domestic +- DOMAIN-SUFFIX,tianyancha.com,Domestic +- DOMAIN-SUFFIX,tmall.com,Domestic +- DOMAIN-SUFFIX,tudou.com,Domestic +- DOMAIN-SUFFIX,umetrip.com,Domestic +- DOMAIN-SUFFIX,upaiyun.com,Domestic +- DOMAIN-SUFFIX,upyun.com,Domestic +- DOMAIN-SUFFIX,veryzhun.com,Domestic +- DOMAIN-SUFFIX,weather.com,Domestic +- DOMAIN-SUFFIX,weibo.com,Domestic +- DOMAIN-SUFFIX,xiami.com,Domestic +- DOMAIN-SUFFIX,xiami.net,Domestic +- DOMAIN-SUFFIX,xiaomicp.com,Domestic +- DOMAIN-SUFFIX,ximalaya.com,Domestic +- DOMAIN-SUFFIX,xmcdn.com,Domestic +- DOMAIN-SUFFIX,xunlei.com,Domestic +- DOMAIN-SUFFIX,yhd.com,Domestic +- DOMAIN-SUFFIX,yihaodianimg.com,Domestic +- DOMAIN-SUFFIX,yinxiang.com,Domestic +- DOMAIN-SUFFIX,ykimg.com,Domestic +- DOMAIN-SUFFIX,youdao.com,Domestic +- DOMAIN-SUFFIX,youku.com,Domestic +- DOMAIN-SUFFIX,zealer.com,Domestic +- DOMAIN-SUFFIX,zhihu.com,Domestic +- DOMAIN-SUFFIX,zhimg.com,Domestic +- DOMAIN-SUFFIX,zimuzu.tv,Domestic + +- DOMAIN,dler.cloud,DIRECT + +# > Telegram +- IP-CIDR,91.108.4.0/22,Telegram +- IP-CIDR,91.108.56.0/22,Telegram +- IP-CIDR,91.108.56.0/23,Telegram +- IP-CIDR,109.239.140.0/24,Telegram +- IP-CIDR,149.154.160.0/22,Telegram +- IP-CIDR,149.154.164.0/22,Telegram +- IP-CIDR,149.154.168.0/22,Telegram +- IP-CIDR,149.154.172.0/22,Telegram # > LAN - DOMAIN-SUFFIX,local,DIRECT @@ -580,4 +615,4 @@ Rule: # > 最终规则 - GEOIP,CN,Domestic -- MATCH,Others \ No newline at end of file +- MATCH,Others diff --git a/package/ctcgfw/luci-app-openclash/files/etc/openclash/openclash_version b/package/ctcgfw/luci-app-openclash/files/etc/openclash/openclash_version index 4fa7832308..0e02d52f99 100644 --- a/package/ctcgfw/luci-app-openclash/files/etc/openclash/openclash_version +++ b/package/ctcgfw/luci-app-openclash/files/etc/openclash/openclash_version @@ -1,2 +1,2 @@ -v0.35.6-beta -data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXMAAAAqCAMAAAB2kksrAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkMzQjgzNjk2MUE0NTExRUFBOUIyRDJCQzREODk1NEM2IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkMzQjgzNjk3MUE0NTExRUFBOUIyRDJCQzREODk1NEM2Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QzNCODM2OTQxQTQ1MTFFQUE5QjJEMkJDNEQ4OTU0QzYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QzNCODM2OTUxQTQ1MTFFQUE5QjJEMkJDNEQ4OTU0QzYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6j8b7rAAADAFBMVEUKa6FtbW09PT0ahMEHVYNFmso5lcqEhIS6urokhbyizOVFgKKStcnR4uwGZp0HX5EkapKTvdUGcKwJeLhXpdKCgoKenp7a2tqlpaVTiqp4eHgEXI7f398JdbTn5+djlbE/Pz/GxsbR5vKhoaHOzs5paWllnLsHXI2RkZF3t9txcXG/v78GbalGRkaDs86srKy1tbWTxeEEYJUne6sEU4Cy1urKysoJca0Xgb5iYmJkq9M0dJoJZJllrNUFWYlLS0tDQ0Pg4OCLi4t2dnYYdKjAwMCcnJyiwNEGVYIGdLLCwsJBQUGx0OJGirEFZZorjsZCQkImcJq2trajxdkYYo0micGXl5dFkb3S0tLB1eDQ0NBHnM1lZWWNjY0FcrA2kcZzobvB2eaxzdwFaaIIbKUJWIWDrcU0fKWvr69yn7hgYGDw9feiyd9Jns6UlJRKn88EYph0dHSyytijzuaJweIwk8tDfqB0sdVFiK01h7VortcGdbQIYZURgsMCc7QBcrNRUVFUVFRVVVVSUlILfL1WVlYFdrcHeLkOf8AEdrYDdbUKe7wDdLQOgMADdLUEdbYSg8MSg8RXV1cShMQAcrIThMUGd7gOf78Nfr8Nfr4Mfb4Mfb0IebkJeroJe7sJers6OjqqqqrU1NTV1dVISEirq6upqak7Ozt+fn6AgIDw9/tJSUnC3u6EvN2BgYHq6urz8/NMTEz19fWHv9+ErML09PTh7/eEvd2Fvt5/f3+DvNyGvt/v9PeCqcDA1N/B3e0HVoShv9Dg6u/B3e6Gv9+JiYng6vAIaJ/B3u7o6OgUXokEVYPW1tbD3++Fvd4FZ6ALdK+DqsGDq8LR3+iEtNBGm8tVos/C3+8Fa6YLerl0s9hInc3g7vbg7/eButrv9vqSuc8VaJcHWIcLb6gKeLc1jsIJcKyDr8hInc6Fvd0vkstIlcBUnMXP5PEIV4RTU1MPgMEQgcJZWVlaWlpfX19bW1tcXFxNTU1eXl5PT09QUFBYWFhdXV1OTk7///////899n7LAAABAHRSTlP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AU/cHJQAADE1JREFUeNrsVwtYVGUaPgzCeEGHCAccFEZEIWUEbbyg5gyUq4himphsPqappRXmpau1uyIg7bLthW2tLULu0oByEdAUFO+3ymtldrey2kvTum4gxst+/3/OGc4ZwHi2Hp9n5vF9Hv7/+77/+97vP+9/5pyD0NZWvXzoFXfAX10ATzxua2sT2mJXX3EP/MUl8ERom1A9vNlN8GfXwBM2Ybm7SN78JxfB48LwH9wFv3IR/F344abmNxpupPm/XQXC926Dm5rfeHznKhD+6za4qfmNxwuuAuE/boPnXQWdaq6NioqK8Pj5VVmg1S5wOB5abXdqDFpDN9lf6goveN+2/TZv5+h3nv7bt29/TPIe2y7jMefEWeu2b7/NUx07iIMv/d8QLjsjZk41OGLnxVz+idBGaVV+NeY57CkY2x2KN/FmN5u93AU83+KX85bn88roLDGKOn/uHoSMg+pqbzEvRR096JymQMrBlJeviw6aGyqAgGK9PowaaX+q5lGIUroZIxDrOFog4ufV/G9d4D30HTZzWF+8p4qmAGdraqY+QnIydzcO14j4hyptHfDI1Jqasylqzt3Y3VW7665xCNfUiIlFpYeGwaKv7H/tJ4I0V/nDAQ/JnIfKtO5QmDSmbjZ7pXOsw+G5y5Ytm/sI1inDKVN9l7HwYaxl7m4kLxMhqLKAGh49pSYlXV/pCtdb4xA6qFQZo7GWWSwmjSZjaHe1NfRXHU///h6da14UjCGSWYg5rVKyoXPGGCfNPdRNYuQMGa92jjFYpPv81Oe6GoxRhk8tW6YThEkXa/Aec3disSDilDKrjiQPnyQIulNq0p3Y+eqrs1K8FaEU2eNrjqAqR4Sz5tXQG9dzK8PCjEJs4942FErjvFig+FoximOK6THEVovZC6C6mOcBMdwtjOGOCAf7ej0qJAHpjs8m4aaw9Yq4ayrya0MqeJhRFkMkjuOhKTFSpgcvnKPceroEf9RJVh3809NtePp9st/vA1u6AqcmfcGm9z/Fo2zeifL0jvDH4W+FTuKUvtO/jr8NRN9zLWRPvuj0dO86bth2OhUL2SpsA7LTZCc1NTubNJcWCvkYW8hoirOLMZaLsC2bnkbVPQN7VmMKyyOlUB0WQG9gcgorUFFIaOe/DBi4MQTBpuxsQzUqlgcOBuapyOcAPQMDB9JesqlRMcufBwwOXF6B2BieWV2NY2HBJLpi745rqsMHfPZm4nsCd3HvLsCzo3jeNtvsrjVfCx/B8+uvvTvR3AYsTqbdfiCyoO6WczOAdenp5XWoKyekp3+AunvOnbvHhlHX1ZyksKgCzpqjdkiVpiyGpMCxQ0bNhu+zoxFQRo//skrcyTU/doi8GO5p9NCzV0M7nSUA0dyIRX4LG+ewdQ9UK8nZb4CFN4Q5NKeQljUJ4CdLmfOHk6vldTKyJFw6g7XcWIszl7LuA8ZxbxxwX5Yab79djuSJk17JJZPfkqGjZm1WJpTDZwy/VS+kO1XCNnJuY+Pcs/iIuaGY2kign1JWVuMZnGFOVlZSHzY3TqTDVhULGSoUI8yuChTidT6/jkI+HmtiapRR4mAjs1ro+yNVYyozaSIQTRmARaOxJmimozgjwzoEQ6yEdjq7FrVsvpMeOBkZcQjWGE1lVsqOU5C/DhBjWQK9UdiOiChjCqbTW8ZkZMfBMgM0GqPVqKmUdsfhuKbZ4kV62jA7i2n+BY9+0VFzpnLIxUHM/AZ9y8s/InW9nRI+6n3u3FlIp+jAN3iwsVGna/QFqIAeQY3h/Y4817gG/lm6r/CVjkCn3Bg+6ciRSReTnRp30Hx+xnU1D9OUpWak2ilRb6wiDVPjEJCQSkupHjwDYOEMenAXOwRToglM3gz6cZjYmG9iwRae7SAfCozw2EDkVgfFQEQw2tSESvF0wqwt5FaFKTXPlXFkDe6g6Q6sOZKbS5pLYdI8V42ZM0OSEerJzNu/Zbfk7LOweSoSgJHPsXiIWFsugqwdePDIrbm5t4YfZq1GwWcSSx93GjvY2g6xetC43Pt27BizuN6psbBBhXwMVAdIcz6T5nycb5cTA2XDAZYBiAmByOeL+Wq6DVXTMYKmCkSkMXIH8hXkV+l5joEj9EM3OCiAO8WlMClTZPultDuOzTIGhSCUpnIMGLR58zuAFAbe2axGjx49wh/AWmYe6RGu002eNhfwVyQAfSbTrRreI5lnSXsli3QVWyUzo7z9Ona0r5Flk6Lqxk6aG4CHr6t5oeNw8mWjIp8jUB/HxVGud9TcbkDtw8SDJk5eKNcaFORpGm0A36peobkhQ1I5X5FZqNT8NQeeq8f9uUnY/xzZvQApCvR6TY1x45bqBsDGzEE8MCg8GVsVCcC9fF56Gkdp6iOCrK1y2mJmHMXRrRwjT9+rWBsF3DPs6dm6ZLyjaiukqgGsVPkFeIPPb6DAMTKQFLIRsEVCAq9Xrjuy2mGtpA4jML2Mk5fItUVKcisFPCLos2S1TFGJQ+LSQOgVmfLuOHIcWOqD3jlbcTqS7Et0l/Eg3fCXcjpiJqDwbiXNFW4yJojGSBylcZoIsrbKaaF4KifnKHykpWn92tfGAyt4LFnagQwnzVvnoOKK7FwZyq7q0PU1p1/GD0UiLN3RvGoOxqbWQptGdiCCpdKiViV5qsXIziGYVYsUYYgWt0Q3/I9rnuML2/gPMZuZ8bJuF5Ac34nmE1Cv8Mbb8E+Fy86OYwxuodFPBFm7sIvHk4Bn2Ins7yfBr32NDnnaqqV+8XQkTprbVUiz05O0WbQP1Zba7T0xgtnNI1BAUykfGUpQIhqmYIyVYqvpj57ninW9WK0CfejoUXk3M+lrXS8Gm4cqyFdTX3tTkSaArXKiVPrcaRZpg4sUmQUobSfObEfiHozBmn7cXoEDfA7FCjZNOHDgfjZfSOJROpp9NCV5iYW9UX8Xmw8cGMWmhwCenET/WGUqQbpK+fuXZObQh9IFMT6eWJ9Cb257Abez+f4P4aUqdtLcbtXSV3L0odLSEvqXpJRdHkpKS6Nr0ZXmTXSjj6XE1fqKkg6ae4DOrXmlukMwahFt4edVQux0UsRfqiAn41CzfWgJKq/IRMYADCQmyjc0dUPzePrQwIB4bn+2H72TMpNIzc9EtUQF9mLMBS+vXTbU++YwfUJ3eXlNOECPg0i2CuzlZ7cPtgmZme+GYl+ik+a2Ue/y/D7jMjP7+QC76HC8RtmI+xnQOH5Cpt9+hE7werc38COaX83zCJZetpX6y6RQNLePrexKc7vGUCkV/KKD5kVitapDawRFHpaa6eX3/XAF+XCJsFJbJBOVmcS3Kv1nZO9K800KBNVjv040T06s55X1E08yl2nO5kelxvsnjibvS3kfDSeeZaukOZv8ju8Tw/vMkUp6xiKShkwmL97cINd/SQ3Fmk2RUuNb+oodHRDSnFCWl2fQh4WFlRjy8qrS0ix52ilh0+lK9XG0uICPDAa9QbIseSZtz/kFPVfaTeTp9cr11jzt9ILBQ9QdMvR6vVU0TXn2iMHzC6K0pioFeZVJGzW/YHCESXPV0ciYR03m6+15FuU24vQL2nmVl7VkQMOA86L57OTji2bsnbHouKjmvQ0Nvfip+K7Yt2fPvpDjJ+LJi/QN8dmzZ0+Dr5kfzKaGhmF8Hm1+0ufA3k+fNI9WqUYsfQZMTd7j4yuexSqz76IHKC/kOLl+5pAH9s74atOmyb7E+elD5hCxY9eap1Vp8kRsMbWQa+W2xpRnZGt8FNUyyQWWLVIBi+TlqdbFanUDE0WaFDZHk4K8SSLcUtZO1CLtqixNuQ0juy1kbFTihPmibPqdMDOc8ONeotkcyebRZhEXedhP8syJYo3ZHMTnZ6W00RvVSJTSTyxR+2Y/B7Wj8arJYkcHhPUdYTEZjUarRfKKEowJRetbTUVki6OYY3HktxTdbTTebWpltsmkXmfVJjV9q8lkUjgJ1KuoRUXeUmQ1UlmLqhFrIvZozyySAhyq6zqfeN5hx5/UBelOxotOZGLiQm4sPDk6KGj0Eim8cMmqoKAgaYk0TJTE3BhJ8VWRTpITS+R5Kl8l5xObjlyJbQn1Y4e3kNX6bVzioO1acxfFb10FQovb4I+uAjfS/HeuAuGq2+APrgI30vz3rgJhQau74Ncugl5C4E3NbzA+Fmrd5kb/jWug1ydC28CYJveASyj+Yq9/tQltbbWB7qH6iy6AXh9/0tb2PwEGAPfufw5HNwcIAAAAAElFTkSuQmCC \ No newline at end of file +v0.36.1-beta +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXMAAAAqCAMAAAB2kksrAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ4IDc5LjE2NDAzNiwgMjAxOS8wOC8xMy0wMTowNjo1NyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjIyNjhBQjYyMkZEMDExRUFCRjg0ODZBQzgyMTA0MzNFIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjIyNjhBQjYzMkZEMDExRUFCRjg0ODZBQzgyMTA0MzNFIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MjI2OEFCNjAyRkQwMTFFQUJGODQ4NkFDODIxMDQzM0UiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MjI2OEFCNjEyRkQwMTFFQUJGODQ4NkFDODIxMDQzM0UiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4K2+SHAAADAFBMVEUreqdFgKJtbW09PT2EvN2EhISGv9+6urokhbyiwNIGVYLB1eAJeLiCgoKenp7a2tqlpaVYpdLR4uxTiqp4eHgGXpHf39/n5+c6lco/Pz8GcKwKaqHGxsahoaEkapLOzs5jlLFpaWnR5vKizOVlnr2y1uoJdbSSu9KRkZFxcXEJca2/v79GRkasrKx3t9u1tbWTxeE0dJpFmspmrdbKysoGbaliYmIWaJcEXI4FYJQEU4BLS0tDQ0Pg4OAEWYmLi4tkq9Ox0OIIYpWyytmixdh2dnYIXY4XgL3AwMCcnJyCss4GdLLCwsJBQUEJZJhHi7OStciEtNBCQkImcZy2trZHnM2Xl5d0sdUnicGyzdwFaaJFkr7S0tLQ0NA2kcZJns5lZWWNjY2jyN4ahMEFcrArjscYYo1zobsGZJoHZ53w9fcHbKWDrcVyn7jR3+ikzuavr6+DqsEJWIVgYGAchsODq8IZdaqUlJTB2uhKns90dHQEYpiJweIwk8s1h7UFZZsRgsMCc7QBcrNRUVFUVFRVVVVSUlILfL1WVlYHeLkFdrcKe7wEdrYOgMADdbUOf8ADdLUEdbYDdLQSg8NXV1cSg8QShMQAcrIThMUOf78Gd7gNfr8Nfr4Mfb4Mfb0IebkJeroJe7sJers6OjqqqqrU1NTV1dVISEirq6upqak7OzuAgIB+fn7w9/sHVoRJSUnC3u6BgYHq6urz8/NMTEz19fX09PTh7/d/f3+DvNyCqcDv9PeFvt7B3e2ErMLg6u+JiYnA1N/B3e5Gm8vo6OgIaJ/B3u4UXokFZ5+hv9CEvd3g6vALerlHm8zW1tbD3+9Inc1Inc50s9jg7vYFZ6AFa6YGdbQIV4QZg8AHWIfC3++CutsEVYMKeLeDr8gLb6jv9voLdK9Vos+EtM9EmcoJcKwEV4fg7/cmbpVjo8hEf6FGiK5Uk7fP5PFkmbdTU1MPgMEQgcJZWVlaWlpfX19bW1tcXFxNTU1eXl5PT09QUFBYWFhdXV1OTk7///////+owvY1AAABAHRSTlP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AU/cHJQAADCBJREFUeNrsVwtUVNcVHRkHFeKAcRAGVFCDoj6CoqAyICqJWhFjojGKVl3xF5P6Ixp/MbQV+YSmX9o0pglB/oYB5BvBL/Gv+WlMYmJMjDW1sc201gbEsOm59314b0aU1WS51sxyr8W955x7zj737fvmvYeutbV6db/rroA/OwG2PG9tbdW1xmy67hr4k1NgS/9WXfXgJhfBH50DW6y61a4iedMfnATP6wZ/7yr4uZNgi+77e5rfbbiQ5v92Fui+cxnc0/zu41/OAt1/XQb3NL/7eMlZoPuPy+A3zoJbam4IDQ0NcfvxVVloMCxUHDeDoSM1JoOpg+yvtIeX/B50f9DPIbq2h7u7+0b7wBKH8iUb1zrE3OH+yv8N3TV7RM6sBkfMrMhrPxCGUIPGr8YsxZ6MkR2heBfvdrDZq+1g7Xv8ct5bq4kuEaOo7SGnreK+u0P9e9joEHO/RZ6Mje4bX70tHDQ3VQBeRUZjIPU3/FDNQxGqdtOHIkY5WiDkx9X8L+3gU9z/wCMP3I9PNdEFwMp9+7o9DSzgvp8ViNu3L26PXfWCYDlDjT3Y0167265x6G5qERmDSjc9g8VY2f3mDwRprvEHA26SOQuVqR2hEPRCB5u9dmuMwMmlSUlJS5/GCHV4QbcrSSx8EquYu8iKOJaWFKAp3sPv/QUOpKTra+3hdmscOgeVKiP10WUWi6DXp/frqLam7prj6d7d7daaF/qij2QWYGaLlGy6NWOkneZu2iaRcoaM12+NYCyb+MXFLybuQ7A6fDEpaWJAwPJz+/Apc1fBMylpWkDAtABN8YI9j+4jzR1ISdfXX1+ywE+dKnt8TQlqckTYa14No3krt9ItzCjALu7tQoE0zooBim4WoSiyiB5DbLWIvQCqi3geEMndgkjuiFDYtxpRIQlId3wWCTeZrVfE3tSQ3+xTwcOMsggicSwPTY6UMt144Uz11tMk9ECtZNWiR1qaFVdPkX2qE6xpKlyc9jmbTg3CkzQtAq5MOJXmgICPP/4Y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 \ No newline at end of file diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/controller/openclash.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/controller/openclash.lua index 8639e16168..0bca629564 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/controller/openclash.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/controller/openclash.lua @@ -19,15 +19,26 @@ function index() entry({"admin", "services", "openclash", "coreupdate"},call("action_coreupdate")) entry({"admin", "services", "openclash", "ping"}, call("act_ping")) entry({"admin", "services", "openclash", "settings"},cbi("openclash/settings"),_("Global Settings"), 30).leaf = true - entry({"admin", "services", "openclash", "servers"},cbi("openclash/servers"),_("Severs&Groups"), 40).leaf = true + entry({"admin", "services", "openclash", "servers"},cbi("openclash/servers"),_("Severs and Groups"), 40).leaf = true + entry({"admin", "services", "openclash", "config-subscribe"},cbi("openclash/config-subscribe"),_("Config Update"), 50).leaf = true entry({"admin", "services", "openclash", "servers-config"},cbi("openclash/servers-config"), nil).leaf = true entry({"admin", "services", "openclash", "groups-config"},cbi("openclash/groups-config"), nil).leaf = true - entry({"admin", "services", "openclash", "config"},form("openclash/config"),_("Server Config"), 50).leaf = true - entry({"admin", "services", "openclash", "log"},form("openclash/log"),_("Logs"), 60).leaf = true + entry({"admin", "services", "openclash", "proxy-provider-config"},cbi("openclash/proxy-provider-config"), nil).leaf = true + entry({"admin", "services", "openclash", "config"},form("openclash/config"),_("Server Config"), 60).leaf = true + entry({"admin", "services", "openclash", "log"},form("openclash/log"),_("Logs"), 70).leaf = true - end +local fs = require "luci.openclash" +CONFIG_FILE=string.sub(luci.sys.exec("uci get openclash.config.config_path"), 1, -2) +if CONFIG_FILE == "" or not fs.isfile(CONFIG_FILE) then + CONFIG_FILE_FIRST=luci.sys.exec("ls -lt '/etc/openclash/config/' | grep -E '.yaml|.yml' | head -n 1 |awk '{print $9}'") + if CONFIG_FILE_FIRST ~= "" then + CONFIG_FILE="/etc/openclash/config/" .. string.sub(CONFIG_FILE_FIRST, 1, -2) + else + CONFIG_FILE = "" + end +end local function is_running() return luci.sys.call("pidof clash >/dev/null") == 0 @@ -42,25 +53,23 @@ local function is_watchdog() end local function config_check() - local yaml = luci.sys.call("ls -l /etc/openclash/config.yaml >/dev/null 2>&1") - local yml = luci.sys.call("ls -l /etc/openclash/config.yml >/dev/null 2>&1") + local yaml = fs.isfile(CONFIG_FILE) local proxy,group,rule - if (yaml == 0) then - proxy = luci.sys.call("egrep '^ {0,}Proxy:' /etc/openclash/config.yaml >/dev/null 2>&1") - group = luci.sys.call("egrep '^ {0,}Proxy Group:' /etc/openclash/config.yaml >/dev/null 2>&1") - rule = luci.sys.call("egrep '^ {0,}Rule:' /etc/openclash/config.yaml >/dev/null 2>&1") - else - if (yml == 0) then - proxy = luci.sys.call("egrep '^ {0,}Proxy:' /etc/openclash/config.yml >/dev/null 2>&1") - group = luci.sys.call("egrep '^ {0,}Proxy Group:' /etc/openclash/config.yml >/dev/null 2>&1") - rule = luci.sys.call("egrep '^ {0,}Rule:' /etc/openclash/config.yml >/dev/null 2>&1") - end + if yaml then + proxy_provier = luci.sys.call(string.format('egrep "^ {0,}proxy-provider:" "%s" >/dev/null 2>&1',CONFIG_FILE)) + proxy = luci.sys.call(string.format('egrep "^ {0,}Proxy:" "%s" >/dev/null 2>&1',CONFIG_FILE)) + group = luci.sys.call(string.format('egrep "^ {0,}Proxy Group:" "%s" >/dev/null 2>&1',CONFIG_FILE)) + rule = luci.sys.call(string.format('egrep "^ {0,}Rule:" "%s" >/dev/null 2>&1',CONFIG_FILE)) end - if (yaml == 0) or (yml == 0) then + if yaml then if (proxy == 0) then proxy = "" else - proxy = " - 代理服务器" + if (proxy_provier == 0) then + proxy = "" + else + proxy = " - 代理服务器" + end end if (group == 0) then group = "" @@ -73,7 +82,7 @@ local function config_check() rule = " - 规则" end return proxy..group..rule - elseif (yaml ~= 0) and (yml ~= 0) then + elseif (yaml ~= 0) then return "1" end end @@ -87,38 +96,27 @@ local function mode() end local function config() - local config_update = luci.sys.exec("ls -l --full-time /etc/openclash/config.bak 2>/dev/null |awk '{print $6,$7;}'") - if (config_update ~= "") then - return config_update + if CONFIG_FILE ~= "" then + return string.sub(CONFIG_FILE, 23, -1) else - local yaml = luci.sys.call("ls -l /etc/openclash/config.yaml >/dev/null 2>&1") - if (yaml == 0) then - return "0" - else - local yml = luci.sys.call("ls -l /etc/openclash/config.yml >/dev/null 2>&1") - if (yml == 0) then - return "0" - else - return "1" - end - end + return "1" end end local function ipdb() - return luci.sys.exec("ls -l --full-time /etc/openclash/Country.mmdb 2>/dev/null |awk '{print $6,$7;}'") + return os.date("%Y-%m-%d %H:%M:%S",fs.mtime("/etc/openclash/Country.mmdb")) end local function lhie1() - return luci.sys.exec("ls -l --full-time /etc/openclash/lhie1.yaml 2>/dev/null |awk '{print $6,$7;}'") + return os.date("%Y-%m-%d %H:%M:%S",fs.mtime("/etc/openclash/lhie1.yaml")) end local function ConnersHua() - return luci.sys.exec("ls -l --full-time /etc/openclash/ConnersHua.yaml 2>/dev/null |awk '{print $6,$7;}'") + return os.date("%Y-%m-%d %H:%M:%S",fs.mtime("/etc/openclash/ConnersHua.yaml")) end local function ConnersHua_return() - return luci.sys.exec("ls -l --full-time /etc/openclash/ConnersHua_return.yaml 2>/dev/null |awk '{print $6,$7;}'") + return os.date("%Y-%m-%d %H:%M:%S",fs.mtime("/etc/openclash/ConnersHua_return.yaml")) end local function daip() @@ -187,10 +185,10 @@ local function corever() end local function upchecktime() - local corecheck = luci.sys.exec("ls -l --full-time /tmp/clash_last_version 2>/dev/null |awk '{print $6,$7;}'") + local corecheck = os.date("%Y-%m-%d %H:%M:%S",fs.mtime("/tmp/clash_last_version")) local opcheck if not corecheck or corecheck == "" then - opcheck = luci.sys.exec("ls -l --full-time /tmp/openclash_last_version 2>/dev/null |awk '{print $6,$7;}'") + opcheck = os.date("%Y-%m-%d %H:%M:%S",fs.mtime("/tmp/openclash_last_version")) if not opcheck or opcheck == "" then return "1" else diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/client.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/client.lua index a128244c04..f232e05877 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/client.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/client.lua @@ -4,35 +4,9 @@ local SYS = require "luci.sys" local HTTP = require "luci.http" local DISP = require "luci.dispatcher" local UTIL = require "luci.util" +local fs = require "luci.openclash" local uci = require("luci.model.uci").cursor() -local t = { - {enable, disable} -} - -a = SimpleForm("apply") -a.reset = false -a.submit = false -s = a:section(Table, t) - -o = s:option(Button, "enable") -o.inputtitle = translate("Enable Clash") -o.inputstyle = "apply" -o.write = function() - uci:set("openclash", "config", "enable", 1) - uci:commit("openclash") - SYS.call("/etc/init.d/openclash restart >/dev/null 2>&1 &") -end - -o = s:option(Button, "disable") -o.inputtitle = translate("Disable Clash") -o.inputstyle = "reset" -o.write = function() - uci:set("openclash", "config", "enable", 0) - uci:commit("openclash") - SYS.call("/etc/init.d/openclash stop >/dev/null 2>&1 &") -end - m = Map("openclash") m.title = translate("OpenClash") m.description = translate("A Clash Client For OpenWrt") @@ -40,11 +14,92 @@ m.pageaction = false m:section(SimpleSection).template = "openclash/status" m:section(SimpleSection).template = "openclash/state" -m:section(SimpleSection).template = "openclash/myip" + +local e,a={} +for t,o in ipairs(fs.glob("/etc/openclash/config/*"))do +a=fs.stat(o) +if a then +e[t]={} +e[t].name=fs.basename(o) +BACKUP_FILE="/etc/openclash/backup/".. e[t].name +e[t].mtime=os.date("%Y-%m-%d %H:%M:%S",fs.mtime(BACKUP_FILE)) or os.date("%Y-%m-%d %H:%M:%S",a.mtime) +if string.sub(luci.sys.exec("uci get openclash.config.config_path"), 23, -2) == e[t].name then + e[t].state=translate("Enable") +else + e[t].state=translate("Disable") +end +e[t].size=tostring(a.size) +e[t].remove=0 +e[t].enable=false +end +end + +form=SimpleForm("filelist") +form.reset=false +form.submit=false +tb=form:section(Table,e) +st=tb:option(DummyValue,"state",translate("State")) +nm=tb:option(DummyValue,"name",translate("File Name")) +mt=tb:option(DummyValue,"mtime",translate("Update Time")) + +function IsYamlFile(e) +e=e or"" +local e=string.lower(string.sub(e,-5,-1)) +return e==".yaml" +end + +btnis=tb:option(Button,"switch",translate("Switch Config")) +btnis.template="openclash/other_button" +btnis.render=function(o,t,a) +if not e[t]then return false end +if IsYamlFile(e[t].name)then +a.display="" +else +a.display="none" +end +o.inputstyle="apply" +Button.render(o,t,a) +end +btnis.write=function(a,t) +luci.sys.exec(string.format('uci set openclash.config.config_path="/etc/openclash/config/%s"',e[t].name)) +uci:commit("openclash") +HTTP.redirect(luci.dispatcher.build_url("admin", "services", "openclash", "client")) +end + +s = Map("openclash") +s.pageaction = false +s:section(SimpleSection).template = "openclash/myip" + +local t = { + {enable, disable} +} + +ap = SimpleForm("apply") +ap.reset = false +ap.submit = false +ss = ap:section(Table, t) + +o = ss:option(Button, "enable") +o.inputtitle = translate("Enable Clash") +o.inputstyle = "apply" +o.write = function() + uci:set("openclash", "config", "enable", 1) + uci:commit("openclash") + SYS.call("/etc/init.d/openclash restart >/dev/null 2>&1 &") +end + +o = ss:option(Button, "disable") +o.inputtitle = translate("Disable Clash") +o.inputstyle = "reset" +o.write = function() + uci:set("openclash", "config", "enable", 0) + uci:commit("openclash") + SYS.call("/etc/init.d/openclash stop >/dev/null 2>&1 &") +end d = Map("openclash") d.title = translate("Technical Support") d.pageaction = false d:section(SimpleSection).template = "openclash/developer" -return m, a, d +return m, form, s, ap, d diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/config-subscribe.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/config-subscribe.lua new file mode 100644 index 0000000000..f8b2636ba5 --- /dev/null +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/config-subscribe.lua @@ -0,0 +1,126 @@ + +local m, s, o +local openclash = "openclash" +local NXFS = require "nixio.fs" +local SYS = require "luci.sys" +local HTTP = require "luci.http" +local DISP = require "luci.dispatcher" +local UTIL = require "luci.util" +local uci = require "luci.model.uci".cursor() + +font_red = [[]] +font_off = [[]] +bold_on = [[]] +bold_off = [[]] + +m = Map(openclash, translate("Config Update")) +m.pageaction = false + +s = m:section(TypedSection, "openclash") +s.anonymous = true + +---- update Settings +o = s:option(ListValue, "auto_update", translate("Auto Update")) +o.description = translate("Auto Update Server subscription") +o:value("0", translate("Disable")) +o:value("1", translate("Enable")) +o.default=0 + +o = s:option(ListValue, "config_update_week_time", translate("Update Time (Every Week)")) +o:value("*", translate("Every Day")) +o:value("1", translate("Every Monday")) +o:value("2", translate("Every Tuesday")) +o:value("3", translate("Every Wednesday")) +o:value("4", translate("Every Thursday")) +o:value("5", translate("Every Friday")) +o:value("6", translate("Every Saturday")) +o:value("0", translate("Every Sunday")) +o.default=1 + +o = s:option(ListValue, "auto_update_time", translate("Update time (every day)")) +for t = 0,23 do +o:value(t, t..":00") +end +o.default=0 +o.rmempty = false + +o = s:option(Button, translate("Config File Update")) +o.title = translate("Update Subcription") +o.inputtitle = translate("Check And Update") +o.inputstyle = "reload" +o.write = function() + m.uci:set("openclash", "config", "enable", 1) + m.uci:commit("openclash") + local config_name=string.sub(luci.sys.exec("uci get openclash.config.config_path"), 23, -2) + SYS.call("rm -rf /etc/openclash/backup/* 2>/dev/null") + SYS.call("/usr/share/openclash/openclash.sh >/dev/null 2>&1 &") + HTTP.redirect(DISP.build_url("admin", "services", "openclash")) +end + +-- [[ Edit Server ]] -- +s = m:section(TypedSection, "config_subscribe") +s.anonymous = true +s.addremove = true +s.sortable = false +s.template = "cbi/tblsection" +s.rmempty = false + +---- enable flag +o = s:option(Flag, "enabled", translate("Enable"), font_red..bold_on..translate("(Enable or Disable Subscribe)")..bold_off..font_off) +o.rmempty = false +o.default = o.enabled +o.cfgvalue = function(...) + return Flag.cfgvalue(...) or "1" +end + +---- name +o = s:option(Value, "name", translate("Config Alias")) +o.description = font_red..bold_on..translate("(Name For Distinguishing)")..bold_off..font_off +o.placeholder = translate("config") +o.rmempty = true + +---- type +o = s:option(ListValue, "type", translate("Subscribe Type")) +o.description = font_red..bold_on..translate("(Power By fndroid)")..bold_off..font_off +o:value("clash", translate("Clash")) +o:value("v2rayn", translate("V2rayN")) +o:value("surge", translate("Surge")) +o.default="clash" +o.rempty = false + +---- address +o = s:option(Value, "address", translate("Subscribe Address")) +o.description = font_red..bold_on..translate("(Not Null)")..bold_off..font_off +o.placeholder = translate("Not Null") +o.datatype = "or(host, string)" +o.rmempty = true + +---- key +o = s:option(DynamicList, "keyword", font_red..bold_on..translate("Keyword Match")..bold_off..font_off) +o.description = font_red..bold_on..translate("(eg: hk or tw&bgp)")..bold_off..font_off +o.rmempty = true + +local t = { + {Commit, Apply} +} + +a = m:section(Table, t) + +o = a:option(Button, "Commit") +o.inputtitle = translate("Commit Configurations") +o.inputstyle = "apply" +o.write = function() + m.uci:commit("openclash") +end + +o = a:option(Button, "Apply") +o.inputtitle = translate("Apply Configurations") +o.inputstyle = "apply" +o.write = function() + m.uci:set("openclash", "config", "enable", 1) + m.uci:commit("openclash") + SYS.call("/etc/init.d/openclash restart >/dev/null 2>&1 &") + HTTP.redirect(DISP.build_url("admin", "services", "openclash")) +end + +return m diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/config.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/config.lua index ad475b0787..b7dcc457e5 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/config.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/config.lua @@ -8,7 +8,18 @@ local fs = require "luci.openclash" local uci = require("luci.model.uci").cursor() local CHIF = "0" -ful = SimpleForm("upload", translate("Server Configuration"), nil) +function IsYamlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-5,-1)) + return e == ".yaml" +end +function IsYmlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-4,-1)) + return e == ".yml" +end + +ful = SimpleForm("upload", translate("Server Config"), nil) ful.reset = false ful.submit = false @@ -20,13 +31,24 @@ um.template = "openclash/dvalue" local dir, fd, clash clash = "/etc/openclash/clash" -dir = "/etc/openclash/" +dir = "/etc/openclash/config/" +bakck_dir="/etc/openclash/backup" +proxy_pro_dir="/etc/openclash/proxy_provider/" +create_bakck_dir=fs.mkdir(bakck_dir) +create_proxy_pro_dir=fs.mkdir(proxy_pro_dir) + + HTTP.setfilehandler( function(meta, chunk, eof) + local fp = HTTP.formvalue("file_type") if not fd then if not meta then return end - - if meta and chunk then fd = nixio.open(dir .. meta.file, "w") end + + if fp == "config" then + if meta and chunk then fd = nixio.open(dir .. meta.file, "w") end + elseif fp == "proxy-provider" then + if meta and chunk then fd = nixio.open(proxy_pro_dir .. meta.file, "w") end + end if not fd then um.value = translate("upload file error.") @@ -39,17 +61,22 @@ HTTP.setfilehandler( if eof and fd then fd:close() fd = nil - if (meta.file == "config.yml") then - SYS.call("mv /etc/openclash/config.yml /etc/openclash/config.yaml") - SYS.call("cp /etc/openclash/config.yaml /etc/openclash/config.bak") - elseif (meta.file == "config.yaml") then - SYS.call("cp /etc/openclash/config.yaml /etc/openclash/config.bak") + if IsYamlFile(meta.file) and fp ~= "proxy-provider" then + local yamlbackup="/etc/openclash/backup/" .. meta.file + local c=fs.copy(dir .. meta.file,yamlbackup) + end + if IsYmlFile(meta.file) and fp ~= "proxy-provider" then + local ymlname=string.lower(string.sub(meta.file,0,-5)) + local ymlbackup="/etc/openclash/backup/".. ymlname .. ".yaml" + local c=fs.rename(dir .. meta.file,"/etc/openclash/config/".. ymlname .. ".yaml") + local c=fs.copy("/etc/openclash/config/".. ymlname .. ".yaml",ymlbackup) + end + if fp ~= "proxy-provider" then + um.value = translate("File saved to") .. ' "/etc/openclash/config/"' + CHIF = "1" + else + um.value = translate("File saved to") .. ' "/etc/openclash/proxy_provider/"' end - if (meta.file == "clash") then - NXFS.chmod(clash, 755) - end - um.value = translate("File saved to") .. ' "/etc/openclash"' - CHIF = "1" end end ) @@ -61,78 +88,72 @@ if HTTP.formvalue("upload") then end end -m = SimpleForm("openclash") -m.reset = false -m.submit = false - -local tab = { - {user, default} -} - -s = m:section(Table, tab) - -local conf = "/etc/openclash/config.yaml" -local yconf = "/etc/openclash/config.yml" -local dconf = "/etc/openclash/default.yaml" -local bconf = "/etc/openclash/config.bak" - -sev = s:option(Value, "user") -sev.template = "cbi/tvalue" -sev.description = translate("You Can Modify config file Here, Except The Settings That Were Taken Over") -sev.rows = 20 -sev.wrap = "off" -sev.cfgvalue = function(self, section) - return NXFS.readfile(conf) or NXFS.readfile(yconf) or NXFS.readfile(bconf) or NXFS.readfile(dconf) or "" +local e,a={} +for t,o in ipairs(fs.glob("/etc/openclash/config/*"))do +a=fs.stat(o) +if a then +e[t]={} +e[t].name=fs.basename(o) +BACKUP_FILE="/etc/openclash/backup/".. e[t].name +e[t].mtime=os.date("%Y-%m-%d %H:%M:%S",fs.mtime(BACKUP_FILE)) or os.date("%Y-%m-%d %H:%M:%S",a.mtime) +if string.sub(luci.sys.exec("uci get openclash.config.config_path"), 23, -2) == e[t].name then + e[t].state=translate("Enable") +else + e[t].state=translate("Disable") end -sev.write = function(self, section, value) -if (CHIF == "0") then - value = value:gsub("\r\n?", "\n") - NXFS.writefile("/etc/openclash/config.yaml", value) +e[t].size=tostring(a.size) +e[t].remove=0 +e[t].enable=false end end -def = s:option(Value, "default") -def.template = "cbi/tvalue" -def.description = translate("Default Config File With Correct General-Settings") -def.rows = 20 -def.wrap = "off" -def.readonly = true -def.cfgvalue = function(self, section) - return NXFS.readfile(dconf) or "" +function IsYamlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-5,-1)) + return e == ".yaml" end -def.write = function(self, section, value) +function IsYmlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-4,-1)) + return e == ".yml" +end + +form=SimpleForm("config_file_list",translate("Config File List")) +form.reset=false +form.submit=false +tb=form:section(Table,e) +st=tb:option(DummyValue,"state",translate("State")) +nm=tb:option(DummyValue,"name",translate("File Name")) +mt=tb:option(DummyValue,"mtime",translate("Update Time")) +sz=tb:option(DummyValue,"size",translate("Size")) + +btnis=tb:option(Button,"switch",translate("Switch Config")) +btnis.template="openclash/other_button" +btnis.render=function(o,t,a) +if not e[t] then return false end +if IsYamlFile(e[t].name) or IsYmlFile(e[t].name) then +a.display="" +else +a.display="none" +end +o.inputstyle="apply" +Button.render(o,t,a) +end +btnis.write=function(a,t) +luci.sys.exec(string.format('uci set openclash.config.config_path="/etc/openclash/config/%s"',e[t].name)) +uci:commit("openclash") +HTTP.redirect(luci.dispatcher.build_url("admin", "services", "openclash", "config")) end - -local t = { - {Commit, Apply, Download} -} - -a = m:section(Table, t) - -o = a:option(Button, "Commit") -o.inputtitle = translate("Commit Configurations") -o.inputstyle = "apply" -o.write = function() - uci:commit("openclash") +btndl = tb:option(Button,"download",translate("Download Configurations")) +btndl.template="openclash/other_button" +btndl.render=function(e,t,a) +e.inputstyle="remove" +Button.render(e,t,a) end - -o = a:option(Button, "Apply") -o.inputtitle = translate("Apply Configurations") -o.inputstyle = "apply" -o.write = function() - uci:set("openclash", "config", "enable", 1) - uci:commit("openclash") - SYS.call("/etc/init.d/openclash restart >/dev/null 2>&1 &") - HTTP.redirect(DISP.build_url("admin", "services", "openclash")) -end - -o = a:option(Button, "Download") -o.inputtitle = translate("Download Configurations") -o.inputstyle = "apply" -o.write = function () +btndl.write = function (a,t) local sPath, sFile, fd, block - sPath = "/etc/openclash/config.yaml" + sPath = "/etc/openclash/config/"..e[t].name sFile = NXFS.basename(sPath) if fs.isdirectory(sPath) then fd = io.popen('tar -C "%s" -cz .' % {sPath}, "r") @@ -157,4 +178,145 @@ o.write = function () HTTP.close() end -return ful , m +btnrm=tb:option(Button,"remove",translate("Remove")) +btnrm.render=function(e,t,a) +e.inputstyle="reset" +Button.render(e,t,a) +end +btnrm.write=function(a,t) +local a=fs.unlink("/etc/openclash/config/"..luci.openclash.basename(e[t].name)) +local db=fs.unlink("/etc/openclash/backup/"..luci.openclash.basename(e[t].name)) +if a then table.remove(e,t)end +return a +end + +local p,r={} +for x,y in ipairs(fs.glob("/etc/openclash/proxy_provider/*"))do +r=fs.stat(y) +if r then +p[x]={} +p[x].name=fs.basename(y) +p[x].mtime=os.date("%Y-%m-%d %H:%M:%S",r.mtime) +p[x].size=tostring(r.size) +p[x].remove=0 +p[x].enable=false +end +end + +proxy_form=SimpleForm("proxy_provider_file_list",translate("Proxy Provider File List")) +proxy_form.reset=false +proxy_form.submit=false +tb1=proxy_form:section(Table,p) +nm1=tb1:option(DummyValue,"name",translate("File Name")) +mt1=tb1:option(DummyValue,"mtime",translate("Update Time")) +sz1=tb1:option(DummyValue,"size",translate("Size")) + +btndl1 = tb1:option(Button,"download1",translate("Download Configurations")) +btndl1.template="openclash/other_button" +btndl1.render=function(y,x,r) +y.inputstyle="remove" +Button.render(y,x,r) +end +btndl1.write = function (r,x) + local sPath, sFile, fd, block + sPath = "/etc/openclash/proxy_provider/"..p[x].name + sFile = NXFS.basename(sPath) + if fs.isdirectory(sPath) then + fd = io.popen('tar -C "%s" -cz .' % {sPath}, "r") + sFile = sFile .. ".tar.gz" + else + fd = nixio.open(sPath, "r") + end + if not fd then + return + end + HTTP.header('Content-Disposition', 'attachment; filename="%s"' % {sFile}) + HTTP.prepare_content("application/octet-stream") + while true do + block = fd:read(nixio.const.buffersize) + if (not block) or (#block ==0) then + break + else + HTTP.write(block) + end + end + fd:close() + HTTP.close() +end + +btnrm1=tb1:option(Button,"remove1",translate("Remove")) +btnrm1.render=function(p,x,r) +p.inputstyle="reset" +Button.render(p,x,r) +end +btnrm1.write=function(r,x) +local r=fs.unlink("/etc/openclash/proxy_provider/"..luci.openclash.basename(p[x].name)) +if r then table.remove(p,x)end +return r +end + +m = SimpleForm("openclash") +m.reset = false +m.submit = false + +local tab = { + {user, default} +} + +s = m:section(Table, tab) + +local conf = string.sub(luci.sys.exec("uci get openclash.config.config_path"), 1, -2) +local dconf = "/etc/openclash/default.yaml" + +sev = s:option(Value, "user") +sev.template = "cbi/tvalue" +sev.description = translate("You Can Modify config file Here, Except The Settings That Were Taken Over") +sev.rows = 20 +sev.wrap = "off" +sev.cfgvalue = function(self, section) + return NXFS.readfile(conf) or NXFS.readfile(dconf) or "" +end +sev.write = function(self, section, value) +if (CHIF == "0") then + value = value:gsub("\r\n?", "\n") + NXFS.writefile(conf, value) +end +end + +def = s:option(Value, "default") +def.template = "cbi/tvalue" +def.description = translate("Default Config File With Correct General-Settings") +def.rows = 20 +def.wrap = "off" +def.readonly = true +def.cfgvalue = function(self, section) + return NXFS.readfile(dconf) or "" +end +def.write = function(self, section, value) +end + + +local t = { + {Commit, Apply} +} + +a = m:section(Table, t) + +o = a:option(Button, "Commit") +o.inputtitle = translate("Commit Configurations") +o.inputstyle = "apply" +o.write = function() + uci:commit("openclash") +end + +o = a:option(Button, "Apply") +o.inputtitle = translate("Apply Configurations") +o.inputstyle = "apply" +o.write = function() + uci:set("openclash", "config", "enable", 1) + uci:commit("openclash") + SYS.call("/etc/init.d/openclash restart >/dev/null 2>&1 &") + HTTP.redirect(DISP.build_url("admin", "services", "openclash")) +end + +return ful , form , proxy_form , m diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/groups-config.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/groups-config.lua index 8e6ab6109f..53de1e66ad 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/groups-config.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/groups-config.lua @@ -2,7 +2,7 @@ local m, s, o local openclash = "openclash" local uci = luci.model.uci.cursor() -local fs = require "nixio.fs" +local fs = require "luci.openclash" local sys = require "luci.sys" local sid = arg[1] @@ -11,6 +11,17 @@ font_off = [[]] bold_on = [[]] bold_off = [[]] +function IsYamlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-5,-1)) + return e == ".yaml" +end +function IsYmlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-4,-1)) + return e == ".yml" +end + m = Map(openclash, translate("Edit Group")) m.pageaction = false m.redirect = luci.dispatcher.build_url("admin/services/openclash/servers") @@ -24,6 +35,20 @@ s = m:section(NamedSection, sid, "groups") s.anonymous = true s.addremove = false +o = s:option(ListValue, "config", translate("Config File")) +o:value("all", translate("Use For All Config File")) +local e,a={} +for t,f in ipairs(fs.glob("/etc/openclash/config/*"))do + a=fs.stat(f) + if a then + e[t]={} + e[t].name=fs.basename(f) + if IsYamlFile(e[t].name) or IsYmlFile(e[t].name) then + o:value(e[t].name) + end + end +end + o = s:option(ListValue, "type", translate("Group Type")) o.rmempty = true o.description = translate("Choose The Operation Mode") diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/proxy-provider-config.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/proxy-provider-config.lua new file mode 100644 index 0000000000..7377a8f48b --- /dev/null +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/proxy-provider-config.lua @@ -0,0 +1,128 @@ + +local m, s, o +local openclash = "openclash" +local uci = luci.model.uci.cursor() +local sys = require "luci.sys" +local sid = arg[1] +local fs = require "luci.openclash" + +font_red = [[]] +font_off = [[]] +bold_on = [[]] +bold_off = [[]] + +function IsYamlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-5,-1)) + return e == ".yaml" +end +function IsYmlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-4,-1)) + return e == ".yml" +end + +m = Map(openclash, translate("Edit Proxy-Provider")) +m.pageaction = false +m.redirect = luci.dispatcher.build_url("admin/services/openclash/servers") +if m.uci:get(openclash, sid) ~= "proxy-provider" then + luci.http.redirect(m.redirect) + return +end + +-- [[ Provider Setting ]]-- +s = m:section(NamedSection, sid, "proxy-provider") +s.anonymous = true +s.addremove = false + +o = s:option(ListValue, "config", translate("Config File")) +o:value("all", translate("Use For All Config File")) +local e,a={} +for t,f in ipairs(fs.glob("/etc/openclash/config/*"))do + a=fs.stat(f) + if a then + e[t]={} + e[t].name=fs.basename(f) + if IsYamlFile(e[t].name) or IsYmlFile(e[t].name) then + o:value(e[t].name) + end + end +end + +o = s:option(ListValue, "type", translate("Provider Type")) +o.rmempty = true +o.description = translate("Choose The Provider Type") +o:value("http") +o:value("file") + +o = s:option(Value, "name", translate("Provider Name")) +o.rmempty = false + +o = s:option(ListValue, "path", translate("Provider Path")) +o.description = translate("Update Your Proxy Provider File From Config Luci Page") +local p,h={} +for t,f in ipairs(fs.glob("/etc/openclash/proxy_provider/*"))do + h=fs.stat(f) + if h then + p[t]={} + p[t].name=fs.basename(f) + if IsYamlFile(p[t].name) or IsYmlFile(p[t].name) then + o:value("./proxy_provider/"..p[t].name) + end + end +end +o.rmempty = false +o:depends("type", "file") + +o = s:option(Value, "provider_url", translate("Provider URL")) +o.rmempty = false +o:depends("type", "http") + +o = s:option(Value, "provider_interval", translate("Provider Interval(s)")) +o.default = "3600" +o.rmempty = false +o:depends("type", "http") + +o = s:option(ListValue, "health_check", translate("Provider Health Check")) +o:value("false", translate("Disable")) +o:value("true", translate("Enable")) +o.default=true + +o = s:option(Value, "health_check_url", translate("Health Check URL")) +o.default = "http://www.gstatic.com/generate_204" +o.rmempty = false + +o = s:option(Value, "health_check_interval", translate("Health Check Interval(s)")) +o.default = "300" +o.rmempty = false + +o = s:option(DynamicList, "groups", translate("Proxy Group")) +o.description = font_red..bold_on..translate("No Need Set when Config Create, The added Proxy Groups Must Exist")..bold_off..font_off +o.rmempty = true +m.uci:foreach("openclash", "groups", + function(s) + o:value(s.name) + end) + +local t = { + {Commit, Back} +} +a = m:section(Table, t) + +o = a:option(Button,"Commit") +o.inputtitle = translate("Commit Configurations") +o.inputstyle = "apply" +o.write = function() + m.uci:commit(openclash) + luci.http.redirect(m.redirect) +end + +o = a:option(Button,"Back") +o.inputtitle = translate("Back Configurations") +o.inputstyle = "reset" +o.write = function() + m.uci:revert(openclash) + luci.http.redirect(m.redirect) +end + +return m diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers-config.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers-config.lua index 27a196982f..fbb9cbe848 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers-config.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers-config.lua @@ -2,7 +2,7 @@ local m, s, o local openclash = "openclash" local uci = luci.model.uci.cursor() -local fs = require "nixio.fs" +local fs = require "luci.openclash" local sys = require "luci.sys" local sid = arg[1] local uuid = luci.sys.exec("cat /proc/sys/kernel/random/uuid") @@ -12,6 +12,17 @@ font_off = [[]] bold_on = [[]] bold_off = [[]] +function IsYamlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-5,-1)) + return e == ".yaml" +end +function IsYmlFile(e) + e=e or"" + local e=string.lower(string.sub(e,-4,-1)) + return e == ".yml" +end + local encrypt_methods_ss = { -- stream @@ -52,13 +63,26 @@ s = m:section(NamedSection, sid, "servers") s.anonymous = true s.addremove = false +o = s:option(ListValue, "config", translate("Config File")) +o:value("all", translate("Use For All Config File")) +local e,a={} +for t,f in ipairs(fs.glob("/etc/openclash/config/*"))do + a=fs.stat(f) + if a then + e[t]={} + e[t].name=fs.basename(f) + if IsYamlFile(e[t].name) or IsYmlFile(e[t].name) then + o:value(e[t].name) + end + end +end + o = s:option(ListValue, "type", translate("Server Node Type")) o:value("ss", translate("Shadowsocks")) o:value("vmess", translate("Vmess")) o:value("snell", translate("Snell")) o:value("socks5", translate("Socks5")) o:value("http", translate("HTTP(S)")) - o.description = translate("Using incorrect encryption mothod may causes service fail to start") o = s:option(Value, "name", translate("Server Alias")) diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers.lua index e6c519ba44..d2abf8c368 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/servers.lua @@ -57,6 +57,11 @@ function s.create(...) end end +o = s:option(DummyValue, "config", translate("Config File")) +function o.cfgvalue(...) + return Value.cfgvalue(...) or translate("all") +end + o = s:option(DummyValue, "type", translate("Group Type")) function o.cfgvalue(...) return Value.cfgvalue(...) or translate("None") @@ -67,6 +72,43 @@ function o.cfgvalue(...) return Value.cfgvalue(...) or translate("None") end +-- [[ Proxy-Provider Manage ]]-- +s = m:section(TypedSection, "proxy-provider", translate("Proxy-Provider")) +s.anonymous = true +s.addremove = true +s.sortable = true +s.template = "cbi/tblsection" +s.extedit = luci.dispatcher.build_url("admin/services/openclash/proxy-provider-config/%s") +function s.create(...) + local sid = TypedSection.create(...) + if sid then + luci.http.redirect(s.extedit % sid) + return + end +end + +o = s:option(Flag, "enabled", translate("Enable")) +o.rmempty = false +o.default = o.enabled +o.cfgvalue = function(...) + return Flag.cfgvalue(...) or "1" +end + +o = s:option(DummyValue, "config", translate("Config File")) +function o.cfgvalue(...) + return Value.cfgvalue(...) or translate("all") +end + +o = s:option(DummyValue, "type", translate("Provider Type")) +function o.cfgvalue(...) + return Value.cfgvalue(...) or translate("None") +end + +o = s:option(DummyValue, "name", translate("Provider Name")) +function o.cfgvalue(...) + return Value.cfgvalue(...) or translate("None") +end + -- [[ Servers Manage ]]-- s = m:section(TypedSection, "servers", translate("Proxys")) s.anonymous = true @@ -90,6 +132,11 @@ o.cfgvalue = function(...) return Flag.cfgvalue(...) or "1" end +o = s:option(DummyValue, "config", translate("Config File")) +function o.cfgvalue(...) + return Value.cfgvalue(...) or translate("all") +end + o = s:option(DummyValue, "type", translate("Type")) function o.cfgvalue(...) return Value.cfgvalue(...) or translate("None") @@ -115,7 +162,7 @@ o.template="openclash/ping" o.width="10%" local tt = { - {Delete_Unused_Servers, Delete_Severs, Delete_Groups} + {Delete_Unused_Servers, Delete_Severs, Delete_Proxy_Provider, Delete_Groups} } b = m:section(Table, tt) @@ -140,6 +187,16 @@ o.write = function() luci.http.redirect(luci.dispatcher.build_url("admin", "services", "openclash", "servers")) end +o = b:option(Button,"Delete_Proxy_Provider") +o.inputtitle = translate("Delete Proxy Provider") +o.inputstyle = "reset" +o.write = function() + m.uci:set("openclash", "config", "enable", 0) + m.uci:delete_all("openclash", "proxy-provider", function(s) return true end) + m.uci:commit("openclash") + luci.http.redirect(luci.dispatcher.build_url("admin", "services", "openclash", "servers")) +end + o = b:option(Button,"Delete_Groups") o.inputtitle = translate("Delete Groups") o.inputstyle = "reset" diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/settings.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/settings.lua index 3be35d9557..62d29f11f4 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/settings.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/model/cbi/openclash/settings.lua @@ -21,7 +21,6 @@ s:tab("settings", translate("General Settings")) s:tab("dns", translate("DNS Setting")) s:tab("rules", translate("Rules Setting")) s:tab("dashboard", translate("Dashboard Settings")) -s:tab("config_update", translate("Config Update")) s:tab("rules_update", translate("Rules Update")) s:tab("geo_update", translate("GEOIP Update")) s:tab("version_update", translate("Version Update")) @@ -49,6 +48,9 @@ o = s:taboption("settings", ListValue, "en_mode", font_red..bold_on..translate(" o.description = translate("Select Mode For OpenClash Work, Network Error Try Flush DNS Cache") o:value("redir-host", translate("redir-host")) o:value("fake-ip", translate("fake-ip")) +o:value("fake-ip-tun", translate("fake-ip(tun mode)")) +o:value("redir-host-vpn", translate("redir-host-vpn(game mode)")) +o:value("fake-ip-vpn", translate("fake-ip-vpn(game mode)")) o.default = "redir-host" o = s:taboption("settings", ListValue, "proxy_mode", font_red..bold_on..translate("Proxy Mode")..bold_off..font_off) @@ -151,13 +153,13 @@ custom_fake_black.wrap = "off" custom_fake_black:depends("dns_advanced_setting", "1") function custom_fake_black.cfgvalue(self, section) - return NXFS.readfile("/etc/config/openclash_custom_fake_black.conf") or "" + return NXFS.readfile("/etc/openclash/custom/openclash_custom_fake_black.conf") or "" end function custom_fake_black.write(self, section, value) if value then value = value:gsub("\r\n?", "\n") - NXFS.writefile("/etc/config/openclash_custom_fake_black.conf", value) + NXFS.writefile("/etc/openclash/custom/openclash_custom_fake_black.conf", value) end end @@ -207,6 +209,24 @@ o:depends("rule_source", "ConnersHua") o:value(l) end file:seek("set") +o = s:taboption("rules", ListValue, "Netease_Music", translate("Netease Music")) +o:depends("rule_source", "lhie1") + for l in file:lines() do + o:value(l) + end + file:seek("set") +o = s:taboption("rules", ListValue, "Speedtest", translate("Speedtest")) +o:depends("rule_source", "lhie1") + for l in file:lines() do + o:value(l) + end + file:seek("set") +o = s:taboption("rules", ListValue, "Telegram", translate("Telegram")) +o:depends("rule_source", "lhie1") + for l in file:lines() do + o:value(l) + end + file:seek("set") o = s:taboption("rules", ListValue, "AdBlock", translate("AdBlock")) ---- o:depends("rule_source", "lhie1") o:depends("rule_source", "ConnersHua") @@ -232,58 +252,6 @@ o.description = translate("Choose Proxy Group, Base On Your Servers Group in con file:close() ---- update Settings -o = s:taboption("config_update", ListValue, "auto_update", translate("Auto Update")) -o.description = translate("Auto Update Server subscription") -o:value("0", translate("Disable")) -o:value("1", translate("Enable")) -o.default=0 - -o = s:taboption("config_update", DynamicList, "servers_update_keyword", font_red..bold_on..translate("Keyword Matching Setting")..bold_off..font_off) -o.description = font_red..bold_on..translate("Only Keep Servers which Matching Keywords, eg: hk or tw&bgp")..bold_off..font_off -o.rmempty = true - -o = s:taboption("config_update", ListValue, "config_update_week_time", translate("Update Time (Every Week)")) -o:value("*", translate("Every Day")) -o:value("1", translate("Every Monday")) -o:value("2", translate("Every Tuesday")) -o:value("3", translate("Every Wednesday")) -o:value("4", translate("Every Thursday")) -o:value("5", translate("Every Friday")) -o:value("6", translate("Every Saturday")) -o:value("0", translate("Every Sunday")) -o.default=1 - -o = s:taboption("config_update", ListValue, "auto_update_time", translate("Update time (every day)")) -for t = 0,23 do -o:value(t, t..":00") -end -o.default=0 -o.rmempty = false - -o = s:taboption("config_update", ListValue, "config_update_url_type", translate("Update Url Type")) -o:value("clash", translate("Clash")) -o:value("v2rayn", translate("V2rayN")) -o:value("surge", translate("Surge")) -o.description = translate("Power By fndroid,Use Other Rules If V2rayN Subcription") -o.default="clash" - -o = s:taboption("config_update", Value, "subscribe_url") -o.title = translate("Subcription Url") -o.description = translate("Server Subscription Address") -o.rmempty = true - -o = s:taboption("config_update", Button, translate("Config File Update")) -o.title = translate("Update Subcription") -o.inputtitle = translate("Check And Update") -o.inputstyle = "reload" -o.write = function() - m.uci:set("openclash", "config", "enable", 1) - m.uci:commit("openclash") - SYS.call("rm -rf /etc/openclash/config.bak 2>/dev/null") - SYS.call("/usr/share/openclash/openclash.sh >/dev/null 2>&1 &") - HTTP.redirect(DISP.build_url("admin", "services", "openclash")) -end - o = s:taboption("rules_update", ListValue, "other_rule_auto_update", translate("Auto Update")) o.description = font_red..bold_on..translate("Auto Update Other Rules")..bold_off..font_off o:value("0", translate("Disable")) @@ -455,12 +423,12 @@ custom_rules.rows = 20 custom_rules.wrap = "off" function custom_rules.cfgvalue(self, section) - return NXFS.readfile("/etc/config/openclash_custom_rules.list") or "" + return NXFS.readfile("/etc/openclash/custom/openclash_custom_rules.list") or "" end function custom_rules.write(self, section, value) if value then value = value:gsub("\r\n?", "\n") - NXFS.writefile("/etc/config/openclash_custom_rules.list", value) + NXFS.writefile("/etc/openclash/custom/openclash_custom_rules.list", value) end end @@ -474,12 +442,12 @@ custom_hosts.rows = 20 custom_hosts.wrap = "off" function custom_hosts.cfgvalue(self, section) - return NXFS.readfile("/etc/config/openclash_custom_hosts.list") or "" + return NXFS.readfile("/etc/openclash/custom/openclash_custom_hosts.list") or "" end function custom_hosts.write(self, section, value) if value then value = value:gsub("\r\n?", "\n") - NXFS.writefile("/etc/config/openclash_custom_hosts.list", value) + NXFS.writefile("/etc/openclash/custom/openclash_custom_hosts.list", value) end end diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/openclash.lua b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/openclash.lua index d54c1b9756..c9bd2c4c4e 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/openclash.lua +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/openclash.lua @@ -1,11 +1,244 @@ +--[[ +LuCI - Filesystem tools +Description: +A module offering often needed filesystem manipulation functions + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +Licensed 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. + +]]-- + +local io = require "io" +local os = require "os" +local ltn12 = require "luci.ltn12" local fs = require "nixio.fs" +local nutil = require "nixio.util" + local type = type +--- LuCI filesystem library. module "luci.openclash" ---- LuCI filesystem library. +--- Test for file access permission on given path. +-- @class function +-- @name access +-- @param str String value containing the path +-- @return Number containing the return code, 0 on sucess or nil on error +-- @return String containing the error description (if any) +-- @return Number containing the os specific errno (if any) +access = fs.access +--- Evaluate given shell glob pattern and return a table containing all matching +-- file and directory entries. +-- @class function +-- @name glob +-- @param filename String containing the path of the file to read +-- @return Table containing file and directory entries or nil if no matches +-- @return String containing the error description (if no matches) +-- @return Number containing the os specific errno (if no matches) +function glob(...) + local iter, code, msg = fs.glob(...) + if iter then + return nutil.consume(iter) + else + return nil, code, msg + end +end + +--- Checks wheather the given path exists and points to a regular file. +-- @param filename String containing the path of the file to test +-- @return Boolean indicating wheather given path points to regular file +function isfile(filename) + return fs.stat(filename, "type") == "reg" +end + +--- Checks wheather the given path exists and points to a directory. +-- @param dirname String containing the path of the directory to test +-- @return Boolean indicating wheather given path points to directory function isdirectory(dirname) return fs.stat(dirname, "type") == "dir" end + +--- Read the whole content of the given file into memory. +-- @param filename String containing the path of the file to read +-- @return String containing the file contents or nil on error +-- @return String containing the error message on error +readfile = fs.readfile + +--- Write the contents of given string to given file. +-- @param filename String containing the path of the file to read +-- @param data String containing the data to write +-- @return Boolean containing true on success or nil on error +-- @return String containing the error message on error +writefile = fs.writefile + +--- Copies a file. +-- @param source Source file +-- @param dest Destination +-- @return Boolean containing true on success or nil on error +copy = fs.datacopy + +--- Renames a file. +-- @param source Source file +-- @param dest Destination +-- @return Boolean containing true on success or nil on error +rename = fs.move + +--- Get the last modification time of given file path in Unix epoch format. +-- @param path String containing the path of the file or directory to read +-- @return Number containing the epoch time or nil on error +-- @return String containing the error description (if any) +-- @return Number containing the os specific errno (if any) +function mtime(path) + return fs.stat(path, "mtime") +end + +--- Set the last modification time of given file path in Unix epoch format. +-- @param path String containing the path of the file or directory to read +-- @param mtime Last modification timestamp +-- @param atime Last accessed timestamp +-- @return 0 in case of success nil on error +-- @return String containing the error description (if any) +-- @return Number containing the os specific errno (if any) +function utime(path, mtime, atime) + return fs.utimes(path, atime, mtime) +end + +--- Return the last element - usually the filename - from the given path with +-- the directory component stripped. +-- @class function +-- @name basename +-- @param path String containing the path to strip +-- @return String containing the base name of given path +-- @see dirname +basename = fs.basename + +--- Return the directory component of the given path with the last element +-- stripped of. +-- @class function +-- @name dirname +-- @param path String containing the path to strip +-- @return String containing the directory component of given path +-- @see basename +dirname = fs.dirname + +--- Return a table containing all entries of the specified directory. +-- @class function +-- @name dir +-- @param path String containing the path of the directory to scan +-- @return Table containing file and directory entries or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +function dir(...) + local iter, code, msg = fs.dir(...) + if iter then + local t = nutil.consume(iter) + t[#t+1] = "." + t[#t+1] = ".." + return t + else + return nil, code, msg + end +end + +--- Create a new directory, recursively on demand. +-- @param path String with the name or path of the directory to create +-- @param recursive Create multiple directory levels (optional, default is true) +-- @return Number with the return code, 0 on sucess or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +function mkdir(path, recursive) + return recursive and fs.mkdirr(path) or fs.mkdir(path) +end + +--- Remove the given empty directory. +-- @class function +-- @name rmdir +-- @param path String containing the path of the directory to remove +-- @return Number with the return code, 0 on sucess or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +rmdir = fs.rmdir + +local stat_tr = { + reg = "regular", + dir = "directory", + lnk = "link", + chr = "character device", + blk = "block device", + fifo = "fifo", + sock = "socket" +} +--- Get information about given file or directory. +-- @class function +-- @name stat +-- @param path String containing the path of the directory to query +-- @return Table containing file or directory properties or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +function stat(path, key) + local data, code, msg = fs.stat(path) + if data then + data.mode = data.modestr + data.type = stat_tr[data.type] or "?" + end + return key and data and data[key] or data, code, msg +end + +--- Set permissions on given file or directory. +-- @class function +-- @name chmod +-- @param path String containing the path of the directory +-- @param perm String containing the permissions to set ([ugoa][+-][rwx]) +-- @return Number with the return code, 0 on sucess or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +chmod = fs.chmod + +--- Create a hard- or symlink from given file (or directory) to specified target +-- file (or directory) path. +-- @class function +-- @name link +-- @param path1 String containing the source path to link +-- @param path2 String containing the destination path for the link +-- @param symlink Boolean indicating wheather to create a symlink (optional) +-- @return Number with the return code, 0 on sucess or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +function link(src, dest, sym) + return sym and fs.symlink(src, dest) or fs.link(src, dest) +end + +--- Remove the given file. +-- @class function +-- @name unlink +-- @param path String containing the path of the file to remove +-- @return Number with the return code, 0 on sucess or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +unlink = fs.unlink + +--- Retrieve target of given symlink. +-- @class function +-- @name readlink +-- @param path String containing the path of the symlink to read +-- @return String containing the link target or nil on error +-- @return String containing the error description on error +-- @return Number containing the os specific errno on error +readlink = fs.readlink diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/dvalue.htm b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/dvalue.htm index 4a437e2b9f..bf79fcd8c2 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/dvalue.htm +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/dvalue.htm @@ -1,7 +1,7 @@ <%+cbi/valueheader%>
-<%:Rename your config file to config.yaml before upload what would be save to /etc/openclash%> +<%:Only Support yaml what would be save to /etc/openclash/config/%>
<% diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/myip.htm b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/myip.htm index bacf9d6121..295aabf388 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/myip.htm +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/myip.htm @@ -7,14 +7,14 @@ - + - + @@ -309,8 +309,8 @@ $$.getElementById('ip-pcol-ipip').innerHTML = `${data.pro} ${data.city} ${data.region} ${pcisp[1]}`; }; function getIpsbIP(data){ - $$.getElementById('ip-ipsb').innerHTML = data.address; - $$.getElementById('ip-ipsb-geo').innerHTML = `${data.country} ${data.province} ${data.city} ${data.isp.name}` + $$.getElementById('ip-ipsb').innerHTML = data.ip; + IP.parseIPIpip(data.ip, 'ip-ipsb-geo'); }; window.onload=myip_Load(); @@ -326,7 +326,7 @@ var sbip = document.getElementsByTagName('HEAD').item(0); var sbipScript= document.createElement("script"); sbipScript.defer = "defer"; - sbipScript.src='https://ip.sb/addrinfo?callback=getIpsbIP'; + sbipScript.src='https://api-ipv4.ip.sb/jsonip?callback=getIpsbIP'; sbip.appendChild(sbipScript); const $$ = document; @@ -342,8 +342,8 @@ $$.getElementById('ip-pcol-ipip').innerHTML = `${data.pro} ${data.city} ${data.region} ${pcisp[1]}`; }; function getIpsbIP(data){ - $$.getElementById('ip-ipsb').innerHTML = data.address; - $$.getElementById('ip-ipsb-geo').innerHTML = `${data.country} ${data.province} ${data.city} ${data.isp.name}` + $$.getElementById('ip-ipsb').innerHTML = data.ip; + IP.parseIPIpip(data.ip, 'ip-ipsb-geo'); }; setTimeout("myip_Load()",1000*10); @@ -351,7 +351,7 @@ - + diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/other_button.htm b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/other_button.htm new file mode 100644 index 0000000000..1c391ad984 --- /dev/null +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/other_button.htm @@ -0,0 +1,7 @@ +<%+cbi/valueheader%> + <% if self:cfgvalue(section) ~= false then %> + " style="display: <%= display %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> /> + <% else %> + - + <% end %> +<%+cbi/valuefooter%> diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/state.htm b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/state.htm index 1a785fefa8..92ac622af3 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/state.htm +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/state.htm @@ -6,7 +6,7 @@ 一些必要的状态显示,运行前请确保各项目显示正常,需要更新请到全局设置页面操作

- 启动参数检查(配置文件) <%:Collecting data...%> 配置文件更新日期 <%:Collecting data...%> + 正在使用的配置文件 <%:Collecting data...%> 启动参数检查(配置文件) <%:Collecting data...%> GEOIP(By MaxMind)数据库日期 <%:Collecting data...%> lhie1 规则更新日期 <%:Collecting data...%> ConnersHua 规则更新日期 <%:Collecting data...%> ConnersHua 回国规则更新日期 <%:Collecting data...%> @@ -33,9 +33,6 @@ if (status.config == "1") { config.innerHTML = "<%:未找到配置文件%>"; } - else if (status.config == "0") { - config.innerHTML = "<%:原始配置文件%>"; - } else { config.innerHTML = ""+status.config+""; } @@ -59,9 +56,6 @@ if (status.config == "1") { config.innerHTML = "<%:未找到配置文件%>"; } - else if (status.config == "0") { - config.innerHTML = "<%:原始配置文件%>"; - } else { config.innerHTML = ""+status.config+""; } diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/status.htm b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/status.htm index bfc728da9b..adb8d8c386 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/status.htm +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/status.htm @@ -55,6 +55,10 @@ { mode.innerHTML = status.clash ? "<%: Redir-Host(兼容)模式 %>" : '<%:NOT RUNNING%>'; } + else + { + mode.innerHTML = status.clash ? "<%: Fake-IP(TUN)模式 %>" : '<%:NOT RUNNING%>'; + } watchdog.innerHTML = status.watchdog ? '<%:RUNNING%> ' : '<%:NOT RUNNING%>'; daip.innerHTML = status.daip ? ""+status.daip+"" : ""+"<%:Not Set%>"+""; dase.innerHTML = status.dase ? ""+status.dase+"" : ""+"<%:Not Set%>"+""; @@ -74,6 +78,10 @@ { mode.innerHTML = status.clash ? "<%: Redir-Host(兼容)模式 %>" : '<%:NOT RUNNING%>'; } + else + { + mode.innerHTML = status.clash ? "<%: Fake-IP(TUN)模式 %>" : '<%:NOT RUNNING%>'; + } watchdog.innerHTML = status.watchdog ? '<%:RUNNING%> ' : '<%:NOT RUNNING%>'; daip.innerHTML = status.daip ? ""+status.daip+"" : ""+"<%:Not Set%>"+""; dase.innerHTML = status.dase ? ""+status.dase+"" : ""+"<%:Not Set%>"+""; diff --git a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/upload.htm b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/upload.htm index 3401bd9833..71085b108e 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/upload.htm +++ b/package/ctcgfw/luci-app-openclash/files/usr/lib/lua/luci/view/openclash/upload.htm @@ -1,6 +1,10 @@ <%+cbi/valueheader%>
-
diff --git a/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/cfg_servers_address_fake_block.sh b/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/cfg_servers_address_fake_block.sh index fb1578224e..b256537802 100755 --- a/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/cfg_servers_address_fake_block.sh +++ b/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/cfg_servers_address_fake_block.sh @@ -4,7 +4,7 @@ status=$(ps|grep -c /usr/share/openclash/cfg_servers_address_fake_block.sh) [ "$status" -gt "3" ] && exit 0 en_mode=$(uci get openclash.config.en_mode 2>/dev/null) -if pidof clash >/dev/null && [ "$en_mode" = "fake-ip" ]; then +if pidof clash >/dev/null && [ "$en_mode" != "redir-host" ]; then rm -rf /tmp/dnsmasq.d/dnsmasq_openclash.conf >/dev/null 2>&1 /usr/share/openclash/openclash_fake_block.sh mkdir -p /tmp/dnsmasq.d diff --git a/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/dashboard/index.html b/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/dashboard/index.html index 39d7be5693..13a494dc27 100644 --- a/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/dashboard/index.html +++ b/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/dashboard/index.html @@ -5,8 +5,8 @@ Clash - +
- + diff --git a/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/dashboard/js/1.bundle.0bddb85299f970595cb5.min.js b/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/dashboard/js/1.bundle.0bddb85299f970595cb5.min.js new file mode 100644 index 0000000000..7bc830d892 --- /dev/null +++ b/package/ctcgfw/luci-app-openclash/files/usr/share/openclash/dashboard/js/1.bundle.0bddb85299f970595cb5.min.js @@ -0,0 +1,2 @@ +/*! For license information please see 1.bundle.0bddb85299f970595cb5.min.js.LICENSE */ +(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[function(e,t,n){"use strict";e.exports=n(51)},function(e,t,n){e.exports=n(55)()},function(e,t,n){var r;!function(){"use strict";var n={}.hasOwnProperty;function o(){for(var e=[],t=0;t=0||(o[n]=e[n]);return o}n.d(t,"a",(function(){return r}))},,,,function(e,t,n){"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){0;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}}(),e.exports=n(52)},function(e,t,n){var r=n(42)(Object,"create");e.exports=r},function(e,t,n){var r=n(110);e.exports=function(e,t){for(var n=e.length;n--;)if(r(e[n][0],t))return n;return-1}},function(e,t,n){var r=n(116);e.exports=function(e,t){var n=e.__data__;return r(t)?n["string"==typeof t?"string":"hash"]:n.map}},function(e,t,n){"use strict";var r=Array.isArray,o=Object.keys,i=Object.prototype.hasOwnProperty;e.exports=function e(t,n){if(t===n)return!0;if(t&&n&&"object"==typeof t&&"object"==typeof n){var a,u,l,c=r(t),s=r(n);if(c&&s){if((u=t.length)!=n.length)return!1;for(a=u;0!=a--;)if(!e(t[a],n[a]))return!1;return!0}if(c!=s)return!1;var f=t instanceof Date,d=n instanceof Date;if(f!=d)return!1;if(f&&d)return t.getTime()==n.getTime();var p=t instanceof RegExp,h=n instanceof RegExp;if(p!=h)return!1;if(p&&h)return t.toString()==n.toString();var m=o(t);if((u=m.length)!==o(n).length)return!1;for(a=u;0!=a--;)if(!i.call(n,m[a]))return!1;for(a=u;0!=a--;)if(!e(t[l=m[a]],n[l]))return!1;return!0}return t!=t&&n!=n}},,function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(r){"object"==typeof window&&(n=window)}e.exports=n},function(e,t){var n=Array.isArray;e.exports=n},function(e,t,n){var r=n(41),o=n(89),i="[object Symbol]";e.exports=function(e){return"symbol"==typeof e||o(e)&&r(e)==i}},function(e,t,n){var r=n(23).Symbol;e.exports=r},function(e,t,n){var r=n(86),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();e.exports=i},function(e,t,n){e.exports=function(){"use strict";var e="millisecond",t="second",n="minute",r="hour",o="day",i="week",a="month",u="quarter",l="year",c=/^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/,s=/\[([^\]]+)]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,f=function(e,t,n){var r=String(e);return!r||r.length>=t?e:""+Array(t+1-r.length).join(n)+e},d={s:f,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),r=Math.floor(n/60),o=n%60;return(t<=0?"+":"-")+f(r,2,"0")+":"+f(o,2,"0")},m:function(e,t){var n=12*(t.year()-e.year())+(t.month()-e.month()),r=e.clone().add(n,a),o=t-r<0,i=e.clone().add(n+(o?-1:1),a);return Number(-(n+(t-r)/(o?r-i:i-r))||0)},a:function(e){return e<0?Math.ceil(e)||0:Math.floor(e)},p:function(c){return{M:a,y:l,w:i,d:o,h:r,m:n,s:t,ms:e,Q:u}[c]||String(c||"").toLowerCase().replace(/s$/,"")},u:function(e){return void 0===e}},p={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},h="en",m={};m[h]=p;var v=function(e){return e instanceof w},g=function(e,t,n){var r;if(!e)return h;if("string"==typeof e)m[e]&&(r=e),t&&(m[e]=t,r=e);else{var o=e.name;m[o]=e,r=o}return n||(h=r),r},y=function(e,t,n){if(v(e))return e.clone();var r=t?"string"==typeof t?{format:t,pl:n}:t:{};return r.date=e,new w(r)},b=d;b.l=g,b.i=v,b.w=function(e,t){return y(e,{locale:t.$L,utc:t.$u,$offset:t.$offset})};var w=function(){function f(e){this.$L=this.$L||g(e.locale,null,!0),this.parse(e)}var d=f.prototype;return d.parse=function(e){this.$d=function(e){var t=e.date,n=e.utc;if(null===t)return new Date(NaN);if(b.u(t))return new Date;if(t instanceof Date)return new Date(t);if("string"==typeof t&&!/Z$/i.test(t)){var r=t.match(c);if(r)return n?new Date(Date.UTC(r[1],r[2]-1,r[3]||1,r[4]||0,r[5]||0,r[6]||0,r[7]||0)):new Date(r[1],r[2]-1,r[3]||1,r[4]||0,r[5]||0,r[6]||0,r[7]||0)}return new Date(t)}(e),this.init()},d.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},d.$utils=function(){return b},d.isValid=function(){return!("Invalid Date"===this.$d.toString())},d.isSame=function(e,t){var n=y(e);return this.startOf(t)<=n&&n<=this.endOf(t)},d.isAfter=function(e,t){return y(e)=0;d--){var p=o[d];"."===p?s(o,d):".."===p?(s(o,d),f++):f&&(s(o,d),f--)}if(!u)for(;f--;f)o.unshift("..");!u||""===o[0]||o[0]&&c(o[0])||o.unshift("");var h=o.join("/");return n&&"/"!==h.substr(-1)&&(h+="/"),h};function d(e){return e.valueOf?e.valueOf():Object.prototype.valueOf.call(e)}var p=function e(t,n){if(t===n)return!0;if(null==t||null==n)return!1;if(Array.isArray(t))return Array.isArray(n)&&t.length===n.length&&t.every((function(t,r){return e(t,n[r])}));if("object"==typeof t||"object"==typeof n){var r=d(t),o=d(n);return r!==t||o!==n?e(r,o):Object.keys(Object.assign({},t,n)).every((function(r){return e(t[r],n[r])}))}return!1},h=!0,m="Invariant failed";var v=function(e,t){if(!e)throw h?new Error(m):new Error(m+": "+(t||""))};function g(e){return"/"===e.charAt(0)?e:"/"+e}function y(e){return"/"===e.charAt(0)?e.substr(1):e}function b(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function w(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function x(e){var t=e.pathname,n=e.search,r=e.hash,o=t||"/";return n&&"?"!==n&&(o+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(o+="#"===r.charAt(0)?r:"#"+r),o}function S(e,t,n,r){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",o=t.indexOf("#");-1!==o&&(r=t.substr(o),t=t.substr(0,o));var i=t.indexOf("?");return-1!==i&&(n=t.substr(i),t=t.substr(0,i)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e)).state=t:(void 0===(o=Object(l.a)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(i){throw i instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):i}return n&&(o.key=n),r?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=f(o.pathname,r.pathname)):o.pathname=r.pathname:o.pathname||(o.pathname="/"),o}function E(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,o){if(null!=e){var i="function"==typeof e?e(t,n):e;"string"==typeof i?"function"==typeof r?r(i,o):o(!0):o(!1!==i)}else o(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;rt?n.splice(t,n.length-t,r):n.push(r),f({action:"PUSH",location:r,index:t,entries:n})}}))},replace:function(e,t){var r=S(e,t,d(),g.location);s.confirmTransitionTo(r,"REPLACE",n,(function(e){e&&(g.entries[g.index]=r,f({action:"REPLACE",location:r}))}))},go:v,goBack:function(){v(-1)},goForward:function(){v(1)},canGo:function(e){var t=g.index+e;return t>=0&&t=0;t--){var n=e[t][a];n.modified||(Array.isArray(n.base)?K(n)&&A(n):w(n.base)?Q(n)&&A(n):S(n.base)?X(n)&&A(n):G(n)&&A(n))}}function G(e){for(var t=e.base,n=e.draft,r=Object.keys(n),o=r.length-1;o>=0;o--){var i=r[o],u=t[i];if(void 0===u&&!m(t,i))return!0;var l=n[i],c=l&&l[a];if(c?c.base!==u:!g(l,u))return!0}return r.length!==Object.keys(t).length}function K(e){var t=e.draft;if(t.length!==e.base.length)return!0;var n=Object.getOwnPropertyDescriptor(t,t.length-1);return!(!n||n.get)}function Q(e){var t=e.base,n=e.draft;if(t.size!==n.size)return!0;var r=!1;return n.forEach((function(e,n){r||(r=l(e)?e.modified:e!==t.get(n))})),r}function X(e){var t=e.base,n=e.draft;if(t.size!==n.size)return!0;var r=!1;return n.forEach((function(e,n){r||(r=l(e)?e.modified:!t.has(n))})),r}var J,Z,ee=Object.freeze({__proto__:null,willFinalize:function(e,t,n){e.drafts.forEach((function(e){e[a].finalizing=!0})),n?u(t)&&t[a].scope===e&&Y(e.drafts):(e.patches&&function e(t){if(!t||"object"!=typeof t)return;var n=t[a];if(!n)return;var r=n.base,o=n.draft,i=n.assigned;if(Array.isArray(t)){if(K(n)){if(A(n),i.length=!0,o.lengths&&a[f-1]===u[f+c-1];)--f;for(var d=s;d=f;--d){p=t.concat([d]);n[h+d-f]={op:"add",path:p,value:u[d]},r.push({op:"remove",path:p})}}function he(e,t,n,r){var o=e.base,i=e.copy;p(e.assigned,(function(e,a){var u=v(o,e),l=v(i,e),c=a?m(o,e)?"replace":"add":"remove";if(u!==l||"replace"!==c){var s=t.concat(e);n.push("remove"===c?{op:c,path:s}:{op:c,path:s,value:l}),r.push("add"===c?{op:"remove",path:s}:"remove"===c?{op:"add",path:s,value:u}:{op:"replace",path:s,value:u})}}))}function me(e,t,n,r){var o=e.base,i=e.copy,a=0;o.forEach((function(e){if(!i.has(e)){var o=t.concat([a]);n.push({op:"remove",path:o,value:e}),r.unshift({op:"add",path:o,value:e})}a++})),a=0,i.forEach((function(e){if(!o.has(e)){var i=t.concat([a]);n.push({op:"add",path:i,value:e}),r.unshift({op:"remove",path:i,value:e})}a++}))}function ve(e,t){return t.forEach((function(t){var n=t.path,r=t.op;if(!n.length)throw new Error("Illegal state");for(var o=e,i=0;i=0;n--){var r=t[n];if(0===r.path.length&&"replace"===r.op){e=r.value;break}}return u(e)?ve(e,t):this.produce(e,(function(e){return ve(e,t.slice(n+1))}))},e.prototype.processResult=function(e,t){var n=t.drafts[0],r=void 0!==e&&e!==n;if(this.willFinalize(t,e,r),r){if(n[a].modified)throw t.revoke(),new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.");l(e)&&(e=this.finalize(e,null,t),this.maybeFreeze(e)),t.patches&&(t.patches.push({op:"replace",path:[],value:e}),t.inversePatches.push({op:"replace",path:[],value:n[a].base}))}else e=this.finalize(n,[],t);return t.revoke(),t.patches&&t.patchListener(t.patches,t.inversePatches),e!==o?e:void 0},e.prototype.finalize=function(e,t,n){var r=this,o=e[a];if(!o)return Object.isFrozen(e)?e:this.finalizeTree(e,null,n);if(o.scope!==n)return e;if(!o.modified)return this.maybeFreeze(o.base,!0),o.base;if(!o.finalized){if(o.finalized=!0,this.finalizeTree(o.draft,t,n),this.onDelete&&!S(o.base))if(this.useProxies){p(o.assigned,(function(e,t){var n,i;t||null===(i=(n=r).onDelete)||void 0===i||i.call(n,o,e)}))}else{var i=o.base,u=o.copy;p(i,(function(e){var t,n;m(u,e)||null===(n=(t=r).onDelete)||void 0===n||n.call(t,o,e)}))}this.onCopy&&this.onCopy(o),this.autoFreeze&&n.canAutoFreeze&&P(o.copy,!1),t&&n.patches&&function(e,t,n,r){(Array.isArray(e.base)?pe:S(e.base)?me:he)(e,t,n,r)}(o,t,n.patches,n.inversePatches)}return o.copy},e.prototype.finalizeTree=function(e,t,n){var r=this,o=e[a];o&&(this.useProxies||(o.copy=d(o.draft,!0)),e=o.copy);var i=!!t&&!!n.patches,c=function(a,s,f){if(s===f)throw Error("Immer forbids circular references");var d=!!o&&f===e,y=S(f);if(u(s)){var b=d&&i&&!y&&!m(o.assigned,a)?t.concat(a):null;if(function(e,t,n){w(e)?e.set(t,n):S(e)?(e.delete(t),e.add(n)):Array.isArray(e)||h(e,t)?e[t]=n:Object.defineProperty(e,t,{value:n,writable:!0,configurable:!0})}(f,a,s=r.finalize(s,b,n)),u(s)&&(n.canAutoFreeze=!1),d&&s===v(o.base,a))return}else{if(d&&g(s,v(o.base,a)))return;l(s)&&!Object.isFrozen(s)&&(p(s,c),r.maybeFreeze(s))}d&&r.onAssign&&!y&&r.onAssign(o,a,s)};return p(e,c),e},e.prototype.maybeFreeze=function(e,t){void 0===t&&(t=!1),this.autoFreeze&&!u(e)&&P(e,t)},e}()),be=ye.produce;ye.produceWithPatches.bind(ye),ye.setAutoFreeze.bind(ye),ye.setUseProxies.bind(ye),ye.applyPatches.bind(ye),ye.createDraft.bind(ye),ye.finishDraft.bind(ye);t.a=be}).call(this,n(35))},function(e,t,n){"use strict";var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(o){return!1}}()?Object.assign:function(e,t){for(var n,u,l=a(e),c=1;c=200&&e<300}};l.headers={common:{Accept:"application/json, text/plain, */*"}},r.forEach(["delete","get","head"],(function(e){l.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){l.headers[e]=r.merge(i)})),e.exports=l}).call(this,n(35))},function(e,t){var n,r,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function u(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:i}catch(e){n=i}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var l,c=[],s=!1,f=-1;function d(){s&&l&&(s=!1,l.length?c=l.concat(c):f=-1,c.length&&p())}function p(){if(!s){var e=u(d);s=!0;for(var t=c.length;t;){for(l=c,c=[];++f1)for(var n=1;nM.length&&M.push(e)}function j(e,t,n){return null==e?0:function e(t,n,r,o){var u=typeof t;"undefined"!==u&&"boolean"!==u||(t=null);var l=!1;if(null===t)l=!0;else switch(u){case"string":case"number":l=!0;break;case"object":switch(t.$$typeof){case i:case a:l=!0}}if(l)return r(o,t,""===n?"."+N(t,0):n),1;if(l=0,n=""===n?".":n+":",Array.isArray(t))for(var c=0;c