From a09b2054729ceee59302c3f1bea3dbd20cced58e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Jun 2026 20:45:50 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=8B=20Sync=202026-06-26=2020:45:50?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cloudreve/Makefile | 6 +- luci-app-passwall/Makefile | 2 +- .../cbi/passwall/client/type/hysteria2.lua | 14 ++ .../model/cbi/passwall/client/type/ray.lua | 12 + .../cbi/passwall/client/type/sing-box.lua | 12 + .../cbi/passwall/server/type/hysteria2.lua | 14 ++ .../model/cbi/passwall/server/type/ray.lua | 12 + .../cbi/passwall/server/type/sing-box.lua | 12 + .../luasrc/passwall/util_hysteria2.lua | 23 ++ .../luasrc/passwall/util_sing-box.lua | 44 +++- .../luasrc/passwall/util_xray.lua | 24 +- .../passwall/node_config/link_share_man.htm | 20 +- luci-app-passwall/po/zh-cn/passwall.po | 10 +- .../usr/share/passwall/clash_subconverter.lua | 2 + .../root/usr/share/passwall/subscribe.lua | 4 + luci-app-run/Makefile | 2 +- .../luci-static/resources/view/run/i18n.json | 6 +- .../luci-static/resources/view/run/index.js | 209 ++++++++++++++---- mihomo-alpha/Makefile | 8 +- natflow/Makefile | 4 +- 20 files changed, 369 insertions(+), 71 deletions(-) diff --git a/cloudreve/Makefile b/cloudreve/Makefile index 697ccc84..eaf10644 100644 --- a/cloudreve/Makefile +++ b/cloudreve/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=cloudreve -PKG_VERSION:=4.16.1 -PKG_RELEASE:=12 +PKG_VERSION:=4.17.0 +PKG_RELEASE:=13 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/cloudreve/Cloudreve.git -PKG_SOURCE_VERSION:=ba2e870bbd17f1918dd2321de861e453f696d6a3 +PKG_SOURCE_VERSION:=d4a145c3ac68f08b9071dec0f2ac025c6dc7362d PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-3.0-only diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index 700827cb..86e7cde8 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall PKG_VERSION:=26.6.2 -PKG_RELEASE:=166 +PKG_RELEASE:=167 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua index 53f06f01..d57f6f12 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua @@ -76,6 +76,20 @@ o.rewrite_option = o.option o:depends({ [_n("obfs_type")] = "salamander" }) o:depends({ [_n("obfs_type")] = "gecko" }) +o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) +o.datatype = "uinteger" +o.placeholder = "512" +o.default = "512" +o:depends({ [_n("obfs_type")] = "gecko" }) +o.rewrite_option = o.option + +o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) +o.datatype = "uinteger" +o.placeholder = "1200" +o.default = "1200" +o:depends({ [_n("obfs_type")] = "gecko" }) +o.rewrite_option = o.option + o = s:option(Flag, _n("fast_open"), translate("Fast Open")) o.default = "0" diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua index bc25597a..0744bb27 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua @@ -376,6 +376,18 @@ o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) +o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) +o.datatype = "uinteger" +o.placeholder = "512" +o.default = "512" +o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + +o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) +o.datatype = "uinteger" +o.placeholder = "1200" +o.default = "1200" +o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o:depends({ [_n("protocol")] = "hysteria2" }) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua index 5ebab8fc..1c7e5723 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua @@ -400,6 +400,18 @@ if singbox_tags:find("with_quic") then o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) + o.datatype = "uinteger" + o.placeholder = "512" + o.default = "512" + o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + + o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) + o.datatype = "uinteger" + o.placeholder = "1200" + o.default = "1200" + o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o:depends({ [_n("protocol")] = "hysteria2" }) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua index 6d80af23..e3402ac3 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua @@ -65,6 +65,20 @@ o.rewrite_option = o.option o:depends({ [_n("obfs_type")] = "salamander" }) o:depends({ [_n("obfs_type")] = "gecko" }) +o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) +o.datatype = "uinteger" +o.placeholder = "512" +o.default = "512" +o:depends({ [_n("obfs_type")] = "gecko" }) +o.rewrite_option = o.option + +o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) +o.datatype = "uinteger" +o.placeholder = "1200" +o.default = "1200" +o:depends({ [_n("obfs_type")] = "gecko" }) +o.rewrite_option = o.option + o = s:option(Flag, _n("udp"), translate("UDP")) o.default = "1" o.rewrite_option = o.option diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua index 85499e0b..64b6f2fb 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua @@ -155,6 +155,18 @@ o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) +o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) +o.datatype = "uinteger" +o.placeholder = "512" +o.default = "512" +o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + +o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) +o.datatype = "uinteger" +o.placeholder = "1200" +o.default = "1200" +o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control")) o.default = 0 o:depends({ [_n("protocol")] = "hysteria2" }) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua index 572478ac..92ff2fa2 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua @@ -188,6 +188,18 @@ if singbox_tags:find("with_quic") then o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) + o.datatype = "uinteger" + o.placeholder = "512" + o.default = "512" + o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + + o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) + o.datatype = "uinteger" + o.placeholder = "1200" + o.default = "1200" + o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) + o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm")) o.default = 0 o:depends({ [_n("protocol")] = "hysteria2" }) diff --git a/luci-app-passwall/luasrc/passwall/util_hysteria2.lua b/luci-app-passwall/luasrc/passwall/util_hysteria2.lua index b8497914..8c43f811 100644 --- a/luci-app-passwall/luasrc/passwall/util_hysteria2.lua +++ b/luci-app-passwall/luasrc/passwall/util_hysteria2.lua @@ -39,6 +39,18 @@ function gen_config_server(node) stunServers = node.hysteria2_realm_stun } or nil } + + if config.obfs and config.obfs.gecko then + local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512 + local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200 + if min <= 0 or min > max or max > 2048 then + min = 512 + max = 1200 + end + config.obfs.gecko.minPacketSize = min + config.obfs.gecko.maxPacketSize = max + end + return config end @@ -157,6 +169,17 @@ function gen_config(var) } or nil } + if config.obfs and config.obfs.gecko then + local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512 + local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200 + if min <= 0 or min > max or max > 2048 then + min = 512 + max = 1200 + end + config.obfs.gecko.minPacketSize = min + config.obfs.gecko.maxPacketSize = max + end + return jsonc.stringify(config, 1) end diff --git a/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/luci-app-passwall/luasrc/passwall/util_sing-box.lua index a720b85f..4784c864 100644 --- a/luci-app-passwall/luasrc/passwall/util_sing-box.lua +++ b/luci-app-passwall/luasrc/passwall/util_sing-box.lua @@ -561,10 +561,24 @@ function gen_outbound(flag, node, tag, proxy_table) hop_interval_max = interval_max, up_mbps = (node.hysteria2_up_mbps and tonumber(node.hysteria2_up_mbps)) and tonumber(node.hysteria2_up_mbps) or nil, down_mbps = (node.hysteria2_down_mbps and tonumber(node.hysteria2_down_mbps)) and tonumber(node.hysteria2_down_mbps) or nil, - obfs = node.hysteria2_obfs_type and { - type = node.hysteria2_obfs_type, - password = node.hysteria2_obfs_password - } or nil, + obfs = (function(t) + if not t or t == "" then return nil end + local o = { + type = t, + password = node.hysteria2_obfs_password + } + if t == "gecko" then + local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512 + local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200 + if min <= 0 or min > max or max > 2048 then + min = 512 + max = 1200 + end + o.min_packet_size = min + o.max_packet_size = max + end + return o + end)(node.hysteria2_obfs_type), password = node.hysteria2_auth_password or nil, idle_timeout = (function(t) if not version_ge_1_14_0 then return nil end @@ -917,10 +931,24 @@ function gen_config_server(node) protocol_table = { up_mbps = (node.hysteria2_ignore_client_bandwidth ~= "1" and node.hysteria2_up_mbps and tonumber(node.hysteria2_up_mbps)) and tonumber(node.hysteria2_up_mbps) or nil, down_mbps = (node.hysteria2_ignore_client_bandwidth ~= "1" and node.hysteria2_down_mbps and tonumber(node.hysteria2_down_mbps)) and tonumber(node.hysteria2_down_mbps) or nil, - obfs = node.hysteria2_obfs_type and { - type = node.hysteria2_obfs_type, - password = node.hysteria2_obfs_password - } or nil, + obfs = (function(t) + if not t or t == "" then return nil end + local o = { + type = t, + password = node.hysteria2_obfs_password + } + if t == "gecko" then + local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512 + local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200 + if min <= 0 or min > max or max > 2048 then + min = 512 + max = 1200 + end + o.min_packet_size = min + o.max_packet_size = max + end + return o + end)(node.hysteria2_obfs_type), users = { { name = "user1", diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua index 460072c5..f2e78585 100644 --- a/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -284,10 +284,18 @@ function gen_outbound(flag, node, tag, proxy_table) local o = { type = "salamander", settings = node.hysteria2_obfs_password and { - password = node.hysteria2_obfs_password, - packetSize = node.hysteria2_obfs_type == "gecko" and "512-1200" or nil + password = node.hysteria2_obfs_password } or nil } + if node.hysteria2_obfs_type == "gecko" then + local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512 + local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200 + if min <= 0 or min > max or max > 2048 then + min = 512 + max = 1200 + end + o.settings.packetSize = min .. "-" .. max + end udp[#udp+1] = o end if node.hysteria2_realms then @@ -724,10 +732,18 @@ function gen_config_server(node) local o = { type = "salamander", settings = node.hysteria2_obfs_password and { - password = node.hysteria2_obfs_password, - packetSize = node.hysteria2_obfs_type == "gecko" and "512-1200" or nil + password = node.hysteria2_obfs_password } or nil } + if node.hysteria2_obfs_type == "gecko" then + local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512 + local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200 + if min <= 0 or min > max or max > 2048 then + min = 512 + max = 1200 + end + o.settings.packetSize = min .. "-" .. max + end udp[#udp+1] = o end if node.hysteria2_realms then diff --git a/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm b/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm index 13a28126..9f28d93e 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm @@ -598,10 +598,14 @@ local current_node = map:get(section) if (v_type === "Hysteria2") { v_password = opt.get("hysteria2_auth_password"); - var dom_obfs = opt.get("hysteria2_obfs_password"); - if (dom_obfs && dom_obfs.value != "") { + var dom_obfs_type = opt.get("hysteria2_obfs_type"); + if (dom_obfs_type && dom_obfs_type.value != "") { params += opt.query("obfs", "hysteria2_obfs_type"); params += opt.query("obfs-password", "hysteria2_obfs_password"); + if (dom_obfs_type.value === "gecko") { + params += opt.query("minPacketSize", "hysteria2_obfs_MinPacketSize"); + params += opt.query("maxPacketSize", "hysteria2_obfs_MaxPacketSize"); + } } params += opt.query("mport", "hysteria2_hop"); } else { @@ -610,6 +614,10 @@ local current_node = map:get(section) if (dom_obfs_type && dom_obfs_type.value != "") { params += opt.query("obfs", dom_prefix + "hysteria2_obfs_type"); params += opt.query("obfs-password", dom_prefix + "hysteria2_obfs_password"); + if (dom_obfs_type.value === "gecko") { + params += opt.query("minPacketSize", dom_prefix + "hysteria2_obfs_MinPacketSize"); + params += opt.query("maxPacketSize", dom_prefix + "hysteria2_obfs_MaxPacketSize"); + } } params += opt.query("mport", dom_prefix + "hysteria2_hop"); if (opt.get(dom_prefix + "use_finalmask")?.checked) { @@ -1600,6 +1608,10 @@ local current_node = map:get(section) opt.set(dom_prefix + 'hysteria2_obfs_type', queryParam.obfs || "salamander"); opt.set(dom_prefix + 'hysteria2_obfs_password', queryParam["obfs-password"] || queryParam["obfs_password"]); } + if (queryParam.obfs === "gecko") { + opt.set(dom_prefix + 'hysteria2_obfs_MinPacketSize', queryParam.minPacketSize || "512"); + opt.set(dom_prefix + 'hysteria2_obfs_MaxPacketSize', queryParam.maxPacketSize || "1200"); + } opt.set(dom_prefix + 'hysteria2_hop', queryParam.mport || ""); opt.set(dom_prefix + 'use_finalmask', !!queryParam.fm); opt.set(dom_prefix + 'finalmask', queryParam.fm || ""); @@ -1611,6 +1623,10 @@ local current_node = map:get(section) opt.set(dom_prefix + 'obfs_type', queryParam.obfs || "salamander"); opt.set(dom_prefix + 'obfs_password', queryParam["obfs-password"] || queryParam["obfs_password"]); } + if (queryParam.obfs === "gecko") { + opt.set(dom_prefix + 'obfs_MinPacketSize', queryParam.minPacketSize || "512"); + opt.set(dom_prefix + 'obfs_MaxPacketSize', queryParam.maxPacketSize || "1200"); + } opt.set(dom_prefix + 'hop', queryParam.mport || ""); } diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po index f2d8ae6b..35cea130 100644 --- a/luci-app-passwall/po/zh-cn/passwall.po +++ b/luci-app-passwall/po/zh-cn/passwall.po @@ -740,10 +740,10 @@ msgid "Certificate fingerprint" msgstr "证书指纹" msgid "Max upload Mbps" -msgstr "最大上行(Mbps)" +msgstr "最大上行(Mbps)" msgid "Max download Mbps" -msgstr "最大下行(Mbps)" +msgstr "最大下行(Mbps)" msgid "QUIC stream receive window" msgstr "QUIC 流接收窗口" @@ -2170,3 +2170,9 @@ msgstr "正在处理,请稍候…" msgid "Invalid Realm URL." msgstr "Realm URL 不正确。" + +msgid "Gecko Packet Size (min)" +msgstr "Gecko 包大小(最小)" + +msgid "Gecko Packet Size (max)" +msgstr "Gecko 包大小(最大)" diff --git a/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua b/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua index c2aaea13..57d278e6 100644 --- a/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua +++ b/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua @@ -398,6 +398,8 @@ local function encode_hysteria2(node) if node["ports"] then table.insert(p, "mport=" .. urlencode(node["ports"])) end if node.obfs then table.insert(p, "obfs=" .. node.obfs) end if node["obfs-password"] then table.insert(p, "obfs-password=" .. node["obfs-password"]) end + if node["obfs-min-packet-size"] then table.insert(p, "minPacketSize=" .. node["obfs-min-packet-size"]) end + if node["obfs-max-packet-size"] then table.insert(p, "maxPacketSize=" .. node["obfs-max-packet-size"]) end if node.up then table.insert(p, "upmbps=" .. node.up) end if node.down then table.insert(p, "downmbps=" .. node.down) end diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua index 87066bc1..39bde9da 100755 --- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -1419,6 +1419,10 @@ local function processData(szType, content, add_mode, group, sub_cfg) result.hysteria2_obfs_type = params.obfs or "salamander" result.hysteria2_obfs_password = params["obfs-password"] or params["obfs_password"] end + if params.obfs == "gecko" then + result.hysteria2_obfs_MinPacketSize = params.minpacketsize or "512" + result.hysteria2_obfs_MaxPacketSize = params.maxpacketsize or "1200" + end if (sub_hysteria2_type == "sing-box" and has_singbox) or (sub_hysteria2_type == "xray" and has_xray) then local is_singbox = sub_hysteria2_type == "sing-box" and has_singbox diff --git a/luci-app-run/Makefile b/luci-app-run/Makefile index 4da7a4fb..334ffed9 100644 --- a/luci-app-run/Makefile +++ b/luci-app-run/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-run PKG_VERSION:=1.0.0 -PKG_RELEASE:=7 +PKG_RELEASE:=8 LUCI_TITLE:=LuCI support for running installers and packages LUCI_DESCRIPTION:=Upload and execute .run, .sh, .ipk and .apk files from LuCI. diff --git a/luci-app-run/htdocs/luci-static/resources/view/run/i18n.json b/luci-app-run/htdocs/luci-static/resources/view/run/i18n.json index 8cdc09ad..e4dad520 100644 --- a/luci-app-run/htdocs/luci-static/resources/view/run/i18n.json +++ b/luci-app-run/htdocs/luci-static/resources/view/run/i18n.json @@ -32,7 +32,8 @@ "download_run": "下载并执行 .run", "download_url": "请输入 .run 文件的下载地址", "downloading": "正在下载...", - "only_run": "仅支持 .run 文件下载" + "only_run": "仅支持 .run 文件下载", + "no_file": "请先选择sh文件" }, "en": { "title": "Run Installer", @@ -67,6 +68,7 @@ "download_run": "Download and execute .run", "download_url": "Enter .run file download URL", "downloading": "Downloading...", - "only_run": "Only .run files are supported" + "only_run": "Only .run files are supported", + "no_file": "Please select a file or enter a download URL first" } } \ No newline at end of file diff --git a/luci-app-run/htdocs/luci-static/resources/view/run/index.js b/luci-app-run/htdocs/luci-static/resources/view/run/index.js index 56e3c5cd..a9b640cc 100644 --- a/luci-app-run/htdocs/luci-static/resources/view/run/index.js +++ b/luci-app-run/htdocs/luci-static/resources/view/run/index.js @@ -93,11 +93,27 @@ function getDefaultText(key) { 'download_run': '下载并执行 .run', 'download_url': '请输入 .run 文件的下载地址', 'downloading': '正在下载...', - 'only_run': '仅支持 .run 文件下载' + 'only_run': '仅支持 .run 文件下载', + 'no_file': '请先选择sh文件' }; return defaults[key] || null; } +function cleanupStaleDialogs() { + var allDivs = document.getElementsByTagName('div'); + for (var i = allDivs.length - 1; i >= 0; i--) { + var div = allDivs[i]; + if (!div || !div.parentNode) continue; + var style = window.getComputedStyle ? window.getComputedStyle(div) : div.style; + var isFixed = style.position === 'fixed' || (div.style && div.style.position === 'fixed'); + var hasHighZ = (style.zIndex && parseInt(style.zIndex) >= 9000) || (div.style.zIndex && parseInt(div.style.zIndex) >= 9000); + var isOurDialog = div.style && (div.style.zIndex === '9999' || div.style.background && div.style.background.indexOf('rgba(0,0,0,0.5)') !== -1); + if (isFixed && (hasHighZ || isOurDialog)) { + div.parentNode.removeChild(div); + } + } +} + var uploadStart = rpc.declare({ object: 'luci-app-run', method: 'upload_start', @@ -242,6 +258,8 @@ return view.extend({ render: function (status) { var self = this; + cleanupStaleDialogs(); + var fileInput = E('input', { 'type': 'file', accept: '.run,.sh,.ipk,.apk,application/x-shellscript,application/octet-stream', @@ -278,6 +296,8 @@ return view.extend({ style: 'background:#333!important;background-color:#333!important;background-image:none!important;color:#fff!important;border-color:#333!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important', click: function (ev) { ev.preventDefault(); + cleanupStaleDialogs(); + fileInput.value = ''; fileInput.click(); } }, [_('choose_file')]); @@ -287,6 +307,8 @@ return view.extend({ style: 'margin-left:10px;background:#2E7D32!important;background-color:#2E7D32!important;background-image:none!important;color:#fff!important;border-color:#2E7D32!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important', click: function (ev) { ev.preventDefault(); + cleanupStaleDialogs(); + ipkInput.value = ''; ipkInput.click(); } }, [_('choose_ipk')]); @@ -296,6 +318,8 @@ return view.extend({ style: 'margin-left:10px;background:#1565C0!important;background-color:#1565C0!important;background-image:none!important;color:#fff!important;border-color:#1565C0!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important', click: function (ev) { ev.preventDefault(); + cleanupStaleDialogs(); + apkInput.value = ''; apkInput.click(); } }, [_('choose_apk')]); @@ -305,6 +329,7 @@ return view.extend({ style: 'margin-left:10px;background:#E65100!important;background-color:#E65100!important;background-image:none!important;color:#fff!important;border-color:#E65100!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important', click: function (ev) { ev.preventDefault(); + cleanupStaleDialogs(); self.showDownloadDialog(function (url) { if (url !== null) { log.textContent = ''; @@ -316,22 +341,26 @@ return view.extend({ var runButton = E('button', { class: 'cbi-button cbi-button-action run-btn', - disabled: true, - style: 'min-width:140px;margin-left:15px;background:#7B1FA2!important;background-color:#7B1FA2!important;background-image:none!important;color:#fff!important;border-color:#7B1FA2!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important', + disabled: false, + style: 'min-width:140px;margin-left:15px;background:#7B1FA2!important;background-color:#7B1FA2!important;background-image:none!important;color:#fff!important;border-color:#7B1FA2!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important;pointer-events:auto!important;cursor:pointer!important', click: function (ev) { ev.preventDefault(); + ev.stopPropagation(); + + cleanupStaleDialogs(); + + log.textContent = ''; if (self.currentFileType === '.sh') { self.showArgsDialog(function (args) { if (args !== null) { - log.textContent = ''; self.startRun(runButton, state, args.trim()); } }); } else { - log.textContent = ''; self.startRun(runButton, state, ''); } + return false; } }, [_('execute')]); @@ -340,17 +369,21 @@ return view.extend({ style: 'margin-left:35px;background:#C62828!important;background-color:#C62828!important;background-image:none!important;color:#fff!important;border-color:#C62828!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important', click: function (ev) { ev.preventDefault(); + cleanupStaleDialogs(); cleanup().then(function (res) { if (res && res.error) throw new Error(res.error); self.currentUploadId = null; + self.currentFileType = null; self.logOffset = 0; self.prevRunning = false; self.autoCleanType = null; - runButton.disabled = true; log.textContent = ''; progress.style.display = 'none'; + fileInput.value = ''; + ipkInput.value = ''; + apkInput.value = ''; state.textContent = _('clean_done'); }).catch(function (err) { ui.addNotification(null, E('p', [err.message || err]), 'danger'); @@ -370,6 +403,7 @@ return view.extend({ }, drop: function (ev) { ev.preventDefault(); + cleanupStaleDialogs(); drop.style.borderStyle = 'dashed'; if (ev.dataTransfer.files && ev.dataTransfer.files.length) self.uploadFile(ev.dataTransfer.files[0], progress, state, runButton); @@ -386,20 +420,37 @@ return view.extend({ ]); fileInput.addEventListener('change', function () { - if (fileInput.files && fileInput.files.length) - self.uploadFile(fileInput.files[0], progress, state, runButton); + if (fileInput.files && fileInput.files.length) { + var selectedFile = fileInput.files[0]; + fileInput.value = ''; + self.uploadFile(selectedFile, progress, state, runButton); + } }); ipkInput.addEventListener('change', function () { - if (ipkInput.files && ipkInput.files.length) - self.uploadFile(ipkInput.files[0], progress, state, runButton); + if (ipkInput.files && ipkInput.files.length) { + var selectedFile = ipkInput.files[0]; + ipkInput.value = ''; + self.uploadFile(selectedFile, progress, state, runButton); + } }); apkInput.addEventListener('change', function () { - if (apkInput.files && apkInput.files.length) - self.uploadFile(apkInput.files[0], progress, state, runButton); + if (apkInput.files && apkInput.files.length) { + var selectedFile = apkInput.files[0]; + apkInput.value = ''; + self.uploadFile(selectedFile, progress, state, runButton); + } }); + setTimeout(function () { + cleanupStaleDialogs(); + runButton.disabled = false; + runButton.removeAttribute('disabled'); + runButton.style.pointerEvents = 'auto'; + runButton.style.cursor = 'pointer'; + }, 100); + poll.add(function () { return self.refreshLog(log, state); }, 1); @@ -434,13 +485,16 @@ return view.extend({ return Promise.reject(); } - // 记录文件类型 + self.currentUploadId = null; + self.logOffset = 0; + self.prevRunning = false; + self.autoCleanType = null; + var ext = file.name.match(/\.(run|sh|ipk|apk)$/i); self.currentFileType = ext ? ext[0].toLowerCase() : null; progress.style.display = ''; progress.value = 0; - runButton.disabled = true; state.textContent = _('prepare_upload', file.name, formatBytes(file.size)); return uploadStart(file.name, file.size).then(function (res) { @@ -473,12 +527,10 @@ return view.extend({ }; xhr.onerror = function () { - document.querySelector('.cbi-button-action').disabled = false; reject(new Error(_('upload_err'))); }; xhr.onload = function () { - document.querySelector('.cbi-button-action').disabled = false; progress.value = 100; uploadFinish(session.id).then(function () { @@ -497,9 +549,11 @@ return view.extend({ startRun: function (runButton, state, args) { var self = this; - if (!this.currentUploadId) return; + if (!this.currentUploadId) { + ui.addNotification(null, E('p', [_('no_file')]), 'danger'); + return; + } - runButton.disabled = true; state.textContent = _('starting'); self.autoCleanType = null; @@ -521,23 +575,52 @@ return view.extend({ } state.textContent = _('started', res.pid); }).catch(function (err) { - runButton.disabled = false; ui.addNotification(null, E('p', [err.message || err]), 'danger'); }); }, showArgsDialog: function (callback) { + var closed = false; + function closeDialog(result) { + if (closed) return; + closed = true; + document.removeEventListener('keydown', escHandler); + if (dialog.parentNode) { + document.body.removeChild(dialog); + } + callback(result); + } + + function escHandler(ev) { + if (ev.key === 'Escape' || ev.keyCode === 27) { + closeDialog(null); + } + } + var dialog = E('div', { - style: 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999' + style: 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999', + click: function (ev) { + if (ev.target === dialog) { + closeDialog(null); + } + } }, [ E('div', { - style: 'background:#fff;border-radius:8px;padding:20px;width:400px;box-shadow:0 4px 20px rgba(0,0,0,0.3)' + style: 'background:#fff;border-radius:8px;padding:20px;width:400px;box-shadow:0 4px 20px rgba(0,0,0,0.3)', + click: function (ev) { ev.stopPropagation(); } }, [ E('input', { type: 'text', placeholder: _('args_hint'), style: 'width:100%;padding:10px;margin-bottom:15px;border:1px solid #ccc;border-radius:4px;box-sizing:border-box;font-size:14px', - id: 'run-args-dialog-input' + id: 'run-args-dialog-input', + keydown: function (ev) { + if (ev.key === 'Enter' || ev.keyCode === 13) { + ev.preventDefault(); + var args = this.value; + closeDialog(args); + } + } }), E('div', { style: 'display:flex;justify-content:flex-end;gap:10px' @@ -546,8 +629,7 @@ return view.extend({ class: 'cbi-button cbi-button-reset', style: 'padding:8px 20px;text-transform:none', click: function () { - document.body.removeChild(dialog); - callback(null); + closeDialog(null); } }, [_('cancel')]), E('button', { @@ -555,30 +637,62 @@ return view.extend({ style: 'padding:8px 20px;text-transform:none', click: function () { var args = document.getElementById('run-args-dialog-input').value; - document.body.removeChild(dialog); - callback(args); + closeDialog(args); } }, [_('confirm')]) ]) ]) ]); + document.addEventListener('keydown', escHandler); document.body.appendChild(dialog); - document.getElementById('run-args-dialog-input').focus(); + setTimeout(function () { + var input = document.getElementById('run-args-dialog-input'); + if (input) input.focus(); + }, 50); }, showDownloadDialog: function (callback) { + var closed = false; + function closeDialog(result) { + if (closed) return; + closed = true; + document.removeEventListener('keydown', escHandler); + if (dialog.parentNode) { + document.body.removeChild(dialog); + } + callback(result); + } + + function escHandler(ev) { + if (ev.key === 'Escape' || ev.keyCode === 27) { + closeDialog(null); + } + } + var dialog = E('div', { - style: 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999' + style: 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999', + click: function (ev) { + if (ev.target === dialog) { + closeDialog(null); + } + } }, [ E('div', { - style: 'background:#fff;border-radius:8px;padding:20px;width:400px;box-shadow:0 4px 20px rgba(0,0,0,0.3)' + style: 'background:#fff;border-radius:8px;padding:20px;width:400px;box-shadow:0 4px 20px rgba(0,0,0,0.3)', + click: function (ev) { ev.stopPropagation(); } }, [ E('input', { type: 'text', placeholder: _('download_url'), style: 'width:100%;padding:10px;margin-bottom:15px;border:1px solid #ccc;border-radius:4px;box-sizing:border-box;font-size:14px', - id: 'run-download-url-input' + id: 'run-download-url-input', + keydown: function (ev) { + if (ev.key === 'Enter' || ev.keyCode === 13) { + ev.preventDefault(); + confirmBtn.click(); + } + } }), E('div', { style: 'display:flex;justify-content:flex-end;gap:10px' @@ -587,18 +701,16 @@ return view.extend({ class: 'cbi-button cbi-button-reset', style: 'padding:8px 20px;text-transform:none', click: function () { - document.body.removeChild(dialog); - callback(null); + closeDialog(null); } }, [_('cancel')]), E('button', { - class: 'cbi-button cbi-button-action', + class: 'cbi-button cbi-button-action confirm-btn', style: 'padding:8px 20px;text-transform:none', click: function () { var url = document.getElementById('run-download-url-input').value.trim(); if (!url) { - document.body.removeChild(dialog); - callback(null); + closeDialog(null); return; } @@ -608,22 +720,26 @@ return view.extend({ return; } - document.body.removeChild(dialog); - callback(url); + closeDialog(url); } }, [_('confirm')]) ]) ]) ]); + var confirmBtn = dialog.querySelector('.confirm-btn'); + + document.addEventListener('keydown', escHandler); document.body.appendChild(dialog); - document.getElementById('run-download-url-input').focus(); + setTimeout(function () { + var input = document.getElementById('run-download-url-input'); + if (input) input.focus(); + }, 50); }, startDownload: function (runButton, state, url) { var self = this; - runButton.disabled = true; state.textContent = _('downloading'); self.prevRunning = false; self.autoCleanType = null; @@ -637,7 +753,6 @@ return view.extend({ self.prevRunning = true; state.textContent = _('started', res.pid); }).catch(function (err) { - runButton.disabled = false; ui.addNotification(null, E('p', [err.message || err]), 'danger'); }); }, @@ -665,9 +780,19 @@ return view.extend({ // Auto-cleanup for .sh and .run files if (self.autoCleanType) { self.autoCleanType = null; + var oldUploadId = self.currentUploadId; + var oldFileType = self.currentFileType; cleanup().then(function () { - self.currentUploadId = null; - state.textContent = _('auto_cleaned'); + // Only clear state if user hasn't uploaded a new file in the meantime + if (self.currentUploadId === oldUploadId) { + self.currentUploadId = null; + self.currentFileType = null; + } + if (oldFileType) { + state.textContent = _('auto_cleaned'); + } else { + state.textContent = _('clean_done'); + } }).catch(function () { state.textContent = _('clean_done'); }); diff --git a/mihomo-alpha/Makefile b/mihomo-alpha/Makefile index c4de6937..efbc097f 100644 --- a/mihomo-alpha/Makefile +++ b/mihomo-alpha/Makefile @@ -1,14 +1,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mihomo-alpha -PKG_VERSION:=2026.06.12 -PKG_RELEASE:=16 +PKG_VERSION:=2026.06.23 +PKG_RELEASE:=17 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git -PKG_SOURCE_VERSION:=793f61ec6798e335a0f8eed3b310a084c3d68514 +PKG_SOURCE_VERSION:=59ffb637343092d3fc94d6cc600b86e1f78d6c1a PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL3.0+ @@ -18,7 +18,7 @@ PKG_BUILD_DEPENDS:=golang/host PKG_BUILD_PARALLEL:=1 PKG_BUILD_FLAGS:=no-mips16 -PKG_BUILD_VERSION:=alpha-793f61e +PKG_BUILD_VERSION:=alpha-59ffb63 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) PKG_BUILD_TIME:=$(shell date -u -Iseconds) diff --git a/natflow/Makefile b/natflow/Makefile index ab036f14..1336b39f 100644 --- a/natflow/Makefile +++ b/natflow/Makefile @@ -10,12 +10,12 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=natflow PKG_VERSION:=20260531 -PKG_RELEASE:=24 +PKG_RELEASE:=25 PKG_SOURCE:=$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=https://github.com/ptpt52/natflow.git PKG_SOURCE_PROTO:=git -PKG_SOURCE_VERSION:=4ad0c8a91f950d18c1b97d055f7dd9a8d17d554c +PKG_SOURCE_VERSION:=bf84c587500484bfd7f0f11cbc4dc2ff9b8ad1af PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_MAINTAINER:=Chen Minqiang PKG_LICENSE:=GPL-2.0