update 2026-08-23 16:22:34

This commit is contained in:
action committed 2026-08-23 16:22:34 +08:00
1 parent 36fa3c9afa
commit c9995f5dbc
38 files changed
+841 -1678

No files matched your search

@@ -50,20 +50,11 @@ end
o = s:option(DummyValue, "sources", translate("Source"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local e = ''
local v = Value.cfgvalue(t, n) or '-'
string.gsub(v, '[^' .. " " .. ']+', function(w)
local a = w
if mac_t[w] then
a = a .. ' (' .. mac_t[w].ip .. ')'
end
if #e > 0 then
e = e .. "<br />"
end
e = e .. a
end)
return e
o.cfgvalue = function(self, section)
local v = self.map:get(section, self.option) or {}
if type(v) == "table" then
return table.concat(v, "<br/>")
end
end
i = s:option(DummyValue, "mode", translate("Mode"))
@@ -71,6 +62,8 @@ i.cfgvalue = function(t, n)
local v = Value.cfgvalue(t, n) or '0'
if v == "1" then
return translate("Proxy")
elseif v == "2" then
return translate("Proxy") .. " " .. translate("Use global config")
end
return translate("No Proxy")
end
@@ -98,25 +98,9 @@ sources.description = "<ul><li>" .. translate("Example:")
.. "</li><li>" .. translate("IP range") .. ": 192.168.1.100-192.168.1.200"
.. "</li><li>" .. translate("IPSet") .. ": ipset:lanlist"
.. "</li></ul>"
sources.cast = "string"
for _, key in pairs(mac_t) do
sources:value(key.mac, "%s (%s)" % {key.mac, key.ip})
end
sources.cfgvalue = function(self, section)
local value
if self.tag_error[section] then
value = self:formvalue(section)
else
value = self.map:get(section, self.option)
if type(value) == "string" then
local value2 = {}
string.gsub(value, '[^' .. " " .. ']+', function(w) table.insert(value2, w) end)
value = value2
end
end
return value
end
sources.validate = function(self, value, t)
local err = {}
for _, v in ipairs(value) do
@@ -155,47 +139,75 @@ sources.validate = function(self, value, t)
return value
end
local NODE = m:get("@global[0]", "node")
o = s:option(ListValue, "mode", translate("Mode"))
o:value("0", translate("No Proxy"))
o:value("1", translate("Proxy"))
o:value("2", translate("Proxy") .. " " .. translate("Use global config") .. "(" .. api.get_node_name(NODE) .. ")")
---- TCP No Redir Ports
local TCP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "tcp_no_redir_ports")
o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
o:value("", translate("Use global config") .. "(" .. TCP_NO_REDIR_PORTS .. ")")
o:value("disable", translate("No patterns are used"))
o:value("", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o:depends("mode", "1")
o:depends("mode", "2")
o.validate = port_validate
---- UDP No Redir Ports
local UDP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_no_redir_ports")
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"))
o:value("", translate("Use global config") .. "(" .. UDP_NO_REDIR_PORTS .. ")")
o:value("disable", translate("No patterns are used"))
o:value("", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o:depends("mode", "1")
o:depends("mode", "2")
o.validate = port_validate
o = s:option(DummyValue, "_hide_node_option", "")
o = s:option(DummyValue, "_show_tcp_redir", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ tcp_no_redir_ports = "1:65535", ['!reverse'] = true })
o = s:option(DummyValue, "_show_udp_redir", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ udp_no_redir_ports = "1:65535", ['!reverse'] = true })
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
o:value("1:65535", translate("All"))
o:value("22,25,53,80,143,443,465,587,853,873,993,995,5222,8080,8443,9418", translate("Common Use"))
o:value("80,443", "80,443")
o.default = o.keylist[1]
o.validate = port_validate
o:depends({ _node = "1", _show_tcp_redir = "1" })
o:depends({ mode = "2", _show_tcp_redir = "1" })
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
o:value("1:65535", translate("All"))
o.default = o.keylist[1]
o.validate = port_validate
o:depends({ _node = "1", _show_udp_redir = "1" })
o:depends({ mode = "2", _show_udp_redir = "1" })
o = s:option(DummyValue, "tips", " ")
o.rawhtml = true
o.cfgvalue = function(t, n)
return string.format('<font color="red">%s</font>',
translate("The port settings support single ports and ranges.<br>Separate multiple ports with commas (,).<br>Example: 21,80,443,1000:2000."))
end
o:depends("mode", "1")
o:depends("mode", "2")
o = s:option(DummyValue, "_no_proxy", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends("mode", "0")
o:depends({ tcp_no_redir_ports = "1:65535", udp_no_redir_ports = "1:65535" })
if TCP_NO_REDIR_PORTS == "1:65535" and UDP_NO_REDIR_PORTS == "1:65535" then
o:depends({ tcp_no_redir_ports = "", udp_no_redir_ports = "" })
end
local GLOBAL_ENABLED = m:get("@global[0]", "enabled")
local NODE = m:get("@global[0]", "node")
o = s:option(DummyValue, "_show_node_option", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ mode = "1", _no_proxy = "" })
o = s:option(ListValue, "node", "<a style='color: red'>" .. translate("Node") .. "</a>")
if GLOBAL_ENABLED == "1" and NODE then
o:value("", translate("Use global config") .. "(" .. api.get_node_name(NODE) .. ")")
o.group = {""}
else
o.group = {}
end
o:depends({ _hide_node_option = "1", ['!reverse'] = true })
o.group = {}
o:depends("_show_node_option", "1")
o.template = m:template_path("/cbi/nodes_listvalue")
current_node_id = o:formvalue(arg[1])
@@ -204,9 +216,14 @@ if not current_node_id then
end
current_node = current_node_id and m:get(current_node_id) or {}
o = s:option(Flag, "log", translate("Enable Node Log"))
o = s:option(DummyValue, "_node", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ node = "", ['!reverse'] = true })
o = s:option(Flag, "log", translate("Enable Node Log"))
o:depends("_node", "1")
o = s:option(ListValue, "loglevel", translate("Log Level"))
o.default = "warn"
o:value("debug", "Debug")
@@ -218,8 +235,8 @@ o:depends("log", "1")
o = s:option(DummyValue, "_acl_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_path = api.TMP_PATH .. "/acl/" .. arg[1] .. "/node.log"
local log_url = api.url("get_acl_log") .. "?id=" .. arg[1]
local log_path = api.TMP_PATH .. "/acl/" .. arg[1] .. ".log"
local log_url = api.url("get_redir_log") .. "?id=" .. arg[1]
return string.format(
'<code>%s</code>&nbsp;&nbsp;<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
log_path,
@@ -229,46 +246,17 @@ o.cfgvalue = function(t, n)
end
o:depends("log", "1")
---- TCP Redir Ports
local TCP_REDIR_PORTS = m:get("@global_forwarding[0]", "tcp_redir_ports")
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
o:value("", translate("Use global config") .. "(" .. TCP_REDIR_PORTS .. ")")
o:value("1:65535", translate("All"))
o:value("22,25,53,80,143,443,465,587,853,873,993,995,5222,8080,8443,9418", translate("Common Use"))
o:value("80,443", "80,443")
o.validate = port_validate
o:depends({ _hide_node_option = "1", ['!reverse'] = true })
---- UDP Redir Ports
local UDP_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_redir_ports")
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
o:value("", translate("Use global config") .. "(" .. UDP_REDIR_PORTS .. ")")
o:value("1:65535", translate("All"))
o.validate = port_validate
o:depends({ _hide_node_option = "1", ['!reverse'] = true })
o = s:option(DummyValue, "tips", " ")
o.rawhtml = true
o.cfgvalue = function(t, n)
return string.format('<font color="red">%s</font>',
translate("The port settings support single ports and ranges.<br>Separate multiple ports with commas (,).<br>Example: 21,80,443,1000:2000."))
end
o:depends({ _hide_node_option = "1", ['!reverse'] = true })
o = s:option(DummyValue, "_hide_dns_option", "")
o = s:option(DummyValue, "_show_dns_option", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ node = "" })
if GLOBAL_ENABLED == "1" and NODE then
o:depends({ node = NODE })
end
o:depends("_hide", "1")
o = s:option(ListValue, "direct_dns_query_strategy", translate("Direct Query Strategy"))
o.default = "UseIP"
o:value("UseIP")
o:value("UseIPv4")
o:value("UseIPv6")
o:depends({ _hide_dns_option = "1", ['!reverse'] = true })
o:depends("_show_dns_option", "1")
o = s:option(ListValue, "remote_dns_protocol", translate("Remote DNS Protocol"))
o:value("tcp", "TCP")
@@ -279,7 +267,7 @@ if current_node.type == "sing-box" then
o:value("quic", "QUIC(DoQ)")
o:value("http3", "HTTP3(DoH3)")
end
o:depends({ _hide_dns_option = "1", ['!reverse'] = true })
o:depends("_show_dns_option", "1")
---- DNS over TCP or UDP or TLS (DoT) or QUIC (DoQ)
o = s:option(Value, "remote_dns", translate("Remote DNS"))
@@ -356,12 +344,13 @@ o:depends("remote_dns_protocol", "tls")
o = s:option(Value, "remote_rewrite_ttl", translate("Remote DNS") .. " TTL")
o.datatype = "min(1)"
o.default = "30"
o:depends("_hide", "1")
o = s:option(ListValue, "dns_hosts_mode", translate("Domain Override"))
o:value("default", translate("Use global config"))
o:value("disable", translate("No patterns are used"))
o:value("custom", translate("-- custom --"))
o:depends({ _hide_dns_option = "1", ['!reverse'] = true })
o:depends("_show_dns_option", "1")
o = s:option(TextValue, "dns_hosts", translate("Domain Override"))
o.rows = 5
@@ -385,6 +374,7 @@ for k, v in pairs(nodes_table) do
if v.type == "sing-box" then
s.fields["remote_rewrite_ttl"]:depends({ node = v.id })
end
s.fields["_show_dns_option"]:depends({ mode = "1", node = v.id })
if v.node_type == "normal" or v.protocol == "_balancing" or v.protocol == "_urltest" then
--Shunt node has its own separate options.
s.fields["remote_fakedns"]:depends({ node = v.id, remote_dns_protocol = "tcp" })
@@ -282,8 +282,8 @@ loglevel:value("error")
o = s:taboption("log", DummyValue, "_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_path = api.TMP_PATH .. "/acl/default/global.log"
local log_url = api.url("get_redir_log") .. "?id=default&name=global"
local log_path = api.TMP_PATH .. "/acl/default.log"
local log_url = api.url("get_redir_log") .. "?id=default"
return string.format(
'<code>%s</code>&nbsp;&nbsp;<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
log_path,
@@ -4,7 +4,6 @@ api.set_default_cbi()
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria")
local ss_type = {}
local trojan_type = {}
local vmess_type = {}
@@ -33,10 +32,6 @@ if has_xray then
table.insert(hysteria2_type, s)
end
end
if has_hysteria2 then
local s = "hysteria2"
table.insert(hysteria2_type, s)
end
m = Map()
@@ -17,7 +17,6 @@ m:appendTemplate("/cbi/nodes_listvalue_com")
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria")
local ss_type = {}
local trojan_type = {}
local vmess_type = {}
@@ -46,10 +45,6 @@ if has_xray then
table.insert(hysteria2_type, s)
end
end
if has_hysteria2 then
local s = "hysteria2"
table.insert(hysteria2_type, s)
end
local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
@@ -72,8 +72,7 @@ s = m:section(NamedSection, "@global_forwarding[0]", "global_forwarding", transl
---- TCP No Redir Ports
o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
o.default = "disable"
o:value("disable", translate("No patterns are used"))
o:value("", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o.validate = port_validate
@@ -82,23 +81,22 @@ o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
"<font color='red'>" ..
translate("Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
"</font>")
o.default = "disable"
o:value("disable", translate("No patterns are used"))
o:value("", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o.validate = port_validate
---- TCP Redir Ports
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
o.default = "22,25,53,80,143,443,465,587,853,873,993,995,5222,8080,8443,9418"
o:value("1:65535", translate("All"))
o:value("22,25,53,80,143,443,465,587,853,873,993,995,5222,8080,8443,9418", translate("Common Use"))
o:value("80,443", translate("Only Web"))
o.default = o.keylist[1]
o.validate = port_validate
---- UDP Redir Ports
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
o.default = "1:65535"
o:value("1:65535", translate("All"))
o.default = o.keylist[1]
o.validate = port_validate
o = s:option(DummyValue, "tips", " ")
@@ -1,106 +0,0 @@
if not api.finded_com("hysteria") then
return
end
-- [[ Hysteria2 ]]
local m, s1 = ...
local type_name = "Hysteria2"
s1.fields["type"]:value(type_name, "Hysteria2")
if s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "hysteria2_"
o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o:depends({ realms = false })
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o:depends({ realms = false })
o = s:option(Value, "hop", translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o:depends({ realms = false })
o = s:option(Value, "hop_interval", translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)"
o.placeholder = "30"
o.default = "30"
o:depends({ realms = false })
o = s:option(Flag, "realms", translate("Realms"))
o.default = "0"
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ realms = "1" })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
if realm then return value end
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ realms = "1" })
o = s:option(Value, "auth_password", translate("Auth Password"))
o.password = true
o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o = s:option(Value, "obfs_password", translate("Obfs Password"))
o:depends({ obfs_type = "salamander" })
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ obfs_type = "gecko" })
o = s:option(Flag, "fast_open", translate("Fast Open"))
o.default = "0"
o = s:option(Value, "tls_serverName", translate("Domain"))
o = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
o.default = "0"
o = s:option(Value, "tls_pinSHA256", translate("PinSHA256"),translate("Certificate fingerprint"))
o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o = s:option(Value, "recv_window", translate("QUIC stream receive window"))
o = s:option(Value, "recv_window_conn", translate("QUIC connection receive window"))
o = s:option(Value, "idle_timeout", translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o.datatype = "range(4,120)"
o = s:option(Value, "keep_alive_period", translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o.datatype = "range(2,60)"
o = s:option(Flag, "disable_mtu_discovery", translate("Disable MTU detection"))
o.default = "0"
o = s:option(Flag, "lazy_start", translate("Lazy Start"))
o.default = "0"
api.luci_types(s1, s)
@@ -1,143 +0,0 @@
if not api.finded_com("hysteria") then
return
end
-- [[ Hysteria2 ]]
local m, s1 = ...
local type_name = "Hysteria2"
s1.fields["type"]:value(type_name, "Hysteria2")
if not s1.val["type"] then
s1.val["type"] = type_name
end
if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "hysteria2_"
o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(TextValue, "custom_config", translate("Custom Config"))
o.rows = 10
o.wrap = "off"
o:depends({ custom = true })
o.datatype = "json"
local o_validate = o.validate
o.validate = function(self, value)
local v = o_validate(self, value)
if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str")
if config_str then
return api.base64Decode(config_str)
end
end
o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ custom = false })
o = s:option(DynamicList, "users", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ custom = false })
o = s:option(Flag, "realms", translate("Realms"))
o.default = "0"
o:depends({ custom = false })
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ realms = true })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
if realm then return value end
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ realms = true })
o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o:depends({ custom = false })
o = s:option(Value, "obfs_password", translate("Obfs Password"))
o:depends({ obfs_type = "salamander" })
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ obfs_type = "gecko" })
o = s:option(Flag, "udp", translate("UDP"))
o.default = "1"
o:depends({ custom = false })
o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o:depends({ custom = false })
o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o:depends({ custom = false })
o = s:option(Flag, "ignoreClientBandwidth", translate("ignoreClientBandwidth"))
o.default = "0"
o:depends({ custom = false })
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ custom = false })
o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ custom = false })
o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(s1, s)