update 2026-09-18 16:40:42

This commit is contained in:
action committed 2026-09-18 16:40:42 +08:00
1 parent 619e2de88b
commit bd132e5713
21 files changed
+159 -146

No files matched your search

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 640 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

@@ -188,8 +188,8 @@ domain_list.validate = function(self, value)
tmp_host = host:gsub("full:", "") tmp_host = host:gsub("full:", "")
elseif host:find("geosite:") and host:find("geosite:") == 1 then elseif host:find("geosite:") and host:find("geosite:") == 1 then
flag = 0 flag = 0
elseif host:find("ext:") and host:find("ext:") == 1 then -- elseif host:find("ext:") and host:find("ext:") == 1 then
flag = 0 -- flag = 0
elseif host:find("rule-set:", 1, true) == 1 or host:find("rs:") == 1 then elseif host:find("rule-set:", 1, true) == 1 or host:find("rs:") == 1 then
local w = host:sub(host:find(":") + 1, #host) local w = host:sub(host:find(":") + 1, #host)
if w:find("local:") == 1 or w:find("remote:") == 1 then if w:find("local:") == 1 or w:find("remote:") == 1 then
@@ -230,7 +230,7 @@ ip_list.validate = function(self, value)
string.gsub(value, "[^\r\n]+", function(w) table.insert(ipmasks, w) end) string.gsub(value, "[^\r\n]+", function(w) table.insert(ipmasks, w) end)
for index, ipmask in ipairs(ipmasks) do for index, ipmask in ipairs(ipmasks) do
if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 and not ipmask:find("%s") then if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 and not ipmask:find("%s") then
elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 and not ipmask:find("%s") then -- elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 and not ipmask:find("%s") then
elseif ipmask:find("rule-set:", 1, true) == 1 or ipmask:find("rs:") == 1 then elseif ipmask:find("rule-set:", 1, true) == 1 or ipmask:find("rs:") == 1 then
local w = ipmask:sub(ipmask:find(":") + 1, #ipmask) local w = ipmask:sub(ipmask:find(":") + 1, #ipmask)
if w:find("local:") == 1 or w:find("remote:") == 1 then if w:find("local:") == 1 or w:find("remote:") == 1 then
@@ -691,12 +691,40 @@ local current_node = map:get(section)
params += opt.query("congestion_control", dom_prefix + "naive_congestion_control"); params += opt.query("congestion_control", dom_prefix + "naive_congestion_control");
} }
params += "#" + encodeURI(v_alias.value);
if (params[0] == "&") {
params = params.substring(1);
}
url += params;
} else if (v_type === "sing-box" && opt.get(dom_prefix + "protocol").value === "snell") {
protocol = "snell";
var v_userkey = opt.get(dom_prefix + "password");
var v_psk = opt.get(dom_prefix + "snell_psk");
var v_port = opt.get(dom_prefix + "port");
url = encodeURIComponent(v_psk.value) +
"@" + _address +
":" + v_port.value + "?";
var params = "psk=" + encodeURIComponent(v_psk.value);
if (v_userkey.value !== "") {
params += "&userkey=" + encodeURIComponent(v_userkey.value);
}
params += opt.query("version", dom_prefix + "snell_version");
if (opt.get(dom_prefix + "snell_version").value === '4') {
params += opt.query("obfs", dom_prefix + "snell_obfs_mode");
params += opt.query("obfs-host", dom_prefix + "snell_obfs_host");
} else {
params += opt.query("mode", dom_prefix + "snell_mode");
}
params += opt.query("reuse", dom_prefix + "snell_reuse");
params += "#" + encodeURI(v_alias.value); params += "#" + encodeURI(v_alias.value);
if (params[0] == "&") { if (params[0] == "&") {
params = params.substring(1); params = params.substring(1);
} }
url += params; url += params;
} }
if (url) { if (url) {
url = protocol.toLowerCase() + "://" + url; url = protocol.toLowerCase() + "://" + url;
return url; return url;
@@ -1705,6 +1733,37 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(hash.substr(1))); opt.set('remarks', decodeURIComponent(hash.substr(1)));
} }
} }
if (link_type === "snell") {
if (has_singbox) {
dom_prefix = "singbox_";
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "snell");
var m = parseNodeUrl(link);
opt.set(dom_prefix + 'address', unbracketIP(m.hostname));
opt.set(dom_prefix + 'port', m.port || "443");
var queryParam = {};
if (m.search.length > 1) {
var query = m.search.replace('/?', '?').split('?')
var queryParams = query[1];
var queryArray = queryParams.split('&');
var params;
for (i = 0; i < queryArray.length; i++) {
params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
}
}
opt.set(dom_prefix + 'password', queryParam.userkey || '');
opt.set(dom_prefix + 'snell_psk', queryParam.psk || m.passwd || '');
opt.set(dom_prefix + 'snell_version', queryParam.version || '4');
opt.set(dom_prefix + 'snell_reuse', queryParam.reuse === '1');
opt.set(dom_prefix + 'snell_obfs_mode', queryParam.obfs || 'none');
opt.set(dom_prefix + 'snell_obfs_host', queryParam['obfs-host'] || queryParam.obfs_host || '');
opt.set(dom_prefix + 'snell_mode', queryParam.mode || 'default');
if (m.hash) {
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (switch_type) { if (switch_type) {
sessionStorage.setItem("fromUrl", JSON.stringify({ sessionStorage.setItem("fromUrl", JSON.stringify({
+1 -1
View File
@@ -1452,7 +1452,7 @@ msgid "Private Key"
msgstr "私钥" msgstr "私钥"
msgid "Pre shared key" msgid "Pre shared key"
msgstr "额外的对称加密密钥" msgstr "预共享密钥"
msgid "Allowed IPs" msgid "Allowed IPs"
msgstr "允许的 IP" msgstr "允许的 IP"
@@ -513,6 +513,42 @@ local function encode_ssr(node)
return 0, "ssr://" .. base64(link) return 0, "ssr://" .. base64(link)
end end
-- snell
local function encode_snell(node)
local err_msg
local obfs = node["obfs-opts"]
if obfs and obfs.mode ~= "http" and obfs.mode ~= "none" then
err_msg = obfs.mode
end
if err_msg then
err_msg = "订阅转换 → 丢弃 Snell 节点:" .. (node.name or "") .. ",因 Sing-Box 不支持 Snell + " .. err_msg
return 1, err_msg
end
local version = node.version and tonumber(node.version) or 4
version = (version == 5) and 4 or version
if version < 4 then
err_msg = "订阅转换 → 丢弃 Snell 节点:" .. (node.name or "") .. ",因 Sing-Box 不支持 Snell 版本小于 4"
return 1, err_msg
end
local link = "snell://" .. host_format(node.server) .. ":" .. node.port
local p = {}
if node.psk then table.insert(p, "psk=" .. urlencode(node.psk)) end
table.insert(p, "version=" .. version)
if obfs.mode == "http" then
table.insert(p, "obfs=http")
if obfs.host then table.insert(p, "obfs-host=" .. urlencode(obfs.host)) end
end
table.insert(p, "reuse=" .. (node.reuse and "1" or "0"))
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
end
return 0, link .. "#" .. urlencode(node.name or "")
end
local function encode_node(node) local function encode_node(node)
if (not node.type) or (not node.name) then return nil end if (not node.type) or (not node.name) then return nil end
@@ -529,6 +565,7 @@ local function encode_node(node)
elseif t == "tuic" then return encode_tuic(node) elseif t == "tuic" then return encode_tuic(node)
elseif t == "anytls" then return encode_anytls(node) elseif t == "anytls" then return encode_anytls(node)
elseif t == "ssr" then return encode_ssr(node) elseif t == "ssr" then return encode_ssr(node)
elseif t == "snell" then return encode_snell(node)
else api.log("订阅转换 → 丢弃不支持的节点:" .. node.name .. ",节点类型:" .. t) else api.log("订阅转换 → 丢弃不支持的节点:" .. node.name .. ",节点类型:" .. t)
end end
end end
@@ -1628,6 +1628,62 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.naive_quic = "1" result.naive_quic = "1"
result.naive_congestion_control = params.congestion_control or "bbr" result.naive_congestion_control = params.congestion_control or "bbr"
end end
elseif szType == "snell" then
if has_singbox then
result.type = 'sing-box'
result.protocol = "snell"
else
log("跳过 Snell 节点,因未安装 Snell 核心程序 Sing-box 1.14。")
return nil
end
local alias = ""
if content:find("#") then
local idx_sp = content:find("#")
alias = content:sub(idx_sp + 1, -1)
content = content:sub(0, idx_sp - 1)
end
result.remarks = UrlDecode(alias)
local Info = content
if content:find("@") then
local info = split(content, "@")
result.snell_psk = UrlDecode(info[1])
Info = info[2]
end
Info = (Info or ""):gsub("/%?", "?")
local query = split(Info, "%?")
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local s = v:find("=", 1, true)
if s and s > 1 then
params[UrlDecode(v:sub(1, s - 1)):lower()] = UrlDecode(v:sub(s + 1))
end
end
-- [2001:4860:4860::8888]:443
-- 8.8.8.8:443
result.port = "443"
if host_port:find(":") then
local sp = split(host_port, ":")
result.port = sp[#sp]
if api.is_ipv6addrport(host_port) then
result.address = api.get_ipv6_only(host_port)
else
result.address = sp[1]
end
else
result.address = host_port
end
result.snell_psk = params.psk or result.snell_psk
result.password = params.userkey
result.snell_version = params.version or "4"
if result.snell_version == "4" then
result.snell_obfs_mode = params.obfs or "none"
result.snell_obfs_host = params['obfs-host'] or params.obfs_host
else
result.snell_mode = params.mode or "default"
end
result.snell_reuse = (params.reuse == "1") and "1" or "0"
else else
log("暂时不支持 " .. szType .. " 类型的节点订阅,跳过此节点。") log("暂时不支持 " .. szType .. " 类型的节点订阅,跳过此节点。")
return nil return nil
@@ -191,8 +191,8 @@ domain_list.validate = function(self, value)
tmp_host = host:gsub("full:", "") tmp_host = host:gsub("full:", "")
elseif host:find("geosite:") and host:find("geosite:") == 1 then elseif host:find("geosite:") and host:find("geosite:") == 1 then
flag = 0 flag = 0
elseif host:find("ext:") and host:find("ext:") == 1 then -- elseif host:find("ext:") and host:find("ext:") == 1 then
flag = 0 -- flag = 0
elseif host:find("rule-set:", 1, true) == 1 or host:find("rs:") == 1 then elseif host:find("rule-set:", 1, true) == 1 or host:find("rs:") == 1 then
local w = host:sub(host:find(":") + 1, #host) local w = host:sub(host:find(":") + 1, #host)
if w:find("local:") == 1 or w:find("remote:") == 1 then if w:find("local:") == 1 or w:find("remote:") == 1 then
@@ -233,7 +233,7 @@ ip_list.validate = function(self, value)
string.gsub(value, "[^\r\n]+", function(w) table.insert(ipmasks, w) end) string.gsub(value, "[^\r\n]+", function(w) table.insert(ipmasks, w) end)
for index, ipmask in ipairs(ipmasks) do for index, ipmask in ipairs(ipmasks) do
if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 and not ipmask:find("%s") then if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 and not ipmask:find("%s") then
elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 and not ipmask:find("%s") then -- elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 and not ipmask:find("%s") then
elseif ipmask:find("rule-set:", 1, true) == 1 or ipmask:find("rs:") == 1 then elseif ipmask:find("rule-set:", 1, true) == 1 or ipmask:find("rs:") == 1 then
local w = ipmask:sub(ipmask:find(":") + 1, #ipmask) local w = ipmask:sub(ipmask:find(":") + 1, #ipmask)
if w:find("local:") == 1 or w:find("remote:") == 1 then if w:find("local:") == 1 or w:find("remote:") == 1 then
-22
View File
@@ -1,22 +0,0 @@
# Copyright (C) 2020 Gyj1109
# 适配 OpenWrt 25.12 修改版
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-syncthing
PKG_VERSION:=1.0
PKG_RELEASE:=3
LUCI_TITLE:=LuCI support for Syncthing
LUCI_PKGARCH:=all
# 适配 25.12:确保安装了 syncthing 后,LuCI 界面能正确拉起
LUCI_DEPENDS:=+syncthing
include $(TOPDIR)/feeds/luci/luci.mk
# 明确定义配置文件的归属,这对 25.12 的 apk 管理器非常重要
define Package/$(PKG_NAME)/conffiles
/etc/config/syncthing
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
-2
View File
@@ -1,2 +0,0 @@
# luci-app-syncthing
在Potat0000源码基础上进行了汉化优化,官方openwrt23.05.3编译后可使用
@@ -1,17 +0,0 @@
module("luci.controller.syncthing", package.seeall)
function index()
if not nixio.fs.access("/etc/config/syncthing") then
return
end
entry({"admin", "services", "syncthing"}, cbi("syncthing"), _("文件同步"), 10).dependent = true
entry({"admin", "services", "syncthing", "status"}, call("act_status")).leaf = true
end
function act_status()
local e = {}
e.running = luci.sys.call("pgrep syncthing >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
@@ -1,48 +0,0 @@
require("nixio.fs")
m = Map("syncthing", translate("Syncthing同步工具"))
m:section(SimpleSection).template = "syncthing/syncthing_status"
s = m:section(TypedSection, "syncthing")
s.anonymous = true
o = s:option(Flag, "enabled", translate("启用"))
o.default = 0
o.rmempty = false
gui_address = s:option(Value, "gui_address", translate("GUI访问地址"))
gui_address.description = translate("使用0.0.0.0以监控所有访问。")
gui_address.default = "http://0.0.0.0:8384"
gui_address.placeholder = "http://0.0.0.0:8384"
gui_address.rmempty = false
home = s:option(Value, "home", translate("配置文件目录"))
home.description = translate("只有保存在/etc/syncthing中的配置会自动备份!")
home.default = "/etc/syncthing"
home.placeholder = "/etc/syncthing"
home.rmempty = false
user = s:option(ListValue, "user", translate("用户"))
user.description = translate("默认是syncthing,但这可能会导致权限被拒绝。Syncthing官方不建议以root身份运行。")
user:value("", translate("syncthing"))
for u in luci.util.execi("cat /etc/passwd | cut -d ':' -f1") do
user:value(u)
end
macprocs = s:option(Value, "macprocs", translate("线程限制"))
macprocs.description = translate("0表示匹配CPU数量(默认),>0表示显式指定并发数。")
macprocs.default = "0"
macprocs.placeholder = "0"
macprocs.datatype = "range(0,32)"
macprocs.rmempty = false
nice = s:option(Value, "nice", translate("优先级"))
nice.description = translate("显式指定优先级值。0是最高,19是最低。(暂时不允许设置负值)")
nice.default = "19"
nice.placeholder = "19"
nice.datatype = "range(0,19)"
nice.rmempty = false
return m
@@ -1,27 +0,0 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(1, '<%=url([[admin]], [[services]], [[syncthing]], [[status]])%>', null,
function (x, data) {
var tb = document.getElementById('syncthing_status');
if (data && tb) {
if (data.running) {
var links = '<em><b><font color="green">Syncthing <%:运行中%></font></b></em><input class="btn cbi-button mar-10" type="button" value="<%:打开Syncthing页面%>" onclick="openwebui();" />';
tb.innerHTML = links;
} else {
tb.innerHTML = '<em><b><font color="red">Syncthing <%:未运行%></font></b></em>';
}
}
}
);
function openwebui(){
var url = window.location.host+":<%=luci.sys.exec("uci -q get syncthing.syncthing.gui_address"):match(":[0-9]+"):gsub(":", "")%>";
window.open('http://'+url,'target','');
}
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="syncthing_status">
<em><%:正在收集数据...%></em>
</p>
</fieldset>
@@ -1,12 +0,0 @@
#!/bin/sh
touch /etc/config/syncthing
uci -q batch <<-EOF >/dev/null
delete ucitrack.@syncthing[-1]
add ucitrack syncthing
set ucitrack.@syncthing[-1].exec='/etc/init.d/syncthing stop && /etc/init.d/syncthing start'
commit ucitrack
EOF
# remove LuCI cache
rm -f /tmp/luci*
exit 0
@@ -1,11 +0,0 @@
{
"luci-app-syncthing": {
"description": "Grant UCI access for luci-app-syncthing",
"read": {
"uci": [ "syncthing" ]
},
"write": {
"uci": [ "syncthing" ]
}
}
}