diff --git a/filebrowser/Makefile b/filebrowser/Makefile index 0e9db33b..01809053 100644 --- a/filebrowser/Makefile +++ b/filebrowser/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=filebrowser -PKG_VERSION:=1.3.3-stable +PKG_VERSION:=1.4.0-stable PKG_RELEASE=1 ifeq ($(ARCH),aarch64) diff --git a/gecoosac/Makefile b/gecoosac/Makefile index 699723f9..a543d9ac 100644 --- a/gecoosac/Makefile +++ b/gecoosac/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=gecoosac PKG_VERSION:=2.2.20251015 -PKG_RELEASE:=8 +PKG_RELEASE:=9 ifeq ($(ARCH),aarch64) PKG_ARCH:=ac_linux_arm64 @@ -49,6 +49,11 @@ define Package/$(PKG_NAME)/conffiles /etc/gecoosac/ endef +define Build/Prepare + $(call Build/Prepare/Default) + $(CP) ../LICENSE $(PKG_BUILD_DIR)/LICENSE +endef + define Build/Compile endef diff --git a/gecoosac/files/etc/init.d/gecoosac b/gecoosac/files/etc/init.d/gecoosac index 148d96a8..994070c5 100644 --- a/gecoosac/files/etc/init.d/gecoosac +++ b/gecoosac/files/etc/init.d/gecoosac @@ -43,6 +43,29 @@ is_abs_path() { esac } +strip_trailing_slashes() { + local path="$1" + + while [ "$path" != "/" ] && [ "${path%/}" != "$path" ]; do + path="${path%/}" + done + + printf '%s\n' "$path" +} + +is_safe_upload_dir() { + local path + + is_abs_path "$1" || return 1 + path="$(strip_trailing_slashes "$1")" + + case "$path" in + */gecoosac/upload) return 0 ;; + esac + + return 1 +} + is_port() { case "$1" in ""|*[!0-9]*) return 1 ;; @@ -175,7 +198,11 @@ generate_default_cert() { } normalize_conf() { - is_abs_path "$upload_dir" || upload_dir="$DEFAULT_UPLOAD_DIR" + if is_safe_upload_dir "$upload_dir"; then + upload_dir="$(strip_trailing_slashes "$upload_dir")/" + else + upload_dir="$DEFAULT_UPLOAD_DIR" + fi is_abs_path "$db_dir" || db_dir="$DEFAULT_DB_DIR" is_abs_path "$crt_file" || crt_file="$DEFAULT_CRT_FILE" is_abs_path "$key_file" || key_file="$DEFAULT_KEY_FILE" @@ -250,7 +277,7 @@ start_service() { procd_set_param pidfile "${piddir%/}/gecoosac.pid" procd_set_param stdout "$log" procd_set_param stderr "$log" - procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5} + procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}" procd_close_instance } diff --git a/gecoosac/files/etc/uci-defaults/gecoosac b/gecoosac/files/etc/uci-defaults/gecoosac index 4755b746..7cb9eb4c 100644 --- a/gecoosac/files/etc/uci-defaults/gecoosac +++ b/gecoosac/files/etc/uci-defaults/gecoosac @@ -1,6 +1,7 @@ #!/bin/sh changed=0 +DEFAULT_UPLOAD_DIR=/tmp/gecoosac/upload/ DEFAULT_CRT_FILE=/etc/gecoosac/tls/gecoosac.crt DEFAULT_KEY_FILE=/etc/gecoosac/tls/gecoosac.key OLD_CRT_FILE=/etc/gecoosac/tls/1.crt @@ -21,6 +22,53 @@ set_default() { changed=1 } +strip_trailing_slashes() { + local path="$1" + + while [ "$path" != "/" ] && [ "${path%/}" != "$path" ]; do + path="${path%/}" + done + + printf '%s\n' "$path" +} + +is_abs_path() { + case "$1" in + /*) return 0 ;; + *) return 1 ;; + esac +} + +is_safe_upload_dir() { + local path + + is_abs_path "$1" || return 1 + path="$(strip_trailing_slashes "$1")" + + case "$path" in + */gecoosac/upload) return 0 ;; + esac + + return 1 +} + +normalize_upload_dir() { + local upload_dir normalized + + upload_dir="$(uci -q get gecoosac.config.upload_dir)" + [ -n "$upload_dir" ] || return 0 + + if is_safe_upload_dir "$upload_dir"; then + normalized="$(strip_trailing_slashes "$upload_dir")/" + else + normalized="$DEFAULT_UPLOAD_DIR" + fi + + [ "$upload_dir" = "$normalized" ] && return 0 + uci -q set "gecoosac.config.upload_dir=${normalized}" + changed=1 +} + migrate_legacy_cert_paths() { local crt_file key_file local migrate @@ -46,6 +94,7 @@ migrate_legacy_cert_paths() { ensure_section migrate_legacy_cert_paths +normalize_upload_dir set_default enabled 0 set_default port 60650 set_default isonlyoneprot 1 @@ -53,7 +102,7 @@ set_default m_port 8080 set_default https 0 set_default crt_file "$DEFAULT_CRT_FILE" set_default key_file "$DEFAULT_KEY_FILE" -set_default upload_dir /tmp/gecoosac/upload/ +set_default upload_dir "$DEFAULT_UPLOAD_DIR" set_default db_dir /etc/gecoosac/ set_default piddir /var/run/ set_default lang zh diff --git a/luci-app-daede/Makefile b/luci-app-daede/Makefile index 332a12f8..7fc3c1e4 100644 --- a/luci-app-daede/Makefile +++ b/luci-app-daede/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-daede PKG_VERSION:=1.13.0 -PKG_RELEASE:=16 +PKG_RELEASE:=17 PKG_MAINTAINER:=kenzok8 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) diff --git a/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js b/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js index a8eb2a7c..15cdfe2d 100644 --- a/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js +++ b/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js @@ -377,10 +377,21 @@ return view.extend({ poll.add(refresh); refresh(); - // First paint reads the cached index (fast). Refresh the index, then - // re-probe once it's current so a freshly published version shows - // without waiting for a poll tick. refresh-index.sh self-throttles. - fs.exec('/usr/share/luci-app-daede/refresh-index.sh', []).then(refresh).catch(function() {}); + // Start the potentially slow index refresh without holding a LuCI RPC + // request open, then re-probe promptly when its status changes to done. + const waitIndexRefresh = function(tries) { + return L.resolveDefault(fs.read_direct('/tmp/luci-app-daede.idx.status', 'text'), '').then(function(status) { + if (String(status).trim() === 'done') + return refresh(); + if (tries <= 0) + return; + return new Promise(function(resolve) { setTimeout(resolve, 1000); }) + .then(function() { return waitIndexRefresh(tries - 1); }); + }); + }; + fs.exec('/usr/share/luci-app-daede/refresh-index.sh', []) + .then(function() { return waitIndexRefresh(30); }) + .catch(function() {}); // === Geo data source (preset / custom URL + auto-update) === const geoSettings = (function() { diff --git a/luci-app-daede/root/usr/share/luci-app-daede/refresh-index.sh b/luci-app-daede/root/usr/share/luci-app-daede/refresh-index.sh index a20952e7..53055ca6 100755 --- a/luci-app-daede/root/usr/share/luci-app-daede/refresh-index.sh +++ b/luci-app-daede/root/usr/share/luci-app-daede/refresh-index.sh @@ -1,20 +1,28 @@ #!/bin/sh -# Refresh the apk/opkg index for the Updates view. Runs in the FOREGROUND so -# the caller can re-probe once the index is current; throttled to 90s. +# Refresh the apk/opkg index for the Updates view without holding the LuCI RPC +# request open. The view watches STATUS and re-probes as soon as it reads done. LOCK=/tmp/luci-app-daede.idx.lock +STATUS=/tmp/luci-app-daede.idx.status if [ -f "$LOCK" ]; then mtime=$(date -r "$LOCK" +%s 2>/dev/null || echo 0) - [ "$(( $(date +%s) - mtime ))" -lt 90 ] && exit 0 + if [ "$(( $(date +%s) - mtime ))" -lt 90 ]; then + [ -f "$STATUS" ] || printf '%s\n' done > "$STATUS" + exit 0 + fi fi : > "$LOCK" +printf '%s\n' running > "$STATUS" # -n: skip if a package op holds the shared apk lock (no need to refresh then) -if command -v apk >/dev/null 2>&1; then - flock -n /tmp/luci-app-daede.apk.lock apk update -elif command -v opkg >/dev/null 2>&1; then - opkg update -fi >/dev/null 2>&1 +( + if command -v apk >/dev/null 2>&1; then + flock -n /tmp/luci-app-daede.apk.lock apk update + elif command -v opkg >/dev/null 2>&1; then + opkg update + fi + printf '%s\n' done > "$STATUS" +) >/dev/null 2>&1 = 1 && port <= 65535 ? String(port) : defaultValue; } +function stripTrailingSlashes(value) { + let path = String(value || ''); + + while (path.length > 1 && path.endsWith('/')) + path = path.slice(0, -1); + + return path; +} + +function validUploadDir(value) { + const path = stripTrailingSlashes(value); + + return path.charAt(0) === '/' && path.endsWith('/gecoosac/upload'); +} + function serviceRunning(status) { const service = status && status.gecoosac; const instances = service && service.instances; @@ -167,19 +182,25 @@ return view.extend({ o.placeholder = DEFAULT_CRT_FILE; o.default = DEFAULT_CRT_FILE; o.datatype = 'file'; - o.depends('https', '1'); + o.depends({ isonlyoneprot: '0', https: '1' }); o = s.option(form.Value, 'key_file', _('Specify key certificate file')); o.placeholder = DEFAULT_KEY_FILE; o.default = DEFAULT_KEY_FILE; o.datatype = 'file'; - o.depends('https', '1'); + o.depends({ isonlyoneprot: '0', https: '1' }); - o = s.option(form.Value, 'upload_dir', _('Upload dir path'), _('The path to upload AP upgrade firmware')); + o = s.option(form.Value, 'upload_dir', _('Upload dir path'), + _('Upload AP upgrade firmware here. For safe cleanup, use an absolute path ending with /gecoosac/upload, for example /tmp/gecoosac/upload/.')); o.placeholder = DEFAULT_UPLOAD_DIR; o.default = DEFAULT_UPLOAD_DIR; o.datatype = 'directory'; o.rmempty = false; + o.validate = function(section_id, value) { + return validUploadDir(value) + ? true + : _('Upload directory must be an absolute path ending with /gecoosac/upload.'); + }; o = s.option(form.Value, 'db_dir', _('Database dir path'), _('The path to store the config database')); o.placeholder = DEFAULT_DB_DIR; diff --git a/luci-app-gecoosac/po/zh-cn/gecoosac.po b/luci-app-gecoosac/po/zh-cn/gecoosac.po index ea3d3231..ff5124fb 100644 --- a/luci-app-gecoosac/po/zh-cn/gecoosac.po +++ b/luci-app-gecoosac/po/zh-cn/gecoosac.po @@ -51,8 +51,11 @@ msgstr "指定 key 证书文件" msgid "Upload dir path" msgstr "上传目录" -msgid "The path to upload AP upgrade firmware" -msgstr "存放AP升级固件的路径" +msgid "Upload AP upgrade firmware here. For safe cleanup, use an absolute path ending with /gecoosac/upload, for example /tmp/gecoosac/upload/." +msgstr "AP 升级固件会上传到此目录。为确保安全清理,请使用以 /gecoosac/upload 结尾的绝对路径,例如 /tmp/gecoosac/upload/。" + +msgid "Upload directory must be an absolute path ending with /gecoosac/upload." +msgstr "上传目录必须是以 /gecoosac/upload 结尾的绝对路径。" msgid "Database dir path" msgstr "数据库目录" diff --git a/luci-app-xwan/Makefile b/luci-app-xwan/Makefile index c5bfddb3..6d600e16 100644 --- a/luci-app-xwan/Makefile +++ b/luci-app-xwan/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-xwan PKG_VERSION:=1.0.0 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_LICENSE:=GPLv3 PKG_LICENSE_FILES:=LICENSE diff --git a/luci-app-xwan/files/luci/controller/xwan.lua b/luci-app-xwan/files/luci/controller/xwan.lua index ef14c735..2e9b1807 100644 --- a/luci-app-xwan/files/luci/controller/xwan.lua +++ b/luci-app-xwan/files/luci/controller/xwan.lua @@ -5,7 +5,7 @@ module("luci.controller.xwan", package.seeall) function index() local page - page = entry({"admin", "network", "xwan"}, cbi("xwan/xwan"), _("Xwan")) + page = entry({"admin", "network", "xwan"}, cbi("xwan/xwan"), _("Multi-WAN Dialing")) page.leaf = true page.acl_depends = { "luci-app-xwan" } end diff --git a/luci-app-xwan/files/luci/i18n/xwan.zh-cn.po b/luci-app-xwan/files/luci/i18n/xwan.zh-cn.po index f3087dc3..d039d865 100644 --- a/luci-app-xwan/files/luci/i18n/xwan.zh-cn.po +++ b/luci-app-xwan/files/luci/i18n/xwan.zh-cn.po @@ -1,20 +1,38 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8\n" -msgid "Xwan" -msgstr "多拨" +msgid "Multi-WAN Dialing" +msgstr "单线多拨" -msgid "Xwan Settings" -msgstr "多拨设置" +msgid "Multi-WAN Dialing Settings" +msgstr "单线多拨设置" -msgid "Enable xwan" -msgstr "开启多拨" +msgid "Enable multi-WAN dialing" +msgstr "启用多拨" -msgid "multiwan on single interface" -msgstr "单网口多拨号" +msgid "Create multiple WAN connections on a single physical interface." +msgstr "在同一个物理接口上创建多个 WAN 连接。" -msgid "Number of xwan" -msgstr "多少拨" +msgid "Number of WAN instances" +msgstr "多拨数量" -msgid "Auto balanced setup" -msgstr "自动负载均衡和叠加" +msgid "Configure load balancing automatically" +msgstr "自动配置负载均衡" + +msgid "Health check hostnames or IP addresses" +msgstr "健康检查主机名或 IP 地址" + +msgid "Ping these hosts to determine whether each WAN link is up. Leave empty to treat the links as always online." +msgstr "通过 ping 这些主机判断每条 WAN 链路是否在线。留空则视为链路始终在线。" + +msgid "IP family" +msgstr "IP 协议族" + +msgid "IPv4 and IPv6" +msgstr "IPv4 和 IPv6" + +msgid "IPv4" +msgstr "IPv4" + +msgid "IPv6" +msgstr "IPv6" diff --git a/luci-app-xwan/files/luci/model/cbi/xwan/xwan.lua b/luci-app-xwan/files/luci/model/cbi/xwan/xwan.lua index 48daca8c..b346ed5e 100644 --- a/luci-app-xwan/files/luci/model/cbi/xwan/xwan.lua +++ b/luci-app-xwan/files/luci/model/cbi/xwan/xwan.lua @@ -1,29 +1,29 @@ -- Copyright 2019 X-WRT -m = Map("xwan", translate("Xwan")) +m = Map("xwan", translate("Multi-WAN Dialing")) -s = m:section(TypedSection, "xwan", translate("Xwan Settings")) +s = m:section(TypedSection, "xwan", translate("Multi-WAN Dialing Settings")) s.addremove = false s.anonymous = true -e = s:option(Flag, "enabled", translate("Enable xwan"), translate("multiwan on single interface")) +e = s:option(Flag, "enabled", translate("Enable multi-WAN dialing"), translate("Create multiple WAN connections on a single physical interface.")) e.default = e.disabled e.rmempty = false -e = s:option(Value, "number", translate("Number of xwan")) +e = s:option(Value, "number", translate("Number of WAN instances")) e.datatype = "and(uinteger,min(1),max(60))" e.rmempty = false -e = s:option(Flag, "balanced", translate("Auto balanced setup")) +e = s:option(Flag, "balanced", translate("Configure load balancing automatically")) e.default = e.disabled e.rmempty = false -e = s:option(DynamicList, "track_ip", translate("Tracking hostname or IP address"), translate("This hostname or IP address will be pinged to determine if the link is up or down. Leave blank to assume interface is always online")) +e = s:option(DynamicList, "track_ip", translate("Health check hostnames or IP addresses"), translate("Ping these hosts to determine whether each WAN link is up. Leave empty to treat the links as always online.")) e.datatype = 'host' e.default = "" e.placeholder = "gateway" -e = s:option(ListValue, 'family', translate("Internet Protocol")) +e = s:option(ListValue, 'family', translate("IP family")) e.default = '' e:value('', translate('IPv4 and IPv6')) e:value('ipv4', translate('IPv4'))