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:", "")
elseif host:find("geosite:") and host:find("geosite:") == 1 then
flag = 0
elseif host:find("ext:") and host:find("ext:") == 1 then
flag = 0
-- elseif host:find("ext:") and host:find("ext:") == 1 then
-- flag = 0
elseif host:find("rule-set:", 1, true) == 1 or host:find("rs:") == 1 then
local w = host:sub(host:find(":") + 1, #host)
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)
for index, ipmask in ipairs(ipmasks) do
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
local w = ipmask:sub(ipmask:find(":") + 1, #ipmask)
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 += "#" + 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);
if (params[0] == "&") {
params = params.substring(1);
}
url += params;
}
if (url) {
url = protocol.toLowerCase() + "://" + url;
return url;
@@ -1705,6 +1733,37 @@ local current_node = map:get(section)
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) {
sessionStorage.setItem("fromUrl", JSON.stringify({
+1 -1
View File
@@ -1452,7 +1452,7 @@ msgid "Private Key"
msgstr "私钥"
msgid "Pre shared key"
msgstr "额外的对称加密密钥"
msgstr "预共享密钥"
msgid "Allowed IPs"
msgstr "允许的 IP"
@@ -513,6 +513,42 @@ local function encode_ssr(node)
return 0, "ssr://" .. base64(link)
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)
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 == "anytls" then return encode_anytls(node)
elseif t == "ssr" then return encode_ssr(node)
elseif t == "snell" then return encode_snell(node)
else api.log("订阅转换 → 丢弃不支持的节点:" .. node.name .. ",节点类型:" .. t)
end
end
@@ -1628,6 +1628,62 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.naive_quic = "1"
result.naive_congestion_control = params.congestion_control or "bbr"
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
log("暂时不支持 " .. szType .. " 类型的节点订阅,跳过此节点。")
return nil
@@ -191,8 +191,8 @@ domain_list.validate = function(self, value)
tmp_host = host:gsub("full:", "")
elseif host:find("geosite:") and host:find("geosite:") == 1 then
flag = 0
elseif host:find("ext:") and host:find("ext:") == 1 then
flag = 0
-- elseif host:find("ext:") and host:find("ext:") == 1 then
-- flag = 0
elseif host:find("rule-set:", 1, true) == 1 or host:find("rs:") == 1 then
local w = host:sub(host:find(":") + 1, #host)
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)
for index, ipmask in ipairs(ipmasks) do
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
local w = ipmask:sub(ipmask:find(":") + 1, #ipmask)
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" ]
}
}
}