⛄ Sync 2026-11-01 05:43:29
No files matched your search
@@ -5,10 +5,10 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=dae
|
PKG_NAME:=dae
|
||||||
PKG_VERSION:=2026.10.18
|
PKG_VERSION:=2026.10.30
|
||||||
PKG_RELEASE:=52
|
PKG_RELEASE:=53
|
||||||
|
|
||||||
PKG_SOURCE:=dae-src-2026.10.18-32ed09f0d784.tar.gz
|
PKG_SOURCE:=dae-src-2026.10.30-55c59e68c059.tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
||||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=daed
|
PKG_NAME:=daed
|
||||||
PKG_VERSION:=2026.10.18
|
PKG_VERSION:=2026.10.30
|
||||||
PKG_RELEASE:=64
|
PKG_RELEASE:=65
|
||||||
|
|
||||||
PKG_SOURCE:=daed-src-2026.10.18-97c371b47eb5.tar.gz
|
PKG_SOURCE:=daed-src-2026.10.30-1850c01d032c.tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
||||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
PKG_NAME:=dockerd
|
PKG_NAME:=dockerd
|
||||||
PKG_FLAGS:=hold
|
PKG_FLAGS:=hold
|
||||||
PKG_VERSION:=27.3.1
|
PKG_VERSION:=27.3.1
|
||||||
PKG_RELEASE:=6
|
PKG_RELEASE:=8
|
||||||
PKG_LICENSE:=Apache-2.0
|
PKG_LICENSE:=Apache-2.0
|
||||||
PKG_LICENSE_FILES:=LICENSE
|
PKG_LICENSE_FILES:=LICENSE
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,8 @@ start_service() {
|
|||||||
procd_set_param command /usr/bin/dockerd --config-file="${DOCKERD_CONF}"
|
procd_set_param command /usr/bin/dockerd --config-file="${DOCKERD_CONF}"
|
||||||
fi
|
fi
|
||||||
procd_set_param limits nofile="${nofile} ${nofile}"
|
procd_set_param limits nofile="${nofile} ${nofile}"
|
||||||
|
# Give dockerd the time to stop the containers before it gets killed
|
||||||
|
procd_set_param term_timeout 300
|
||||||
procd_close_instance
|
procd_close_instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,8 +288,50 @@ iptables_add_blocking_rule() {
|
|||||||
config_list_foreach "${cfg}" blocked_interfaces handle_iptables_rule "${device}" "${extra_iptables_args}"
|
config_list_foreach "${cfg}" blocked_interfaces handle_iptables_rule "${device}" "${extra_iptables_args}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Read procd's notifications until one contains the given text, or none comes for the given seconds
|
||||||
|
procd_notification() {
|
||||||
|
local line
|
||||||
|
|
||||||
|
while read -r -t "${2}" line <&3; do
|
||||||
|
case "${line}" in
|
||||||
|
*"${1}"*) return 0 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
stop_service() {
|
stop_service() {
|
||||||
if /etc/init.d/dockerd running; then
|
local i=0
|
||||||
service_stop "/usr/bin/dockerd"
|
|
||||||
fi
|
procd_running dockerd || return 0
|
||||||
|
|
||||||
|
# Subscribe to procd's notifications before it stops dockerd, to learn when it is done.
|
||||||
|
# The subscriber leaves procd's lock (fd 1000) to this script, which releases it on exit.
|
||||||
|
STOP_EVENTS="/var/run/dockerd.stop.$$"
|
||||||
|
mkfifo "${STOP_EVENTS}" || return 0
|
||||||
|
ubus -t 330 subscribe service > "${STOP_EVENTS}" 1000>&- &
|
||||||
|
STOP_SUBSCRIBER=$!
|
||||||
|
exec 3< "${STOP_EVENTS}"
|
||||||
|
|
||||||
|
# The subscription takes effect a moment later, and a stop notified before it would be missed:
|
||||||
|
# wait until procd passes on an event sent to itself
|
||||||
|
while [ "${i}" -lt 10 ]; do
|
||||||
|
ubus call service event '{ "type": "dockerd.stopping", "data": {} }'
|
||||||
|
if procd_notification '"type":"dockerd.stopping"' 1; then
|
||||||
|
STOP_SUBSCRIBED=1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
service_stopped() {
|
||||||
|
[ -n "${STOP_EVENTS}" ] || return 0
|
||||||
|
|
||||||
|
# Wait for dockerd, which exits after stopping the containers, so that a shutdown unmounts the data root after them
|
||||||
|
[ -n "${STOP_SUBSCRIBED}" ] && procd_notification '"service.stop": {"service":"dockerd"}' 330
|
||||||
|
exec 3<&-
|
||||||
|
# A restart ignores SIGTERM while it stops dockerd, and so does the subscriber it started
|
||||||
|
kill -KILL "${STOP_SUBSCRIBER}" 2> /dev/null
|
||||||
|
rm -f "${STOP_EVENTS}"
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=gately
|
PKG_NAME:=gately
|
||||||
PKG_VERSION:=0.6.1.1
|
PKG_VERSION:=0.6.2
|
||||||
PKG_RELEASE:=19
|
PKG_RELEASE:=20
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://codeload.github.com/wangwanjie/gately/tar.gz/v$(PKG_VERSION)?
|
PKG_SOURCE_URL:=https://codeload.github.com/wangwanjie/gately/tar.gz/v$(PKG_VERSION)?
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=gowebdav
|
PKG_NAME:=gowebdav
|
||||||
PKG_VERSION:=5.17.2
|
PKG_VERSION:=5.17.3
|
||||||
PKG_RELEASE:=27
|
PKG_RELEASE:=28
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?
|
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=grpcurl
|
PKG_NAME:=grpcurl
|
||||||
PKG_VERSION:=1.9.4
|
PKG_VERSION:=1.9.4
|
||||||
PKG_RELEASE:=8
|
PKG_RELEASE:=9
|
||||||
|
|
||||||
PKG_SOURCE:=v$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=v$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/fullstorydev/grpcurl/archive/refs/tags/
|
PKG_SOURCE_URL:=https://github.com/fullstorydev/grpcurl/archive/refs/tags/
|
||||||
@@ -32,7 +32,7 @@ GO_PKG_LDFLAGS:=-s -w
|
|||||||
GO_PKG_TAGS:=master
|
GO_PKG_TAGS:=master
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
include $(INCLUDE_DIR)/package.mk
|
||||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
include ../golang/golang-package.mk
|
||||||
|
|
||||||
define Package/grpcurl
|
define Package/grpcurl
|
||||||
TITLE:=grpcurl is a command-line tool that lets you interact with gRPC servers
|
TITLE:=grpcurl is a command-line tool that lets you interact with gRPC servers
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ KAIPLUS_ARCH_x86_64:=amd64
|
|||||||
KAIPLUS_ARCH_aarch64:=arm64
|
KAIPLUS_ARCH_aarch64:=arm64
|
||||||
KAIPLUS_ARCH:=$(KAIPLUS_ARCH_$(ARCH))
|
KAIPLUS_ARCH:=$(KAIPLUS_ARCH_$(ARCH))
|
||||||
|
|
||||||
KAIPLUS_HASH_x86_64:=5588a5e1b7874bc5e631ab8ff1288226f567a84d3115808f22352d142c40f5d6
|
KAIPLUS_HASH_x86_64:=9e28ac8f2ec7b59dfa546b5ed58375397dfda3cd95d35b776fe7f252acb8878f
|
||||||
KAIPLUS_HASH_aarch64:=5be29e4715a1181937c0156a6954738ce96fde2f338a1b03cd32c68b00e61524
|
KAIPLUS_HASH_aarch64:=af045078037244a4fd80e21ef5a3ba3a9391918adc4a7791fda5ae777386c122
|
||||||
|
|
||||||
PKG_NAME:=kaiplus
|
PKG_NAME:=kaiplus
|
||||||
PKG_VERSION:=1.0.9
|
PKG_VERSION:=1.0.10
|
||||||
PKG_RELEASE:=15
|
PKG_RELEASE:=16
|
||||||
|
|
||||||
KAIPLUS_URL_FILE:=kaiplus-linux-$(KAIPLUS_ARCH)
|
KAIPLUS_URL_FILE:=kaiplus-linux-$(KAIPLUS_ARCH)
|
||||||
KAIPLUS_DOWNLOAD:=$(KAIPLUS_URL_FILE)-$(KAIPLUS_HASH_$(ARCH))
|
KAIPLUS_DOWNLOAD:=$(KAIPLUS_URL_FILE)-$(KAIPLUS_HASH_$(ARCH))
|
||||||
|
|||||||
@@ -130,13 +130,14 @@ return view.extend({
|
|||||||
|
|
||||||
o = s.option(form.RichListValue, "offloading_type", _("Flow offloading type"));
|
o = s.option(form.RichListValue, "offloading_type", _("Flow offloading type"));
|
||||||
o.value('0', _("None"));
|
o.value('0', _("None"));
|
||||||
o.value('1', _("Software flow offloading"), _('Software based offloading for routing/NAT.'));
|
if (L.hasSystemFeature('offloading'))
|
||||||
|
o.value('1', _("Software flow offloading"), _('Software based offloading for routing/NAT.'));
|
||||||
if (L.hasSystemFeature('offloading_hw'))
|
if (L.hasSystemFeature('offloading_hw'))
|
||||||
o.value('2', _("Hardware flow offloading"), _('Hardware based offloading for routing with/without NAT.') + ' ' + _(' Requires hardware NAT support.'));
|
o.value('2', _("Hardware flow offloading"), _('Hardware based offloading for routing with/without NAT.') + ' ' + _(' Requires hardware NAT support.'));
|
||||||
if (hasMediatekHNAT)
|
if (hasMediatekHNAT)
|
||||||
o.value('3', "Mediatek HNAT", "基于Mediatek原厂闭源驱动的HNAT卸载");
|
o.value('3', _('MediaTek HNAT'), _('Hardware NAT offloading using the MediaTek vendor driver.'));
|
||||||
if (hasQualcommECM)
|
if (hasQualcommECM)
|
||||||
o.value('4', 'Qualcomm NSS ECM 加速', 'ECM Auto 模式,自动选择加速引擎。');
|
o.value('4', _('Qualcomm NSS ECM acceleration'), _('Automatically select the acceleration engine in ECM Auto mode.'));
|
||||||
o.optional = false;
|
o.optional = false;
|
||||||
o.load = function (section_id) {
|
o.load = function (section_id) {
|
||||||
const flow_offloading = uci.get('firewall', section_id, 'flow_offloading');
|
const flow_offloading = uci.get('firewall', section_id, 'flow_offloading');
|
||||||
@@ -146,7 +147,7 @@ return view.extend({
|
|||||||
return '4';
|
return '4';
|
||||||
if (hasMediatekHNAT && flow_offloading === '3')
|
if (hasMediatekHNAT && flow_offloading === '3')
|
||||||
return '3';
|
return '3';
|
||||||
return (flow_offloading === '1')
|
return (flow_offloading === '1' && L.hasSystemFeature('offloading'))
|
||||||
? ((flow_offloading_hw === '1' && has_hw_offloading) ? '2' : '1')
|
? ((flow_offloading_hw === '1' && has_hw_offloading) ? '2' : '1')
|
||||||
: '0';
|
: '0';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1587,3 +1587,19 @@ msgstr "Shell 脚本语法错误。"
|
|||||||
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/custom.js
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/custom.js
|
||||||
msgid "Unable to restart the firewall."
|
msgid "Unable to restart the firewall."
|
||||||
msgstr "无法重启防火墙。"
|
msgstr "无法重启防火墙。"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "MediaTek HNAT"
|
||||||
|
msgstr "MediaTek HNAT"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "Hardware NAT offloading using the MediaTek vendor driver."
|
||||||
|
msgstr "使用 MediaTek 原厂驱动的硬件 NAT 卸载。"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "Qualcomm NSS ECM acceleration"
|
||||||
|
msgstr "Qualcomm NSS ECM 加速"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "Automatically select the acceleration engine in ECM Auto mode."
|
||||||
|
msgstr "在 ECM Auto 模式下自动选择加速引擎。"
|
||||||
@@ -1573,3 +1573,19 @@ msgstr "Shell 指令碼語法錯誤。"
|
|||||||
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/custom.js
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/custom.js
|
||||||
msgid "Unable to restart the firewall."
|
msgid "Unable to restart the firewall."
|
||||||
msgstr "無法重新啟動防火牆。"
|
msgstr "無法重新啟動防火牆。"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "MediaTek HNAT"
|
||||||
|
msgstr "MediaTek HNAT"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "Hardware NAT offloading using the MediaTek vendor driver."
|
||||||
|
msgstr "使用 MediaTek 原廠驅動的硬體 NAT 卸載。"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "Qualcomm NSS ECM acceleration"
|
||||||
|
msgstr "Qualcomm NSS ECM 加速"
|
||||||
|
|
||||||
|
#: applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
|
||||||
|
msgid "Automatically select the acceleration engine in ECM Auto mode."
|
||||||
|
msgstr "在 ECM Auto 模式下自動選擇加速引擎。"
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=luci-app-open-box
|
PKG_NAME:=luci-app-open-box
|
||||||
PKG_VERSION:=0.1.378
|
PKG_VERSION:=0.1.383
|
||||||
PKG_RELEASE:=54
|
PKG_RELEASE:=55
|
||||||
|
|
||||||
PKG_MAINTAINER:=liandu2024
|
PKG_MAINTAINER:=liandu2024
|
||||||
PKG_LICENSE:=GPL-3.0-or-later
|
PKG_LICENSE:=GPL-3.0-or-later
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-openvpn-plus
|
PKG_NAME:=luci-app-openvpn-plus
|
||||||
PKG_VERSION:=1.0.0
|
PKG_VERSION:=1.0.0
|
||||||
PKG_RELEASE:=10
|
PKG_RELEASE:=11
|
||||||
PKG_LICENSE:=Apache-2.0
|
PKG_LICENSE:=Apache-2.0
|
||||||
PKG_MAINTAINER:=Manfred Jaider <info@masmbit.com>
|
PKG_MAINTAINER:=Manfred Jaider <info@masmbit.com>
|
||||||
|
|
||||||
|
|||||||
@@ -51,15 +51,15 @@ Even though the traffic has to pass through **Double-NAT** and simple port forwa
|
|||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
| 01 - Server/Client View | 02 - Status View |
|
| Server/Client View | Status View |
|
||||||
| :---: | :---: |
|
| :---: | :---: |
|
||||||
| <img width="1200" alt="01" src="https://github.com/user-attachments/assets/b5f13132-0414-4490-9c80-9a361e1bda40" /> | <img width="1200" alt="02" src="https://github.com/user-attachments/assets/7635f2bd-4839-45e4-9804-d01ce38bde7b" />
|
|  |  |
|
||||||
| <img width="2398" alt="03" src="https://github.com/user-attachments/assets/0170376e-3dfe-4ca3-8f03-d450cc7ed0e4" /> | <img width="2430" alt="15" src="https://github.com/user-attachments/assets/1710419c-4f14-4594-a3a4-d8147ec1fe50" />
|
|  |  |
|
||||||
| <img width="1796" alt="09" src="https://github.com/user-attachments/assets/1c77aefe-197d-4e64-ab87-898ff50faab1" /> | <img width="1798" alt="10" src="https://github.com/user-attachments/assets/f37365a6-4090-443b-89fe-05a5cd92e714" />
|
|  |  |
|
||||||
| <img width="1798" alt="13" src="https://github.com/user-attachments/assets/147250df-079c-4f77-9c5c-00819b4b495c" /> | <img width="1800" alt="11" src="https://github.com/user-attachments/assets/814bbeb0-a0b8-4b38-93b8-03a978d75692" />
|
|  |  |
|
||||||
| <img width="1798" alt="12" src="https://github.com/user-attachments/assets/57726ec2-c18d-4531-96ec-0d58a818648a" /> | <img width="2384" alt="04" src="https://github.com/user-attachments/assets/8470fa8c-b4a4-4369-b9c5-bfbd8e27526d" />
|
|  |  |
|
||||||
| <img width="2382" alt="05" src="https://github.com/user-attachments/assets/1ef91c41-675c-40d8-a9da-644370ac2fdf" /> | <img width="2394" alt="06" src="https://github.com/user-attachments/assets/95fbdf13-142f-4f67-9117-15eef266e304" />
|
|  |  |
|
||||||
|
|  | 
|
||||||
|
|
||||||
## Configuration Layout
|
## Configuration Layout
|
||||||
|
|
||||||
@@ -84,6 +84,7 @@ luci-app-openvpn-plus/
|
|||||||
│ │ └── luci-app-openvpn-plus.po
|
│ │ └── luci-app-openvpn-plus.po
|
||||||
│ ├── zh-cn/
|
│ ├── zh-cn/
|
||||||
│ │ └── luci-app-openvpn-plus.po
|
│ │ └── luci-app-openvpn-plus.po
|
||||||
|
├── screenshots/
|
||||||
└── root/
|
└── root/
|
||||||
├── etc/
|
├── etc/
|
||||||
│ ├── config/
|
│ ├── config/
|
||||||
|
|||||||
@@ -730,6 +730,33 @@ openssldgst() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Clean up empty symlink folder
|
||||||
|
internal_cleansymlink() {
|
||||||
|
# internal function (no shift)
|
||||||
|
local ram_link_dir="${1}"
|
||||||
|
local www_link_dir="${2}"
|
||||||
|
|
||||||
|
# Remove the temporary RAM directory ONLY if it is completely empty
|
||||||
|
[ -d "${ram_link_dir}" ] && rmdir "${ram_link_dir}" 2>/dev/null
|
||||||
|
|
||||||
|
# Smart Web-Folder Cleanup: Check if files other than index.html exist
|
||||||
|
if [ -d "${www_link_dir}" ]; then
|
||||||
|
local has_profiles="0"
|
||||||
|
local check_link
|
||||||
|
# If a real symlink or folder matching the pattern exists, has_profiles becomes 1
|
||||||
|
for check_link in "${www_link_dir}"/ovpn-tmp-*-*; do
|
||||||
|
if [ -e "${check_link}" ] || [ -L "${check_link}" ]; then
|
||||||
|
has_profiles="1"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Remove the web folder if no active profile links are left
|
||||||
|
if [ "${has_profiles}" = "0" ]; then
|
||||||
|
rm -rf "${www_link_dir}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Manage temporary download links for mobile profiles [create|delete]
|
# Manage temporary download links for mobile profiles [create|delete]
|
||||||
symlink() {
|
symlink() {
|
||||||
shift # Remove "symlink" from the arguments list
|
shift # Remove "symlink" from the arguments list
|
||||||
@@ -834,25 +861,8 @@ symlink() {
|
|||||||
# Remove the temporary session files for this instance from RAM
|
# Remove the temporary session files for this instance from RAM
|
||||||
rm -rf "${ram_link_dir}"/ovpn-tmp-*-"${ovpn_instance}"
|
rm -rf "${ram_link_dir}"/ovpn-tmp-*-"${ovpn_instance}"
|
||||||
|
|
||||||
# Remove RAM base directory if it has become completely empty
|
# Clean up empty symlink folder
|
||||||
rmdir "${ram_link_dir}" 2>/dev/null
|
internal_cleansymlink "${ram_link_dir}" "${www_link_dir}"
|
||||||
|
|
||||||
# Smart Web-Folder Cleanup: Check if files other than index.html exist
|
|
||||||
if [ -d "${www_link_dir}" ]; then
|
|
||||||
local has_profiles="0"
|
|
||||||
local check_link
|
|
||||||
# If a real symlink or folder matching the pattern exists, has_profiles becomes 1
|
|
||||||
for check_link in "${www_link_dir}"/ovpn-tmp-*-*; do
|
|
||||||
if [ -e "${check_link}" ] || [ -L "${check_link}" ]; then
|
|
||||||
has_profiles="1"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Remove the web folder if no active profile links are left
|
|
||||||
if [ "${has_profiles}" = "0" ]; then
|
|
||||||
rm -rf "${www_link_dir}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "%s\n" "SUCCESS: Link directory and static profile deleted from RAM."
|
printf "%s\n" "SUCCESS: Link directory and static profile deleted from RAM."
|
||||||
else
|
else
|
||||||
@@ -869,7 +879,6 @@ symlink() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Helper to prevent routing loops for IPv4 and IPv6 subnets
|
# Helper to prevent routing loops for IPv4 and IPv6 subnets
|
||||||
internal_checkloopback() {
|
internal_checkloopback() {
|
||||||
# internal (no shift)
|
# internal (no shift)
|
||||||
@@ -1944,7 +1953,6 @@ internal_cleaninst() {
|
|||||||
if internal_checkname "${instname}"; then
|
if internal_checkname "${instname}"; then
|
||||||
# Safely remove files, dead symlinks or directories
|
# Safely remove files, dead symlinks or directories
|
||||||
rm -rf "${f}"
|
rm -rf "${f}"
|
||||||
logger -t openvpn-luci "Cleanup: Removed orphaned item '${f}'."
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -1988,20 +1996,7 @@ cleanup() {
|
|||||||
# Step 3: Remove orphan symlink files
|
# Step 3: Remove orphan symlink files
|
||||||
internal_cleaninst "${active_list}" "${www_link_dir}/ovpn-tmp-*-*"
|
internal_cleaninst "${active_list}" "${www_link_dir}/ovpn-tmp-*-*"
|
||||||
internal_cleaninst "${active_list}" "${ram_link_dir}/ovpn-tmp-*-*"
|
internal_cleaninst "${active_list}" "${ram_link_dir}/ovpn-tmp-*-*"
|
||||||
|
internal_cleansymlink "${ram_link_dir}" "${www_link_dir}"
|
||||||
# Remove the temporary RAM directory ONLY if it is completely empty
|
|
||||||
[ -d "${ram_link_dir}" ] && rmdir "${ram_link_dir}" 2>/dev/null
|
|
||||||
|
|
||||||
# Smart Web-Folder Cleanup: Check if files other than index.html exist
|
|
||||||
if [ -d "${www_link_dir}" ]; then
|
|
||||||
local found_profiles
|
|
||||||
found_profiles="${www_link_dir}/ovpn-tmp-"*-*
|
|
||||||
|
|
||||||
# If the text still has a '*', no active profiles were found
|
|
||||||
if [ "${found_profiles}" = "${www_link_dir}/ovpn-tmp-*-*" ]; then
|
|
||||||
rm -rf "${www_link_dir}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -774,10 +774,11 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w
|
|||||||
if (optionalShowBtnCancel !== true) {
|
if (optionalShowBtnCancel !== true) {
|
||||||
styleHide = ' display:none !important;';
|
styleHide = ' display:none !important;';
|
||||||
}
|
}
|
||||||
|
const backupHide = (wizardParams && wizardParams.isServerBackup) ? '' : styleHide;
|
||||||
|
|
||||||
const yesBtn = E('button', { 'class': 'btn cbi-button cbi-button-action important', 'style': 'margin-right:10px;' }, TXT.KEY.generate_unique_keys);
|
const yesBtn = E('button', { 'class': 'btn cbi-button cbi-button-action important', 'style': 'margin-right:10px;' }, TXT.KEY.generate_unique_keys);
|
||||||
const noBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' }, TXT.KEY.clone_default_keys);
|
const noBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' }, TXT.KEY.clone_default_keys);
|
||||||
const backupBtn = E('button', { 'class': 'btn cbi-button cbi-button-positive important', 'style': 'margin-right:10px;' }, TXT.BTN.backup);
|
const backupBtn = E('button', { 'class': 'btn cbi-button cbi-button-positive important', 'style': 'margin-right:10px;' + backupHide }, TXT.BTN.backup);
|
||||||
const cancelBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' + styleHide }, TXT.BTN.cancel);
|
const cancelBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' + styleHide }, TXT.BTN.cancel);
|
||||||
const closeBtn = E('button', { 'class': 'cbi-button cbi-button-action important', 'style': 'display:none;' }, TXT.BTN.close);
|
const closeBtn = E('button', { 'class': 'cbi-button cbi-button-action important', 'style': 'display:none;' }, TXT.BTN.close);
|
||||||
|
|
||||||
@@ -904,7 +905,14 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w
|
|||||||
callbacks.uciSaveApplyRestart(instance_id);
|
callbacks.uciSaveApplyRestart(instance_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
L.ui.showModal(TXT.KEY.add_server_instance + ' (' + instance_id + ')', [
|
let displayName;
|
||||||
|
if (cnName) {
|
||||||
|
displayName = ': ' + cnName;
|
||||||
|
} else {
|
||||||
|
displayName = ' (' + instance_id + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
L.ui.showModal(TXT.KEY.add_server_instance + displayName, [
|
||||||
E('div', { 'class': 'cbi-map' }, [
|
E('div', { 'class': 'cbi-map' }, [
|
||||||
E('div', { 'class': 'cbi-section' }, [
|
E('div', { 'class': 'cbi-section' }, [
|
||||||
promptMessageNode,
|
promptMessageNode,
|
||||||
|
|||||||
@@ -392,54 +392,10 @@ const getValidCommonName = function (name) {
|
|||||||
return cleanName;
|
return cleanName;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate and clean any given IPv4 or IPv6 address string
|
|
||||||
*/
|
|
||||||
const checkIPAddress = function (rawIp) {
|
|
||||||
if (!rawIp) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all spaces, tabs and hidden lines from the text (tr -d ' \t\r\n')
|
|
||||||
const ip = rawIp.replace(/[\s\t\r\n]/g, '');
|
|
||||||
if (ip === '') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ipv4Regex = /^([0-9]{1,3}\.){3}[0-9]{1,3}$/;
|
|
||||||
|
|
||||||
// FIXED: Standard compliant IPv6 pattern that correctly handles "::" and blocks ":::"
|
|
||||||
const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}$|^:(?::[0-9a-fA-F]{1,4}){1,7}$|^::$/;
|
|
||||||
|
|
||||||
// Case 1: Check if the text is a valid IPv4 address (numbers 0 to 255)
|
|
||||||
if (ipv4Regex.test(ip)) {
|
|
||||||
const segments = ip.split('.');
|
|
||||||
const octet1 = parseInt(segments[0], 10);
|
|
||||||
const octet2 = parseInt(segments[1], 10);
|
|
||||||
const octet3 = parseInt(segments[2], 10);
|
|
||||||
const octet4 = parseInt(segments[3], 10);
|
|
||||||
|
|
||||||
// Match the awk rule: $1<=255 && $2<=255 && $3<=255 && $4<=255
|
|
||||||
if (octet1 <= 255 && octet2 <= 255 && octet3 <= 255 && octet4 <= 255) {
|
|
||||||
return { ip: ip, isV6: false };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case 2: Check if the text matches the verified IPv6 regex
|
|
||||||
if (ipv6Regex.test(ip)) {
|
|
||||||
if (ip.indexOf(':') !== -1) {
|
|
||||||
// Change all hex letters to lowercase for safe matching later (tr 'A-Z' 'a-z')
|
|
||||||
return { ip: ip.toLowerCase(), isV6: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an isolated, self-validating Site-to-Site network inputs row layout block
|
* Generates an isolated, self-validating Site-to-Site network inputs row layout block
|
||||||
*/
|
*/
|
||||||
const renderSubnetInputs = function () {
|
const renderSubnetInputs = function (checkIpCallback) {
|
||||||
// Create the 3 input fields with simple, easy text placeholders
|
// Create the 3 input fields with simple, easy text placeholders
|
||||||
const cnInput = E('input', { 'type': 'text', 'class': 'cbi-input-text', 'placeholder': TXT.INFO.remote_office_name, 'style': 'width:32%; font-family:monospace; margin-right:1%;' });
|
const cnInput = E('input', { 'type': 'text', 'class': 'cbi-input-text', 'placeholder': TXT.INFO.remote_office_name, 'style': 'width:32%; font-family:monospace; margin-right:1%;' });
|
||||||
const subnetInput = E('input', { 'type': 'text', 'class': 'cbi-input-text', 'placeholder': TXT.INFO.lan_ip + ' (192.168.2.0)', 'style': 'width:32%; font-family:monospace; margin-right:1%;' });
|
const subnetInput = E('input', { 'type': 'text', 'class': 'cbi-input-text', 'placeholder': TXT.INFO.lan_ip + ' (192.168.2.0)', 'style': 'width:32%; font-family:monospace; margin-right:1%;' });
|
||||||
@@ -503,7 +459,7 @@ const renderSubnetInputs = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Validate IP Address using your verified template logic
|
// 2. Validate IP Address using your verified template logic
|
||||||
const ipResult = checkIPAddress(rawSub);
|
const ipResult = (typeof checkIpCallback === 'function') ? checkIpCallback(rawSub) : null
|
||||||
if (!ipResult) {
|
if (!ipResult) {
|
||||||
subnetInput.classList.add('cbi-input-invalid');
|
subnetInput.classList.add('cbi-input-invalid');
|
||||||
hasValidationError = true;
|
hasValidationError = true;
|
||||||
@@ -1111,7 +1067,6 @@ const showMessage = function (text, type, box) {
|
|||||||
box.style.setProperty('display', 'block', 'important');
|
box.style.setProperty('display', 'block', 'important');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const hideMessage = function (box) {
|
const hideMessage = function (box) {
|
||||||
box.style.setProperty('display', 'none', 'important');
|
box.style.setProperty('display', 'none', 'important');
|
||||||
}
|
}
|
||||||
@@ -1411,7 +1366,7 @@ const renderOptionSiteServer = function (portForwardingAlert, wizardData) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 4. Load the input fields row component
|
// 4. Load the input fields row component
|
||||||
const networkInputComponent = renderSubnetInputs();
|
const networkInputComponent = renderSubnetInputs(wizardData.networkCallbacks.checkIp);
|
||||||
|
|
||||||
// 5. Create the "Add" button
|
// 5. Create the "Add" button
|
||||||
const addEntryBtn = E('button', {
|
const addEntryBtn = E('button', {
|
||||||
|
|||||||
@@ -572,27 +572,71 @@ const sha256sum = async function (textToHash) {
|
|||||||
return 'ERROR';
|
return 'ERROR';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate and clean any given IPv4 or IPv6 address string safely
|
||||||
|
*/
|
||||||
|
const checkIp = function (rawIp) {
|
||||||
|
if (!rawIp) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all spaces, tabs and hidden lines from the text (tr -d ' \t\r\n')
|
||||||
|
const ip = rawIp.replace(/[\s\t\r\n]/g, '').trim();
|
||||||
|
if (ip === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ipv4Regex = /^([0-9]{1,3}\.){3}[0-9]{1,3}$/;
|
||||||
|
const ipv6Regex = /^([\da-fA-F]{1,4}:|:){1,7}([\da-fA-F]{1,4}|:)$/;
|
||||||
|
|
||||||
|
// Case 1: Check if the text is a valid IPv4 address (numbers 0 to 255)
|
||||||
|
if (ipv4Regex.test(ip)) {
|
||||||
|
const segments = ip.split('.');
|
||||||
|
if (parseInt(segments[0], 10) <= 255 &&
|
||||||
|
parseInt(segments, 10) <= 255 &&
|
||||||
|
parseInt(segments, 10) <= 255 &&
|
||||||
|
parseInt(segments, 10) <= 255) {
|
||||||
|
return { ip: ip, isV6: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2: Check if the text is a syntactically valid IPv6 address. Extra check for triple colons (:::) to keep the regex small
|
||||||
|
if (ipv6Regex.test(ip) && ip.indexOf(':') !== -1 && ip.indexOf(':::') === -1) {
|
||||||
|
return { ip: ip.toLowerCase(), isV6: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global helper function to determine the fallback IP and mask from browser or UCI config.
|
* Global helper function to determine the fallback IP and mask from browser or UCI config.
|
||||||
*/
|
*/
|
||||||
const getHostnameFallback = function (returnMask) {
|
const getHostnameFallback = function (returnMask) {
|
||||||
// Basic regex patterns to check for valid IPv4 or IPv6 layouts
|
|
||||||
const ipv4Pattern = /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/;
|
|
||||||
const ipv6Pattern = /^(([0-9a-fA-F]{1,4}:){1,7}|:)(:[0-9a-fA-F]{1,4}){1,7}$|^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
|
|
||||||
|
|
||||||
// Get the address string from the browser URL bar and remove IPv6 brackets
|
// Get the address string from the browser URL bar and remove IPv6 brackets
|
||||||
let ip = (window.location.hostname || "").trim().replace(/^\[|\]$/g, '');
|
let browserIp = (window.location.hostname || "").trim().replace(/^\[|\]$/g, '');
|
||||||
|
let ip = '';
|
||||||
let mask = '255.255.255.0';
|
let mask = '255.255.255.0';
|
||||||
|
|
||||||
// If browser URL is text (like openwrt.lan), fallback to configured UCI IPs
|
// Use our solid validation function to check the browser IP
|
||||||
if (!ipv4Pattern.test(ip) && !ipv6Pattern.test(ip)) {
|
let checked = checkIp(browserIp);
|
||||||
ip = L.uci.get('network', 'lan', 'ipaddr') || L.uci.get('network', 'lan', 'ip6addr') || '192.168.1.1';
|
|
||||||
|
if (checked) {
|
||||||
|
ip = checked.ip;
|
||||||
|
} else {
|
||||||
|
// If browser URL is a hostname (like openwrt.lan), fallback to configured UCI IPs
|
||||||
|
let uciIp = L.uci.get('network', 'lan', 'ipaddr') || L.uci.get('network', 'lan', 'ip6addr') || '192.168.1.1';
|
||||||
|
|
||||||
|
// Remove any trailing subnet mask from UCI strings (like /64) safely
|
||||||
|
ip = uciIp.split('/')[0].trim();
|
||||||
|
|
||||||
|
// Re-validate the UCI fallback IP to know its family (v4 or v6)
|
||||||
|
checked = checkIp(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the matching subnet mask format based on the detected IP family
|
// Set the matching subnet mask format based on the detected IP family
|
||||||
if (ipv4Pattern.test(ip)) {
|
if (checked && checked.isV6 === false) {
|
||||||
mask = L.uci.get('network', 'lan', 'netmask') || OPENVPN.IPv4.MASK24;
|
mask = L.uci.get('network', 'lan', 'netmask') || OPENVPN.IPv4.MASK24;
|
||||||
} else if (ipv6Pattern.test(ip)) {
|
} else if (checked && checked.isV6 === true) {
|
||||||
mask = L.uci.get('network', 'lan', 'ip6assign') || '64';
|
mask = L.uci.get('network', 'lan', 'ip6assign') || '64';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,7 +1009,7 @@ const checkNetworkStructure = function (appData) {
|
|||||||
gatewayIp = gw4;
|
gatewayIp = gw4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check WAN IPv4 addresses for Double NAT and extract WAN net-boundaries securely [sh]
|
// Check WAN IPv4 addresses for Double NAT and extract WAN net-boundaries securely
|
||||||
const ipaddrs = net.getIPAddrs ? net.getIPAddrs() : [];
|
const ipaddrs = net.getIPAddrs ? net.getIPAddrs() : [];
|
||||||
let deviceMask = (net.getNetmask && net.getNetmask()) ? net.getNetmask() : (net.data && net.data.netmask) ? net.data.netmask : null;
|
let deviceMask = (net.getNetmask && net.getNetmask()) ? net.getNetmask() : (net.data && net.data.netmask) ? net.data.netmask : null;
|
||||||
if (deviceMask === null || deviceMask === undefined || deviceMask === '') {
|
if (deviceMask === null || deviceMask === undefined || deviceMask === '') {
|
||||||
@@ -977,7 +1021,7 @@ const checkNetworkStructure = function (appData) {
|
|||||||
const wanIpStr = String(rawWanIp);
|
const wanIpStr = String(rawWanIp);
|
||||||
const ip = wanIpStr.split('/')[0];
|
const ip = wanIpStr.split('/')[0];
|
||||||
|
|
||||||
// If the WAN IP is private, we have a Double NAT [sh]
|
// If the WAN IP is private, we have a Double NAT
|
||||||
if (privateIpRegex.test(ip)) {
|
if (privateIpRegex.test(ip)) {
|
||||||
isDoubleNat = true;
|
isDoubleNat = true;
|
||||||
}
|
}
|
||||||
@@ -1004,7 +1048,7 @@ const checkNetworkStructure = function (appData) {
|
|||||||
gatewayIp = OPENVPN.IPv4.ZERO;
|
gatewayIp = OPENVPN.IPv4.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set values to global object (Fixed: changed 'gateway' to 'gatewayIp')
|
// Set values to global object
|
||||||
networkStructure.doubleNat = isDoubleNat;
|
networkStructure.doubleNat = isDoubleNat;
|
||||||
networkStructure.apMode = isApMode;
|
networkStructure.apMode = isApMode;
|
||||||
networkStructure.gateway = gatewayIp;
|
networkStructure.gateway = gatewayIp;
|
||||||
@@ -1038,6 +1082,7 @@ const checkNetworkStructure = function (appData) {
|
|||||||
*/
|
*/
|
||||||
const networkCallbacks = ({
|
const networkCallbacks = ({
|
||||||
sha256sum: sha256sum,
|
sha256sum: sha256sum,
|
||||||
|
checkIp: checkIp,
|
||||||
getLanIp: getLanIp,
|
getLanIp: getLanIp,
|
||||||
queryPublicIp: queryPublicIp,
|
queryPublicIp: queryPublicIp,
|
||||||
getDdnsOrPublicIp: getDdnsOrPublicIp,
|
getDdnsOrPublicIp: getDdnsOrPublicIp,
|
||||||
@@ -2785,7 +2830,7 @@ const openSiteToSiteExportModal = function (instance_id, nextId, serverAssets, a
|
|||||||
selectDropdown.appendChild(E('option', { 'disabled': true }, ['- - -']));
|
selectDropdown.appendChild(E('option', { 'disabled': true }, ['- - -']));
|
||||||
selectDropdown.appendChild(E('option', { 'value': 'backup_server_profile' }, [ICON.BACKUP + TXT.MSG.backup_server_profile + ': ' + displayId]));
|
selectDropdown.appendChild(E('option', { 'value': 'backup_server_profile' }, [ICON.BACKUP + TXT.MSG.backup_server_profile + ': ' + displayId]));
|
||||||
|
|
||||||
const subnetInputs = appData.wizardClass.renderSubnetInputs();
|
const subnetInputs = appData.wizardClass.renderSubnetInputs(networkCallbacks.checkIp);
|
||||||
|
|
||||||
// Creates a hidden yellowish warning message box for network conflicts or duplicate names
|
// Creates a hidden yellowish warning message box for network conflicts or duplicate names
|
||||||
const errorDisplayNode = E('div', {
|
const errorDisplayNode = E('div', {
|
||||||
@@ -4789,7 +4834,7 @@ const generate_s2s_nft_file = async function (deviceName, localSubnet, remoteSub
|
|||||||
|
|
||||||
let lanDevice = 'br-lan';
|
let lanDevice = 'br-lan';
|
||||||
const res = await luci_app_openvpn_plus([CFG.LIBEXEC.getl3dev]);
|
const res = await luci_app_openvpn_plus([CFG.LIBEXEC.getl3dev]);
|
||||||
if (res && res !='' && !res.includes("ERROR:")) {
|
if (res && res != '' && !res.includes("ERROR:")) {
|
||||||
lanDevice = res;
|
lanDevice = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 231 KiB |
|
After Width: | Height: | Size: 278 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 95 KiB |
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-passwall
|
PKG_NAME:=luci-app-passwall
|
||||||
PKG_VERSION:=26.10.29
|
PKG_VERSION:=26.10.29
|
||||||
PKG_RELEASE:=359
|
PKG_RELEASE:=360
|
||||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||||
|
|
||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
|
|||||||
@@ -963,17 +963,17 @@ function read_rulelist()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local backup_files = {
|
local backup_files = {
|
||||||
"/etc/config/passwall",
|
"/etc/config/passwall",
|
||||||
"/etc/config/passwall_server",
|
"/etc/config/passwall_server",
|
||||||
"/etc/passwall/rules/block_host",
|
"/etc/passwall/rules/block_host",
|
||||||
"/etc/passwall/rules/block_ip",
|
"/etc/passwall/rules/block_ip",
|
||||||
"/etc/passwall/rules/direct_host",
|
"/etc/passwall/rules/direct_host",
|
||||||
"/etc/passwall/rules/direct_ip",
|
"/etc/passwall/rules/direct_ip",
|
||||||
"/etc/passwall/rules/proxy_host",
|
"/etc/passwall/rules/proxy_host",
|
||||||
"/etc/passwall/rules/proxy_ip",
|
"/etc/passwall/rules/proxy_ip",
|
||||||
"/etc/passwall/rules/lanlist_ipv4",
|
"/etc/passwall/rules/lanlist_ipv4",
|
||||||
"/etc/passwall/rules/lanlist_ipv6",
|
"/etc/passwall/rules/lanlist_ipv6",
|
||||||
"/etc/passwall/rules/domains_excluded"
|
"/etc/passwall/rules/domains_excluded"
|
||||||
}
|
}
|
||||||
|
|
||||||
function create_backup()
|
function create_backup()
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ o.validate = port_validate
|
|||||||
---- UDP No Redir Ports
|
---- UDP No Redir Ports
|
||||||
local UDP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_no_redir_ports")
|
local UDP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_no_redir_ports")
|
||||||
o = s:taboption("Proxy", Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
|
o = s:taboption("Proxy", Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
|
||||||
"<font color='red'>" ..
|
"<font color='red'>" ..
|
||||||
translate("Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
|
translate("Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
|
||||||
"</font>")
|
"</font>")
|
||||||
o:value("", translate("Use global config") .. "(" .. UDP_NO_REDIR_PORTS .. ")")
|
o:value("", translate("Use global config") .. "(" .. UDP_NO_REDIR_PORTS .. ")")
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ o.rawhtml = true
|
|||||||
o.cfgvalue = function(self, section)
|
o.cfgvalue = function(self, section)
|
||||||
return string.format([[
|
return string.format([[
|
||||||
<input type="button" class="btn cbi-button cbi-button-apply" onclick="ManualSubscribeAll()" value="%s" />]],
|
<input type="button" class="btn cbi-button cbi-button-apply" onclick="ManualSubscribeAll()" value="%s" />]],
|
||||||
translate("Manual subscription All"))
|
translate("Manual subscription All"))
|
||||||
end
|
end
|
||||||
|
|
||||||
s = m:section(TypedSection, "subscribe_list", "", "<font color='red'>" .. translate("When adding a new subscription, please save and apply before manually subscribing. If you only change the subscription URL, you can subscribe manually, and the system will save it automatically.") .. "</font>")
|
s = m:section(TypedSection, "subscribe_list", "", "<font color='red'>" .. translate("When adding a new subscription, please save and apply before manually subscribing. If you only change the subscription URL, you can subscribe manually, and the system will save it automatically.") .. "</font>")
|
||||||
|
|||||||
@@ -68,9 +68,9 @@ o.validate = port_validate
|
|||||||
|
|
||||||
---- UDP No Redir Ports
|
---- UDP No Redir Ports
|
||||||
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
|
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
|
||||||
"<font color='red'>" .. translate(
|
"<font color='red'>" .. translate(
|
||||||
"Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
|
"Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
|
||||||
"</font>")
|
"</font>")
|
||||||
o.default = "disable"
|
o.default = "disable"
|
||||||
o:value("disable", translate("No patterns are used"))
|
o:value("disable", translate("No patterns are used"))
|
||||||
o:value("1:65535", translate("All"))
|
o:value("1:65535", translate("All"))
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ s.type_name = type_name
|
|||||||
s.option_prefix = "socks_"
|
s.option_prefix = "socks_"
|
||||||
api.set_type_cbi(s)
|
api.set_type_cbi(s)
|
||||||
|
|
||||||
o = s:option(ListValue, "del_protocol", " ") --始终隐藏,用于删除 protocol
|
o = s:option(ListValue, "del_protocol", " ") --始终隐藏,用于删除 protocol
|
||||||
o:depends({ __hide = "1" })
|
o:depends({ __hide = "1" })
|
||||||
o.rewrite_option = "protocol"
|
o.rewrite_option = "protocol"
|
||||||
|
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ if load_urltest_options then -- [[ URLTest Start ]]
|
|||||||
o = s:option(Flag, "urltest_interrupt_exist_connections", translate("Interrupt existing connections"))
|
o = s:option(Flag, "urltest_interrupt_exist_connections", translate("Interrupt existing connections"))
|
||||||
o:depends({ protocol = "_urltest" })
|
o:depends({ protocol = "_urltest" })
|
||||||
o.default = "0"
|
o.default = "0"
|
||||||
o.description = translate("Interrupt existing connections when the selected outbound has changed.")
|
o.description = translate("Interrupt existing connections when the selected outbound has changed.")
|
||||||
end -- [[ URLTest End ]]
|
end -- [[ URLTest End ]]
|
||||||
|
|
||||||
if load_iface_options then -- [[ 自定义接口 Start ]]
|
if load_iface_options then -- [[ 自定义接口 Start ]]
|
||||||
|
|||||||
@@ -2037,7 +2037,7 @@ function gen_config(var)
|
|||||||
table.insert(dns.servers, remote_server)
|
table.insert(dns.servers, remote_server)
|
||||||
|
|
||||||
fakedns_tag = "remote_fakeip"
|
fakedns_tag = "remote_fakeip"
|
||||||
if remote_dns_fake or inner_fakedns == "1" then
|
if remote_dns_fake or inner_fakedns == "1" then
|
||||||
table.insert(dns.servers, {
|
table.insert(dns.servers, {
|
||||||
tag = fakedns_tag,
|
tag = fakedns_tag,
|
||||||
type = "fakeip",
|
type = "fakeip",
|
||||||
@@ -2178,7 +2178,7 @@ function gen_config(var)
|
|||||||
local remote_dns_server = api.clone(remote_server)
|
local remote_dns_server = api.clone(remote_server)
|
||||||
remote_dns_server.tag = value.shunt_tag
|
remote_dns_server.tag = value.shunt_tag
|
||||||
local is_local = (remote_server.address and api.is_local_ip(remote_server.address)) or
|
local is_local = (remote_server.address and api.is_local_ip(remote_server.address)) or
|
||||||
(remote_server.server and api.is_local_ip(remote_server.server)) --dns为本地ip,不走代理
|
(remote_server.server and api.is_local_ip(remote_server.server)) --dns为本地ip,不走代理
|
||||||
remote_dns_server.detour = is_local and "direct" or value.outboundTag
|
remote_dns_server.detour = is_local and "direct" or value.outboundTag
|
||||||
table.insert(dns.servers, remote_dns_server)
|
table.insert(dns.servers, remote_dns_server)
|
||||||
dns_rule.server = remote_dns_server.tag
|
dns_rule.server = remote_dns_server.tag
|
||||||
|
|||||||
@@ -175,7 +175,7 @@
|
|||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
return function(colorStr) {
|
return function(colorStr) {
|
||||||
if (!colorStr)
|
if (!colorStr)
|
||||||
return null;
|
return null;
|
||||||
ctx.clearRect(0, 0, 1, 1);
|
ctx.clearRect(0, 0, 1, 1);
|
||||||
// 2. Apply the color
|
// 2. Apply the color
|
||||||
ctx.fillStyle = colorStr;
|
ctx.fillStyle = colorStr;
|
||||||
|
|||||||
@@ -169,7 +169,7 @@
|
|||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
return function(colorStr) {
|
return function(colorStr) {
|
||||||
if (!colorStr)
|
if (!colorStr)
|
||||||
return null;
|
return null;
|
||||||
ctx.clearRect(0, 0, 1, 1);
|
ctx.clearRect(0, 0, 1, 1);
|
||||||
// 2. Apply the color
|
// 2. Apply the color
|
||||||
ctx.fillStyle = colorStr;
|
ctx.fillStyle = colorStr;
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ local groups = {}
|
|||||||
local group_order = {}
|
local group_order = {}
|
||||||
for _, item in ipairs(values) do
|
for _, item in ipairs(values) do
|
||||||
local g = item.group
|
local g = item.group
|
||||||
if not g or g == "" then
|
if not g or g == "" then
|
||||||
g = translate("default")
|
g = translate("default")
|
||||||
end
|
end
|
||||||
if not groups[g] then
|
if not groups[g] then
|
||||||
groups[g] = {}
|
groups[g] = {}
|
||||||
table.insert(group_order, g)
|
table.insert(group_order, g)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
return function(colorStr) {
|
return function(colorStr) {
|
||||||
if (!colorStr)
|
if (!colorStr)
|
||||||
return null;
|
return null;
|
||||||
ctx.clearRect(0, 0, 1, 1);
|
ctx.clearRect(0, 0, 1, 1);
|
||||||
// 2. Apply the color
|
// 2. Apply the color
|
||||||
ctx.fillStyle = colorStr;
|
ctx.fillStyle = colorStr;
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
const keyword = searchInput.value.trim().toLowerCase();
|
const keyword = searchInput.value.trim().toLowerCase();
|
||||||
const isSearching = keyword.length > 0;
|
const isSearching = keyword.length > 0;
|
||||||
// 当前实例下的所有 checkbox
|
// 当前实例下的所有 checkbox
|
||||||
const cbs = isSearching
|
const cbs = isSearching
|
||||||
? Array.from(nodeList.querySelectorAll("input[type=checkbox]")).filter(cb => cb.closest("li").style.display !== "none")
|
? Array.from(nodeList.querySelectorAll("input[type=checkbox]")).filter(cb => cb.closest("li").style.display !== "none")
|
||||||
: nodeList.querySelectorAll("input[type=checkbox]");
|
: nodeList.querySelectorAll("input[type=checkbox]");
|
||||||
let checked = 0;
|
let checked = 0;
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ local dropdown_data = {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!-- 隐藏 input(保存实际配置值) -->
|
<!-- 隐藏 input(保存实际配置值) -->
|
||||||
<input type="text" id="<%=cbid%>" name="<%=cbid%>" value="<%=current_key%>" data-update="change" style="display:none !important;" />
|
<input type="text" id="<%=cbid%>" name="<%=cbid%>" value="<%=current_key%>" data-update="change" style="display:none !important;" />
|
||||||
<!-- 模拟 ListValue 控件外观 -->
|
<!-- 模拟 ListValue 控件外观 -->
|
||||||
<div class="cbi-input-value cbi-input-select v-dropdown-display" id="<%=cbid%>.display" tabindex="0">
|
<div class="cbi-input-value cbi-input-select v-dropdown-display" id="<%=cbid%>.display" tabindex="0">
|
||||||
<span id="<%=cbid%>.label" class="v-dropdown-label" title="<%=pcdata(current_label)%>">
|
<span id="<%=cbid%>.label" class="v-dropdown-label" title="<%=pcdata(current_label)%>">
|
||||||
|
|||||||
@@ -164,7 +164,7 @@
|
|||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
return function(colorStr) {
|
return function(colorStr) {
|
||||||
if (!colorStr)
|
if (!colorStr)
|
||||||
return null;
|
return null;
|
||||||
ctx.clearRect(0, 0, 1, 1);
|
ctx.clearRect(0, 0, 1, 1);
|
||||||
// 2. Apply the color
|
// 2. Apply the color
|
||||||
ctx.fillStyle = colorStr;
|
ctx.fillStyle = colorStr;
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ local appname = map.config
|
|||||||
local sid = "@global[0]"
|
local sid = "@global[0]"
|
||||||
-%>
|
-%>
|
||||||
<style>
|
<style>
|
||||||
#cbi-<%=appname%>-socks .td.cbi-value-field > div {
|
#cbi-<%=appname%>-socks .td.cbi-value-field > div {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
@media (max-width: 1152px) {
|
@media (max-width: 1152px) {
|
||||||
#cbi-<%=appname%>-socks .td.cbi-value-field > div {
|
#cbi-<%=appname%>-socks .td.cbi-value-field > div {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ local sid = "@global[0]"
|
|||||||
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_socks_log")%>?name=' + id.replace("cbi-<%=appname%>-", "") + '\', \'_blank\')"><%:Log%></a>';
|
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_socks_log")%>?name=' + id.replace("cbi-<%=appname%>-", "") + '\', \'_blank\')"><%:Log%></a>';
|
||||||
|
|
||||||
node_select.insertAdjacentHTML("afterend",
|
node_select.insertAdjacentHTML("afterend",
|
||||||
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
|
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
|
||||||
+ html + '</div>'
|
+ html + '</div>'
|
||||||
);
|
);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
//<![CDATA[
|
//<![CDATA[
|
||||||
var imgs = document.getElementsByTagName('img');
|
var imgs = document.getElementsByTagName('img');
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ local has_old_geosite = fs.access("/tmp/bak_v2ray/geosite.dat")
|
|||||||
var checkBoxList = [];
|
var checkBoxList = [];
|
||||||
var len = domList.length;
|
var len = domList.length;
|
||||||
while(len--) {
|
while(len--) {
|
||||||
var dom = domList[len];
|
var dom = domList[len];
|
||||||
if(dom.type == 'checkbox' && dom.checked) {
|
if(dom.type == 'checkbox' && dom.checked) {
|
||||||
checkBoxList.push(dom.name);
|
checkBoxList.push(dom.name);
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ local has_old_geosite = fs.access("/tmp/bak_v2ray/geosite.dat")
|
|||||||
var checkBoxList = [];
|
var checkBoxList = [];
|
||||||
var len = domList.length;
|
var len = domList.length;
|
||||||
while(len--) {
|
while(len--) {
|
||||||
var dom = domList[len];
|
var dom = domList[len];
|
||||||
if(dom.type == 'checkbox' && dom.checked) {
|
if(dom.type == 'checkbox' && dom.checked) {
|
||||||
if(type == 'geoip' && (dom.name === 'chnroute' || dom.name === 'chnroute6')) {
|
if(type == 'geoip' && (dom.name === 'chnroute' || dom.name === 'chnroute6')) {
|
||||||
checkBoxList.push(dom.name);
|
checkBoxList.push(dom.name);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ EEOF
|
|||||||
|
|
||||||
if [ -e "/etc/config/ucitrack" ]; then
|
if [ -e "/etc/config/ucitrack" ]; then
|
||||||
uci -q batch <<-EOF
|
uci -q batch <<-EOF
|
||||||
delete ucitrack.@passwall[-1]
|
delete ucitrack.@passwall[-1]
|
||||||
add ucitrack passwall
|
add ucitrack passwall
|
||||||
set ucitrack.@passwall[-1].init=passwall
|
set ucitrack.@passwall[-1].init=passwall
|
||||||
commit ucitrack
|
commit ucitrack
|
||||||
|
|||||||
@@ -1096,7 +1096,7 @@ start_dns() {
|
|||||||
local ACL_RULE_DNSMASQ=0
|
local ACL_RULE_DNSMASQ=0
|
||||||
for acl_section in $(uci show ${CONFIG} | grep "=acl_rule" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do
|
for acl_section in $(uci show ${CONFIG} | grep "=acl_rule" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do
|
||||||
if [ "$(config_n_get $acl_section enabled)" = "1" ] && \
|
if [ "$(config_n_get $acl_section enabled)" = "1" ] && \
|
||||||
[ "$(config_n_get $acl_section dns_shunt)" = "dnsmasq" ]; then
|
[ "$(config_n_get $acl_section dns_shunt)" = "dnsmasq" ]; then
|
||||||
ACL_RULE_DNSMASQ=1
|
ACL_RULE_DNSMASQ=1
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
@@ -1109,7 +1109,7 @@ start_dns() {
|
|||||||
china_ng_local_dns=${LOCAL_DNS}
|
china_ng_local_dns=${LOCAL_DNS}
|
||||||
v2ray_local_dns="direct_dns_udp_server=${LOCAL_DNS}"
|
v2ray_local_dns="direct_dns_udp_server=${LOCAL_DNS}"
|
||||||
;;
|
;;
|
||||||
tcp)
|
tcp)
|
||||||
local DIRECT_DNS=$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")
|
local DIRECT_DNS=$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")
|
||||||
china_ng_local_dns="tcp://${DIRECT_DNS}"
|
china_ng_local_dns="tcp://${DIRECT_DNS}"
|
||||||
v2ray_local_dns="direct_dns_tcp_server=${DIRECT_DNS}"
|
v2ray_local_dns="direct_dns_tcp_server=${DIRECT_DNS}"
|
||||||
|
|||||||
@@ -625,7 +625,7 @@ local function encode_node(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function parseClashNode(raw, remark)
|
function parseClashNode(raw, remark)
|
||||||
if not raw then return "" end
|
if not raw then return "" end
|
||||||
domain_redir = {}
|
domain_redir = {}
|
||||||
local ok, lyaml = pcall(require, "lyaml")
|
local ok, lyaml = pcall(require, "lyaml")
|
||||||
if not ok then return raw end
|
if not ok then return raw end
|
||||||
|
|||||||
@@ -605,7 +605,7 @@ if CHNLIST == "proxy" then DEFAULT_TAG = "chn" end
|
|||||||
--全局模式,默认使用远程DNS
|
--全局模式,默认使用远程DNS
|
||||||
if only_global then
|
if only_global then
|
||||||
DEFAULT_TAG = "gfw"
|
DEFAULT_TAG = "gfw"
|
||||||
if NO_IPV6_TRUST == "1" and not IS_SHUNT_NODE then
|
if NO_IPV6_TRUST == "1" and not IS_SHUNT_NODE then
|
||||||
table.insert(config_lines, "no-ipv6")
|
table.insert(config_lines, "no-ipv6")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -761,7 +761,7 @@ function add_rule(var)
|
|||||||
server_out:close()
|
server_out:close()
|
||||||
ipset_out:close()
|
ipset_out:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
if not GEO_SUCCESS then new_text = "" end
|
if not GEO_SUCCESS then new_text = "" end
|
||||||
local f_out = io.open(CACHE_TEXT_FILE, "a")
|
local f_out = io.open(CACHE_TEXT_FILE, "a")
|
||||||
f_out:write(new_text)
|
f_out:write(new_text)
|
||||||
|
|||||||
@@ -172,9 +172,7 @@ local auto_config_lines = {}
|
|||||||
-- 直连规则显式覆盖远程兜底的缓存、IPv6、测速和双栈优选设置。
|
-- 直连规则显式覆盖远程兜底的缓存、IPv6、测速和双栈优选设置。
|
||||||
local DIRECT_RULE_OPTIONS = " -enable-cache -address -6 -speed-check-mode ping,tcp:80,tcp:443 -response-mode first-ping -d yes"
|
local DIRECT_RULE_OPTIONS = " -enable-cache -address -6 -speed-check-mode ping,tcp:80,tcp:443 -response-mode first-ping -d yes"
|
||||||
--设置默认 DNS 分组(兜底组),全局代理模式仍使用远程组。
|
--设置默认 DNS 分组(兜底组),全局代理模式仍使用远程组。
|
||||||
local DEFAULT_DNS_GROUP = (USE_DEFAULT_DNS == "direct" and "direct") or
|
local DEFAULT_DNS_GROUP = (USE_DEFAULT_DNS == "direct" and "direct") or (USE_DEFAULT_DNS == "remote" and "proxy") or (USE_DEFAULT_DNS == "auto" and AUTO_GROUP)
|
||||||
(USE_DEFAULT_DNS == "remote" and "proxy") or
|
|
||||||
(USE_DEFAULT_DNS == "auto" and AUTO_GROUP)
|
|
||||||
local only_global = (DEFAULT_PROXY_MODE == "proxy" and CHN_LIST == "0" and USE_GFW_LIST == "0") and 1
|
local only_global = (DEFAULT_PROXY_MODE == "proxy" and CHN_LIST == "0" and USE_GFW_LIST == "0") and 1
|
||||||
if only_global == 1 then
|
if only_global == 1 then
|
||||||
DEFAULT_DNS_GROUP = "proxy"
|
DEFAULT_DNS_GROUP = "proxy"
|
||||||
|
|||||||
@@ -817,7 +817,7 @@ filter_node() {
|
|||||||
local address=$(config_n_get "$node" address)
|
local address=$(config_n_get "$node" address)
|
||||||
local port=$(config_n_get "$node" port)
|
local port=$(config_n_get "$node" port)
|
||||||
local hop=$(config_n_get "$node" hysteria2_hop)
|
local hop=$(config_n_get "$node" hysteria2_hop)
|
||||||
[ -n "$hop" ] && port="${port:+$port,}$hop"
|
[ -n "$hop" ] && port="${port:+$port,}$hop"
|
||||||
[ -z "$address" ] && return 1
|
[ -z "$address" ] && return 1
|
||||||
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
|
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
|
||||||
[ -z "$port" ] && return 1
|
[ -z "$port" ] && return 1
|
||||||
|
|||||||
@@ -197,20 +197,20 @@ insert_nftset() {
|
|||||||
RS = "[ \t\n\r]+"
|
RS = "[ \t\n\r]+"
|
||||||
ORS = ""
|
ORS = ""
|
||||||
}
|
}
|
||||||
$0 != "" {
|
$0 != "" {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
printf "add element %s %s { \n", t, n
|
printf "add element %s %s { \n", t, n
|
||||||
first = 1
|
first = 1
|
||||||
} else {
|
} else {
|
||||||
print ",\n"
|
print ",\n"
|
||||||
}
|
}
|
||||||
printf " %s", $0
|
printf " %s", $0
|
||||||
}
|
}
|
||||||
END {
|
END {
|
||||||
if (first) {
|
if (first) {
|
||||||
print "\n }\n"
|
print "\n }\n"
|
||||||
}
|
}
|
||||||
}' | nft -f -
|
}' | nft -f -
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_nftset() {
|
gen_nftset() {
|
||||||
@@ -300,7 +300,7 @@ load_acl() {
|
|||||||
use_shunt_node=0
|
use_shunt_node=0
|
||||||
[ -n "$node" ] && [ "$(config_n_get $node protocol)" = "_shunt" ] && use_shunt_node=1
|
[ -n "$node" ] && [ "$(config_n_get $node protocol)" = "_shunt" ] && use_shunt_node=1
|
||||||
|
|
||||||
[ "${use_global_config}" = "1" ] && {
|
[ "${use_global_config}" = "1" ] && {
|
||||||
if [ "$(config_get_type $NODE)" = "socks" ]; then
|
if [ "$(config_get_type $NODE)" = "socks" ]; then
|
||||||
node_remark="Socks 配置($(config_n_get $NODE port) 端口)"
|
node_remark="Socks 配置($(config_n_get $NODE port) 端口)"
|
||||||
else
|
else
|
||||||
@@ -856,7 +856,7 @@ filter_node() {
|
|||||||
local address=$(config_n_get "$node" address)
|
local address=$(config_n_get "$node" address)
|
||||||
local port=$(config_n_get "$node" port)
|
local port=$(config_n_get "$node" port)
|
||||||
local hop=$(config_n_get "$node" hysteria2_hop)
|
local hop=$(config_n_get "$node" hysteria2_hop)
|
||||||
[ -n "$hop" ] && port="${port:+$port,}$hop"
|
[ -n "$hop" ] && port="${port:+$port,}$hop"
|
||||||
[ -z "$address" ] && return 1
|
[ -z "$address" ] && return 1
|
||||||
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
|
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
|
||||||
[ -z "$port" ] && return 1
|
[ -z "$port" ] && return 1
|
||||||
@@ -954,7 +954,7 @@ add_firewall_rule() {
|
|||||||
add_script_mwan3
|
add_script_mwan3
|
||||||
mwan3_start
|
mwan3_start
|
||||||
set_tproxy_sysctl
|
set_tproxy_sysctl
|
||||||
gen_nftset $NFTSET_WAN ipv4_addr 0
|
gen_nftset $NFTSET_WAN ipv4_addr 0
|
||||||
gen_nftset $NFTSET_VPS ipv4_addr 0
|
gen_nftset $NFTSET_VPS ipv4_addr 0
|
||||||
gen_nftset $NFTSET_GFW ipv4_addr "2d"
|
gen_nftset $NFTSET_GFW ipv4_addr "2d"
|
||||||
gen_nftset $NFTSET_LOCAL ipv4_addr 0
|
gen_nftset $NFTSET_LOCAL ipv4_addr 0
|
||||||
@@ -976,7 +976,7 @@ add_firewall_rule() {
|
|||||||
gen_nftset $NFTSET_SHUNT ipv4_addr "2d"
|
gen_nftset $NFTSET_SHUNT ipv4_addr "2d"
|
||||||
gen_nftset $NFTSET_SHUNT_STATIC ipv4_addr 0
|
gen_nftset $NFTSET_SHUNT_STATIC ipv4_addr 0
|
||||||
|
|
||||||
gen_nftset $NFTSET_WAN6 ipv6_addr 0
|
gen_nftset $NFTSET_WAN6 ipv6_addr 0
|
||||||
gen_nftset $NFTSET_VPS6 ipv6_addr 0
|
gen_nftset $NFTSET_VPS6 ipv6_addr 0
|
||||||
gen_nftset $NFTSET_GFW6 ipv6_addr "2d"
|
gen_nftset $NFTSET_GFW6 ipv6_addr "2d"
|
||||||
gen_nftset $NFTSET_LOCAL6 ipv6_addr 0
|
gen_nftset $NFTSET_LOCAL6 ipv6_addr 0
|
||||||
@@ -1502,7 +1502,7 @@ add_firewall_rule() {
|
|||||||
|
|
||||||
del_firewall_rule() {
|
del_firewall_rule() {
|
||||||
for nft in "dstnat" "srcnat" "nat_output" "mangle_prerouting" "mangle_output"; do
|
for nft in "dstnat" "srcnat" "nat_output" "mangle_prerouting" "mangle_output"; do
|
||||||
local handles=$(nft -a list chain $NFTABLE_NAME ${nft} 2>/dev/null | grep -E "PSW_" | awk -F '# handle ' '{print$2}')
|
local handles=$(nft -a list chain $NFTABLE_NAME ${nft} 2>/dev/null | grep -E "PSW_" | awk -F '# handle ' '{print$2}')
|
||||||
for handle in $handles; do
|
for handle in $handles; do
|
||||||
nft delete rule $NFTABLE_NAME ${nft} handle ${handle} 2>/dev/null
|
nft delete rule $NFTABLE_NAME ${nft} handle ${handle} 2>/dev/null
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ local function extract_domain(s)
|
|||||||
local last_dot = nil
|
local last_dot = nil
|
||||||
for i = 1, len do
|
for i = 1, len do
|
||||||
local b = s:byte(i)
|
local b = s:byte(i)
|
||||||
-- 允许的域名字符:a-zA-Z0-9.-
|
-- 允许的域名字符:a-zA-Z0-9.-
|
||||||
if (b >= 48 and b <= 57) or (b >= 65 and b <= 90) or (b >= 97 and b <= 122) or b == 45 or b == 46 then
|
if (b >= 48 and b <= 57) or (b >= 65 and b <= 90) or (b >= 97 and b <= 122) or b == 45 or b == 46 then
|
||||||
if not start then start = i end
|
if not start then start = i end
|
||||||
if b == 46 then last_dot = i end
|
if b == 46 then last_dot = i end
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
|
|||||||
result.protocol = hostInfo[#hostInfo-3]
|
result.protocol = hostInfo[#hostInfo-3]
|
||||||
result.method = hostInfo[#hostInfo-2]
|
result.method = hostInfo[#hostInfo-2]
|
||||||
result.obfs = hostInfo[#hostInfo-1]
|
result.obfs = hostInfo[#hostInfo-1]
|
||||||
result.password = base64Decode(hostInfo[#hostInfo])
|
result.password = base64Decode(hostInfo[#hostInfo])
|
||||||
local params = {}
|
local params = {}
|
||||||
for _, v in pairs(split(dat[2], '&')) do
|
for _, v in pairs(split(dat[2], '&')) do
|
||||||
local s = v:find("=", 1, true)
|
local s = v:find("=", 1, true)
|
||||||
@@ -695,7 +695,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
|
|||||||
|
|
||||||
if not info.net then info.net = "tcp" end
|
if not info.net then info.net = "tcp" end
|
||||||
info.net = string.lower(info.net)
|
info.net = string.lower(info.net)
|
||||||
if result.type == "sing-box" and info.net == "raw" then
|
if result.type == "sing-box" and info.net == "raw" then
|
||||||
info.net = "tcp"
|
info.net = "tcp"
|
||||||
elseif result.type == "Xray" and info.net == "tcp" then
|
elseif result.type == "Xray" and info.net == "tcp" then
|
||||||
info.net = "raw"
|
info.net = "raw"
|
||||||
@@ -962,7 +962,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
|
|||||||
|
|
||||||
if params.type then
|
if params.type then
|
||||||
params.type = string.lower(params.type)
|
params.type = string.lower(params.type)
|
||||||
if result.type == "sing-box" and params.type == "raw" then
|
if result.type == "sing-box" and params.type == "raw" then
|
||||||
params.type = "tcp"
|
params.type = "tcp"
|
||||||
elseif result.type == "Xray" and params.type == "tcp" then
|
elseif result.type == "Xray" and params.type == "tcp" then
|
||||||
params.type = "raw"
|
params.type = "raw"
|
||||||
@@ -1175,7 +1175,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
|
|||||||
|
|
||||||
if not params.type then params.type = "tcp" end
|
if not params.type then params.type = "tcp" end
|
||||||
params.type = string.lower(params.type)
|
params.type = string.lower(params.type)
|
||||||
if result.type == "sing-box" and params.type == "raw" then
|
if result.type == "sing-box" and params.type == "raw" then
|
||||||
params.type = "tcp"
|
params.type = "tcp"
|
||||||
elseif result.type == "Xray" and params.type == "tcp" then
|
elseif result.type == "Xray" and params.type == "tcp" then
|
||||||
params.type = "raw"
|
params.type = "raw"
|
||||||
@@ -1302,7 +1302,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
|
|||||||
if ({ xhttp=true, kcp=true, mkcp=true })[params.type] and result.type ~= "Xray" and has_xray then
|
if ({ xhttp=true, kcp=true, mkcp=true })[params.type] and result.type ~= "Xray" and has_xray then
|
||||||
result.type = "Xray"
|
result.type = "Xray"
|
||||||
end
|
end
|
||||||
if result.type == "sing-box" and params.type == "raw" then
|
if result.type == "sing-box" and params.type == "raw" then
|
||||||
params.type = "tcp"
|
params.type = "tcp"
|
||||||
elseif result.type == "Xray" and params.type == "tcp" then
|
elseif result.type == "Xray" and params.type == "tcp" then
|
||||||
params.type = "raw"
|
params.type = "raw"
|
||||||
@@ -2117,7 +2117,7 @@ local function update_node(manual)
|
|||||||
uci_set(cfgid, "chain_proxy", "3")
|
uci_set(cfgid, "chain_proxy", "3")
|
||||||
uci_set(cfgid, "outbound_iface", outbound_iface_group)
|
uci_set(cfgid, "outbound_iface", outbound_iface_group)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-passwall2
|
PKG_NAME:=luci-app-passwall2
|
||||||
PKG_VERSION:=26.10.29
|
PKG_VERSION:=26.10.29
|
||||||
PKG_RELEASE:=160
|
PKG_RELEASE:=161
|
||||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||||
|
|
||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ o.rawhtml = true
|
|||||||
o.cfgvalue = function(self, section)
|
o.cfgvalue = function(self, section)
|
||||||
return string.format([[
|
return string.format([[
|
||||||
<input type="button" class="btn cbi-button cbi-button-apply" onclick="ManualSubscribeAll()" value="%s" />]],
|
<input type="button" class="btn cbi-button cbi-button-apply" onclick="ManualSubscribeAll()" value="%s" />]],
|
||||||
translate("Manual subscription All"))
|
translate("Manual subscription All"))
|
||||||
end
|
end
|
||||||
|
|
||||||
s = m:section(TypedSection, "subscribe_list", "", "<font color='red'>" .. translate("When adding a new subscription, please save and apply before manually subscribing. If you only change the subscription URL, you can subscribe manually, and the system will save it automatically.") .. "</font>")
|
s = m:section(TypedSection, "subscribe_list", "", "<font color='red'>" .. translate("When adding a new subscription, please save and apply before manually subscribing. If you only change the subscription URL, you can subscribe manually, and the system will save it automatically.") .. "</font>")
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ if has_xray then
|
|||||||
|
|
||||||
o = s_xray:option(Flag, "fragment", translate("Fragment"), translate("TCP fragments, which can deceive the censorship system in some cases, such as bypassing SNI blacklists."))
|
o = s_xray:option(Flag, "fragment", translate("Fragment"), translate("TCP fragments, which can deceive the censorship system in some cases, such as bypassing SNI blacklists."))
|
||||||
o.default = 0
|
o.default = 0
|
||||||
|
|
||||||
o = s_xray:option(ListValue, "fragment_packets", translate("Fragment Packets"), translate(" \"1-3\" is for segmentation at TCP layer, applying to the beginning 1 to 3 data writes by the client. \"tlshello\" is for TLS client hello packet fragmentation."))
|
o = s_xray:option(ListValue, "fragment_packets", translate("Fragment Packets"), translate(" \"1-3\" is for segmentation at TCP layer, applying to the beginning 1 to 3 data writes by the client. \"tlshello\" is for TLS client hello packet fragmentation."))
|
||||||
o.default = "tlshello"
|
o.default = "tlshello"
|
||||||
o:value("tlshello", "tlshello")
|
o:value("tlshello", "tlshello")
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ m.description = (m.description or "") .. [[
|
|||||||
<style>
|
<style>
|
||||||
/* Scope to passwall2 CBI fields so it won't leak elsewhere */
|
/* Scope to passwall2 CBI fields so it won't leak elsewhere */
|
||||||
div[id^="cbid.passwall2."] .cbi-value-field {
|
div[id^="cbid.passwall2."] .cbi-value-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
div[id^="cbid.passwall2."] .cbi-checkbox {
|
div[id^="cbid.passwall2."] .cbi-checkbox {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
]]
|
]]
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ o = s:option(Value, "autoswitch_retry_num", translate("Timeout retry num"))
|
|||||||
o.datatype = "min(1)"
|
o.datatype = "min(1)"
|
||||||
o.default = 1
|
o.default = 1
|
||||||
o:depends("enable_autoswitch", true)
|
o:depends("enable_autoswitch", true)
|
||||||
|
|
||||||
o = s:option(Flag, "autoswitch_restore_switch", translate("Restore Switch"), translate("When detects main node is available, switch back to the main node."))
|
o = s:option(Flag, "autoswitch_restore_switch", translate("Restore Switch"), translate("When detects main node is available, switch back to the main node."))
|
||||||
o:depends("enable_autoswitch", true)
|
o:depends("enable_autoswitch", true)
|
||||||
|
|
||||||
|
|||||||
@@ -715,7 +715,7 @@ if singbox_tags:find("with_utls") then
|
|||||||
|
|
||||||
o = s:option(Value, "reality_publicKey", translate("Public Key"))
|
o = s:option(Value, "reality_publicKey", translate("Public Key"))
|
||||||
o:depends({ reality = true })
|
o:depends({ reality = true })
|
||||||
|
|
||||||
o = s:option(Value, "reality_shortId", translate("Short Id"))
|
o = s:option(Value, "reality_shortId", translate("Short Id"))
|
||||||
o:depends({ reality = true })
|
o:depends({ reality = true })
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -304,10 +304,10 @@ if singbox_tags:find("with_utls") then
|
|||||||
o:depends({ protocol = "vless", tls = true })
|
o:depends({ protocol = "vless", tls = true })
|
||||||
o:depends({ protocol = "trojan", tls = true })
|
o:depends({ protocol = "trojan", tls = true })
|
||||||
o:depends({ protocol = "anytls", tls = true })
|
o:depends({ protocol = "anytls", tls = true })
|
||||||
|
|
||||||
o = s:option(Value, "reality_private_key", translate("Private Key"))
|
o = s:option(Value, "reality_private_key", translate("Private Key"))
|
||||||
o:depends({ reality = true })
|
o:depends({ reality = true })
|
||||||
|
|
||||||
o = s:option(Value, "reality_shortId", translate("Short Id"))
|
o = s:option(Value, "reality_shortId", translate("Short Id"))
|
||||||
o:depends({ reality = true })
|
o:depends({ reality = true })
|
||||||
|
|
||||||
|
|||||||
@@ -536,7 +536,7 @@ function strToTable(str)
|
|||||||
if str == nil or type(str) ~= "string" then
|
if str == nil or type(str) ~= "string" then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
return loadstring("return " .. str)()
|
return loadstring("return " .. str)()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -922,7 +922,7 @@ function clone(org)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local res = {}
|
local res = {}
|
||||||
copy(org, res)
|
copy(org, res)
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ function gen_config(var)
|
|||||||
fast_open = (node.tcp_fast_open and node.tcp_fast_open == "1") and true or false,
|
fast_open = (node.tcp_fast_open and node.tcp_fast_open == "1") and true or false,
|
||||||
reuse_port = true
|
reuse_port = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.type == "SSR" then
|
if node.type == "SSR" then
|
||||||
config.protocol = node.protocol
|
config.protocol = node.protocol
|
||||||
config.protocol_param = node.protocol_param
|
config.protocol_param = node.protocol_param
|
||||||
@@ -115,7 +115,7 @@ function gen_config(var)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return jsonc.stringify(config, 1)
|
return jsonc.stringify(config, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1730,7 +1730,7 @@ function gen_config(var)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local rule = {
|
local rule = {
|
||||||
action = "route",
|
action = "route",
|
||||||
inbound = inboundTag,
|
inbound = inboundTag,
|
||||||
@@ -2326,7 +2326,7 @@ function gen_config(var)
|
|||||||
table.insert(route.rule_set, v)
|
table.insert(route.rule_set, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if inbounds or outbounds then
|
if inbounds or outbounds then
|
||||||
local config = {
|
local config = {
|
||||||
log = {
|
log = {
|
||||||
@@ -2468,7 +2468,7 @@ function gen_proto_config(var)
|
|||||||
}
|
}
|
||||||
if outbound then table.insert(outbounds, outbound) end
|
if outbound then table.insert(outbounds, outbound) end
|
||||||
end
|
end
|
||||||
|
|
||||||
local config = {
|
local config = {
|
||||||
log = {
|
log = {
|
||||||
disabled = true,
|
disabled = true,
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ function gen_outbound(flag, node, tag, proxy_table)
|
|||||||
else
|
else
|
||||||
dialer_proxy_tag = proxy_tag
|
dialer_proxy_tag = proxy_tag
|
||||||
end
|
end
|
||||||
|
|
||||||
if node.type == "Xray" then
|
if node.type == "Xray" then
|
||||||
if node.tls and node.tls == "1" then
|
if node.tls and node.tls == "1" then
|
||||||
node.stream_security = "tls"
|
node.stream_security = "tls"
|
||||||
@@ -982,7 +982,7 @@ function gen_config(var)
|
|||||||
local routing = nil
|
local routing = nil
|
||||||
local observatory = nil
|
local observatory = nil
|
||||||
local burstObservatory = nil
|
local burstObservatory = nil
|
||||||
local strategy = nil
|
local strategy = nil
|
||||||
local COMMON = {}
|
local COMMON = {}
|
||||||
|
|
||||||
local CACHE_TEXT_FILE = CACHE_PATH .. "/cache_" .. flag .. ".txt"
|
local CACHE_TEXT_FILE = CACHE_PATH .. "/cache_" .. flag .. ".txt"
|
||||||
@@ -1253,7 +1253,7 @@ function gen_config(var)
|
|||||||
table.insert(rules, { inboundTag = { inbound_tag }, balancerTag = balancer_tag })
|
table.insert(rules, { inboundTag = { inbound_tag }, balancerTag = balancer_tag })
|
||||||
return balancer_tag, loopback_outbound
|
return balancer_tag, loopback_outbound
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_outbound_detour(node, outbound, outbounds_table)
|
function set_outbound_detour(node, outbound, outbounds_table)
|
||||||
if not node or not outbound or not outbounds_table then return nil end
|
if not node or not outbound or not outbounds_table then return nil end
|
||||||
local default_outTag = outbound.tag
|
local default_outTag = outbound.tag
|
||||||
@@ -1684,7 +1684,7 @@ function gen_config(var)
|
|||||||
rules = {}
|
rules = {}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local dns_host = ""
|
local dns_host = ""
|
||||||
if flag == "global" then
|
if flag == "global" then
|
||||||
dns_host = api.uci_get_c("@global[0]", "dns_hosts") or ""
|
dns_host = api.uci_get_c("@global[0]", "dns_hosts") or ""
|
||||||
@@ -1708,7 +1708,7 @@ function gen_config(var)
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local _remote_dns = {
|
local _remote_dns = {
|
||||||
tag = remote_dns_tag,
|
tag = remote_dns_tag,
|
||||||
queryStrategy = (remote_dns_query_strategy and remote_dns_query_strategy ~= "") and remote_dns_query_strategy or "UseIPv4"
|
queryStrategy = (remote_dns_query_strategy and remote_dns_query_strategy ~= "") and remote_dns_query_strategy or "UseIPv4"
|
||||||
@@ -1858,12 +1858,12 @@ function gen_config(var)
|
|||||||
outboundTag = "dns-out"
|
outboundTag = "dns-out"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local default_dns_tag_name = remote_dns_tag
|
local default_dns_tag_name = remote_dns_tag
|
||||||
if not COMMON.default_outbound_tag or COMMON.default_outbound_tag == "direct" then
|
if not COMMON.default_outbound_tag or COMMON.default_outbound_tag == "direct" then
|
||||||
default_dns_tag_name = direct_dns_tag
|
default_dns_tag_name = direct_dns_tag
|
||||||
end
|
end
|
||||||
|
|
||||||
if dns_servers and #dns_servers > 0 then
|
if dns_servers and #dns_servers > 0 then
|
||||||
-- Default DNS logic
|
-- Default DNS logic
|
||||||
local default_dns_server = nil
|
local default_dns_server = nil
|
||||||
@@ -2064,7 +2064,7 @@ function gen_config(var)
|
|||||||
udp_inbound.settings.allowedNetwork = "udp"
|
udp_inbound.settings.allowedNetwork = "udp"
|
||||||
table.insert(inbounds, udp_inbound)
|
table.insert(inbounds, udp_inbound)
|
||||||
end
|
end
|
||||||
|
|
||||||
if inbounds or outbounds then
|
if inbounds or outbounds then
|
||||||
local config = {
|
local config = {
|
||||||
env = (function()
|
env = (function()
|
||||||
@@ -2138,7 +2138,7 @@ function gen_config(var)
|
|||||||
for index, value in ipairs(config.outbounds) do
|
for index, value in ipairs(config.outbounds) do
|
||||||
local s = value.settings
|
local s = value.settings
|
||||||
if not value["_flag_proxy_tag"] and value["_id"] and s and not NO_RUN and
|
if not value["_flag_proxy_tag"] and value["_id"] and s and not NO_RUN and
|
||||||
((s.vnext and s.vnext[1] and s.vnext[1].address and s.vnext[1].port) or
|
((s.vnext and s.vnext[1] and s.vnext[1].address and s.vnext[1].port) or
|
||||||
(s.servers and s.servers[1] and s.servers[1].address and s.servers[1].port) or
|
(s.servers and s.servers[1] and s.servers[1].address and s.servers[1].port) or
|
||||||
(s.peers and s.peers[1] and s.peers[1].endpoint) or
|
(s.peers and s.peers[1] and s.peers[1].endpoint) or
|
||||||
(s.address and s.port)) then
|
(s.address and s.port)) then
|
||||||
@@ -2168,7 +2168,7 @@ function gen_proto_config(var)
|
|||||||
local server_port = var["server_port"]
|
local server_port = var["server_port"]
|
||||||
local server_username = var["server_username"]
|
local server_username = var["server_username"]
|
||||||
local server_password = var["server_password"]
|
local server_password = var["server_password"]
|
||||||
|
|
||||||
local inbounds = {}
|
local inbounds = {}
|
||||||
local outbounds = {}
|
local outbounds = {}
|
||||||
local routing = nil
|
local routing = nil
|
||||||
@@ -2194,7 +2194,7 @@ function gen_proto_config(var)
|
|||||||
end
|
end
|
||||||
table.insert(inbounds, inbound)
|
table.insert(inbounds, inbound)
|
||||||
end
|
end
|
||||||
|
|
||||||
if local_http_address and local_http_port then
|
if local_http_address and local_http_port then
|
||||||
local inbound = {
|
local inbound = {
|
||||||
listen = local_http_address,
|
listen = local_http_address,
|
||||||
@@ -2214,7 +2214,7 @@ function gen_proto_config(var)
|
|||||||
end
|
end
|
||||||
table.insert(inbounds, inbound)
|
table.insert(inbounds, inbound)
|
||||||
end
|
end
|
||||||
|
|
||||||
if server_proto ~= "nil" and server_address ~= "nil" and server_port ~= "nil" then
|
if server_proto ~= "nil" and server_address ~= "nil" and server_port ~= "nil" then
|
||||||
local outbound = {
|
local outbound = {
|
||||||
protocol = server_proto,
|
protocol = server_proto,
|
||||||
@@ -2250,7 +2250,7 @@ function gen_proto_config(var)
|
|||||||
sockopt = {mark = 255}
|
sockopt = {mark = 255}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
local config = {
|
local config = {
|
||||||
log = {
|
log = {
|
||||||
loglevel = "warning"
|
loglevel = "warning"
|
||||||
|
|||||||
@@ -175,7 +175,7 @@
|
|||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
return function(colorStr) {
|
return function(colorStr) {
|
||||||
if (!colorStr)
|
if (!colorStr)
|
||||||
return null;
|
return null;
|
||||||
ctx.clearRect(0, 0, 1, 1);
|
ctx.clearRect(0, 0, 1, 1);
|
||||||
// 2. Apply the color
|
// 2. Apply the color
|
||||||
ctx.fillStyle = colorStr;
|
ctx.fillStyle = colorStr;
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
if (!rgb) return 'unknown'; // Handle invalid colors
|
if (!rgb) return 'unknown'; // Handle invalid colors
|
||||||
// Calculate YIQ brightness (human eye perception)
|
// Calculate YIQ brightness (human eye perception)
|
||||||
const brightness = ((rgb.r * 299) + (rgb.g * 587) + (rgb.b * 114)) / 1000;
|
const brightness = ((rgb.r * 299) + (rgb.g * 587) + (rgb.b * 114)) / 1000;
|
||||||
|
|
||||||
return brightness > 128 ? 'light' : 'dark';
|
return brightness > 128 ? 'light' : 'dark';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@
|
|||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
return function(colorStr) {
|
return function(colorStr) {
|
||||||
if (!colorStr)
|
if (!colorStr)
|
||||||
return null;
|
return null;
|
||||||
ctx.clearRect(0, 0, 1, 1);
|
ctx.clearRect(0, 0, 1, 1);
|
||||||
// 2. Apply the color
|
// 2. Apply the color
|
||||||
ctx.fillStyle = colorStr;
|
ctx.fillStyle = colorStr;
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ local groups = {}
|
|||||||
local group_order = {}
|
local group_order = {}
|
||||||
for _, item in ipairs(values) do
|
for _, item in ipairs(values) do
|
||||||
local g = item.group
|
local g = item.group
|
||||||
if not g or g == "" then
|
if not g or g == "" then
|
||||||
g = translate("default")
|
g = translate("default")
|
||||||
end
|
end
|
||||||
if not groups[g] then
|
if not groups[g] then
|
||||||
groups[g] = {}
|
groups[g] = {}
|
||||||
table.insert(group_order, g)
|
table.insert(group_order, g)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
return function(colorStr) {
|
return function(colorStr) {
|
||||||
if (!colorStr)
|
if (!colorStr)
|
||||||
return null;
|
return null;
|
||||||
ctx.clearRect(0, 0, 1, 1);
|
ctx.clearRect(0, 0, 1, 1);
|
||||||
// 2. Apply the color
|
// 2. Apply the color
|
||||||
ctx.fillStyle = colorStr;
|
ctx.fillStyle = colorStr;
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
if (!rgb) return 'unknown'; // Handle invalid colors
|
if (!rgb) return 'unknown'; // Handle invalid colors
|
||||||
// Calculate YIQ brightness (human eye perception)
|
// Calculate YIQ brightness (human eye perception)
|
||||||
const brightness = ((rgb.r * 299) + (rgb.g * 587) + (rgb.b * 114)) / 1000;
|
const brightness = ((rgb.r * 299) + (rgb.g * 587) + (rgb.b * 114)) / 1000;
|
||||||
|
|
||||||
return brightness > 128 ? 'light' : 'dark';
|
return brightness > 128 ? 'light' : 'dark';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
const keyword = searchInput.value.trim().toLowerCase();
|
const keyword = searchInput.value.trim().toLowerCase();
|
||||||
const isSearching = keyword.length > 0;
|
const isSearching = keyword.length > 0;
|
||||||
// 当前实例下的所有 checkbox
|
// 当前实例下的所有 checkbox
|
||||||
const cbs = isSearching
|
const cbs = isSearching
|
||||||
? Array.from(nodeList.querySelectorAll("input[type=checkbox]")).filter(cb => cb.closest("li").style.display !== "none")
|
? Array.from(nodeList.querySelectorAll("input[type=checkbox]")).filter(cb => cb.closest("li").style.display !== "none")
|
||||||
: nodeList.querySelectorAll("input[type=checkbox]");
|
: nodeList.querySelectorAll("input[type=checkbox]");
|
||||||
let checked = 0;
|
let checked = 0;
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ local dropdown_data = {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!-- 隐藏 input(保存实际配置值) -->
|
<!-- 隐藏 input(保存实际配置值) -->
|
||||||
<input type="text" id="<%=cbid%>" name="<%=cbid%>" value="<%=current_key%>" data-update="change" style="display:none !important;" />
|
<input type="text" id="<%=cbid%>" name="<%=cbid%>" value="<%=current_key%>" data-update="change" style="display:none !important;" />
|
||||||
<!-- 模拟 ListValue 控件外观 -->
|
<!-- 模拟 ListValue 控件外观 -->
|
||||||
<div class="cbi-input-value cbi-input-select v-dropdown-display" id="<%=cbid%>.display" tabindex="0">
|
<div class="cbi-input-value cbi-input-select v-dropdown-display" id="<%=cbid%>.display" tabindex="0">
|
||||||
<span id="<%=cbid%>.label" class="v-dropdown-label" title="<%=pcdata(current_label)%>">
|
<span id="<%=cbid%>.label" class="v-dropdown-label" title="<%=pcdata(current_label)%>">
|
||||||
|
|||||||