From 0849b74c005c6bb5f94f5adda2205e888fe0314b Mon Sep 17 00:00:00 2001
From: xiaorouji <60100640+xiaorouji@users.noreply.github.com>
Date: Wed, 23 Aug 2023 16:37:00 +0800
Subject: [PATCH] luci: optimization && modular
---
.../model/cbi/passwall/client/node_config.lua | 1144 +----------------
.../model/cbi/passwall/client/type/brook.lua | 65 +
.../cbi/passwall/client/type/hysteria.lua | 115 ++
.../model/cbi/passwall/client/type/naive.lua | 60 +
.../model/cbi/passwall/client/type/ray.lua | 735 +++++++++++
.../cbi/passwall/client/type/ss-rust.lua | 82 ++
.../model/cbi/passwall/client/type/ss.lua | 83 ++
.../model/cbi/passwall/client/type/ssr.lua | 94 ++
.../cbi/passwall/client/type/trojan-go.lua | 150 +++
.../cbi/passwall/client/type/trojan-plus.lua | 81 ++
.../model/cbi/passwall/client/type/tuic.lua | 158 +++
.../model/cbi/passwall/server/type/brook.lua | 65 +
.../cbi/passwall/server/type/hysteria.lua | 137 ++
.../model/cbi/passwall/server/type/ray.lua | 536 ++++++++
.../model/cbi/passwall/server/type/socks.lua | 71 +
.../cbi/passwall/server/type/ss-rust.lua | 71 +
.../model/cbi/passwall/server/type/ss.lua | 74 ++
.../model/cbi/passwall/server/type/ssr.lua | 98 ++
.../cbi/passwall/server/type/trojan-go.lua | 184 +++
.../cbi/passwall/server/type/trojan-plus.lua | 133 ++
.../luasrc/model/cbi/passwall/server/user.lua | 737 +----------
.../passwall/node_list/link_share_man.htm | 434 ++++---
22 files changed, 3295 insertions(+), 2012 deletions(-)
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/brook.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/naive.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss-rust.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/ssr.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-go.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-plus.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/client/type/tuic.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/brook.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-go.lua
create mode 100644 luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-plus.lua
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua
index 508a362da..0ff297abb 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua
@@ -1,62 +1,13 @@
local api = require "luci.passwall.api"
local appname = api.appname
local uci = api.uci
+local fs = require "nixio.fs"
+local types_dir = "/usr/lib/lua/luci/model/cbi/passwall/client/type/"
if not arg[1] or not uci:get(appname, arg[1]) then
luci.http.redirect(api.url("node_list"))
end
-local ss_encrypt_method_list = {
- "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
- "aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf",
- "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
- "xchacha20-ietf-poly1305"
-}
-
-local ss_rust_encrypt_method_list = {
- "plain", "none",
- "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
- "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
-}
-
-local ssr_encrypt_method_list = {
- "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
- "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
- "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb",
- "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20",
- "chacha20-ietf"
-}
-
-local ssr_protocol_list = {
- "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple",
- "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5",
- "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c",
- "auth_chain_d", "auth_chain_e", "auth_chain_f"
-}
-local ssr_obfs_list = {
- "plain", "http_simple", "http_post", "random_head", "tls_simple",
- "tls1.0_session_auth", "tls1.2_ticket_auth"
-}
-
-local v_ss_encrypt_method_list = {
- "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305"
-}
-
-local x_ss_encrypt_method_list = {
- "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
-}
-
-local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero" }
-
-local header_type_list = {
- "none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
-}
-local encrypt_methods_ss_aead = {
- "chacha20-ietf-poly1305",
- "aes-128-gcm",
- "aes-256-gcm",
-}
-
m = Map(appname, translate("Node Config"))
m.redirect = api.url()
@@ -64,1057 +15,68 @@ s = m:section(NamedSection, arg[1], "nodes", "")
s.addremove = false
s.dynamic = false
-share = s:option(DummyValue, "passwall", " ")
-share.rawhtml = true
-share.template = "passwall/node_list/link_share_man"
-share.value = arg[1]
+o = s:option(DummyValue, "passwall", " ")
+o.rawhtml = true
+o.template = "passwall/node_list/link_share_man"
+o.value = arg[1]
-remarks = s:option(Value, "remarks", translate("Node Remarks"))
-remarks.default = translate("Remarks")
-remarks.rmempty = false
+o = s:option(Value, "remarks", translate("Node Remarks"))
+o.default = translate("Remarks")
+o.rmempty = false
+
+o = s:option(ListValue, "type", translate("Type"))
-type = s:option(ListValue, "type", translate("Type"))
if api.is_finded("ipt2socks") then
- type:value("Socks", translate("Socks"))
-end
-if api.is_finded("ss-redir") then
- type:value("SS", translate("Shadowsocks Libev"))
-end
-if api.is_finded("sslocal") then
- type:value("SS-Rust", translate("Shadowsocks Rust"))
-end
-if api.is_finded("ssr-redir") then
- type:value("SSR", translate("ShadowsocksR Libev"))
-end
-if api.is_finded("v2ray") then
- type:value("V2ray", translate("V2ray"))
-end
-if api.is_finded("xray") then
- type:value("Xray", translate("Xray"))
-end
-if api.is_finded("brook") then
- type:value("Brook", translate("Brook"))
-end
---[[
-if api.is_finded("trojan-plus") or api.is_finded("trojan") then
- type:value("Trojan", translate("Trojan"))
-end
-]]--
-if api.is_finded("trojan-plus") then
- type:value("Trojan-Plus", translate("Trojan-Plus"))
-end
-if api.is_finded("trojan-go") then
- type:value("Trojan-Go", translate("Trojan-Go"))
-end
-if api.is_finded("naive") then
- type:value("Naiveproxy", translate("NaiveProxy"))
-end
-if api.is_finded("hysteria") then
- type:value("Hysteria", translate("Hysteria"))
-end
+ s.fields["type"]:value("Socks", translate("Socks"))
-if api.is_finded("tuic-client") then
- type:value("TUIC", translate("TUIC"))
-end
-
-protocol = s:option(ListValue, "protocol", translate("Protocol"))
-protocol:value("vmess", translate("Vmess"))
-protocol:value("vless", translate("VLESS"))
-protocol:value("http", translate("HTTP"))
-protocol:value("socks", translate("Socks"))
-protocol:value("shadowsocks", translate("Shadowsocks"))
-protocol:value("trojan", translate("Trojan"))
-protocol:value("wireguard", translate("WireGuard"))
-protocol:value("_balancing", translate("Balancing"))
-protocol:value("_shunt", translate("Shunt"))
-protocol:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
-protocol:depends("type", "V2ray")
-protocol:depends("type", "Xray")
-
-iface = s:option(Value, "iface", translate("Interface"))
-iface.default = "eth1"
-iface:depends("protocol", "_iface")
-
-local nodes_table = {}
-local balancers_table = {}
-local iface_table = {}
-for k, e in ipairs(api.get_valid_nodes()) do
- if e.node_type == "normal" then
- nodes_table[#nodes_table + 1] = {
- id = e[".name"],
- remarks = e["remark"]
- }
+ o = s:option(Value, "socks_address", translate("Address (Support Domain Name)"))
+ o:depends("type", "Socks")
+ function o.cfgvalue(self, section)
+ return m:get(section, "address")
end
- if e.protocol == "_balancing" then
- balancers_table[#balancers_table + 1] = {
- id = e[".name"],
- remarks = e["remark"]
- }
+ function o.write(self, section, value)
+ m:set(section, "address", value)
end
- if e.protocol == "_iface" then
- iface_table[#iface_table + 1] = {
- id = e[".name"],
- remarks = e["remark"]
- }
+
+ o = s:option(Value, "socks_port", translate("Port"))
+ o.datatype = "port"
+ o:depends("type", "Socks")
+ function o.cfgvalue(self, section)
+ return m:get(section, "port")
+ end
+ function o.write(self, section, value)
+ m:set(section, "port", value)
+ end
+
+ o = s:option(Value, "socks_username", translate("Username"))
+ o:depends("type", "Socks")
+ function o.cfgvalue(self, section)
+ return m:get(section, "username")
+ end
+ function o.write(self, section, value)
+ m:set(section, "username", value)
+ end
+
+ o = s:option(Value, "socks_password", translate("Password"))
+ o.password = true
+ o:depends("type", "Socks")
+ function o.cfgvalue(self, section)
+ return m:get(section, "password")
+ end
+ function o.write(self, section, value)
+ m:set(section, "password", value)
end
end
--- 负载均衡列表
-local balancing_node = s:option(DynamicList, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, document"))
-for k, v in pairs(nodes_table) do balancing_node:value(v.id, v.remarks) end
-balancing_node:depends("protocol", "_balancing")
-
-local balancingStrategy = s:option(ListValue, "balancingStrategy", translate("Balancing Strategy"))
-balancingStrategy:depends("protocol", "_balancing")
-balancingStrategy:value("random")
-balancingStrategy:value("leastPing")
-balancingStrategy.default = "random"
--- 探测地址
-local useCustomProbeUrl = s:option(Flag, "useCustomProbeUrl", translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
-useCustomProbeUrl:depends("balancingStrategy", "leastPing")
-local probeUrl = s:option(Value, "probeUrl", translate("Probe URL"))
-probeUrl:depends("useCustomProbeUrl", true)
-probeUrl.default = "https://www.google.com/generate_204"
-probeUrl.description = translate("The URL used to detect the connection status.")
--- 探测间隔
-local probeInterval = s:option(Value, "probeInterval", translate("Probe Interval"))
-probeInterval:depends("balancingStrategy", "leastPing")
-probeInterval.default = "1m"
-probeInterval.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are ns
, us
, ms
, s
, m
, h
, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
-
--- 分流
-if #nodes_table > 0 then
- o = s:option(Flag, "preproxy_enabled", translate("Preproxy"))
- o:depends("protocol", "_shunt")
- o = s:option(Value, "main_node", string.format('%s', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including Default
) has a separate switch that controls whether this rule uses the pre-proxy or not."))
- o:depends("preproxy_enabled", "1")
- for k, v in pairs(balancers_table) do
- o:value(v.id, v.remarks)
- end
- for k, v in pairs(iface_table) do
- o:value(v.id, v.remarks)
- end
- for k, v in pairs(nodes_table) do
- o:value(v.id, v.remarks)
- end
- o.default = "nil"
+local type_table = {}
+for filename in fs.dir(types_dir) do
+ table.insert(type_table, filename)
end
-uci:foreach(appname, "shunt_rules", function(e)
- if e[".name"] and e.remarks then
- o = s:option(Value, e[".name"], string.format('* %s', api.url("shunt_rules", e[".name"]), e.remarks))
- o:value("nil", translate("Close"))
- o:value("_default", translate("Default"))
- o:value("_direct", translate("Direct Connection"))
- o:value("_blackhole", translate("Blackhole"))
- o:depends("protocol", "_shunt")
+table.sort(type_table)
- if #nodes_table > 0 then
- for k, v in pairs(balancers_table) do
- o:value(v.id, v.remarks)
- end
- for k, v in pairs(iface_table) do
- o:value(v.id, v.remarks)
- end
- local pt = s:option(ListValue, e[".name"] .. "_proxy_tag", string.format('* %s', e.remarks .. " " .. translate("Preproxy")))
- pt:value("nil", translate("Close"))
- pt:value("main", translate("Preproxy Node"))
- pt.default = "nil"
- for k, v in pairs(nodes_table) do
- o:value(v.id, v.remarks)
- pt:depends({ preproxy_enabled = "1", [e[".name"]] = v.id })
- end
- end
- end
-end)
-
-shunt_tips = s:option(DummyValue, "shunt_tips", " ")
-shunt_tips.rawhtml = true
-shunt_tips.cfgvalue = function(t, n)
- return string.format('%s', translate("No shunt rules? Click me to go to add."))
-end
-shunt_tips:depends("protocol", "_shunt")
-
-local default_node = s:option(Value, "default_node", string.format('* %s', translate("Default")))
-default_node:depends("protocol", "_shunt")
-default_node:value("_direct", translate("Direct Connection"))
-default_node:value("_blackhole", translate("Blackhole"))
-
-if #nodes_table > 0 then
- for k, v in pairs(balancers_table) do
- default_node:value(v.id, v.remarks)
- end
- for k, v in pairs(iface_table) do
- default_node:value(v.id, v.remarks)
- end
- local dpt = s:option(ListValue, "default_proxy_tag", string.format('* %s', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node."))
- dpt:value("nil", translate("Close"))
- dpt:value("main", translate("Preproxy Node"))
- dpt.default = "nil"
- for k, v in pairs(nodes_table) do
- default_node:value(v.id, v.remarks)
- dpt:depends({ preproxy_enabled = "1", default_node = v.id })
- end
-end
-
-domainStrategy = s:option(ListValue, "domainStrategy", translate("Domain Strategy"))
-domainStrategy:value("AsIs")
-domainStrategy:value("IPIfNonMatch")
-domainStrategy:value("IPOnDemand")
-domainStrategy.default = "IPOnDemand"
-domainStrategy.description = "
- " .. translate("'AsIs': Only use domain for routing. Default value.")
- .. "
- " .. translate("'IPIfNonMatch': When no rule matches current domain, resolves it into IP addresses (A or AAAA records) and try all rules again.")
- .. "
- " .. translate("'IPOnDemand': As long as there is a IP-based rule, resolves the domain into IP immediately.")
- .. "
"
-domainStrategy:depends("protocol", "_shunt")
-
-domainMatcher = s:option(ListValue, "domainMatcher", translate("Domain matcher"))
-domainMatcher:value("hybrid")
-domainMatcher:value("linear")
-domainMatcher:depends("protocol", "_shunt")
-
-
--- Brook协议
-brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol"))
-brook_protocol:value("client", translate("Brook"))
-brook_protocol:value("wsclient", translate("WebSocket"))
-brook_protocol:depends("type", "Brook")
-function brook_protocol.cfgvalue(self, section)
- return m:get(section, "protocol")
-end
-function brook_protocol.write(self, section, value)
- m:set(section, "protocol", value)
-end
-
-brook_tls = s:option(Flag, "brook_tls", translate("Use TLS"))
-brook_tls:depends("brook_protocol", "wsclient")
-
--- Naiveproxy协议
-naiveproxy_protocol = s:option(ListValue, "naiveproxy_protocol", translate("Protocol"))
-naiveproxy_protocol:value("https", translate("HTTPS"))
-naiveproxy_protocol:value("quic", translate("QUIC"))
-naiveproxy_protocol:depends("type", "Naiveproxy")
-function naiveproxy_protocol.cfgvalue(self, section)
- return m:get(section, "protocol")
-end
-function naiveproxy_protocol.write(self, section, value)
- m:set(section, "protocol", value)
-end
-
-address = s:option(Value, "address", translate("Address (Support Domain Name)"))
-address.rmempty = false
-address:depends("type", "Socks")
-address:depends("type", "SS")
-address:depends("type", "SS-Rust")
-address:depends("type", "SSR")
-address:depends("type", "Brook")
-address:depends("type", "Trojan")
-address:depends("type", "Trojan-Plus")
-address:depends("type", "Trojan-Go")
-address:depends("type", "Naiveproxy")
-address:depends("type", "Hysteria")
-address:depends("type", "TUIC")
-address:depends({ type = "V2ray", protocol = "vmess" })
-address:depends({ type = "V2ray", protocol = "vless" })
-address:depends({ type = "V2ray", protocol = "http" })
-address:depends({ type = "V2ray", protocol = "socks" })
-address:depends({ type = "V2ray", protocol = "shadowsocks" })
-address:depends({ type = "V2ray", protocol = "trojan" })
-address:depends({ type = "Xray", protocol = "vmess" })
-address:depends({ type = "Xray", protocol = "vless" })
-address:depends({ type = "Xray", protocol = "http" })
-address:depends({ type = "Xray", protocol = "socks" })
-address:depends({ type = "Xray", protocol = "shadowsocks" })
-address:depends({ type = "Xray", protocol = "trojan" })
-address:depends({ type = "Xray", protocol = "wireguard" })
-
-port = s:option(Value, "port", translate("Port"))
-port.datatype = "port"
-port.rmempty = false
-port:depends("type", "Socks")
-port:depends("type", "SS")
-port:depends("type", "SS-Rust")
-port:depends("type", "SSR")
-port:depends("type", "Brook")
-port:depends("type", "Trojan")
-port:depends("type", "Trojan-Plus")
-port:depends("type", "Trojan-Go")
-port:depends("type", "Naiveproxy")
-port:depends("type", "Hysteria")
-port:depends("type", "TUIC")
-port:depends({ type = "V2ray", protocol = "vmess" })
-port:depends({ type = "V2ray", protocol = "vless" })
-port:depends({ type = "V2ray", protocol = "http" })
-port:depends({ type = "V2ray", protocol = "socks" })
-port:depends({ type = "V2ray", protocol = "shadowsocks" })
-port:depends({ type = "V2ray", protocol = "trojan" })
-port:depends({ type = "Xray", protocol = "vmess" })
-port:depends({ type = "Xray", protocol = "vless" })
-port:depends({ type = "Xray", protocol = "http" })
-port:depends({ type = "Xray", protocol = "socks" })
-port:depends({ type = "Xray", protocol = "shadowsocks" })
-port:depends({ type = "Xray", protocol = "trojan" })
-port:depends({ type = "Xray", protocol = "wireguard" })
-
-hysteria_hop = s:option(Value, "hysteria_hop", translate("Additional ports for hysteria hop"))
-hysteria_hop:depends("type", "Hysteria")
-
-username = s:option(Value, "username", translate("Username"))
-username:depends("type", "Socks")
-username:depends("type", "Naiveproxy")
-username:depends({ type = "V2ray", protocol = "http" })
-username:depends({ type = "V2ray", protocol = "socks" })
-username:depends({ type = "Xray", protocol = "http" })
-username:depends({ type = "Xray", protocol = "socks" })
-
-password = s:option(Value, "password", translate("Password"))
-password.password = true
-password:depends("type", "Socks")
-password:depends("type", "SS")
-password:depends("type", "SS-Rust")
-password:depends("type", "SSR")
-password:depends("type", "Brook")
-password:depends("type", "Trojan")
-password:depends("type", "Trojan-Plus")
-password:depends("type", "Trojan-Go")
-password:depends("type", "Naiveproxy")
-password:depends({ type = "V2ray", protocol = "http" })
-password:depends({ type = "V2ray", protocol = "socks" })
-password:depends({ type = "V2ray", protocol = "shadowsocks" })
-password:depends({ type = "V2ray", protocol = "trojan" })
-password:depends({ type = "Xray", protocol = "http" })
-password:depends({ type = "Xray", protocol = "socks" })
-password:depends({ type = "Xray", protocol = "shadowsocks" })
-password:depends({ type = "Xray", protocol = "trojan" })
-
-hysteria_protocol = s:option(ListValue, "hysteria_protocol", translate("Protocol"))
-hysteria_protocol:value("udp", "UDP")
-hysteria_protocol:value("faketcp", "faketcp")
-hysteria_protocol:value("wechat-video", "wechat-video")
-hysteria_protocol:depends("type", "Hysteria")
-function hysteria_protocol.cfgvalue(self, section)
- return m:get(section, "protocol")
-end
-function hysteria_protocol.write(self, section, value)
- m:set(section, "protocol", value)
-end
-
-hysteria_obfs = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
-hysteria_obfs:depends("type", "Hysteria")
-
-hysteria_auth_type = s:option(ListValue, "hysteria_auth_type", translate("Auth Type"))
-hysteria_auth_type:value("disable", translate("Disable"))
-hysteria_auth_type:value("string", translate("STRING"))
-hysteria_auth_type:value("base64", translate("BASE64"))
-hysteria_auth_type:depends("type", "Hysteria")
-
-hysteria_auth_password = s:option(Value, "hysteria_auth_password", translate("Auth Password"))
-hysteria_auth_password.password = true
-hysteria_auth_password:depends("hysteria_auth_type", "string")
-hysteria_auth_password:depends("hysteria_auth_type", "base64")
-
-hysteria_alpn = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN"))
-hysteria_alpn:depends("type", "Hysteria")
-
-ss_encrypt_method = s:option(Value, "ss_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(ss_encrypt_method_list) do ss_encrypt_method:value(t) end
-ss_encrypt_method:depends("type", "SS")
-function ss_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function ss_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-ss_rust_encrypt_method = s:option(Value, "ss_rust_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(ss_rust_encrypt_method_list) do ss_rust_encrypt_method:value(t) end
-ss_rust_encrypt_method:depends("type", "SS-Rust")
-function ss_rust_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function ss_rust_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-ssr_encrypt_method = s:option(Value, "ssr_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(ssr_encrypt_method_list) do ssr_encrypt_method:value(t) end
-ssr_encrypt_method:depends("type", "SSR")
-function ssr_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function ssr_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-security = s:option(ListValue, "security", translate("Encrypt Method"))
-for a, t in ipairs(security_list) do security:value(t) end
-security:depends({ type = "V2ray", protocol = "vmess" })
-security:depends({ type = "Xray", protocol = "vmess" })
-
-encryption = s:option(Value, "encryption", translate("Encrypt Method"))
-encryption.default = "none"
-encryption:value("none")
-encryption:depends({ type = "V2ray", protocol = "vless" })
-encryption:depends({ type = "Xray", protocol = "vless" })
-
-v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end
-v_ss_encrypt_method:depends({ type = "V2ray", protocol = "shadowsocks" })
-function v_ss_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function v_ss_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-x_ss_encrypt_method = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(x_ss_encrypt_method_list) do x_ss_encrypt_method:value(t) end
-x_ss_encrypt_method:depends({ type = "Xray", protocol = "shadowsocks" })
-function x_ss_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function x_ss_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-iv_check = s:option(Flag, "iv_check", translate("IV Check"))
-iv_check:depends({ type = "V2ray", protocol = "shadowsocks" })
-iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "aes-128-gcm" })
-iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "aes-256-gcm" })
-iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "chacha20-poly1305" })
-iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "xchacha20-poly1305" })
-
-uot = s:option(Flag, "uot", translate("UDP over TCP"), translate("Need Xray-core or sing-box as server side."))
-uot:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-128-gcm" })
-uot:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-256-gcm" })
-uot:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-chacha20-poly1305" })
-
-ssr_protocol = s:option(Value, "ssr_protocol", translate("Protocol"))
-for a, t in ipairs(ssr_protocol_list) do ssr_protocol:value(t) end
-ssr_protocol:depends("type", "SSR")
-function ssr_protocol.cfgvalue(self, section)
- return m:get(section, "protocol")
-end
-function ssr_protocol.write(self, section, value)
- m:set(section, "protocol", value)
-end
-
-protocol_param = s:option(Value, "protocol_param", translate("Protocol_param"))
-protocol_param:depends("type", "SSR")
-
-obfs = s:option(Value, "obfs", translate("Obfs"))
-for a, t in ipairs(ssr_obfs_list) do obfs:value(t) end
-obfs:depends("type", "SSR")
-
-obfs_param = s:option(Value, "obfs_param", translate("Obfs_param"))
-obfs_param:depends("type", "SSR")
-
-timeout = s:option(Value, "timeout", translate("Connection Timeout"))
-timeout.datatype = "uinteger"
-timeout.default = 300
-timeout:depends("type", "SS")
-timeout:depends("type", "SS-Rust")
-timeout:depends("type", "SSR")
-
-tcp_fast_open = s:option(ListValue, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
-tcp_fast_open:value("false")
-tcp_fast_open:value("true")
-tcp_fast_open:depends("type", "SS")
-tcp_fast_open:depends("type", "SS-Rust")
-tcp_fast_open:depends("type", "SSR")
-tcp_fast_open:depends("type", "Trojan")
-tcp_fast_open:depends("type", "Trojan-Plus")
-tcp_fast_open:depends("type", "Trojan-Go")
-
-fast_open = s:option(Flag, "fast_open", translate("Fast Open"))
-fast_open.default = "0"
-fast_open:depends("type", "Hysteria")
-
-ss_plugin = s:option(ListValue, "ss_plugin", translate("plugin"))
-ss_plugin:value("none", translate("none"))
-if api.is_finded("xray-plugin") then ss_plugin:value("xray-plugin") end
-if api.is_finded("v2ray-plugin") then ss_plugin:value("v2ray-plugin") end
-if api.is_finded("obfs-local") then ss_plugin:value("obfs-local") end
-ss_plugin:depends("type", "SS")
-ss_plugin:depends("type", "SS-Rust")
-function ss_plugin.cfgvalue(self, section)
- return m:get(section, "plugin")
-end
-function ss_plugin.write(self, section, value)
- m:set(section, "plugin", value)
-end
-
-ss_plugin_opts = s:option(Value, "ss_plugin_opts", translate("opts"))
-ss_plugin_opts:depends("ss_plugin", "xray-plugin")
-ss_plugin_opts:depends("ss_plugin", "v2ray-plugin")
-ss_plugin_opts:depends("ss_plugin", "obfs-local")
-function ss_plugin_opts.cfgvalue(self, section)
- return m:get(section, "plugin_opts")
-end
-function ss_plugin_opts.write(self, section, value)
- m:set(section, "plugin_opts", value)
-end
-
-uuid = s:option(Value, "uuid", translate("ID"))
-uuid.password = true
-uuid:depends({ type = "V2ray", protocol = "vmess" })
-uuid:depends({ type = "V2ray", protocol = "vless" })
-uuid:depends({ type = "Xray", protocol = "vmess" })
-uuid:depends({ type = "Xray", protocol = "vless" })
-uuid:depends({ type = "TUIC"})
-
-tls = s:option(Flag, "tls", translate("TLS"))
-tls.default = 0
-tls.validate = function(self, value, t)
- if value then
- local type = type:formvalue(t) or ""
- if value == "0" and (type == "Trojan" or type == "Trojan-Plus") then
- return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
- end
- return value
- end
-end
-tls:depends({ type = "V2ray", protocol = "vmess" })
-tls:depends({ type = "V2ray", protocol = "vless" })
-tls:depends({ type = "V2ray", protocol = "socks" })
-tls:depends({ type = "V2ray", protocol = "trojan" })
-tls:depends({ type = "V2ray", protocol = "shadowsocks" })
-tls:depends({ type = "Xray", protocol = "vmess" })
-tls:depends({ type = "Xray", protocol = "vless" })
-tls:depends({ type = "Xray", protocol = "socks" })
-tls:depends({ type = "Xray", protocol = "trojan" })
-tls:depends({ type = "Xray", protocol = "shadowsocks" })
-tls:depends("type", "Trojan")
-tls:depends("type", "Trojan-Plus")
-tls:depends("type", "Trojan-Go")
-
-tlsflow = s:option(Value, "tlsflow", translate("flow"))
-tlsflow.default = ""
-tlsflow:value("", translate("Disable"))
-tlsflow:value("xtls-rprx-vision")
-tlsflow:value("xtls-rprx-vision-udp443")
-tlsflow:depends({ type = "Xray", protocol = "vless", tls = true, transport = "tcp" })
-
-reality = s:option(Flag, "reality", translate("REALITY"), translate("Only recommend to use with VLESS-TCP-XTLS-Vision."))
-reality.default = 0
-reality:depends({ type = "Xray", tls = true, transport = "tcp" })
-reality:depends({ type = "Xray", tls = true, transport = "h2" })
-reality:depends({ type = "Xray", tls = true, transport = "grpc" })
-
-alpn = s:option(ListValue, "alpn", translate("alpn"))
-alpn.default = "default"
-alpn:value("default", translate("Default"))
-alpn:value("h2,http/1.1")
-alpn:value("h2")
-alpn:value("http/1.1")
-alpn:depends({ type = "V2ray", tls = true })
-alpn:depends({ type = "Xray", tls = true, reality = false })
-
--- minversion = s:option(Value, "minversion", translate("minversion"))
--- minversion.default = "1.3"
--- minversion:value("1.3")
--- minversion:depends("tls", true)
-
--- [[ TLS部分 ]] --
-tls_sessionTicket = s:option(Flag, "tls_sessionTicket", translate("Session Ticket"))
-tls_sessionTicket.default = "0"
-tls_sessionTicket:depends({ type = "Trojan", tls = true })
-tls_sessionTicket:depends({ type = "Trojan-Plus", tls = true })
-tls_sessionTicket:depends({ type = "Trojan-Go", tls = true })
-
-trojan_go_fingerprint = s:option(ListValue, "trojan_go_fingerprint", translate("Finger Print"))
-trojan_go_fingerprint:value("disable", translate("Disable"))
-trojan_go_fingerprint:value("firefox")
-trojan_go_fingerprint:value("chrome")
-trojan_go_fingerprint:value("ios")
-trojan_go_fingerprint.default = "disable"
-trojan_go_fingerprint:depends({ type = "Trojan-Go", tls = true })
-function trojan_go_fingerprint.cfgvalue(self, section)
- return m:get(section, "fingerprint")
-end
-function trojan_go_fingerprint.write(self, section, value)
- m:set(section, "fingerprint", value)
-end
-
-tls_serverName = s:option(Value, "tls_serverName", translate("Domain"))
-tls_serverName:depends("tls", true)
-tls_serverName:depends("type", "Hysteria")
-
-tls_allowInsecure = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
-tls_allowInsecure.default = "0"
-tls_allowInsecure:depends({ tls = true, reality = false })
-tls_allowInsecure:depends("type", "Hysteria")
-
-xray_fingerprint = s:option(Value, "xray_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to."))
-xray_fingerprint:value("", translate("Disable"))
-xray_fingerprint:value("chrome")
-xray_fingerprint:value("firefox")
-xray_fingerprint:value("safari")
-xray_fingerprint:value("ios")
--- xray_fingerprint:value("android")
-xray_fingerprint:value("edge")
--- xray_fingerprint:value("360")
-xray_fingerprint:value("qq")
-xray_fingerprint:value("random")
-xray_fingerprint:value("randomized")
-xray_fingerprint.default = ""
-xray_fingerprint:depends({ type = "Xray", tls = true, reality = false })
-function xray_fingerprint.cfgvalue(self, section)
- return m:get(section, "fingerprint")
-end
-function xray_fingerprint.write(self, section, value)
- m:set(section, "fingerprint", value)
-end
-function xray_fingerprint.remove(self, section)
- m:del(section, "fingerprint")
-end
-
--- [[ REALITY部分 ]] --
-reality_publicKey = s:option(Value, "reality_publicKey", translate("Public Key"))
-reality_publicKey:depends({ type = "Xray", tls = true, reality = true })
-
-reality_shortId = s:option(Value, "reality_shortId", translate("Short Id"))
-reality_shortId:depends({ type = "Xray", tls = true, reality = true })
-
-reality_spiderX = s:option(Value, "reality_spiderX", translate("Spider X"))
-reality_spiderX.placeholder = "/"
-reality_spiderX:depends({ type = "Xray", tls = true, reality = true })
-
-reality_fingerprint = s:option(Value, "reality_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to."))
-reality_fingerprint:value("chrome")
-reality_fingerprint:value("firefox")
-reality_fingerprint:value("safari")
-reality_fingerprint:value("ios")
--- reality_fingerprint:value("android")
-reality_fingerprint:value("edge")
--- reality_fingerprint:value("360")
-reality_fingerprint:value("qq")
-reality_fingerprint:value("random")
-reality_fingerprint:value("randomized")
-reality_fingerprint.default = "chrome"
-reality_fingerprint:depends({ type = "Xray", tls = true, reality = true })
-function reality_fingerprint.cfgvalue(self, section)
- return m:get(section, "fingerprint")
-end
-function reality_fingerprint.write(self, section, value)
- m:set(section, "fingerprint", value)
-end
-
-trojan_transport = s:option(ListValue, "trojan_transport", translate("Transport"))
-trojan_transport:value("original", translate("Original"))
-trojan_transport:value("ws", "WebSocket")
-trojan_transport.default = "original"
-trojan_transport:depends("type", "Trojan-Go")
-
-trojan_plugin = s:option(ListValue, "plugin_type", translate("Transport Plugin"))
-trojan_plugin:value("plaintext", "Plain Text")
-trojan_plugin:value("shadowsocks", "ShadowSocks")
-trojan_plugin:value("other", "Other")
-trojan_plugin.default = "plaintext"
-trojan_plugin:depends({ tls = false, trojan_transport = "original" })
-
-trojan_plugin_cmd = s:option(Value, "plugin_cmd", translate("Plugin Binary"))
-trojan_plugin_cmd.placeholder = "eg: /usr/bin/v2ray-plugin"
-trojan_plugin_cmd:depends({ plugin_type = "shadowsocks" })
-trojan_plugin_cmd:depends({ plugin_type = "other" })
-
-trojan_plugin_op = s:option(Value, "plugin_option", translate("Plugin Option"))
-trojan_plugin_op.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
-trojan_plugin_op:depends({ plugin_type = "shadowsocks" })
-trojan_plugin_op:depends({ plugin_type = "other" })
-
-trojan_plugin_arg = s:option(DynamicList, "plugin_arg", translate("Plugin Option Args"))
-trojan_plugin_arg.placeholder = "eg: [\"-config\", \"test.json\"]"
-trojan_plugin_arg:depends({ plugin_type = "shadowsocks" })
-trojan_plugin_arg:depends({ plugin_type = "other" })
-
-transport = s:option(ListValue, "transport", translate("Transport"))
-transport:value("tcp", "TCP")
-transport:value("mkcp", "mKCP")
-transport:value("ws", "WebSocket")
-transport:value("h2", "HTTP/2")
-transport:value("ds", "DomainSocket")
-transport:value("quic", "QUIC")
-transport:value("grpc", "gRPC")
-transport:depends({ type = "V2ray", protocol = "vmess" })
-transport:depends({ type = "V2ray", protocol = "vless" })
-transport:depends({ type = "V2ray", protocol = "socks" })
-transport:depends({ type = "V2ray", protocol = "shadowsocks" })
-transport:depends({ type = "V2ray", protocol = "trojan" })
-transport:depends({ type = "Xray", protocol = "vmess" })
-transport:depends({ type = "Xray", protocol = "vless" })
-transport:depends({ type = "Xray", protocol = "socks" })
-transport:depends({ type = "Xray", protocol = "shadowsocks" })
-transport:depends({ type = "Xray", protocol = "trojan" })
-
---[[
-ss_transport = s:option(ListValue, "ss_transport", translate("Transport"))
-ss_transport:value("ws", "WebSocket")
-ss_transport:value("h2", "HTTP/2")
-ss_transport:value("h2+ws", "HTTP/2 & WebSocket")
-ss_transport:depends({ type = "V2ray", protocol = "shadowsocks" })
-ss_transport:depends({ type = "Xray", protocol = "shadowsocks" })
-]]--
-
-wireguard_public_key = s:option(Value, "wireguard_public_key", translate("Public Key"))
-wireguard_public_key:depends({ type = "Xray", protocol = "wireguard" })
-
-wireguard_secret_key = s:option(Value, "wireguard_secret_key", translate("Private Key"))
-wireguard_secret_key:depends({ type = "Xray", protocol = "wireguard" })
-
-wireguard_preSharedKey = s:option(Value, "wireguard_preSharedKey", translate("Pre shared key"))
-wireguard_preSharedKey:depends({ type = "Xray", protocol = "wireguard" })
-
-wireguard_local_address = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
-wireguard_local_address:depends({ type = "Xray", protocol = "wireguard" })
-
-wireguard_mtu = s:option(Value, "wireguard_mtu", translate("MTU"))
-wireguard_mtu.default = "1420"
-wireguard_mtu:depends({ type = "Xray", protocol = "wireguard" })
-
-if api.compare_versions(api.get_app_version("xray"), ">=", "1.8.0") then
- wireguard_reserved = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
- wireguard_reserved:depends({ type = "Xray", protocol = "wireguard" })
-end
-
-wireguard_keepAlive = s:option(Value, "wireguard_keepAlive", translate("Keep Alive"))
-wireguard_keepAlive.default = "0"
-wireguard_keepAlive:depends({ type = "Xray", protocol = "wireguard" })
-
--- [[ TCP部分 ]]--
-
--- TCP伪装
-tcp_guise = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
-tcp_guise:value("none", "none")
-tcp_guise:value("http", "http")
-tcp_guise:depends("transport", "tcp")
-
--- HTTP域名
-tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
-tcp_guise_http_host:depends("tcp_guise", "http")
-
--- HTTP路径
-tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
-tcp_guise_http_path.placeholder = "/"
-tcp_guise_http_path:depends("tcp_guise", "http")
-
--- [[ mKCP部分 ]]--
-
-mkcp_guise = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
-for a, t in ipairs(header_type_list) do mkcp_guise:value(t) end
-mkcp_guise:depends("transport", "mkcp")
-
-mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
-mkcp_mtu.default = "1350"
-mkcp_mtu:depends("transport", "mkcp")
-
-mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI"))
-mkcp_tti.default = "20"
-mkcp_tti:depends("transport", "mkcp")
-
-mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
-mkcp_uplinkCapacity.default = "5"
-mkcp_uplinkCapacity:depends("transport", "mkcp")
-
-mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
-mkcp_downlinkCapacity.default = "20"
-mkcp_downlinkCapacity:depends("transport", "mkcp")
-
-mkcp_congestion = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
-mkcp_congestion:depends("transport", "mkcp")
-
-mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
-mkcp_readBufferSize.default = "1"
-mkcp_readBufferSize:depends("transport", "mkcp")
-
-mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
-mkcp_writeBufferSize.default = "1"
-mkcp_writeBufferSize:depends("transport", "mkcp")
-
-mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed"))
-mkcp_seed:depends("transport", "mkcp")
-
--- [[ WebSocket部分 ]]--
-ws_host = s:option(Value, "ws_host", translate("WebSocket Host"))
-ws_host:depends("transport", "ws")
-ws_host:depends("ss_transport", "ws")
-ws_host:depends("trojan_transport", "ws")
-
-ws_path = s:option(Value, "ws_path", translate("WebSocket Path"))
-ws_path.placeholder = "/"
-ws_path:depends("transport", "ws")
-ws_path:depends("ss_transport", "ws")
-ws_path:depends("trojan_transport", "ws")
-ws_path:depends({ type = "Brook", brook_protocol = "wsclient" })
-
-ws_enableEarlyData = s:option(Flag, "ws_enableEarlyData", translate("Enable early data"))
-ws_enableEarlyData:depends({ type = "V2ray", transport = "ws" })
-
-ws_maxEarlyData = s:option(Value, "ws_maxEarlyData", translate("Early data length"))
-ws_maxEarlyData.default = "1024"
-ws_maxEarlyData:depends("ws_enableEarlyData", true)
-
-ws_earlyDataHeaderName = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
-ws_earlyDataHeaderName:depends("ws_enableEarlyData", true)
-
--- [[ HTTP/2部分 ]]--
-h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host"))
-h2_host:depends("transport", "h2")
-h2_host:depends("ss_transport", "h2")
-
-h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path"))
-h2_path.placeholder = "/"
-h2_path:depends("transport", "h2")
-h2_path:depends("ss_transport", "h2")
-
-h2_health_check = s:option(Flag, "h2_health_check", translate("Health check"))
-h2_health_check:depends({ type = "Xray", transport = "h2" })
-
-h2_read_idle_timeout = s:option(Value, "h2_read_idle_timeout", translate("Idle timeout"))
-h2_read_idle_timeout.default = "10"
-h2_read_idle_timeout:depends("h2_health_check", true)
-
-h2_health_check_timeout = s:option(Value, "h2_health_check_timeout", translate("Health check timeout"))
-h2_health_check_timeout.default = "15"
-h2_health_check_timeout:depends("h2_health_check", true)
-
--- [[ DomainSocket部分 ]]--
-ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running."))
-ds_path:depends("transport", "ds")
-
--- [[ QUIC部分 ]]--
-quic_security = s:option(ListValue, "quic_security", translate("Encrypt Method"))
-quic_security:value("none")
-quic_security:value("aes-128-gcm")
-quic_security:value("chacha20-poly1305")
-quic_security:depends("transport", "quic")
-
-quic_key = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
-quic_key:depends("transport", "quic")
-
-quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
-for a, t in ipairs(header_type_list) do quic_guise:value(t) end
-quic_guise:depends("transport", "quic")
-
--- [[ gRPC部分 ]]--
-grpc_serviceName = s:option(Value, "grpc_serviceName", "ServiceName")
-grpc_serviceName:depends("transport", "grpc")
-
-grpc_mode = s:option(ListValue, "grpc_mode", "gRPC " .. translate("Transfer mode"))
-grpc_mode:value("gun")
-grpc_mode:value("multi")
-grpc_mode:depends({ type = "Xray", transport = "grpc" })
-
-grpc_health_check = s:option(Flag, "grpc_health_check", translate("Health check"))
-grpc_health_check:depends({ type = "Xray", transport = "grpc" })
-
-grpc_idle_timeout = s:option(Value, "grpc_idle_timeout", translate("Idle timeout"))
-grpc_idle_timeout.default = "10"
-grpc_idle_timeout:depends("grpc_health_check", true)
-
-grpc_health_check_timeout = s:option(Value, "grpc_health_check_timeout", translate("Health check timeout"))
-grpc_health_check_timeout.default = "20"
-grpc_health_check_timeout:depends("grpc_health_check", true)
-
-grpc_permit_without_stream = s:option(Flag, "grpc_permit_without_stream", translate("Permit without stream"))
-grpc_permit_without_stream.default = "0"
-grpc_permit_without_stream:depends("grpc_health_check", true)
-
-grpc_initial_windows_size = s:option(Value, "grpc_initial_windows_size", translate("Initial Windows Size"))
-grpc_initial_windows_size.default = "0"
-grpc_initial_windows_size:depends({ type = "Xray", transport = "grpc" })
-
--- [[ Trojan-Go Shadowsocks2 ]] --
-ss_aead = s:option(Flag, "ss_aead", translate("Shadowsocks secondary encryption"))
-ss_aead:depends("type", "Trojan-Go")
-ss_aead.default = "0"
-
-ss_aead_method = s:option(ListValue, "ss_aead_method", translate("Encrypt Method"))
-for _, v in ipairs(encrypt_methods_ss_aead) do ss_aead_method:value(v, v) end
-ss_aead_method.default = "aes-128-gcm"
-ss_aead_method:depends("ss_aead", "1")
-
-ss_aead_pwd = s:option(Value, "ss_aead_pwd", translate("Password"))
-ss_aead_pwd.password = true
-ss_aead_pwd:depends("ss_aead", "1")
-
--- [[ Trojan-Go Mux ]]--
-mux = s:option(Flag, "smux", translate("Smux"))
-mux:depends("type", "Trojan-Go")
-
--- [[ Mux ]]--
-mux = s:option(Flag, "mux", translate("Mux"))
-mux:depends({ type = "V2ray", protocol = "vmess" })
-mux:depends({ type = "V2ray", protocol = "vless" })
-mux:depends({ type = "V2ray", protocol = "http" })
-mux:depends({ type = "V2ray", protocol = "socks" })
-mux:depends({ type = "V2ray", protocol = "shadowsocks" })
-mux:depends({ type = "V2ray", protocol = "trojan" })
-mux:depends({ type = "Xray", protocol = "vmess" })
-mux:depends({ type = "Xray", protocol = "vless", tlsflow = "" })
-mux:depends({ type = "Xray", protocol = "http" })
-mux:depends({ type = "Xray", protocol = "socks" })
-mux:depends({ type = "Xray", protocol = "shadowsocks" })
-mux:depends({ type = "Xray", protocol = "trojan" })
-
--- [[ XUDP Mux ]]--
-xmux = s:option(Flag, "xmux", translate("Mux"))
-xmux.default = 1
-xmux:depends({ type = "Xray", protocol = "vless", tlsflow = "xtls-rprx-vision" })
-xmux:depends({ type = "Xray", protocol = "vless", tlsflow = "xtls-rprx-vision-udp443" })
-
-mux_concurrency = s:option(Value, "mux_concurrency", translate("Mux concurrency"))
-mux_concurrency.default = 8
-mux_concurrency:depends("mux", true)
-mux_concurrency:depends("smux", true)
-
-xudp_concurrency = s:option(Value, "xudp_concurrency", translate("XUDP Mux concurrency"))
-xudp_concurrency.default = 8
-xudp_concurrency:depends("xmux", true)
-
-smux_idle_timeout = s:option(Value, "smux_idle_timeout", translate("Mux idle timeout"))
-smux_idle_timeout.default = 60
-smux_idle_timeout:depends("smux", true)
-
-hysteria_up_mbps = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
-hysteria_up_mbps.default = "10"
-hysteria_up_mbps:depends("type", "Hysteria")
-
-hysteria_down_mbps = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
-hysteria_down_mbps.default = "50"
-hysteria_down_mbps:depends("type", "Hysteria")
-
-hysteria_recv_window_conn = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
-hysteria_recv_window_conn:depends("type", "Hysteria")
-
-hysteria_recv_window = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window"))
-hysteria_recv_window:depends("type", "Hysteria")
-
-hysteria_handshake_timeout = s:option(Value, "hysteria_handshake_timeout", translate("Handshake Timeout"))
-hysteria_handshake_timeout:depends("type", "Hysteria")
-
-hysteria_idle_timeout = s:option(Value, "hysteria_idle_timeout", translate("Idle Timeout"))
-hysteria_idle_timeout:depends("type", "Hysteria")
-
-hysteria_hop_interval = s:option(Value, "hysteria_hop_interval", translate("Hop Interval"))
-hysteria_hop_interval:depends("type", "Hysteria")
-
-hysteria_disable_mtu_discovery = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
-hysteria_disable_mtu_discovery:depends("type", "Hysteria")
-
-hysteria_lazy_start = s:option(Flag, "hysteria_lazy_start", translate("Lazy Start"))
-hysteria_lazy_start:depends("type", "Hysteria")
-
--- [[ TUIC ]]
--- Tuic Password for remote server connect
-tuic_passwd = s:option(Value, "tuic_password", translate("TUIC User Password For Connect Remote Server"))
-tuic_passwd.password = true
-tuic_passwd.rmempty = true
-tuic_passwd.default = ""
-tuic_passwd:depends("type", "TUIC")
-
---[[
--- Tuic username for local socks connect
-tuic_passwd = s:option(Value, "tuic_socks_username", translate("TUIC UserName For Local Socks"))
-tuic_passwd.rmempty = true
-tuic_passwd.default = ""
-tuic_passwd:depends("type", "TUIC")
-
--- Tuic Password for local socks connect
-tuic_passwd = s:option(Value, "tuic_socks_password", translate("TUIC Password For Local Socks"))
-tuic_passwd.password = true
-tuic_passwd.rmempty = true
-tuic_passwd.default = ""
-tuic_passwd:depends("type", "TUIC")
---]]
-
-tuic_ip = s:option(Value, "tuic_ip", translate("Set the TUIC proxy server ip address"))
-tuic_ip:depends("type", "TUIC")
-tuic_ip.datatype = "ipaddr"
-tuic_ip.rmempty = true
-
-tuic_udp_relay_mode = s:option(ListValue, "tuic_udp_relay_mode", translate("UDP relay mode"))
-tuic_udp_relay_mode:depends("type", "TUIC")
-tuic_udp_relay_mode:value("native", translate("native"))
-tuic_udp_relay_mode:value("quic", translate("QUIC"))
-tuic_udp_relay_mode.default = "native"
-tuic_udp_relay_mode.rmempty = true
-
-tuic_congestion_control = s:option(ListValue, "tuic_congestion_control", translate("Congestion control algorithm"))
-tuic_congestion_control:depends("type", "TUIC")
-tuic_congestion_control:value("bbr", translate("BBR"))
-tuic_congestion_control:value("cubic", translate("CUBIC"))
-tuic_congestion_control:value("new_reno", translate("New Reno"))
-tuic_congestion_control.default = "cubic"
-tuic_congestion_control.rmempty = true
-
-tuic_heartbeat = s:option(Value, "tuic_heartbeat", translate("Heartbeat interval(second)"))
-tuic_heartbeat:depends("type", "TUIC")
-tuic_heartbeat.datatype = "uinteger"
-tuic_heartbeat.default = "3"
-tuic_heartbeat.rmempty = true
-
-tuic_timeout = s:option(Value, "tuic_timeout", translate("Timeout for establishing a connection to server(second)"))
-tuic_timeout:depends("type", "TUIC")
-tuic_timeout.datatype = "uinteger"
-tuic_timeout.default = "8"
-tuic_timeout.rmempty = true
-
-tuic_gc_interval = s:option(Value, "tuic_gc_interval", translate("Garbage collection interval(second)"))
-tuic_gc_interval:depends("type", "TUIC")
-tuic_gc_interval.datatype = "uinteger"
-tuic_gc_interval.default = "3"
-tuic_gc_interval.rmempty = true
-
-tuic_gc_lifetime = s:option(Value, "tuic_gc_lifetime", translate("Garbage collection lifetime(second)"))
-tuic_gc_lifetime:depends("type", "TUIC")
-tuic_gc_lifetime.datatype = "uinteger"
-tuic_gc_lifetime.default = "15"
-tuic_gc_lifetime.rmempty = true
-
-tuic_send_window = s:option(Value, "tuic_send_window", translate("TUIC send window"))
-tuic_send_window.datatype = "uinteger"
-tuic_send_window:depends("type", "TUIC")
-tuic_send_window.default = 20971520
-tuic_send_window.rmempty = true
-
-tuic_receive_window = s:option(Value, "tuic_receive_window", translate("TUIC receive window"))
-tuic_receive_window.datatype = "uinteger"
-tuic_receive_window:depends("type", "TUIC")
-tuic_receive_window.default = 10485760
-tuic_receive_window.rmempty = true
-
-tuic_max_package_size = s:option(Value, "tuic_max_package_size", translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes"))
-tuic_max_package_size.datatype = "uinteger"
-tuic_max_package_size:depends("type", "TUIC")
-tuic_max_package_size.default = 1500
-tuic_max_package_size.rmempty = true
-
---Tuic settings for the local inbound socks5 server
-tuic_dual_stack = s:option(Flag, "tuic_dual_stack", translate("Set if the listening socket should be dual-stack"))
-tuic_dual_stack:depends("type", "TUIC")
-tuic_dual_stack.default = 0
-tuic_dual_stack.rmempty = true
-
-tuic_disable_sni = s:option(Flag, "tuic_disable_sni", translate("Disable SNI"))
-tuic_disable_sni:depends("type", "TUIC")
-tuic_disable_sni.default = 0
-tuic_disable_sni.rmempty = true
-
-tuic_zero_rtt_handshake = s:option(Flag, "tuic_zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake"))
-tuic_zero_rtt_handshake:depends("type", "TUIC")
-tuic_zero_rtt_handshake.default = 0
-tuic_zero_rtt_handshake.rmempty = true
-
-tuic_tls_alpn = s:option(DynamicList, "tuic_tls_alpn", translate("TLS ALPN"))
-tuic_tls_alpn:depends({ type = "TUIC"})
-tuic_tls_alpn.rmempty = true
-
-protocol.validate = function(self, value)
- if value == "_shunt" or value == "_balancing" then
- address.rmempty = true
- port.rmempty = true
- end
- return value
+for index, value in ipairs(type_table) do
+ local p_func = loadfile(types_dir .. value)
+ setfenv(p_func, getfenv(1))(m, s)
end
return m
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/brook.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/brook.lua
new file mode 100644
index 000000000..f6ece22f1
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/brook.lua
@@ -0,0 +1,65 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("brook") then
+ return
+end
+
+local option_prefix = "brook_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ Brook ]]
+
+s.fields["type"]:value("Brook", translate("Brook"))
+
+o = s:option(ListValue, "brook_protocol", translate("Protocol"))
+o:value("client", translate("Brook"))
+o:value("wsclient", translate("WebSocket"))
+
+o = s:option(Value, "brook_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "brook_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "brook_ws_path", translate("WebSocket Path"))
+o.placeholder = "/"
+o:depends({ brook_protocol = "wsclient" })
+
+o = s:option(Flag, "brook_tls", translate("Use TLS"))
+o:depends({ brook_protocol = "wsclient" })
+
+o = s:option(Value, "brook_password", translate("Password"))
+o.password = true
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Brook"
+ end
+ else
+ s.fields[key]:depends({ type = "Brook" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria.lua
new file mode 100644
index 000000000..3039accda
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria.lua
@@ -0,0 +1,115 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("hysteria") then
+ return
+end
+
+local option_prefix = "hysteria_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ Hysteria ]]
+
+s.fields["type"]:value("Hysteria", translate("Hysteria"))
+
+o = s:option(ListValue, "hysteria_protocol", translate("Protocol"))
+o:value("udp", "UDP")
+o:value("faketcp", "faketcp")
+o:value("wechat-video", "wechat-video")
+
+o = s:option(Value, "hysteria_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "hysteria_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "hysteria_hop", translate("Additional ports for hysteria hop"))
+o:depends("type", "Hysteria")
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
+o.not_rewrite = true
+
+o = s:option(ListValue, "hysteria_auth_type", translate("Auth Type"))
+o:value("disable", translate("Disable"))
+o:value("string", translate("STRING"))
+o:value("base64", translate("BASE64"))
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_auth_password", translate("Auth Password"))
+o.password = true
+o:depends({ hysteria_auth_type = "string"})
+o:depends({ hysteria_auth_type = "base64"})
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN"))
+o.not_rewrite = true
+
+o = s:option(Flag, "hysteria_fast_open", translate("Fast Open"))
+o.default = "0"
+
+o = s:option(Value, "hysteria_tls_serverName", translate("Domain"))
+
+o = s:option(Flag, "hysteria_tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
+o.default = "0"
+
+o = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
+o.default = "10"
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
+o.default = "50"
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window"))
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_handshake_timeout", translate("Handshake Timeout"))
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_idle_timeout", translate("Idle Timeout"))
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_hop_interval", translate("Hop Interval"))
+o.not_rewrite = true
+
+o = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
+o.not_rewrite = true
+
+o = s:option(Flag, "hysteria_lazy_start", translate("Lazy Start"))
+o.not_rewrite = true
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Hysteria"
+ end
+ else
+ s.fields[key]:depends({ type = "Hysteria" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/naive.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/naive.lua
new file mode 100644
index 000000000..6bc5a4a4a
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/naive.lua
@@ -0,0 +1,60 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("naive") then
+ return
+end
+
+local option_prefix = "naive_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ Naive ]]
+
+s.fields["type"]:value("Naiveproxy", translate("NaiveProxy"))
+
+o = s:option(ListValue, "naive_protocol", translate("Protocol"))
+o:value("https", translate("HTTPS"))
+o:value("quic", translate("QUIC"))
+
+o = s:option(Value, "naive_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "naive_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "naive_username", translate("Username"))
+
+o = s:option(Value, "naive_password", translate("Password"))
+o.password = true
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Naiveproxy"
+ end
+ else
+ s.fields[key]:depends({ type = "Naiveproxy" })
+ end
+ end
+end
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
new file mode 100644
index 000000000..105caabdc
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua
@@ -0,0 +1,735 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("xray") and not api.is_finded("v2ray") then
+ return
+end
+
+local appname = api.appname
+local uci = api.uci
+
+local option_prefix = "xray_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+local function rm_prefix_remove(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:del(section, self.option:sub(1 + #option_prefix))
+ end
+end
+
+local function add_xray_depends(o, field, value)
+ local deps = { type = "Xray" }
+ if field then
+ if type(field) == "string" then
+ deps[field] = value
+ else
+ for key, value in pairs(field) do
+ deps[key] = value
+ end
+ end
+ end
+ o:depends(deps)
+end
+
+local function add_v2ray_depends(o, field, value)
+ local deps = { type = "V2ray" }
+ if field then
+ if type(field) == "string" then
+ deps[field] = value
+ else
+ for key, value in pairs(field) do
+ deps[key] = value
+ end
+ end
+ end
+ o:depends(deps)
+end
+
+local v_ss_encrypt_method_list = {
+ "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305"
+}
+
+local x_ss_encrypt_method_list = {
+ "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
+}
+
+local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero" }
+
+local header_type_list = {
+ "none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
+}
+
+-- [[ Xray ]]
+
+if api.is_finded("xray") then
+ s.fields["type"]:value("Xray", translate("Xray"))
+end
+if api.is_finded("v2ray") then
+ s.fields["type"]:value("V2ray", translate("V2ray"))
+end
+
+o = s:option(ListValue, "xray_protocol", translate("Protocol"))
+o:value("vmess", translate("Vmess"))
+o:value("vless", translate("VLESS"))
+o:value("http", translate("HTTP"))
+o:value("socks", translate("Socks"))
+o:value("shadowsocks", translate("Shadowsocks"))
+o:value("trojan", translate("Trojan"))
+o:value("wireguard", translate("WireGuard"))
+o:value("_balancing", translate("Balancing"))
+o:value("_shunt", translate("Shunt"))
+o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
+add_xray_depends(o)
+add_v2ray_depends(o)
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_iface", translate("Interface"))
+o.default = "eth1"
+add_xray_depends(o, { xray_protocol = "_iface" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+local nodes_table = {}
+local balancers_table = {}
+local iface_table = {}
+for k, e in ipairs(api.get_valid_nodes()) do
+ if e.node_type == "normal" then
+ nodes_table[#nodes_table + 1] = {
+ id = e[".name"],
+ remarks = e["remark"]
+ }
+ end
+ if e.protocol == "_balancing" then
+ balancers_table[#balancers_table + 1] = {
+ id = e[".name"],
+ remarks = e["remark"]
+ }
+ end
+ if e.protocol == "_iface" then
+ iface_table[#iface_table + 1] = {
+ id = e[".name"],
+ remarks = e["remark"]
+ }
+ end
+end
+
+-- 负载均衡列表
+local o = s:option(DynamicList, "xray_balancing_node", translate("Load balancing node list"), translate("Load balancing node list, document"))
+add_xray_depends(o, { xray_protocol = "_balancing" })
+add_v2ray_depends(o, { xray_protocol = "_balancing" })
+for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+local o = s:option(ListValue, "xray_balancingStrategy", translate("Balancing Strategy"))
+add_xray_depends(o, { xray_protocol = "_balancing" })
+add_v2ray_depends(o, { xray_protocol = "_balancing" })
+o:value("random")
+o:value("leastPing")
+o.default = "random"
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+-- 探测地址
+local o = s:option(Flag, "xray_useCustomProbeUrl", translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
+add_xray_depends(o, { xray_balancingStrategy = "leastPing" })
+add_v2ray_depends(o, { xray_balancingStrategy = "leastPing" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+local o = s:option(Value, "xray_probeUrl", translate("Probe URL"))
+add_xray_depends(o, { xray_useCustomProbeUrl = true })
+add_v2ray_depends(o, { xray_useCustomProbeUrl = true })
+o.default = "https://www.google.com/generate_204"
+o.description = translate("The URL used to detect the connection status.")
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+-- 探测间隔
+local o = s:option(Value, "xray_probeInterval", translate("Probe Interval"))
+add_xray_depends(o, { xray_balancingStrategy = "leastPing" })
+add_v2ray_depends(o, { xray_balancingStrategy = "leastPing" })
+o.default = "1m"
+o.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are ns
, us
, ms
, s
, m
, h
, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+-- [[ 分流模块 ]]
+if #nodes_table > 0 then
+ o = s:option(Flag, "preproxy_enabled", translate("Preproxy"))
+ add_xray_depends(o, { xray_protocol = "_shunt" })
+ add_v2ray_depends(o, { xray_protocol = "_shunt" })
+ o = s:option(Value, "main_node", string.format('%s', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including Default
) has a separate switch that controls whether this rule uses the pre-proxy or not."))
+ add_xray_depends(o, { xray_protocol = "_shunt", preproxy_enabled = true })
+ add_v2ray_depends(o, { xray_protocol = "_shunt", preproxy_enabled = true })
+ for k, v in pairs(balancers_table) do
+ o:value(v.id, v.remarks)
+ end
+ for k, v in pairs(iface_table) do
+ o:value(v.id, v.remarks)
+ end
+ for k, v in pairs(nodes_table) do
+ o:value(v.id, v.remarks)
+ end
+ o.default = "nil"
+end
+uci:foreach(appname, "shunt_rules", function(e)
+ if e[".name"] and e.remarks then
+ o = s:option(Value, e[".name"], string.format('* %s', api.url("shunt_rules", e[".name"]), e.remarks))
+ o:value("nil", translate("Close"))
+ o:value("_default", translate("Default"))
+ o:value("_direct", translate("Direct Connection"))
+ o:value("_blackhole", translate("Blackhole"))
+ add_xray_depends(o, { xray_protocol = "_shunt" })
+ add_v2ray_depends(o, { xray_protocol = "_shunt" })
+
+ if #nodes_table > 0 then
+ for k, v in pairs(balancers_table) do
+ o:value(v.id, v.remarks)
+ end
+ for k, v in pairs(iface_table) do
+ o:value(v.id, v.remarks)
+ end
+ local pt = s:option(ListValue, e[".name"] .. "_proxy_tag", string.format('* %s', e.remarks .. " " .. translate("Preproxy")))
+ pt:value("nil", translate("Close"))
+ pt:value("main", translate("Preproxy Node"))
+ pt.default = "nil"
+ for k, v in pairs(nodes_table) do
+ o:value(v.id, v.remarks)
+ add_xray_depends(pt, { xray_protocol = "_shunt", preproxy_enabled = "1", [e[".name"]] = v.id })
+ add_v2ray_depends(pt, { xray_protocol = "_shunt", preproxy_enabled = "1", [e[".name"]] = v.id })
+ end
+ end
+ end
+end)
+
+o = s:option(DummyValue, "shunt_tips", " ")
+o.rawhtml = true
+o.cfgvalue = function(t, n)
+ return string.format('%s', translate("No shunt rules? Click me to go to add."))
+end
+add_xray_depends(o, { xray_protocol = "_shunt" })
+
+local o = s:option(Value, "default_node", string.format('* %s', translate("Default")))
+add_xray_depends(o, { xray_protocol = "_shunt" })
+o:value("_direct", translate("Direct Connection"))
+o:value("_blackhole", translate("Blackhole"))
+
+if #nodes_table > 0 then
+ for k, v in pairs(balancers_table) do
+ o:value(v.id, v.remarks)
+ end
+ for k, v in pairs(iface_table) do
+ o:value(v.id, v.remarks)
+ end
+ local dpt = s:option(ListValue, "default_proxy_tag", string.format('* %s', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node."))
+ dpt:value("nil", translate("Close"))
+ dpt:value("main", translate("Preproxy Node"))
+ dpt.default = "nil"
+ for k, v in pairs(nodes_table) do
+ o:value(v.id, v.remarks)
+ add_xray_depends(dpt, { xray_protocol = "_shunt", preproxy_enabled = "1", default_node = v.id })
+ end
+end
+
+o = s:option(ListValue, "domainStrategy", translate("Domain Strategy"))
+o:value("AsIs")
+o:value("IPIfNonMatch")
+o:value("IPOnDemand")
+o.default = "IPOnDemand"
+o.description = "
- " .. translate("'AsIs': Only use domain for routing. Default value.")
+ .. "
- " .. translate("'IPIfNonMatch': When no rule matches current domain, resolves it into IP addresses (A or AAAA records) and try all rules again.")
+ .. "
- " .. translate("'IPOnDemand': As long as there is a IP-based rule, resolves the domain into IP immediately.")
+ .. "
"
+add_xray_depends(o, { xray_protocol = "_shunt" })
+
+o = s:option(ListValue, "domainMatcher", translate("Domain matcher"))
+o:value("hybrid")
+o:value("linear")
+add_xray_depends(o, { xray_protocol = "_shunt" })
+
+-- [[ 分流模块 End ]]
+
+o = s:option(Value, "xray_address", translate("Address (Support Domain Name)"))
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "http" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+add_xray_depends(o, { xray_protocol = "wireguard" })
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "http" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_port", translate("Port"))
+o.datatype = "port"
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "http" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+add_xray_depends(o, { xray_protocol = "wireguard" })
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "http" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_username", translate("Username"))
+add_xray_depends(o, { xray_protocol = "http" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "http" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_password", translate("Password"))
+o.password = true
+add_xray_depends(o, { xray_protocol = "http" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+add_v2ray_depends(o, { xray_protocol = "http" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(ListValue, "security", translate("Encrypt Method"))
+for a, t in ipairs(security_list) do o:value(t) end
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+
+o = s:option(Value, "encryption", translate("Encrypt Method"))
+o.default = "none"
+o:value("none")
+add_xray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+
+o = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
+for a, t in ipairs(v_ss_encrypt_method_list) do o:value(t) end
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+function o.cfgvalue(self, section)
+ return m:get(section, "method")
+end
+function o.write(self, section, value)
+ m:set(section, "method", value)
+end
+
+o = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method"))
+for a, t in ipairs(x_ss_encrypt_method_list) do o:value(t) end
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+function o.cfgvalue(self, section)
+ return m:get(section, "method")
+end
+function o.write(self, section, value)
+ m:set(section, "method", value)
+end
+
+o = s:option(Flag, "iv_check", translate("IV Check"))
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "aes-128-gcm" })
+add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "aes-256-gcm" })
+add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "chacha20-poly1305" })
+add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "xchacha20-poly1305" })
+
+o = s:option(Flag, "uot", translate("UDP over TCP"), translate("Need Xray-core or sing-box as server side."))
+add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-128-gcm" })
+add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-256-gcm" })
+add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-chacha20-poly1305" })
+
+o = s:option(Value, "xray_uuid", translate("ID"))
+o.password = true
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Flag, "xray_tls", translate("TLS"))
+o.default = 0
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_tlsflow", translate("flow"))
+o.default = ""
+o:value("", translate("Disable"))
+o:value("xtls-rprx-vision")
+o:value("xtls-rprx-vision-udp443")
+add_xray_depends(o, { xray_protocol = "vless", xray_tls = true, transport = "tcp" })
+
+o = s:option(Flag, "reality", translate("REALITY"), translate("Only recommend to use with VLESS-TCP-XTLS-Vision."))
+o.default = 0
+add_xray_depends(o, { xray_tls = true, transport = "tcp" })
+add_xray_depends(o, { xray_tls = true, transport = "h2" })
+add_xray_depends(o, { xray_tls = true, transport = "grpc" })
+
+o = s:option(ListValue, "alpn", translate("alpn"))
+o.default = "default"
+o:value("default", translate("Default"))
+o:value("h2,http/1.1")
+o:value("h2")
+o:value("http/1.1")
+add_xray_depends(o, { xray_tls = true, reality = false })
+add_v2ray_depends(o, { xray_tls = true })
+
+-- o = s:option(Value, "minversion", translate("minversion"))
+-- o.default = "1.3"
+-- o:value("1.3")
+-- add_xray_depends(o, { xray_tls = true })
+-- add_v2ray_depends(o, { xray_tls = true })
+
+o = s:option(Value, "tls_serverName", translate("Domain"))
+add_xray_depends(o, { xray_tls = true })
+add_v2ray_depends(o, { xray_tls = true })
+
+o = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
+o.default = "0"
+add_xray_depends(o, { xray_tls = true, reality = false })
+add_v2ray_depends(o, { xray_tls = true })
+
+o = s:option(Value, "xray_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to."))
+o:value("", translate("Disable"))
+o:value("chrome")
+o:value("firefox")
+o:value("safari")
+o:value("ios")
+-- o:value("android")
+o:value("edge")
+-- o:value("360")
+o:value("qq")
+o:value("random")
+o:value("randomized")
+o.default = ""
+add_xray_depends(o, { xray_tls = true, reality = false })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+o.remove = rm_prefix_remove
+
+-- [[ REALITY部分 ]] --
+o = s:option(Value, "reality_publicKey", translate("Public Key"))
+add_xray_depends(o, { xray_tls = true, reality = true })
+
+o = s:option(Value, "reality_shortId", translate("Short Id"))
+add_xray_depends(o, { xray_tls = true, reality = true })
+
+o = s:option(Value, "reality_spiderX", translate("Spider X"))
+o.placeholder = "/"
+add_xray_depends(o, { xray_tls = true, reality = true })
+
+o = s:option(Value, "reality_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to."))
+o:value("chrome")
+o:value("firefox")
+o:value("safari")
+o:value("ios")
+-- o:value("android")
+o:value("edge")
+-- o:value("360")
+o:value("qq")
+o:value("random")
+o:value("randomized")
+o.default = "chrome"
+add_xray_depends(o, { xray_tls = true, reality = true })
+function o.cfgvalue(self, section)
+ return m:get(section, "fingerprint")
+end
+function o.write(self, section, value)
+ m:set(section, "fingerprint", value)
+end
+
+o = s:option(ListValue, "transport", translate("Transport"))
+o:value("tcp", "TCP")
+o:value("mkcp", "mKCP")
+o:value("ws", "WebSocket")
+o:value("h2", "HTTP/2")
+o:value("ds", "DomainSocket")
+o:value("quic", "QUIC")
+o:value("grpc", "gRPC")
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+
+--[[
+o = s:option(ListValue, "ss_transport", translate("Transport"))
+o:value("ws", "WebSocket")
+o:value("h2", "HTTP/2")
+o:value("h2+ws", "HTTP/2 & WebSocket")
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+]]--
+
+o = s:option(Value, "wireguard_public_key", translate("Public Key"))
+add_xray_depends(o, { xray_protocol = "wireguard" })
+
+o = s:option(Value, "wireguard_secret_key", translate("Private Key"))
+add_xray_depends(o, { xray_protocol = "wireguard" })
+
+o = s:option(Value, "wireguard_preSharedKey", translate("Pre shared key"))
+add_xray_depends(o, { xray_protocol = "wireguard" })
+
+o = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
+add_xray_depends(o, { xray_protocol = "wireguard" })
+
+o = s:option(Value, "wireguard_mtu", translate("MTU"))
+o.default = "1420"
+add_xray_depends(o, { xray_protocol = "wireguard" })
+
+if api.compare_versions(api.get_app_version("xray"), ">=", "1.8.0") then
+ o = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
+ add_xray_depends(o, { xray_protocol = "wireguard" })
+end
+
+o = s:option(Value, "wireguard_keepAlive", translate("Keep Alive"))
+o.default = "0"
+add_xray_depends(o, { xray_protocol = "wireguard" })
+
+-- [[ TCP部分 ]]--
+
+-- TCP伪装
+o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
+o:value("none", "none")
+o:value("http", "http")
+add_xray_depends(o, { transport = "tcp" })
+add_v2ray_depends(o, { transport = "tcp" })
+
+-- HTTP域名
+o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
+add_xray_depends(o, { tcp_guise = "http" })
+add_v2ray_depends(o, { tcp_guise = "http" })
+
+-- HTTP路径
+o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
+o.placeholder = "/"
+add_xray_depends(o, { tcp_guise = "http" })
+add_v2ray_depends(o, { tcp_guise = "http" })
+
+-- [[ mKCP部分 ]]--
+
+o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
+for a, t in ipairs(header_type_list) do o:value(t) end
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
+o.default = "1350"
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_tti", translate("KCP TTI"))
+o.default = "20"
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
+o.default = "5"
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
+o.default = "20"
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
+o.default = "1"
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
+o.default = "1"
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_seed", translate("KCP Seed"))
+add_xray_depends(o, { transport = "mkcp" })
+add_v2ray_depends(o, { transport = "mkcp" })
+
+-- [[ WebSocket部分 ]]--
+o = s:option(Value, "xray_ws_host", translate("WebSocket Host"))
+add_xray_depends(o, { transport = "ws" })
+add_xray_depends(o, { ss_transport = "ws" })
+add_v2ray_depends(o, { transport = "ws" })
+add_v2ray_depends(o, { ss_transport = "ws" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_ws_path", translate("WebSocket Path"))
+o.placeholder = "/"
+add_xray_depends(o, { transport = "ws" })
+add_xray_depends(o, { ss_transport = "ws" })
+add_v2ray_depends(o, { transport = "ws" })
+add_v2ray_depends(o, { ss_transport = "ws" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Flag, "ws_enableEarlyData", translate("Enable early data"))
+add_v2ray_depends(o, { transport = "ws" })
+
+o = s:option(Value, "ws_maxEarlyData", translate("Early data length"))
+o.default = "1024"
+add_v2ray_depends(o, { ws_enableEarlyData = true })
+
+o = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
+add_v2ray_depends(o, { ws_enableEarlyData = true })
+
+-- [[ HTTP/2部分 ]]--
+o = s:option(Value, "xray_h2_host", translate("HTTP/2 Host"))
+add_xray_depends(o, { transport = "h2" })
+add_xray_depends(o, { ss_transport = "h2" })
+add_v2ray_depends(o, { transport = "h2" })
+add_v2ray_depends(o, { ss_transport = "h2" })
+
+o = s:option(Value, "xray_h2_path", translate("HTTP/2 Path"))
+o.placeholder = "/"
+add_xray_depends(o, { transport = "h2" })
+add_xray_depends(o, { ss_transport = "h2" })
+add_v2ray_depends(o, { transport = "h2" })
+add_v2ray_depends(o, { ss_transport = "h2" })
+
+o = s:option(Flag, "h2_health_check", translate("Health check"))
+add_xray_depends(o, { transport = "h2" })
+
+o = s:option(Value, "h2_read_idle_timeout", translate("Idle timeout"))
+o.default = "10"
+add_xray_depends(o, { h2_health_check = true })
+
+o = s:option(Value, "h2_health_check_timeout", translate("Health check timeout"))
+o.default = "15"
+add_xray_depends(o, { h2_health_check = true })
+
+-- [[ DomainSocket部分 ]]--
+o = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running."))
+add_xray_depends(o, { transport = "ds" })
+add_v2ray_depends(o, { transport = "ds" })
+
+-- [[ QUIC部分 ]]--
+o = s:option(ListValue, "quic_security", translate("Encrypt Method"))
+o:value("none")
+o:value("aes-128-gcm")
+o:value("chacha20-poly1305")
+add_xray_depends(o, { transport = "quic" })
+add_v2ray_depends(o, { transport = "quic" })
+
+o = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
+add_xray_depends(o, { transport = "quic" })
+add_v2ray_depends(o, { transport = "quic" })
+
+o = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
+for a, t in ipairs(header_type_list) do o:value(t) end
+add_xray_depends(o, { transport = "quic" })
+add_v2ray_depends(o, { transport = "quic" })
+
+-- [[ gRPC部分 ]]--
+o = s:option(Value, "grpc_serviceName", "ServiceName")
+add_xray_depends(o, { transport = "grpc" })
+add_v2ray_depends(o, { transport = "grpc" })
+
+o = s:option(ListValue, "grpc_mode", "gRPC " .. translate("Transfer mode"))
+o:value("gun")
+o:value("multi")
+add_xray_depends(o, { transport = "grpc" })
+
+o = s:option(Flag, "grpc_health_check", translate("Health check"))
+add_xray_depends(o, { transport = "grpc" })
+
+o = s:option(Value, "grpc_idle_timeout", translate("Idle timeout"))
+o.default = "10"
+add_xray_depends(o, { grpc_health_check = true })
+
+o = s:option(Value, "grpc_health_check_timeout", translate("Health check timeout"))
+o.default = "20"
+add_xray_depends(o, { grpc_health_check = true })
+
+o = s:option(Flag, "grpc_permit_without_stream", translate("Permit without stream"))
+o.default = "0"
+add_xray_depends(o, { grpc_health_check = true })
+
+o = s:option(Value, "grpc_initial_windows_size", translate("Initial Windows Size"))
+o.default = "0"
+add_xray_depends(o, { transport = "grpc" })
+
+-- [[ Mux ]]--
+o = s:option(Flag, "mux", translate("Mux"))
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "http" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless", xray_tlsflow = "" })
+add_xray_depends(o, { xray_protocol = "http" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+
+o = s:option(Value, "mux_concurrency", translate("Mux concurrency"))
+o.default = 8
+add_xray_depends(o, { mux = true })
+add_v2ray_depends(o, { mux = true })
+
+-- [[ XUDP Mux ]]--
+o = s:option(Flag, "xmux", translate("xMux"))
+o.default = 1
+add_xray_depends(o, { xray_protocol = "vless", xray_tlsflow = "xtls-rprx-vision" })
+add_xray_depends(o, { xray_protocol = "vless", xray_tlsflow = "xtls-rprx-vision-udp443" })
+
+o = s:option(Value, "xudp_concurrency", translate("XUDP Mux concurrency"))
+o.default = 8
+add_xray_depends(o, { xmux = true })
+
+s.fields["xray_protocol"].validate = function(self, value)
+ if value == "_shunt" or value == "_balancing" then
+ s.fields["xray_address"].rmempty = true
+ s.fields["xray_port"].rmempty = true
+ end
+ return value
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss-rust.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss-rust.lua
new file mode 100644
index 000000000..57b6c3053
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss-rust.lua
@@ -0,0 +1,82 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("sslocal") then
+ return
+end
+
+local option_prefix = "ssrust_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local ssrust_encrypt_method_list = {
+ "plain", "none",
+ "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
+ "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"
+}
+
+-- [[ Shadowsocks Rust ]]
+
+s.fields["type"]:value("SS-Rust", translate("Shadowsocks Rust"))
+
+o = s:option(Value, "ssrust_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "ssrust_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "ssrust_password", translate("Password"))
+o.password = true
+
+o = s:option(Value, "ssrust_method", translate("Encrypt Method"))
+for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
+
+o = s:option(Value, "ssrust_timeout", translate("Connection Timeout"))
+o.datatype = "uinteger"
+o.default = 300
+
+o = s:option(ListValue, "ssrust_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
+o:value("false")
+o:value("true")
+
+o = s:option(ListValue, "ssrust_plugin", translate("plugin"))
+o:value("none", translate("none"))
+if api.is_finded("xray-plugin") then o:value("xray-plugin") end
+if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
+if api.is_finded("obfs-local") then o:value("obfs-local") end
+
+o = s:option(Value, "ssrust_plugin_opts", translate("opts"))
+o:depends({ ssrust_plugin = "xray-plugin"})
+o:depends({ ssrust_plugin = "v2ray-plugin"})
+o:depends({ ssrust_plugin = "obfs-local"})
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "SS-Rust"
+ end
+ else
+ s.fields[key]:depends({ type = "SS-Rust" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss.lua
new file mode 100644
index 000000000..3fff67294
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ss.lua
@@ -0,0 +1,83 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("ss-redir") then
+ return
+end
+
+local option_prefix = "ss_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local ss_encrypt_method_list = {
+ "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
+ "aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf",
+ "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
+ "xchacha20-ietf-poly1305"
+}
+
+-- [[ Shadowsocks Libev ]]
+
+s.fields["type"]:value("SS", translate("Shadowsocks Libev"))
+
+o = s:option(Value, "ss_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "ss_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "ss_password", translate("Password"))
+o.password = true
+
+o = s:option(Value, "ss_method", translate("Encrypt Method"))
+for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
+
+o = s:option(Value, "ss_timeout", translate("Connection Timeout"))
+o.datatype = "uinteger"
+o.default = 300
+
+o = s:option(ListValue, "ss_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
+o:value("false")
+o:value("true")
+
+o = s:option(ListValue, "ss_plugin", translate("plugin"))
+o:value("none", translate("none"))
+if api.is_finded("xray-plugin") then o:value("xray-plugin") end
+if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
+if api.is_finded("obfs-local") then o:value("obfs-local") end
+
+o = s:option(Value, "ss_plugin_opts", translate("opts"))
+o:depends({ ss_plugin = "xray-plugin"})
+o:depends({ ss_plugin = "v2ray-plugin"})
+o:depends({ ss_plugin = "obfs-local"})
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "SS"
+ end
+ else
+ s.fields[key]:depends({ type = "SS" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ssr.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ssr.lua
new file mode 100644
index 000000000..7df040330
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ssr.lua
@@ -0,0 +1,94 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("ssr-redir") then
+ return
+end
+
+local option_prefix = "ssr_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local ssr_encrypt_method_list = {
+ "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
+ "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
+ "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb",
+ "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20",
+ "chacha20-ietf"
+}
+
+local ssr_protocol_list = {
+ "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple",
+ "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5",
+ "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c",
+ "auth_chain_d", "auth_chain_e", "auth_chain_f"
+}
+local ssr_obfs_list = {
+ "plain", "http_simple", "http_post", "random_head", "tls_simple",
+ "tls1.0_session_auth", "tls1.2_ticket_auth"
+}
+
+-- [[ ShadowsocksR Libev ]]
+
+s.fields["type"]:value("SSR", translate("ShadowsocksR Libev"))
+
+o = s:option(Value, "ssr_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "ssr_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "ssr_password", translate("Password"))
+o.password = true
+
+o = s:option(ListValue, "ssr_method", translate("Encrypt Method"))
+for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
+
+o = s:option(ListValue, "ssr_protocol", translate("Protocol"))
+for a, t in ipairs(ssr_protocol_list) do o:value(t) end
+
+o = s:option(Value, "ssr_protocol_param", translate("Protocol_param"))
+
+o = s:option(ListValue, "ssr_obfs", translate("Obfs"))
+for a, t in ipairs(ssr_obfs_list) do o:value(t) end
+
+o = s:option(Value, "ssr_obfs_param", translate("Obfs_param"))
+
+o = s:option(Value, "ssr_timeout", translate("Connection Timeout"))
+o.datatype = "uinteger"
+o.default = 300
+
+o = s:option(ListValue, "ssr_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
+o:value("false")
+o:value("true")
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "SSR"
+ end
+ else
+ s.fields[key]:depends({ type = "SSR" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-go.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-go.lua
new file mode 100644
index 000000000..04cdbfe45
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-go.lua
@@ -0,0 +1,150 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("trojan-go") then
+ return
+end
+
+local option_prefix = "trojan_go_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local encrypt_methods_ss_aead = {
+ "chacha20-ietf-poly1305",
+ "aes-128-gcm",
+ "aes-256-gcm",
+}
+
+-- [[ Trojan Go ]]
+
+s.fields["type"]:value("Trojan-Go", translate("Trojan-Go"))
+
+o = s:option(Value, "trojan_go_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "trojan_go_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "trojan_go_password", translate("Password"))
+o.password = true
+
+o = s:option(ListValue, "trojan_go_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
+o:value("false")
+o:value("true")
+
+o = s:option(Flag, "trojan_go_tls", translate("TLS"))
+o.default = 1
+
+o = s:option(Flag, "trojan_go_tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
+o.default = "0"
+o:depends({ trojan_go_tls = true })
+
+o = s:option(Value, "trojan_go_tls_serverName", translate("Domain"))
+o:depends({ trojan_go_tls = true })
+
+o = s:option(Flag, "trojan_go_tls_sessionTicket", translate("Session Ticket"))
+o.default = "0"
+o:depends({ trojan_go_tls = true })
+
+o = s:option(ListValue, "trojan_go_fingerprint", translate("Finger Print"))
+o:value("disable", translate("Disable"))
+o:value("firefox")
+o:value("chrome")
+o:value("ios")
+o.default = "disable"
+o:depends({ trojan_go_tls = true })
+
+o = s:option(ListValue, "trojan_go_transport", translate("Transport"))
+o:value("original", translate("Original"))
+o:value("ws", "WebSocket")
+o.default = "original"
+o.not_rewrite = true
+function o.cfgvalue(self, section)
+ return m:get(section, "trojan_transport")
+end
+function o.write(self, section, value)
+ m:set(section, "trojan_transport", value)
+end
+
+o = s:option(ListValue, "trojan_go_plugin_type", translate("Transport Plugin"))
+o:value("plaintext", "Plain Text")
+o:value("shadowsocks", "ShadowSocks")
+o:value("other", "Other")
+o.default = "plaintext"
+o:depends({ trojan_go_tls = false, trojan_go_transport = "original" })
+
+o = s:option(Value, "trojan_go_plugin_cmd", translate("Plugin Binary"))
+o.placeholder = "eg: /usr/bin/v2ray-plugin"
+o:depends({ trojan_go_plugin_type = "shadowsocks" })
+o:depends({ trojan_go_plugin_type = "other" })
+
+o = s:option(Value, "trojan_go_plugin_option", translate("Plugin Option"))
+o.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
+o:depends({ trojan_go_plugin_type = "shadowsocks" })
+o:depends({ trojan_go_plugin_type = "other" })
+
+o = s:option(DynamicList, "trojan_go_plugin_arg", translate("Plugin Option Args"))
+o.placeholder = "eg: [\"-config\", \"test.json\"]"
+o:depends({ trojan_go_plugin_type = "shadowsocks" })
+o:depends({ trojan_go_plugin_type = "other" })
+
+o = s:option(Value, "trojan_go_ws_host", translate("WebSocket Host"))
+o:depends({ trojan_go_transport = "ws" })
+
+o = s:option(Value, "trojan_go_ws_path", translate("WebSocket Path"))
+o.placeholder = "/"
+o:depends({ trojan_go_transport = "ws" })
+
+-- [[ Shadowsocks2 ]] --
+o = s:option(Flag, "trojan_go_ss_aead", translate("Shadowsocks secondary encryption"))
+o.default = "0"
+
+o = s:option(ListValue, "trojan_go_ss_aead_method", translate("Encrypt Method"))
+for _, v in ipairs(encrypt_methods_ss_aead) do o:value(v, v) end
+o.default = "aes-128-gcm"
+o:depends({ trojan_go_ss_aead = true })
+
+o = s:option(Value, "trojan_go_ss_aead_pwd", translate("Password"))
+o.password = true
+o:depends({ trojan_go_ss_aead = true })
+
+o = s:option(Flag, "trojan_go_smux", translate("Smux"))
+
+o = s:option(Value, "trojan_go_mux_concurrency", translate("Mux concurrency"))
+o.default = 8
+o:depends({ trojan_go_smux = true })
+
+o = s:option(Value, "trojan_go_smux_idle_timeout", translate("Mux idle timeout"))
+o.default = 60
+o:depends({ trojan_go_smux = true })
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Trojan-Go"
+ end
+ else
+ s.fields[key]:depends({ type = "Trojan-Go" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-plus.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-plus.lua
new file mode 100644
index 000000000..7364b7c35
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/trojan-plus.lua
@@ -0,0 +1,81 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("trojan-plus") then
+ return
+end
+
+local option_prefix = "trojan_plus_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ Trojan Plus ]]
+
+s.fields["type"]:value("Trojan-Plus", translate("Trojan-Plus"))
+
+o = s:option(Value, "trojan_plus_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "trojan_plus_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "trojan_plus_password", translate("Password"))
+o.password = true
+
+o = s:option(ListValue, "trojan_plus_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
+o:value("false")
+o:value("true")
+
+o = s:option(Flag, "trojan_plus_tls", translate("TLS"))
+o.default = 0
+o.validate = function(self, value, t)
+ if value then
+ local type = s.fields["type"]:formvalue(t) or ""
+ if value == "0" and type == "Trojan-Plus" then
+ return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
+ end
+ return value
+ end
+end
+
+o = s:option(Flag, "trojan_plus_tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
+o.default = "0"
+o:depends({ trojan_plus_tls = true })
+
+o = s:option(Value, "trojan_plus_tls_serverName", translate("Domain"))
+o:depends({ trojan_plus_tls = true })
+
+o = s:option(Flag, "trojan_plus_tls_sessionTicket", translate("Session Ticket"))
+o.default = "0"
+o:depends({ trojan_plus_tls = true })
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Trojan-Plus"
+ end
+ else
+ s.fields[key]:depends({ type = "Trojan-Plus" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/tuic.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/tuic.lua
new file mode 100644
index 000000000..465a02eeb
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/tuic.lua
@@ -0,0 +1,158 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("tuic-client") then
+ return
+end
+
+local option_prefix = "tuic_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ TUIC ]]
+
+s.fields["type"]:value("TUIC", translate("TUIC"))
+
+o = s:option(Value, "tuic_address", translate("Address (Support Domain Name)"))
+
+o = s:option(Value, "tuic_port", translate("Port"))
+o.datatype = "port"
+
+o = s:option(Value, "tuic_uuid", translate("ID"))
+o.password = true
+
+-- Tuic Password for remote server connect
+o = s:option(Value, "tuic_password", translate("TUIC User Password For Connect Remote Server"))
+o.password = true
+o.rmempty = true
+o.default = ""
+o.not_rewrite = true
+
+--[[
+-- Tuic username for local socks connect
+o = s:option(Value, "tuic_socks_username", translate("TUIC UserName For Local Socks"))
+o.rmempty = true
+o.default = ""
+o.not_rewrite = true
+
+-- Tuic Password for local socks connect
+o = s:option(Value, "tuic_socks_password", translate("TUIC Password For Local Socks"))
+o.password = true
+o.rmempty = true
+o.default = ""
+o.not_rewrite = true
+--]]
+
+o = s:option(Value, "tuic_ip", translate("Set the TUIC proxy server ip address"))
+o.datatype = "ipaddr"
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(ListValue, "tuic_udp_relay_mode", translate("UDP relay mode"))
+o:value("native", translate("native"))
+o:value("quic", translate("QUIC"))
+o.default = "native"
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(ListValue, "tuic_congestion_control", translate("Congestion control algorithm"))
+o:value("bbr", translate("BBR"))
+o:value("cubic", translate("CUBIC"))
+o:value("new_reno", translate("New Reno"))
+o.default = "cubic"
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Value, "tuic_heartbeat", translate("Heartbeat interval(second)"))
+o.datatype = "uinteger"
+o.default = "3"
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Value, "tuic_timeout", translate("Timeout for establishing a connection to server(second)"))
+o.datatype = "uinteger"
+o.default = "8"
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Value, "tuic_gc_interval", translate("Garbage collection interval(second)"))
+o.datatype = "uinteger"
+o.default = "3"
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Value, "tuic_gc_lifetime", translate("Garbage collection lifetime(second)"))
+o.datatype = "uinteger"
+o.default = "15"
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Value, "tuic_send_window", translate("TUIC send window"))
+o.datatype = "uinteger"
+o.default = 20971520
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Value, "tuic_receive_window", translate("TUIC receive window"))
+o.datatype = "uinteger"
+o.default = 10485760
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Value, "tuic_max_package_size", translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes"))
+o.datatype = "uinteger"
+o.default = 1500
+o.rmempty = true
+o.not_rewrite = true
+
+--Tuic settings for the local inbound socks5 server
+o = s:option(Flag, "tuic_dual_stack", translate("Set if the listening socket should be dual-stack"))
+o.default = 0
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Flag, "tuic_disable_sni", translate("Disable SNI"))
+o.default = 0
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(Flag, "tuic_zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake"))
+o.default = 0
+o.rmempty = true
+o.not_rewrite = true
+
+o = s:option(DynamicList, "tuic_tls_alpn", translate("TLS ALPN"))
+o.rmempty = true
+o.not_rewrite = true
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "TUIC"
+ end
+ else
+ s.fields[key]:depends({ type = "TUIC" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/brook.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/brook.lua
new file mode 100644
index 000000000..a18e6a13b
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/brook.lua
@@ -0,0 +1,65 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("brook") then
+ return
+end
+
+local option_prefix = "brook_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ Brook ]]
+
+s.fields["type"]:value("Brook", translate("Brook"))
+
+o = s:option(Value, "brook_port", translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(ListValue, "brook_protocol", translate("Protocol"))
+o:value("server", "Brook")
+o:value("wsserver", "WebSocket")
+
+--o = s:option(Flag, "brook_tls", translate("Use TLS"))
+--o:depends({ brook_protocol = "wsserver" })
+
+o = s:option(Value, "brook_password", translate("Password"))
+o.password = true
+
+o = s:option(Value, "brook_ws_path", translate("WebSocket Path"))
+o:depends({ brook_protocol = "wsserver" })
+
+o = s:option(Flag, "brook_log", translate("Log"))
+o.default = "1"
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Brook"
+ end
+ else
+ s.fields[key]:depends({ type = "Brook" })
+ end
+ end
+end
\ No newline at end of file
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria.lua
new file mode 100644
index 000000000..248e06140
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria.lua
@@ -0,0 +1,137 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("hysteria") then
+ return
+end
+
+local option_prefix = "hysteria_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ Hysteria ]]
+
+s.fields["type"]:value("Hysteria", translate("Hysteria"))
+
+o = s:option(Value, "hysteria_port", translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(ListValue, "hysteria_protocol", translate("Protocol"))
+o:value("udp", "UDP")
+o:value("faketcp", "faketcp")
+o:value("wechat-video", "wechat-video")
+
+o = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
+o.not_rewrite = true
+
+o = s:option(ListValue, "hysteria_auth_type", translate("Auth Type"))
+o:value("disable", translate("Disable"))
+o:value("string", translate("STRING"))
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_auth_password", translate("Auth Password"))
+o.password = true
+o:depends({ hysteria_auth_type = "string" })
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN"))
+o.not_rewrite = true
+
+o = s:option(Flag, "hysteria_udp", translate("UDP"))
+o.default = "1"
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
+o.default = "10"
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
+o.default = "50"
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
+o.not_rewrite = true
+
+o = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window"))
+o.not_rewrite = true
+
+o = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
+o.not_rewrite = true
+
+o = s:option(Flag, "hysteria_tls", translate("TLS"))
+o.default = 0
+o.validate = function(self, value, t)
+ if value then
+ if value == "1" then
+ local ca = s.fields["hysteria_tls_certificateFile"]:formvalue(t) or ""
+ local key = s.fields["hysteria_tls_keyFile"]:formvalue(t) or ""
+ if ca == "" or key == "" then
+ return nil, translate("Public key and Private key path can not be empty!")
+ end
+ end
+ return value
+ end
+end
+
+o = s:option(FileUpload, "hysteria_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"
+o:depends({ hysteria_tls = true })
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(FileUpload, "hysteria_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"
+o:depends({ hysteria_tls = true })
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(Flag, "hysteria_log", translate("Log"))
+o.default = "1"
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Hysteria"
+ end
+ else
+ s.fields[key]:depends({ type = "Hysteria" })
+ end
+ end
+end
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
new file mode 100644
index 000000000..48536f775
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua
@@ -0,0 +1,536 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("xray") and not api.is_finded("v2ray")then
+ return
+end
+
+local option_prefix = "xray_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local function add_xray_depends(o, field, value)
+ local deps = { type = "Xray" }
+ if field then
+ if type(field) == "string" then
+ deps[field] = value
+ else
+ for key, value in pairs(field) do
+ deps[key] = value
+ end
+ end
+ end
+ o:depends(deps)
+end
+
+local function add_v2ray_depends(o, field, value)
+ local deps = { type = "V2ray" }
+ if field then
+ if type(field) == "string" then
+ deps[field] = value
+ else
+ for key, value in pairs(field) do
+ deps[key] = value
+ end
+ end
+ end
+ o:depends(deps)
+end
+
+local v_ss_encrypt_method_list = {
+ "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305"
+}
+
+local x_ss_encrypt_method_list = {
+ "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
+}
+
+local header_type_list = {
+ "none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
+}
+
+-- [[ Xray ]]
+
+if api.is_finded("v2ray") then
+ s.fields["type"]:value("V2ray", translate("V2ray"))
+end
+if api.is_finded("xray") then
+ s.fields["type"]:value("Xray", translate("Xray"))
+end
+
+o = s:option(ListValue, "xray_protocol", translate("Protocol"))
+o:value("vmess", "Vmess")
+o:value("vless", "VLESS")
+o:value("http", "HTTP")
+o:value("socks", "Socks")
+o:value("shadowsocks", "Shadowsocks")
+o:value("trojan", "Trojan")
+o:value("dokodemo-door", "dokodemo-door")
+add_xray_depends(o)
+add_v2ray_depends(o)
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_port", translate("Listen Port"))
+o.datatype = "port"
+add_xray_depends(o)
+add_v2ray_depends(o)
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Flag, "xray_auth", translate("Auth"))
+o.validate = function(self, value, t)
+ if value and value == "1" then
+ local user_v = s.fields["xray_username"]:formvalue(t) or ""
+ local pass_v = s.fields["xray_password"]:formvalue(t) or ""
+ if user_v == "" or pass_v == "" then
+ return nil, translate("Username and Password must be used together!")
+ end
+ end
+ return value
+end
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "http" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "http" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_username", translate("Username"))
+add_xray_depends(o, { xray_auth = true })
+add_v2ray_depends(o, { xray_auth = true })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_password", translate("Password"))
+o.password = true
+add_xray_depends(o, { xray_auth = true })
+add_v2ray_depends(o, { xray_auth = true })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(ListValue, "d_protocol", translate("Destination protocol"))
+o:value("tcp", "TCP")
+o:value("udp", "UDP")
+o:value("tcp,udp", "TCP,UDP")
+add_v2ray_depends(o, { xray_protocol = "dokodemo-door" })
+add_xray_depends(o, { xray_protocol = "dokodemo-door" })
+
+o = s:option(Value, "d_address", translate("Destination address"))
+add_v2ray_depends(o, { xray_protocol = "dokodemo-door" })
+add_xray_depends(o, { xray_protocol = "dokodemo-door" })
+
+o = s:option(Value, "d_port", translate("Destination port"))
+o.datatype = "port"
+add_v2ray_depends(o, { xray_protocol = "dokodemo-door" })
+add_xray_depends(o, { xray_protocol = "dokodemo-door" })
+
+o = s:option(Value, "decryption", translate("Encrypt Method"))
+o.default = "none"
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "vless" })
+
+o = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
+for a, t in ipairs(v_ss_encrypt_method_list) do o:value(t) end
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+function o.cfgvalue(self, section)
+ return m:get(section, "method")
+end
+function o.write(self, section, value)
+ m:set(section, "method", value)
+end
+
+o = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method"))
+for a, t in ipairs(x_ss_encrypt_method_list) do o:value(t) end
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+function o.cfgvalue(self, section)
+ return m:get(section, "method")
+end
+function o.write(self, section, value)
+ m:set(section, "method", value)
+end
+
+o = s:option(Flag, "iv_check", translate("IV Check"))
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+
+o = s:option(ListValue, "ss_network", translate("Transport"))
+o.default = "tcp,udp"
+o:value("tcp", "TCP")
+o:value("udp", "UDP")
+o:value("tcp,udp", "TCP,UDP")
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+
+o = s:option(Flag, "udp_forward", translate("UDP Forward"))
+o.default = "1"
+o.rmempty = false
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "socks" })
+
+o = s:option(DynamicList, "xray_uuid", translate("ID") .. "/" .. translate("Password"))
+for i = 1, 3 do
+ o:value(api.gen_uuid(1))
+end
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Flag, "xray_tls", translate("TLS"))
+o.default = 0
+o.validate = function(self, value, t)
+ if value then
+ if value == "1" then
+ local ca = s.fields["xray_tls_certificateFile"]:formvalue(t) or ""
+ local key = s.fields["xray_tls_keyFile"]:formvalue(t) or ""
+ if ca == "" or key == "" then
+ return nil, translate("Public key and Private key path can not be empty!")
+ end
+ end
+ return value
+ end
+end
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "tlsflow", translate("flow"))
+o.default = ""
+o:value("", translate("Disable"))
+o:value("xtls-rprx-vision")
+o:value("xtls-rprx-vision-udp443")
+add_xray_depends(o, { xray_protocol = "vless", xray_tls = true })
+
+o = s:option(ListValue, "alpn", translate("alpn"))
+o.default = "h2,http/1.1"
+o:value("h2,http/1.1")
+o:value("h2")
+o:value("http/1.1")
+add_v2ray_depends(o, { xray_tls = true })
+add_xray_depends(o, { xray_tls = true })
+
+-- o = s:option(Value, "minversion", translate("minversion"))
+-- o.default = "1.3"
+-- o:value("1.3")
+--add_v2ray_depends(o, { xray_tls = true })
+--add_xray_depends(o, { xray_tls = true })
+
+-- [[ TLS部分 ]] --
+
+o = s:option(FileUpload, "xray_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"
+add_v2ray_depends(o, { xray_tls = true })
+add_xray_depends(o, { xray_tls = true })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(FileUpload, "xray_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"
+add_v2ray_depends(o, { xray_tls = true })
+add_xray_depends(o, { xray_tls = true })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(ListValue, "transport", translate("Transport"))
+o:value("tcp", "TCP")
+o:value("mkcp", "mKCP")
+o:value("ws", "WebSocket")
+o:value("h2", "HTTP/2")
+o:value("ds", "DomainSocket")
+o:value("quic", "QUIC")
+o:value("grpc", "gRPC")
+add_v2ray_depends(o, { xray_protocol = "vmess" })
+add_v2ray_depends(o, { xray_protocol = "vless" })
+add_v2ray_depends(o, { xray_protocol = "socks" })
+add_v2ray_depends(o, { xray_protocol = "shadowsocks" })
+add_v2ray_depends(o, { xray_protocol = "trojan" })
+add_xray_depends(o, { xray_protocol = "vmess" })
+add_xray_depends(o, { xray_protocol = "vless" })
+add_xray_depends(o, { xray_protocol = "socks" })
+add_xray_depends(o, { xray_protocol = "shadowsocks" })
+add_xray_depends(o, { xray_protocol = "trojan" })
+
+-- [[ WebSocket部分 ]]--
+
+o = s:option(Value, "xray_ws_host", translate("WebSocket Host"))
+add_v2ray_depends(o, { transport = "ws" })
+add_xray_depends(o, { transport = "ws" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_ws_path", translate("WebSocket Path"))
+add_v2ray_depends(o, { transport = "ws" })
+add_xray_depends(o, { transport = "ws" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+-- [[ HTTP/2部分 ]]--
+
+o = s:option(Value, "xray_h2_host", translate("HTTP/2 Host"))
+add_v2ray_depends(o, { transport = "h2" })
+add_xray_depends(o, { transport = "h2" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(Value, "xray_h2_path", translate("HTTP/2 Path"))
+add_v2ray_depends(o, { transport = "h2" })
+add_xray_depends(o, { transport = "h2" })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+-- [[ TCP部分 ]]--
+
+-- TCP伪装
+o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
+o:value("none", "none")
+o:value("http", "http")
+add_v2ray_depends(o, { transport = "tcp" })
+add_xray_depends(o, { transport = "tcp" })
+
+-- HTTP域名
+o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
+add_v2ray_depends(o, { tcp_guise = "http" })
+add_xray_depends(o, { tcp_guise = "http" })
+
+-- HTTP路径
+o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
+add_v2ray_depends(o, { tcp_guise = "http" })
+add_xray_depends(o, { tcp_guise = "http" })
+
+-- [[ mKCP部分 ]]--
+
+o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
+for a, t in ipairs(header_type_list) do o:value(t) end
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
+o.default = "1350"
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_tti", translate("KCP TTI"))
+o.default = "20"
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
+o.default = "5"
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
+o.default = "20"
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
+o.default = "1"
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
+o.default = "1"
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+o = s:option(Value, "mkcp_seed", translate("KCP Seed"))
+add_v2ray_depends(o, { transport = "mkcp" })
+add_xray_depends(o, { transport = "mkcp" })
+
+-- [[ DomainSocket部分 ]]--
+
+o = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running."))
+add_v2ray_depends(o, { transport = "ds" })
+add_xray_depends(o, { transport = "ds" })
+
+-- [[ QUIC部分 ]]--
+o = s:option(ListValue, "quic_security", translate("Encrypt Method"))
+o:value("none")
+o:value("aes-128-gcm")
+o:value("chacha20-poly1305")
+add_v2ray_depends(o, { transport = "quic" })
+add_xray_depends(o, { transport = "quic" })
+
+o = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
+add_v2ray_depends(o, { transport = "quic" })
+add_xray_depends(o, { transport = "quic" })
+
+o = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
+for a, t in ipairs(header_type_list) do o:value(t) end
+add_v2ray_depends(o, { transport = "quic" })
+add_xray_depends(o, { transport = "quic" })
+
+-- [[ gRPC部分 ]]--
+o = s:option(Value, "grpc_serviceName", "ServiceName")
+add_v2ray_depends(o, { transport = "grpc" })
+add_xray_depends(o, { transport = "grpc" })
+
+o = s:option(Flag, "acceptProxyProtocol", translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used."))
+add_v2ray_depends(o, { transport = "tcp" })
+add_v2ray_depends(o, { transport = "ws" })
+add_xray_depends(o, { transport = "tcp" })
+add_xray_depends(o, { transport = "ws" })
+
+-- [[ Fallback部分 ]]--
+o = s:option(Flag, "fallback", translate("Fallback"))
+add_v2ray_depends(o, { xray_protocol = "vless", transport = "tcp" })
+add_v2ray_depends(o, { xray_protocol = "trojan", transport = "tcp" })
+add_xray_depends(o, { xray_protocol = "vless", transport = "tcp" })
+add_xray_depends(o, { xray_protocol = "trojan", transport = "tcp" })
+
+--[[
+o = s:option(Value, "fallback_alpn", "Fallback alpn")
+add_v2ray_depends(o, { fallback = true })
+add_xray_depends(o, { fallback = true })
+
+o = s:option(Value, "fallback_path", "Fallback path")
+add_v2ray_depends(o, { fallback = true })
+add_xray_depends(o, { fallback = true })
+
+o = s:option(Value, "fallback_dest", "Fallback dest")
+add_v2ray_depends(o, { fallback = true })
+add_xray_depends(o, { fallback = true })
+
+o = s:option(Value, "fallback_xver", "Fallback xver")
+o.default = 0
+add_v2ray_depends(o, { fallback = true })
+add_xray_depends(o, { fallback = true })
+]]--
+
+o = s:option(DynamicList, "fallback_list", "Fallback", translate("dest,path"))
+add_v2ray_depends(o, { fallback = true })
+add_xray_depends(o, { fallback = true })
+
+o = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed locally, It is recommended to turn on when using reverse proxies or be fallback."))
+o.default = "0"
+add_v2ray_depends(o)
+add_xray_depends(o)
+
+o = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
+o.default = "0"
+add_v2ray_depends(o)
+add_xray_depends(o)
+
+local nodes_table = {}
+for k, e in ipairs(api.get_valid_nodes()) do
+ if e.node_type == "normal" and (e.type == "V2ray" or e.type == "Xray") then
+ nodes_table[#nodes_table + 1] = {
+ id = e[".name"],
+ remarks = e["remark"]
+ }
+ end
+end
+
+o = s:option(ListValue, "outbound_node", translate("outbound node"))
+o:value("nil", translate("Close"))
+o:value("_socks", translate("Custom Socks"))
+o:value("_http", translate("Custom HTTP"))
+o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
+for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
+o.default = "nil"
+add_v2ray_depends(o)
+add_xray_depends(o)
+
+o = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)"))
+add_v2ray_depends(o, { outbound_node = "_socks"})
+add_v2ray_depends(o, { outbound_node = "_http"})
+add_xray_depends(o, { outbound_node = "_socks"})
+add_xray_depends(o, { outbound_node = "_http"})
+
+o = s:option(Value, "outbound_node_port", translate("Port"))
+o.datatype = "port"
+add_v2ray_depends(o, { outbound_node = "_socks"})
+add_v2ray_depends(o, { outbound_node = "_http"})
+add_xray_depends(o, { outbound_node = "_socks"})
+add_xray_depends(o, { outbound_node = "_http"})
+
+o = s:option(Value, "outbound_node_username", translate("Username"))
+add_v2ray_depends(o, { outbound_node = "_socks"})
+add_v2ray_depends(o, { outbound_node = "_http"})
+add_xray_depends(o, { outbound_node = "_socks"})
+add_xray_depends(o, { outbound_node = "_http"})
+
+o = s:option(Value, "outbound_node_password", translate("Password"))
+o.password = true
+add_v2ray_depends(o, { outbound_node = "_socks"})
+add_v2ray_depends(o, { outbound_node = "_http"})
+add_xray_depends(o, { outbound_node = "_socks"})
+add_xray_depends(o, { outbound_node = "_http"})
+
+o = s:option(Value, "outbound_node_iface", translate("Interface"))
+o.default = "eth1"
+add_v2ray_depends(o, { outbound_node = "_iface"})
+add_xray_depends(o, { outbound_node = "_iface"})
+
+o = s:option(Flag, "xray_log", translate("Log"))
+o.default = "1"
+add_v2ray_depends(o)
+add_xray_depends(o)
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
+
+o = s:option(ListValue, "xray_loglevel", translate("Log Level"))
+o.default = "warning"
+o:value("debug")
+o:value("info")
+o:value("warning")
+o:value("error")
+add_v2ray_depends(o, { xray_log = true })
+add_xray_depends(o, { xray_log = true })
+o.cfgvalue = rm_prefix_cfgvalue
+o.write = rm_prefix_write
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua
new file mode 100644
index 000000000..17a2c9ae9
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua
@@ -0,0 +1,71 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("microsocks") then
+ return
+end
+
+local option_prefix = "socks_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ microsocks ]]
+
+s.fields["type"]:value("Socks", translate("Socks"))
+
+o = s:option(Value, "socks_port", "socks" .. translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(Flag, "socks_auth", translate("Auth"))
+o.validate = function(self, value, t)
+ if value and value == "1" then
+ local user_v = s.fields["socks_username"]:formvalue(t) or ""
+ local pass_v = s.fields["socks_password"]:formvalue(t) or ""
+ if user_v == "" or pass_v == "" then
+ return nil, translate("Username and Password must be used together!")
+ end
+ end
+ return value
+end
+
+o = s:option(Value, "socks_username", translate("Username"))
+o:depends({ socks_auth = true })
+
+o = s:option(Value, "socks_password", translate("Password"))
+o.password = true
+o:depends({ socks_auth = true })
+
+o = s:option(Flag, "socks_log", translate("Log"))
+o.default = "1"
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Socks"
+ end
+ else
+ s.fields[key]:depends({ type = "Socks" })
+ end
+ end
+end
\ No newline at end of file
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua
new file mode 100644
index 000000000..804d2d5d8
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua
@@ -0,0 +1,71 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("ssserver") then
+ return
+end
+
+local option_prefix = "ssrust_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local ssrust_encrypt_method_list = {
+ "plain", "none",
+ "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
+ "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"
+}
+
+-- [[ Shadowsocks Rust ]]
+
+s.fields["type"]:value("SS-Rust", translate("Shadowsocks Rust"))
+
+o = s:option(Value, "ssrust_port", translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(Value, "ssrust_password", translate("Password"))
+o.password = true
+
+o = s:option(ListValue, "ssrust_method", translate("Encrypt Method"))
+for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
+
+o = s:option(Value, "ssrust_timeout", translate("Connection Timeout"))
+o.datatype = "uinteger"
+o.default = 300
+
+o = s:option(Flag, "ssrust_tcp_fast_open", "TCP " .. translate("Fast Open"))
+o.default = "0"
+
+o = s:option(Flag, "ssrust_log", translate("Log"))
+o.default = "1"
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "SS-Rust"
+ end
+ else
+ s.fields[key]:depends({ type = "SS-Rust" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss.lua
new file mode 100644
index 000000000..b232c1598
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss.lua
@@ -0,0 +1,74 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("ss-server") then
+ return
+end
+
+local option_prefix = "ss_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local ss_encrypt_method_list = {
+ "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
+ "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb",
+ "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20",
+ "chacha20-ietf", -- aead
+ "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
+ "xchacha20-ietf-poly1305"
+}
+
+-- [[ Shadowsocks ]]
+
+s.fields["type"]:value("SS", translate("Shadowsocks"))
+
+o = s:option(Value, "ss_port", translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(Value, "ss_password", translate("Password"))
+o.password = true
+
+o = s:option(ListValue, "ss_method", translate("Encrypt Method"))
+for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
+
+o = s:option(Value, "ss_timeout", translate("Connection Timeout"))
+o.datatype = "uinteger"
+o.default = 300
+
+o = s:option(Flag, "ss_tcp_fast_open", "TCP " .. translate("Fast Open"))
+o.default = "0"
+
+o = s:option(Flag, "ss_log", translate("Log"))
+o.default = "1"
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "SS"
+ end
+ else
+ s.fields[key]:depends({ type = "SS" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua
new file mode 100644
index 000000000..55fa3e25f
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua
@@ -0,0 +1,98 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("ssr-server") then
+ return
+end
+
+local option_prefix = "ssr_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local ssr_encrypt_method_list = {
+ "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
+ "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
+ "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb",
+ "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20",
+ "chacha20-ietf"
+}
+
+local ssr_protocol_list = {
+ "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple",
+ "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5",
+ "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c",
+ "auth_chain_d", "auth_chain_e", "auth_chain_f"
+}
+local ssr_obfs_list = {
+ "plain", "http_simple", "http_post", "random_head", "tls_simple",
+ "tls1.0_session_auth", "tls1.2_ticket_auth"
+}
+
+-- [[ ShadowsocksR ]]
+
+s.fields["type"]:value("SSR", translate("ShadowsocksR"))
+
+o = s:option(Value, "ssr_port", translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(Value, "ssr_password", translate("Password"))
+o.password = true
+
+o = s:option(ListValue, "ssr_method", translate("Encrypt Method"))
+for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
+
+o = s:option(ListValue, "ssr_protocol", translate("Protocol"))
+for a, t in ipairs(ssr_protocol_list) do o:value(t) end
+
+o = s:option(Value, "ssr_protocol_param", translate("Protocol_param"))
+
+o = s:option(ListValue, "ssr_obfs", translate("Obfs"))
+for a, t in ipairs(ssr_obfs_list) do o:value(t) end
+
+o = s:option(Value, "ssr_obfs_param", translate("Obfs_param"))
+
+o = s:option(Value, "ssr_timeout", translate("Connection Timeout"))
+o.datatype = "uinteger"
+o.default = 300
+
+o = s:option(Flag, "ssr_tcp_fast_open", "TCP " .. translate("Fast Open"))
+o.default = "0"
+
+o = s:option(Flag, "ssr_udp_forward", translate("UDP Forward"))
+o.default = "1"
+o.rmempty = false
+
+o = s:option(Flag, "ssr_log", translate("Log"))
+o.default = "1"
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "SSR"
+ end
+ else
+ s.fields[key]:depends({ type = "SSR" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-go.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-go.lua
new file mode 100644
index 000000000..bfaac82eb
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-go.lua
@@ -0,0 +1,184 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("trojan-go") then
+ return
+end
+
+local option_prefix = "trojan_go_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+local encrypt_methods_ss_aead = {
+ "chacha20-ietf-poly1305",
+ "aes-128-gcm",
+ "aes-256-gcm",
+}
+
+-- [[ Trojan-Go ]]
+
+s.fields["type"]:value("Trojan-Go", translate("Trojan-Go"))
+
+o = s:option(Value, "trojan_go_port", translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(DynamicList, "trojan_go_uuid", translate("ID") .. "/" .. translate("Password"))
+for i = 1, 3 do
+ o:value(api.gen_uuid(1))
+end
+
+o = s:option(Flag, "trojan_go_tls", translate("TLS"))
+o.default = 0
+o.validate = function(self, value, t)
+ if value then
+ local type = s.fields["type"]:formvalue(t) or ""
+ if value == "0" and type == "Trojan-Go" then
+ return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
+ end
+ if value == "1" then
+ local ca = s.fields["trojan_go_tls_certificateFile"]:formvalue(t) or ""
+ local key = s.fields["trojan_go_tls_keyFile"]:formvalue(t) or ""
+ if ca == "" or key == "" then
+ return nil, translate("Public key and Private key path can not be empty!")
+ end
+ end
+ return value
+ end
+end
+
+o = s:option(FileUpload, "trojan_go_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"
+o:depends({ trojan_go_tls = true })
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(FileUpload, "trojan_go_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"
+o:depends({ trojan_go_tls = true })
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(Flag, "trojan_go_tls_sessionTicket", translate("Session Ticket"))
+o.default = "0"
+o:depends({ trojan_go_tls = true })
+
+o = s:option(ListValue, "trojan_go_transport", translate("Transport"))
+o:value("original", translate("Original"))
+o:value("ws", "WebSocket")
+o.default = "original"
+
+o = s:option(ListValue, "trojan_go_plugin_type", translate("Transport Plugin"))
+o:value("plaintext", "Plain Text")
+o:value("shadowsocks", "ShadowSocks")
+o:value("other", "Other")
+o.default = "plaintext"
+o:depends({ trojan_go_tls = false, trojan_go_transport = "original" })
+
+o = s:option(Value, "trojan_go_plugin_cmd", translate("Plugin Binary"))
+o.placeholder = "eg: /usr/bin/v2ray-plugin"
+o:depends({ trojan_go_plugin_type = "shadowsocks" })
+o:depends({ trojan_go_plugin_type = "other" })
+
+o = s:option(Value, "trojan_go_plugin_option", translate("Plugin Option"))
+o.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
+o:depends({ trojan_go_plugin_type = "shadowsocks" })
+o:depends({ trojan_go_plugin_type = "other" })
+
+o = s:option(DynamicList, "trojan_go_plugin_arg", translate("Plugin Option Args"))
+o.placeholder = "eg: [\"-config\", \"test.json\"]"
+o:depends({ trojan_go_plugin_type = "shadowsocks" })
+o:depends({ trojan_go_plugin_type = "other" })
+
+o = s:option(Value, "trojan_go_ws_host", translate("WebSocket Host"))
+o:depends({ trojan_go_transport = "ws" })
+
+o = s:option(Value, "trojan_go_ws_path", translate("WebSocket Path"))
+o:depends({ trojan_go_transport = "ws" })
+
+o = s:option(Flag, "trojan_go_ss_aead", translate("Shadowsocks secondary encryption"))
+o.default = "0"
+
+o = s:option(ListValue, "trojan_go_ss_aead_method", translate("Encrypt Method"))
+for _, v in ipairs(encrypt_methods_ss_aead) do o:value(v, v) end
+o.default = "aes-128-gcm"
+o:depends("trojan_go_ss_aead", true)
+
+o = s:option(Value, "trojan_go_ss_aead_pwd", translate("Password"))
+o.password = true
+o:depends("trojan_go_ss_aead", true)
+
+o = s:option(Flag, "trojan_go_tcp_fast_open", translate("TCP Fast Open"))
+o.default = "0"
+
+o = s:option(Flag, "trojan_go_remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray/Xray WebSocket and more."))
+o.default = "1"
+o.rmempty = false
+
+o = s:option(Value, "trojan_go_remote_address", translate("Remote Address"))
+o.default = "127.0.0.1"
+o:depends({ trojan_go_remote_enable = true })
+
+o = s:option(Value, "trojan_go_remote_port", translate("Remote Port"))
+o.datatype = "port"
+o.default = "80"
+o:depends({ trojan_go_remote_enable = true })
+
+o = s:option(Flag, "trojan_go_log", translate("Log"))
+o.default = "1"
+
+o = s:option(ListValue, "trojan_go_loglevel", translate("Log Level"))
+o.default = "2"
+o:value("0", "all")
+o:value("1", "info")
+o:value("2", "warn")
+o:value("3", "error")
+o:value("4", "fatal")
+o:depends({ trojan_go_log = true })
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Trojan-Go"
+ end
+ else
+ s.fields[key]:depends({ type = "Trojan-Go" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-plus.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-plus.lua
new file mode 100644
index 000000000..1515a9f01
--- /dev/null
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/trojan-plus.lua
@@ -0,0 +1,133 @@
+local m, s = ...
+
+local api = require "luci.passwall.api"
+
+if not api.is_finded("trojan-plus") then
+ return
+end
+
+local option_prefix = "trojan_plus_"
+
+local function option_name(name)
+ return option_prefix .. name
+end
+
+local function rm_prefix_cfgvalue(self, section)
+ if self.option:find(option_prefix) == 1 then
+ return m:get(section, self.option:sub(1 + #option_prefix))
+ end
+end
+local function rm_prefix_write(self, section, value)
+ if self.option:find(option_prefix) == 1 then
+ m:set(section, self.option:sub(1 + #option_prefix), value)
+ end
+end
+
+-- [[ Trojan-Plus ]]
+
+s.fields["type"]:value("Trojan-Plus", translate("Trojan-Plus"))
+
+o = s:option(Value, "trojan_plus_port", translate("Listen Port"))
+o.datatype = "port"
+
+o = s:option(DynamicList, "trojan_plus_uuid", translate("ID") .. "/" .. translate("Password"))
+for i = 1, 3 do
+ o:value(api.gen_uuid(1))
+end
+
+o = s:option(Flag, "trojan_plus_tls", translate("TLS"))
+o.default = 0
+o.validate = function(self, value, t)
+ if value then
+ local type = s.fields["type"]:formvalue(t) or ""
+ if value == "0" and type == "Trojan-Plus" then
+ return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
+ end
+ if value == "1" then
+ local ca = s.fields["trojan_plus_tls_certificateFile"]:formvalue(t) or ""
+ local key = s.fields["trojan_plus_tls_keyFile"]:formvalue(t) or ""
+ if ca == "" or key == "" then
+ return nil, translate("Public key and Private key path can not be empty!")
+ end
+ end
+ return value
+ end
+end
+
+o = s:option(FileUpload, "trojan_plus_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"
+o:depends({ trojan_plus_tls = true })
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(FileUpload, "trojan_plus_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"
+o:depends({ trojan_plus_tls = true })
+o.validate = function(self, value, t)
+ if value and value ~= "" then
+ if not nixio.fs.access(value) then
+ return nil, translate("Can't find this file!")
+ else
+ return value
+ end
+ end
+ return nil
+end
+
+o = s:option(Flag, "trojan_plus_tls_sessionTicket", translate("Session Ticket"))
+o.default = "0"
+o:depends({ trojan_plus_tls = true })
+
+o = s:option(Flag, "trojan_plus_tcp_fast_open", translate("TCP Fast Open"))
+o.default = "0"
+
+o = s:option(Flag, "trojan_plus_remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray/Xray WebSocket and more."))
+o.default = "1"
+o.rmempty = false
+
+o = s:option(Value, "trojan_plus_remote_address", translate("Remote Address"))
+o.default = "127.0.0.1"
+o:depends({ trojan_plus_remote_enable = true })
+
+o = s:option(Value, "trojan_plus_remote_port", translate("Remote Port"))
+o.datatype = "port"
+o.default = "80"
+o:depends({ trojan_plus_remote_enable = true })
+
+o = s:option(Flag, "trojan_plus_log", translate("Log"))
+o.default = "1"
+
+o = s:option(ListValue, "trojan_plus_loglevel", translate("Log Level"))
+o.default = "2"
+o:value("0", "all")
+o:value("1", "info")
+o:value("2", "warn")
+o:value("3", "error")
+o:value("4", "fatal")
+o:depends({ trojan_plus_log = true })
+
+for key, value in pairs(s.fields) do
+ if key:find(option_prefix) == 1 then
+ if not s.fields[key].not_rewrite then
+ s.fields[key].cfgvalue = rm_prefix_cfgvalue
+ s.fields[key].write = rm_prefix_write
+ end
+
+ local deps = s.fields[key].deps
+ if #deps > 0 then
+ for index, value in ipairs(deps) do
+ deps[index]["type"] = "Trojan-Plus"
+ end
+ else
+ s.fields[key]:depends({ type = "Trojan-Plus" })
+ end
+ end
+end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua
index 703e11fe7..b4371db33 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua
@@ -1,56 +1,6 @@
local api = require "luci.passwall.api"
-
-local ss_encrypt_method_list = {
- "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
- "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb",
- "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20",
- "chacha20-ietf", -- aead
- "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
- "xchacha20-ietf-poly1305"
-}
-
-local ss_rust_encrypt_method_list = {
- "plain", "none",
- "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
- "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
-}
-
-local ssr_encrypt_method_list = {
- "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
- "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
- "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb",
- "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20",
- "chacha20-ietf"
-}
-
-local ssr_protocol_list = {
- "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple",
- "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5",
- "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c",
- "auth_chain_d", "auth_chain_e", "auth_chain_f"
-}
-local ssr_obfs_list = {
- "plain", "http_simple", "http_post", "random_head", "tls_simple",
- "tls1.0_session_auth", "tls1.2_ticket_auth"
-}
-
-local v_ss_encrypt_method_list = {
- "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305"
-}
-
-local x_ss_encrypt_method_list = {
- "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
-}
-
-local header_type_list = {
- "none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
-}
-
-local encrypt_methods_ss_aead = {
- "chacha20-ietf-poly1305",
- "aes-128-gcm",
- "aes-256-gcm",
-}
+local fs = require "nixio.fs"
+local types_dir = "/usr/lib/lua/luci/model/cbi/passwall/server/type/"
m = Map("passwall_server", translate("Server Config"))
m.redirect = api.url("server")
@@ -59,676 +9,25 @@ s = m:section(NamedSection, arg[1], "user", "")
s.addremove = false
s.dynamic = false
-enable = s:option(Flag, "enable", translate("Enable"))
-enable.default = "1"
-enable.rmempty = false
+o = s:option(Flag, "enable", translate("Enable"))
+o.default = "1"
+o.rmempty = false
-remarks = s:option(Value, "remarks", translate("Remarks"))
-remarks.default = translate("Remarks")
-remarks.rmempty = false
+o = s:option(Value, "remarks", translate("Remarks"))
+o.default = translate("Remarks")
+o.rmempty = false
-type = s:option(ListValue, "type", translate("Type"))
-if api.is_finded("microsocks") then
- type:value("Socks", translate("Socks"))
+o = s:option(ListValue, "type", translate("Type"))
+
+local type_table = {}
+for filename in fs.dir(types_dir) do
+ table.insert(type_table, filename)
end
-if api.is_finded("ss-server") then
- type:value("SS", translate("Shadowsocks"))
+table.sort(type_table)
+
+for index, value in ipairs(type_table) do
+ local p_func = loadfile(types_dir .. value)
+ setfenv(p_func, getfenv(1))(m, s)
end
-if api.is_finded("ssserver") then
- type:value("SS-Rust", translate("Shadowsocks Rust"))
-end
-if api.is_finded("ssr-server") then
- type:value("SSR", translate("ShadowsocksR"))
-end
-if api.is_finded("v2ray") then
- type:value("V2ray", translate("V2ray"))
-end
-if api.is_finded("xray") then
- type:value("Xray", translate("Xray"))
-end
-if api.is_finded("brook") then
- type:value("Brook", translate("Brook"))
-end
---[[
-if api.is_finded("trojan-plus") or api.is_finded("trojan") then
- type:value("Trojan", translate("Trojan"))
-end
-]]--
-if api.is_finded("trojan-plus") then
- type:value("Trojan-Plus", translate("Trojan-Plus"))
-end
-if api.is_finded("trojan-go") then
- type:value("Trojan-Go", translate("Trojan-Go"))
-end
-if api.is_finded("hysteria") then
- type:value("Hysteria", translate("Hysteria"))
-end
-
-protocol = s:option(ListValue, "protocol", translate("Protocol"))
-protocol:value("vmess", "Vmess")
-protocol:value("vless", "VLESS")
-protocol:value("http", "HTTP")
-protocol:value("socks", "Socks")
-protocol:value("shadowsocks", "Shadowsocks")
-protocol:value("trojan", "Trojan")
-protocol:value("dokodemo-door", "dokodemo-door")
-protocol:depends("type", "V2ray")
-protocol:depends("type", "Xray")
-
--- Brook协议
-brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol"))
-brook_protocol:value("server", "Brook")
-brook_protocol:value("wsserver", "WebSocket")
-brook_protocol:depends("type", "Brook")
-function brook_protocol.cfgvalue(self, section)
- return m:get(section, "protocol")
-end
-function brook_protocol.write(self, section, value)
- m:set(section, "protocol", value)
-end
-
---brook_tls = s:option(Flag, "brook_tls", translate("Use TLS"))
---brook_tls:depends("brook_protocol", "wsserver")
-
-port = s:option(Value, "port", translate("Listen Port"))
-port.datatype = "port"
-port.rmempty = false
-
-auth = s:option(Flag, "auth", translate("Auth"))
-auth.validate = function(self, value, t)
- if value and value == "1" then
- local user_v = username:formvalue(t) or ""
- local pass_v = password:formvalue(t) or ""
- if user_v == "" or pass_v == "" then
- return nil, translate("Username and Password must be used together!")
- end
- end
- return value
-end
-auth:depends("type", "Socks")
-auth:depends({ type = "V2ray", protocol = "socks" })
-auth:depends({ type = "V2ray", protocol = "http" })
-auth:depends({ type = "Xray", protocol = "socks" })
-auth:depends({ type = "Xray", protocol = "http" })
-
-username = s:option(Value, "username", translate("Username"))
-username:depends("auth", true)
-
-password = s:option(Value, "password", translate("Password"))
-password.password = true
-password:depends("auth", true)
-password:depends("type", "SS")
-password:depends("type", "SS-Rust")
-password:depends("type", "SSR")
-password:depends("type", "Brook")
-password:depends({ type = "V2ray", protocol = "shadowsocks" })
-password:depends({ type = "Xray", protocol = "shadowsocks" })
-
-d_protocol = s:option(ListValue, "d_protocol", translate("Destination protocol"))
-d_protocol:value("tcp", "TCP")
-d_protocol:value("udp", "UDP")
-d_protocol:value("tcp,udp", "TCP,UDP")
-d_protocol:depends({ type = "V2ray", protocol = "dokodemo-door" })
-d_protocol:depends({ type = "Xray", protocol = "dokodemo-door" })
-
-d_address = s:option(Value, "d_address", translate("Destination address"))
-d_address:depends({ type = "V2ray", protocol = "dokodemo-door" })
-d_address:depends({ type = "Xray", protocol = "dokodemo-door" })
-
-d_port = s:option(Value, "d_port", translate("Destination port"))
-d_port.datatype = "port"
-d_port:depends({ type = "V2ray", protocol = "dokodemo-door" })
-d_port:depends({ type = "Xray", protocol = "dokodemo-door" })
-
-decryption = s:option(Value, "decryption", translate("Encrypt Method"))
-decryption.default = "none"
-decryption:depends({ type = "V2ray", protocol = "vless" })
-decryption:depends({ type = "Xray", protocol = "vless" })
-
-hysteria_protocol = s:option(ListValue, "hysteria_protocol", translate("Protocol"))
-hysteria_protocol:value("udp", "UDP")
-hysteria_protocol:value("faketcp", "faketcp")
-hysteria_protocol:value("wechat-video", "wechat-video")
-hysteria_protocol:depends("type", "Hysteria")
-function hysteria_protocol.cfgvalue(self, section)
- return m:get(section, "protocol")
-end
-function hysteria_protocol.write(self, section, value)
- m:set(section, "protocol", value)
-end
-
-hysteria_obfs = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
-hysteria_obfs:depends("type", "Hysteria")
-
-hysteria_auth_type = s:option(ListValue, "hysteria_auth_type", translate("Auth Type"))
-hysteria_auth_type:value("disable", translate("Disable"))
-hysteria_auth_type:value("string", translate("STRING"))
-hysteria_auth_type:depends("type", "Hysteria")
-
-hysteria_auth_password = s:option(Value, "hysteria_auth_password", translate("Auth Password"))
-hysteria_auth_password.password = true
-hysteria_auth_password:depends("hysteria_auth_type", "string")
-
-hysteria_alpn = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN"))
-hysteria_alpn:depends("type", "Hysteria")
-
-hysteria_udp = s:option(Flag, "hysteria_udp", translate("UDP"))
-hysteria_udp.default = "1"
-hysteria_udp:depends("type", "Hysteria")
-
-hysteria_up_mbps = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
-hysteria_up_mbps.default = "10"
-hysteria_up_mbps:depends("type", "Hysteria")
-
-hysteria_down_mbps = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
-hysteria_down_mbps.default = "50"
-hysteria_down_mbps:depends("type", "Hysteria")
-
-hysteria_recv_window_conn = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
-hysteria_recv_window_conn:depends("type", "Hysteria")
-
-hysteria_recv_window = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window"))
-hysteria_recv_window:depends("type", "Hysteria")
-
-hysteria_disable_mtu_discovery = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
-hysteria_disable_mtu_discovery:depends("type", "Hysteria")
-
-ss_encrypt_method = s:option(ListValue, "ss_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(ss_encrypt_method_list) do ss_encrypt_method:value(t) end
-ss_encrypt_method:depends("type", "SS")
-function ss_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function ss_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-ss_rust_encrypt_method = s:option(ListValue, "ss_rust_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(ss_rust_encrypt_method_list) do ss_rust_encrypt_method:value(t) end
-ss_rust_encrypt_method:depends("type", "SS-Rust")
-function ss_rust_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function ss_rust_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-ssr_encrypt_method = s:option(ListValue, "ssr_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(ssr_encrypt_method_list) do ssr_encrypt_method:value(t) end
-ssr_encrypt_method:depends("type", "SSR")
-function ssr_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function ssr_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end
-v_ss_encrypt_method:depends({ type = "V2ray", protocol = "shadowsocks" })
-function v_ss_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function v_ss_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-x_ss_encrypt_method = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method"))
-for a, t in ipairs(x_ss_encrypt_method_list) do x_ss_encrypt_method:value(t) end
-x_ss_encrypt_method:depends({ type = "Xray", protocol = "shadowsocks" })
-function x_ss_encrypt_method.cfgvalue(self, section)
- return m:get(section, "method")
-end
-function x_ss_encrypt_method.write(self, section, value)
- m:set(section, "method", value)
-end
-
-iv_check = s:option(Flag, "iv_check", translate("IV Check"))
-iv_check:depends({ type = "V2ray", protocol = "shadowsocks" })
-iv_check:depends({ type = "Xray", protocol = "shadowsocks" })
-
-ss_network = s:option(ListValue, "ss_network", translate("Transport"))
-ss_network.default = "tcp,udp"
-ss_network:value("tcp", "TCP")
-ss_network:value("udp", "UDP")
-ss_network:value("tcp,udp", "TCP,UDP")
-ss_network:depends({ type = "V2ray", protocol = "shadowsocks" })
-ss_network:depends({ type = "Xray", protocol = "shadowsocks" })
-
-ssr_protocol = s:option(ListValue, "ssr_protocol", translate("Protocol"))
-for a, t in ipairs(ssr_protocol_list) do ssr_protocol:value(t) end
-ssr_protocol:depends("type", "SSR")
-function ssr_protocol.cfgvalue(self, section)
- return m:get(section, "protocol")
-end
-function ssr_protocol.write(self, section, value)
- m:set(section, "protocol", value)
-end
-
-protocol_param = s:option(Value, "protocol_param", translate("Protocol_param"))
-protocol_param:depends("type", "SSR")
-
-obfs = s:option(ListValue, "obfs", translate("Obfs"))
-for a, t in ipairs(ssr_obfs_list) do obfs:value(t) end
-obfs:depends("type", "SSR")
-
-obfs_param = s:option(Value, "obfs_param", translate("Obfs_param"))
-obfs_param:depends("type", "SSR")
-
-timeout = s:option(Value, "timeout", translate("Connection Timeout"))
-timeout.datatype = "uinteger"
-timeout.default = 300
-timeout:depends("type", "SS")
-timeout:depends("type", "SS-Rust")
-timeout:depends("type", "SSR")
-
-udp_forward = s:option(Flag, "udp_forward", translate("UDP Forward"))
-udp_forward.default = "1"
-udp_forward.rmempty = false
-udp_forward:depends("type", "SSR")
-udp_forward:depends({ type = "V2ray", protocol = "socks" })
-udp_forward:depends({ type = "Xray", protocol = "socks" })
-
-uuid = s:option(DynamicList, "uuid", translate("ID") .. "/" .. translate("Password"))
-for i = 1, 3 do
- uuid:value(api.gen_uuid(1))
-end
-uuid:depends({ type = "V2ray", protocol = "vmess" })
-uuid:depends({ type = "V2ray", protocol = "vless" })
-uuid:depends({ type = "V2ray", protocol = "trojan" })
-uuid:depends({ type = "Xray", protocol = "vmess" })
-uuid:depends({ type = "Xray", protocol = "vless" })
-uuid:depends({ type = "Xray", protocol = "trojan" })
-uuid:depends("type", "Trojan")
-uuid:depends("type", "Trojan-Go")
-uuid:depends("type", "Trojan-Plus")
-
-tls = s:option(Flag, "tls", translate("TLS"))
-tls.default = 0
-tls.validate = function(self, value, t)
- if value then
- local type = type:formvalue(t) or ""
- if value == "0" and (type == "Trojan" or type == "Trojan-Plus") then
- return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
- end
- if value == "1" then
- local ca = tls_certificateFile:formvalue(t) or ""
- local key = tls_keyFile:formvalue(t) or ""
- if ca == "" or key == "" then
- return nil, translate("Public key and Private key path can not be empty!")
- end
- end
- return value
- end
-end
-tls:depends({ type = "V2ray", protocol = "vmess" })
-tls:depends({ type = "V2ray", protocol = "vless" })
-tls:depends({ type = "V2ray", protocol = "socks" })
-tls:depends({ type = "V2ray", protocol = "shadowsocks" })
-tls:depends({ type = "V2ray", protocol = "trojan" })
-tls:depends({ type = "Xray", protocol = "vmess" })
-tls:depends({ type = "Xray", protocol = "vless" })
-tls:depends({ type = "Xray", protocol = "socks" })
-tls:depends({ type = "Xray", protocol = "shadowsocks" })
-tls:depends({ type = "Xray", protocol = "trojan" })
-tls:depends("type", "Trojan")
-tls:depends("type", "Trojan-Plus")
-tls:depends("type", "Trojan-Go")
-
-tlsflow = s:option(Value, "tlsflow", translate("flow"))
-tlsflow.default = ""
-tlsflow:value("", translate("Disable"))
-tlsflow:value("xtls-rprx-vision")
-tlsflow:value("xtls-rprx-vision-udp443")
-tlsflow:depends({ type = "Xray", protocol = "vless", tls = true })
-
-alpn = s:option(ListValue, "alpn", translate("alpn"))
-alpn.default = "h2,http/1.1"
-alpn:value("h2,http/1.1")
-alpn:value("h2")
-alpn:value("http/1.1")
-alpn:depends({ type = "V2ray", tls = true })
-alpn:depends({ type = "Xray", tls = true })
-
--- minversion = s:option(Value, "minversion", translate("minversion"))
--- minversion.default = "1.3"
--- minversion:value("1.3")
--- minversion:depends("tls", true)
-
--- [[ TLS部分 ]] --
-
-tls_certificateFile = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
-tls_certificateFile.validate = function(self, value, t)
- if value and value ~= "" then
- if not nixio.fs.access(value) then
- return nil, translate("Can't find this file!")
- else
- return value
- end
- end
- return nil
-end
-tls_certificateFile.default = "/etc/config/ssl/" .. arg[1] .. ".pem"
-tls_certificateFile:depends("tls", true)
-tls_certificateFile:depends("type", "Hysteria")
-
-tls_keyFile = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
-tls_keyFile.validate = function(self, value, t)
- if value and value ~= "" then
- if not nixio.fs.access(value) then
- return nil, translate("Can't find this file!")
- else
- return value
- end
- end
- return nil
-end
-tls_keyFile.default = "/etc/config/ssl/" .. arg[1] .. ".key"
-tls_keyFile:depends("tls", true)
-tls_keyFile:depends("type", "Hysteria")
-
-tls_sessionTicket = s:option(Flag, "tls_sessionTicket", translate("Session Ticket"))
-tls_sessionTicket.default = "0"
-tls_sessionTicket:depends({ type = "Trojan", tls = true })
-tls_sessionTicket:depends({ type = "Trojan-Plus", tls = true })
-tls_sessionTicket:depends({ type = "Trojan-Go", tls = true })
-
-transport = s:option(ListValue, "transport", translate("Transport"))
-transport:value("tcp", "TCP")
-transport:value("mkcp", "mKCP")
-transport:value("ws", "WebSocket")
-transport:value("h2", "HTTP/2")
-transport:value("ds", "DomainSocket")
-transport:value("quic", "QUIC")
-transport:value("grpc", "gRPC")
-transport:depends({ type = "V2ray", protocol = "vmess" })
-transport:depends({ type = "V2ray", protocol = "vless" })
-transport:depends({ type = "V2ray", protocol = "socks" })
-transport:depends({ type = "V2ray", protocol = "shadowsocks" })
-transport:depends({ type = "V2ray", protocol = "trojan" })
-transport:depends({ type = "Xray", protocol = "vmess" })
-transport:depends({ type = "Xray", protocol = "vless" })
-transport:depends({ type = "Xray", protocol = "socks" })
-transport:depends({ type = "Xray", protocol = "shadowsocks" })
-transport:depends({ type = "Xray", protocol = "trojan" })
-
-trojan_transport = s:option(ListValue, "trojan_transport", translate("Transport"))
-trojan_transport:value("original", translate("Original"))
-trojan_transport:value("ws", "WebSocket")
-trojan_transport.default = "original"
-trojan_transport:depends("type", "Trojan-Go")
-
-trojan_plugin = s:option(ListValue, "plugin_type", translate("Transport Plugin"))
-trojan_plugin:value("plaintext", "Plain Text")
-trojan_plugin:value("shadowsocks", "ShadowSocks")
-trojan_plugin:value("other", "Other")
-trojan_plugin.default = "plaintext"
-trojan_plugin:depends({ tls = false, trojan_transport = "original" })
-
-trojan_plugin_cmd = s:option(Value, "plugin_cmd", translate("Plugin Binary"))
-trojan_plugin_cmd.placeholder = "eg: /usr/bin/v2ray-plugin"
-trojan_plugin_cmd:depends({ plugin_type = "shadowsocks" })
-trojan_plugin_cmd:depends({ plugin_type = "other" })
-
-trojan_plugin_op = s:option(Value, "plugin_option", translate("Plugin Option"))
-trojan_plugin_op.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
-trojan_plugin_op:depends({ plugin_type = "shadowsocks" })
-trojan_plugin_op:depends({ plugin_type = "other" })
-
-trojan_plugin_arg = s:option(DynamicList, "plugin_arg", translate("Plugin Option Args"))
-trojan_plugin_arg.placeholder = "eg: [\"-config\", \"test.json\"]"
-trojan_plugin_arg:depends({ plugin_type = "shadowsocks" })
-trojan_plugin_arg:depends({ plugin_type = "other" })
-
--- [[ WebSocket部分 ]]--
-
-ws_host = s:option(Value, "ws_host", translate("WebSocket Host"))
-ws_host:depends("transport", "ws")
-ws_host:depends("ss_transport", "ws")
-ws_host:depends("trojan_transport", "ws")
-
-ws_path = s:option(Value, "ws_path", translate("WebSocket Path"))
-ws_path:depends("transport", "ws")
-ws_path:depends("ss_transport", "ws")
-ws_path:depends("trojan_transport", "ws")
-ws_path:depends({ type = "Brook", brook_protocol = "wsserver" })
-
--- [[ HTTP/2部分 ]]--
-
-h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host"))
-h2_host:depends("transport", "h2")
-h2_host:depends("ss_transport", "h2")
-h2_host:depends("trojan_transport", "h2")
-
-h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path"))
-h2_path:depends("transport", "h2")
-h2_path:depends("ss_transport", "h2")
-h2_path:depends("trojan_transport", "h2")
-
--- [[ TCP部分 ]]--
-
--- TCP伪装
-tcp_guise = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
-tcp_guise:value("none", "none")
-tcp_guise:value("http", "http")
-tcp_guise:depends("transport", "tcp")
-
--- HTTP域名
-tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
-tcp_guise_http_host:depends("tcp_guise", "http")
-
--- HTTP路径
-tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
-tcp_guise_http_path:depends("tcp_guise", "http")
-
--- [[ mKCP部分 ]]--
-
-mkcp_guise = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
-for a, t in ipairs(header_type_list) do mkcp_guise:value(t) end
-mkcp_guise:depends("transport", "mkcp")
-
-mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
-mkcp_mtu.default = "1350"
-mkcp_mtu:depends("transport", "mkcp")
-
-mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI"))
-mkcp_tti.default = "20"
-mkcp_tti:depends("transport", "mkcp")
-
-mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
-mkcp_uplinkCapacity.default = "5"
-mkcp_uplinkCapacity:depends("transport", "mkcp")
-
-mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
-mkcp_downlinkCapacity.default = "20"
-mkcp_downlinkCapacity:depends("transport", "mkcp")
-
-mkcp_congestion = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
-mkcp_congestion:depends("transport", "mkcp")
-
-mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
-mkcp_readBufferSize.default = "1"
-mkcp_readBufferSize:depends("transport", "mkcp")
-
-mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
-mkcp_writeBufferSize.default = "1"
-mkcp_writeBufferSize:depends("transport", "mkcp")
-
-mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed"))
-mkcp_seed:depends("transport", "mkcp")
-
--- [[ DomainSocket部分 ]]--
-
-ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running."))
-ds_path:depends("transport", "ds")
-
--- [[ QUIC部分 ]]--
-quic_security = s:option(ListValue, "quic_security", translate("Encrypt Method"))
-quic_security:value("none")
-quic_security:value("aes-128-gcm")
-quic_security:value("chacha20-poly1305")
-quic_security:depends("transport", "quic")
-
-quic_key = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
-quic_key:depends("transport", "quic")
-
-quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
-for a, t in ipairs(header_type_list) do quic_guise:value(t) end
-quic_guise:depends("transport", "quic")
-
--- [[ gRPC部分 ]]--
-grpc_serviceName = s:option(Value, "grpc_serviceName", "ServiceName")
-grpc_serviceName:depends("transport", "grpc")
-
-acceptProxyProtocol = s:option(Flag, "acceptProxyProtocol", translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used."))
-acceptProxyProtocol:depends({ type = "V2ray", transport = "tcp" })
-acceptProxyProtocol:depends({ type = "V2ray", transport = "ws" })
-acceptProxyProtocol:depends({ type = "Xray", transport = "tcp" })
-acceptProxyProtocol:depends({ type = "Xray", transport = "ws" })
-
--- [[ Fallback部分 ]]--
-fallback = s:option(Flag, "fallback", translate("Fallback"))
-fallback:depends({ type = "V2ray", protocol = "vless", transport = "tcp" })
-fallback:depends({ type = "V2ray", protocol = "trojan", transport = "tcp" })
-fallback:depends({ type = "Xray", protocol = "vless", transport = "tcp" })
-fallback:depends({ type = "Xray", protocol = "trojan", transport = "tcp" })
-
---[[
-fallback_alpn = s:option(Value, "fallback_alpn", "Fallback alpn")
-fallback_alpn:depends("fallback", true)
-
-fallback_path = s:option(Value, "fallback_path", "Fallback path")
-fallback_path:depends("fallback", true)
-
-fallback_dest = s:option(Value, "fallback_dest", "Fallback dest")
-fallback_dest:depends("fallback", true)
-
-fallback_xver = s:option(Value, "fallback_xver", "Fallback xver")
-fallback_xver.default = 0
-fallback_xver:depends("fallback", true)
-]]--
-
-fallback_list = s:option(DynamicList, "fallback_list", "Fallback", translate("dest,path"))
-fallback_list:depends("fallback", true)
-
-ss_aead = s:option(Flag, "ss_aead", translate("Shadowsocks secondary encryption"))
-ss_aead:depends("type", "Trojan-Go")
-ss_aead.default = "0"
-
-ss_aead_method = s:option(ListValue, "ss_aead_method", translate("Encrypt Method"))
-for _, v in ipairs(encrypt_methods_ss_aead) do ss_aead_method:value(v, v) end
-ss_aead_method.default = "aes-128-gcm"
-ss_aead_method:depends("ss_aead", true)
-
-ss_aead_pwd = s:option(Value, "ss_aead_pwd", translate("Password"))
-ss_aead_pwd.password = true
-ss_aead_pwd:depends("ss_aead", true)
-
-tcp_fast_open = s:option(Flag, "tcp_fast_open", translate("TCP Fast Open"))
-tcp_fast_open.default = "0"
-tcp_fast_open:depends("type", "SS")
-tcp_fast_open:depends("type", "SS-Rust")
-tcp_fast_open:depends("type", "SSR")
-tcp_fast_open:depends("type", "Trojan")
-tcp_fast_open:depends("type", "Trojan-Plus")
-tcp_fast_open:depends("type", "Trojan-Go")
-
-remote_enable = s:option(Flag, "remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray/Xray WebSocket and more."))
-remote_enable.default = "1"
-remote_enable.rmempty = false
-remote_enable:depends("type", "Trojan")
-remote_enable:depends("type", "Trojan-Plus")
-remote_enable:depends("type", "Trojan-Go")
-
-remote_address = s:option(Value, "remote_address", translate("Remote Address"))
-remote_address.default = "127.0.0.1"
-remote_address:depends("remote_enable", 1)
-
-remote_port = s:option(Value, "remote_port", translate("Remote Port"))
-remote_port.datatype = "port"
-remote_port.default = "80"
-remote_port:depends("remote_enable", 1)
-
-bind_local = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed locally, It is recommended to turn on when using reverse proxies or be fallback."))
-bind_local.default = "0"
-bind_local:depends("type", "V2ray")
-bind_local:depends("type", "Xray")
-
-accept_lan = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
-accept_lan.default = "0"
-accept_lan:depends("type", "V2ray")
-accept_lan:depends("type", "Xray")
-
-local nodes_table = {}
-for k, e in ipairs(api.get_valid_nodes()) do
- if e.node_type == "normal" and (e.type == "V2ray" or e.type == "Xray") then
- nodes_table[#nodes_table + 1] = {
- id = e[".name"],
- remarks = e["remark"]
- }
- end
-end
-
-outbound_node = s:option(ListValue, "outbound_node", translate("outbound node"))
-outbound_node:value("nil", translate("Close"))
-outbound_node:value("_socks", translate("Custom Socks"))
-outbound_node:value("_http", translate("Custom HTTP"))
-outbound_node:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
-for k, v in pairs(nodes_table) do outbound_node:value(v.id, v.remarks) end
-outbound_node.default = "nil"
-outbound_node:depends("type", "V2ray")
-outbound_node:depends("type", "Xray")
-
-outbound_node_address = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)"))
-outbound_node_address:depends("outbound_node", "_socks")
-outbound_node_address:depends("outbound_node", "_http")
-
-outbound_node_port = s:option(Value, "outbound_node_port", translate("Port"))
-outbound_node_port.datatype = "port"
-outbound_node_port:depends("outbound_node", "_socks")
-outbound_node_port:depends("outbound_node", "_http")
-
-outbound_node_username = s:option(Value, "outbound_node_username", translate("Username"))
-outbound_node_username:depends("outbound_node", "_socks")
-outbound_node_username:depends("outbound_node", "_http")
-
-outbound_node_password = s:option(Value, "outbound_node_password", translate("Password"))
-outbound_node_password.password = true
-outbound_node_password:depends("outbound_node", "_socks")
-outbound_node_password:depends("outbound_node", "_http")
-
-outbound_node_iface = s:option(Value, "outbound_node_iface", translate("Interface"))
-outbound_node_iface.default = "eth1"
-outbound_node_iface:depends("outbound_node", "_iface")
-
-log = s:option(Flag, "log", translate("Log"))
-log.default = "1"
-log.rmempty = false
-
-loglevel = s:option(ListValue, "loglevel", translate("Log Level"))
-loglevel.default = "warning"
-loglevel:value("debug")
-loglevel:value("info")
-loglevel:value("warning")
-loglevel:value("error")
-loglevel:depends({ type = "V2ray", log = true })
-loglevel:depends({ type = "Xray", log = true })
-
-trojan_loglevel = s:option(ListValue, "trojan_loglevel", translate("Log Level"))
-trojan_loglevel.default = "2"
-trojan_loglevel:value("0", "all")
-trojan_loglevel:value("1", "info")
-trojan_loglevel:value("2", "warn")
-trojan_loglevel:value("3", "error")
-trojan_loglevel:value("4", "fatal")
-function trojan_loglevel.cfgvalue(self, section)
- return m:get(section, "loglevel")
-end
-function trojan_loglevel.write(self, section, value)
- m:set(section, "loglevel", value)
-end
-trojan_loglevel:depends({ type = "Trojan", log = true })
-trojan_loglevel:depends({ type = "Trojan-Plus", log = true })
-trojan_loglevel:depends({ type = "Trojan-Go", log = true })
return m
diff --git a/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm b/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm
index c0806f655..fb929c025 100644
--- a/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm
+++ b/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm
@@ -123,22 +123,45 @@ local has_xray = api.is_finded("xray")
opt.client = urlname.indexOf("server") === -1;
var v_type = opt.get("type").value;
var v_alias = opt.get("remarks");
+
+ var flag = null
+ if (v_type === "SS") {
+ flag = "ss_"
+ } else if (v_type === "SS-Rust") {
+ flag = "ss_rust_"
+ } else if (v_type === "SS-Rust") {
+ flag = "ss_rust_"
+ } else if (v_type === "SSR") {
+ flag = "ssr_"
+ } else if (v_type === "Trojan-Plus") {
+ flag = "trojan_plus_"
+ } else if (v_type === "Trojan-Go") {
+ flag = "trojan_go_"
+ } else if (v_type === "Brook") {
+ flag = "brook_"
+ } else if (v_type === "Hysteria") {
+ flag = "hysteria_"
+ } else if (v_type === "V2ray" || v_type === "Xray") {
+ flag = "xray_"
+ }
var _address = ""
- try {
- var v_server = opt.get("address");
- const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
- if (ipv6Regex.test(v_server.value)) {
- _address = "[" + v_server.value + "]"
- } else {
- _address = v_server.value
+ if (flag && flag != null) {
+ try {
+ var v_server = opt.get(flag + "address");
+ const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
+ if (ipv6Regex.test(v_server.value)) {
+ _address = "[" + v_server.value + "]"
+ } else {
+ _address = v_server.value
+ }
+ } catch(e) {
}
- } catch(e) {
}
var url = null;
if (v_type === "SS") {
- var v_port = opt.get("port");
- var v_method = opt.get("ss_encrypt_method");
- var v_password = opt.get("password");
+ var v_port = opt.get("ss_port");
+ var v_method = opt.get("ss_method");
+ var v_password = opt.get("ss_password");
url = b64encsafe(v_method.value + ":" + v_password.value) + "@" +
_address + ":" +
@@ -164,21 +187,21 @@ local has_xray = api.is_finded("xray")
url += params;
} else if (v_type === "SS-Rust") {
v_type = "SS"
- var v_port = opt.get("port");
- var v_method = opt.get("ss_rust_encrypt_method");
- var v_password = opt.get("password");
+ var v_port = opt.get("ss_rust_port");
+ var v_method = opt.get("ss_rust_method");
+ var v_password = opt.get("ss_rust_password");
url = btoa(v_method.value + ":" + v_password.value) + "@" +
_address + ":" +
v_port.value + "/?";
var params = "";
- var v_plugin = opt.get("ss_plugin").value;
+ var v_plugin = opt.get("ss_rust_plugin").value;
if (v_plugin && v_plugin != "none") {
if (v_plugin == "simple-obfs" || v_plugin == "obfs-local") {
v_plugin = "obfs-local";
}
- var v_plugin_opts = opt.get("ss_plugin_opts").value;
+ var v_plugin_opts = opt.get("ss_rust_plugin_opts").value;
if (v_plugin_opts && v_plugin_opts != "") {
v_plugin += ";" + v_plugin_opts;
}
@@ -191,13 +214,13 @@ local has_xray = api.is_finded("xray")
}
url += params;
} else if (v_type === "SSR") {
- var v_port = opt.get("port");
+ var v_port = opt.get("ssr_port");
var v_protocol = opt.get("ssr_protocol");
- var v_method = opt.get("ssr_encrypt_method");
- var v_obfs = opt.get("obfs");
- var v_password = opt.get("password");
- var v_obfs_param = opt.get("obfs_param");
- var v_protocol_param = opt.get("protocol_param");
+ var v_method = opt.get("ssr_method");
+ var v_obfs = opt.get("ssr_obfs");
+ var v_password = opt.get("ssr_password");
+ var v_obfs_param = opt.get("ssr_obfs_param");
+ var v_protocol_param = opt.get("ssr_protocol_param");
var ssr_str = _address + ":" +
v_port.value + ":" +
v_protocol.value + ":" +
@@ -208,20 +231,20 @@ local has_xray = api.is_finded("xray")
"&protoparam=" + b64encsafe(v_protocol_param.value) +
"&remarks=" + b64encutf8safe(v_alias.value);
url = b64encsafe(ssr_str);
- } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "vmess") {
+ } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("xray_protocol").value === "vmess") {
v_type = "vmess";
var info = {};
info.v = "2";
info.ps = v_alias.value;
- info.add = opt.get("address").value;
+ info.add = opt.get("xray_address").value;
//info.add = _address;
- info.port = opt.get("port").value;
- info.id = opt.get("uuid").value;
+ info.port = opt.get("xray_port").value;
+ info.id = opt.get("xray_uuid").value;
var v_transport = opt.get("transport").value;
if (v_transport === "ws") {
info.host = opt.get("ws_host").value;
- info.path = opt.get("ws_path").value;
+ info.path = opt.get("xray_ws_path").value;
} else if (v_transport === "h2") {
info.host = opt.get("h2_host").value;
info.path = opt.get("h2_path").value;
@@ -247,16 +270,16 @@ local has_xray = api.is_finded("xray")
info.net = v_transport;
info.security = opt.get("security").value || "auto";
- if (opt.get("tls").checked) {
+ if (opt.get("xray_tls").checked) {
var v_security = "tls";
info.tls = "tls";
info.sni = opt.get("tls_serverName").value;
}
url = b64EncodeUnicode(JSON.stringify(info));
- } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "vless") {
+ } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("xray_protocol").value === "vless") {
v_type = "vless";
- var v_password = opt.get("uuid");
- var v_port = opt.get("port");
+ var v_password = opt.get("xray_uuid");
+ var v_port = opt.get("xray_port");
url = encodeURIComponent(v_password.value) +
"@" + _address +
":" + v_port.value + "?";
@@ -265,7 +288,7 @@ local has_xray = api.is_finded("xray")
var v_transport = opt.get("transport").value;
if (v_transport === "ws") {
params += opt.query("host", "ws_host");
- params += opt.query("path", "ws_path");
+ params += opt.query("path", "xray_ws_path");
} else if (v_transport === "h2") {
v_transport = "http";
params += opt.query("host", "h2_host");
@@ -282,7 +305,6 @@ local has_xray = api.is_finded("xray")
params += opt.query("key", "quic_key");
params += opt.query("quicSecurity", "quic_security");
} else if (v_transport === "grpc") {
- //不知道是用path还是serviceName,这里先这样吧
params += opt.query("path", "grpc_serviceName");
params += opt.query("serviceName", "grpc_serviceName");
params += opt.query("mode", "grpc_mode");
@@ -291,7 +313,7 @@ local has_xray = api.is_finded("xray")
params += opt.query("encryption", "encryption");
- if (opt.get("tls").checked) {
+ if (opt.get("xray_tls").checked) {
var v_security = "tls";
if (opt.get("xray_fingerprint") && opt.get("xray_fingerprint").value != "") {
let v_fp = opt.get("xray_fingerprint").value;
@@ -307,8 +329,8 @@ local has_xray = api.is_finded("xray")
params += opt.query("sid", "reality_shortId");
params += opt.query("spx", "reality_spiderX");
}
- if (opt.get("tlsflow") && opt.get("tlsflow").value) {
- let v_flow = opt.get("tlsflow").value;
+ if (opt.get("xray_tlsflow") && opt.get("xray_tlsflow").value) {
+ let v_flow = opt.get("xray_tlsflow").value;
params += "&flow=" + v_flow;
}
params += "&security=" + v_security;
@@ -320,51 +342,76 @@ local has_xray = api.is_finded("xray")
params = params.substring(1);
}
url += params;
- } else if (((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "trojan") || v_type === "Trojan" || v_type === "Trojan-Plus" || v_type === "Trojan-Go") {
- if (((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "trojan") || v_type === "Trojan-Plus") {
- v_type = "trojan";
- }
- var v_password = opt.get(!opt.client && v_type === "Trojan-Go" ? "passwords" : "password");
- var v_port = opt.get("port");
+ } else if (v_type === "Trojan-Plus") {
+ v_type = "trojan";
+ var v_password = opt.get("trojan_plus_password");
+ var v_port = opt.get("trojan_plus_port");
url = encodeURIComponent(v_password.value) +
"@" + _address +
":" + v_port.value + "/?";
var params = "";
- if (opt.get("tls").checked) {
- params += opt.query("sni", "tls_serverName");
- if (v_type !== "Trojan-Go") {
- params += "&tls=1"
- params += opt.query("allowinsecure", "tls_allowInsecure");
- }
+ if (opt.get("trojan_plus_tls").checked) {
+ params += opt.query("sni", "trojan_plus_tls_serverName");
+ params += "&tls=1"
+ params += opt.query("allowinsecure", "trojan_plus_tls_allowInsecure");
}
- if (v_type === "Trojan-Go") {
- if (!opt.get("tls").checked && opt.get("trojan_transport").value === "original") {
+ params += "#" + encodeURI(v_alias.value);
+ if (params[0] == "&") {
+ params = params.substring(1);
+ }
+ url += params;
+ } else if (v_type === "Trojan-Go") {
+ var v_password = opt.get("trojan_go_password");
+ var v_port = opt.get("trojan_go_port");
+ url = encodeURIComponent(v_password.value) +
+ "@" + _address +
+ ":" + v_port.value + "/?";
+ var params = "";
+ if (opt.get("trojan_go_tls").checked) {
+ params += opt.query("sni", "trojan_go_tls_serverName");
+ } else {
+ if (opt.get("trojan_go_transport").value === "original") {
var plugin = {};
- plugin.type = opt.get("plugin_type").value;
+ plugin.type = opt.get("trojan_go_plugin_type").value;
if (plugin.type !== "plaintext") {
- plugin.command = opt.get("plugin_cmd").value;
- plugin.option = opt.get("plugin_option").value;
- plugin.arg = opt.getlist("plugin_arg");
+ plugin.command = opt.get("trojan_go_plugin_cmd").value;
+ plugin.option = opt.get("trojan_go_plugin_option").value;
+ plugin.arg = opt.getlist("trojan_go_plugin_arg");
}
params += "&plugin=" + encodeURIComponent(JSON.stringify(plugin));
}
- params += opt.query("type", "trojan_transport");
- var ws = (opt.get("trojan_transport").value.indexOf("ws") !== -1);
- var h2 = (opt.get("trojan_transport").value.indexOf("h2") !== -1);
- if (ws) {
- params += opt.query("host", "ws_host");
- params += opt.query("path", "ws_path");
- } else if (h2) {
- params += opt.query("host", "h2_host");
- params += opt.query("path", "h2_path");
- }
- var enc = "none";
- if (opt.get("ss_aead").checked === true) {
- enc = "ss;" +
- opt.get("ss_aead_method").value +
- ":" + opt.get("ss_aead_pwd").value;
- }
- params += "&encryption=" + encodeURIComponent(enc);
+ }
+ params += opt.query("type", "trojan_go_transport");
+ var ws = (opt.get("trojan_go_transport").value.indexOf("ws") !== -1);
+ var h2 = (opt.get("trojan_go_transport").value.indexOf("h2") !== -1);
+ if (ws) {
+ params += opt.query("host", "trojan_go_ws_host");
+ params += opt.query("path", "trojan_go_ws_path");
+ }
+ var enc = "none";
+ if (opt.get("trojan_go_ss_aead").checked === true) {
+ enc = "ss;" +
+ opt.get("trojan_go_ss_aead_method").value +
+ ":" + opt.get("trojan_go_ss_aead_pwd").value;
+ }
+ params += "&encryption=" + encodeURIComponent(enc);
+ params += "#" + encodeURI(v_alias.value);
+ if (params[0] == "&") {
+ params = params.substring(1);
+ }
+ url += params;
+ } else if (((v_type === "V2ray" || v_type === "Xray") && opt.get("xray_protocol").value === "trojan")) {
+ v_type = "trojan";
+ var v_password = opt.get("xray_password");
+ var v_port = opt.get("xray_port");
+ url = encodeURIComponent(v_password.value) +
+ "@" + _address +
+ ":" + v_port.value + "/?";
+ var params = "";
+ if (opt.get("xray_tls").checked) {
+ params += opt.query("sni", "tls_serverName");
+ params += "&tls=1"
+ params += opt.query("allowinsecure", "tls_allowInsecure");
}
params += "#" + encodeURI(v_alias.value);
if (params[0] == "&") {
@@ -375,12 +422,12 @@ local has_xray = api.is_finded("xray")
var url = "";
var params = "?";
var v_protocol = opt.get("brook_protocol");
- var v_port = opt.get("port");
- var v_password = opt.get("password");
+ var v_port = opt.get("brook_port");
+ var v_password = opt.get("brook_password");
var b_protocol_value = v_protocol.value.split('client').join('server');
var url_protocol = b_protocol_value;
- params += opt.query("password", "password");
+ params += opt.query("password", "brook_password");
if (b_protocol_value == "wsserver") {
var server = '';
var prefix = "ws://";
@@ -388,7 +435,7 @@ local has_xray = api.is_finded("xray")
prefix = "wss://";
url_protocol = 'wssserver';
}
- var v_path = opt.get("ws_path");
+ var v_path = opt.get("brook_ws_path");
var v_path_value = v_path.value || '/ws';
if (v_path_value.length > 1 && v_path_value.indexOf('/') < 0) {
v_path_value = '/' + v_path_value;
@@ -400,12 +447,12 @@ local has_xray = api.is_finded("xray")
url += url_protocol;
url += params;
} else if (v_type === "Hysteria") {
- var v_port = opt.get("port");
+ var v_port = opt.get("hysteria_port");
var params = "";
params += opt.query("protocol", "hysteria_protocol");
params += opt.query("auth", "hysteria_auth_password");
- params += opt.query("peer", "tls_serverName");
- params += opt.query("insecure", "tls_allowInsecure");
+ params += opt.query("peer", "hysteria_tls_serverName");
+ params += opt.query("insecure", "hysteria_tls_allowInsecure");
params += opt.query("upmbps", "hysteria_up_mbps", 1000);
params += opt.query("downmbps", "hysteria_down_mbps", 1000);
params += opt.query("alpn", "hysteria_alpn");
@@ -536,7 +583,11 @@ local has_xray = api.is_finded("xray")
var ssu = ssrurl.split('://');
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
+
+ var flag = null
+
if (ssu[0] === "ssr") {
+ flag = "ssr_"
//var b64c = ssu[1].match(/([A-Za-z0-9_-]+)/);
var sstr = b64decsafe(ssu[1]);
var ploc = sstr.indexOf("/?");
@@ -561,18 +612,20 @@ local has_xray = api.is_finded("xray")
}
}
opt.set('type', "SSR");
- opt.set('address', ssm[1]);
- opt.set('port', ssm[2]);
+ opt.set('ssr_address', ssm[1]);
+ opt.set('ssr_port', ssm[2]);
opt.set('ssr_protocol', ssm[3]);
- opt.set('ssr_encrypt_method', ssm[4]);
- opt.set('obfs', ssm[5]);
- opt.set('password', b64decsafe(ssm[6]));
- opt.set('obfs_param', dictvalue(pdict, 'obfsparam'));
- opt.set('protocol_param', dictvalue(pdict, 'protoparam'));
+ opt.set('ssr_method', ssm[4]);
+ opt.set('ssr_obfs', ssm[5]);
+ opt.set('ssr_password', b64decsafe(ssm[6]));
+ opt.set('ssr_obfs_param', dictvalue(pdict, 'obfsparam'));
+ opt.set('ssr_protocol_param', dictvalue(pdict, 'protoparam'));
var rem = pdict['remarks'];
if (typeof(rem) !== 'undefined' && rem !== '' && rem.length > 0)
opt.set('remarks', b64decutf8safe(rem));
- } else if (ssu[0] === "ss") {
+ }
+ if (ssu[0] === "ss") {
+ flag = "ss_"
var url0 = "", param = "";
var sipIndex = ssu[1].indexOf("@");
var ploc = ssu[1].indexOf("#");
@@ -604,17 +657,24 @@ local has_xray = api.is_finded("xray")
}
if (["2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"].includes(method)) {
opt.set('type', "SS-Rust");
- opt.set('ss_rust_encrypt_method', method || "");
+ opt.set('ss_rust_address', server);
+ opt.set('ss_rust_port', port);
+ opt.set('ss_rust_password', password || "");
+ opt.set('ss_rust_method', method || "");
+ opt.set('ss_rust_plugin', plugin || "none");
+ if (plugin && plugin != "none") {
+ opt.set('ss_rust_plugin_opts', pluginOpts || "");
+ }
} else {
opt.set('type', "SS");
- opt.set('ss_encrypt_method', method || "");
- }
- opt.set('address', server);
- opt.set('port', port);
- opt.set('password', password || "");
- opt.set('ss_plugin', plugin || "none");
- if (plugin && plugin != "none") {
- opt.set('ss_plugin_opts', pluginOpts || "");
+ opt.set('ss_address', server);
+ opt.set('ss_port', port);
+ opt.set('ss_password', password || "");
+ opt.set('ss_method', method || "");
+ opt.set('ss_plugin', plugin || "none");
+ if (plugin && plugin != "none") {
+ opt.set('ss_plugin_opts', pluginOpts || "");
+ }
}
if (param !== undefined) {
opt.set('remarks', decodeURI(param));
@@ -625,17 +685,19 @@ local has_xray = api.is_finded("xray")
opt.set('type', "SS");
var part1 = team[0].split(':');
var part2 = team[1].split(':');
- opt.set('address', part2[0]);
- opt.set('port', part2[1]);
- opt.set('password', part1[1]);
- opt.set('ss_encrypt_method', part1[0]);
+ opt.set('ss_address', part2[0]);
+ opt.set('ss_port', part2[1]);
+ opt.set('ss_password', part1[1]);
+ opt.set('ss_method', part1[0]);
opt.set('ss_plugin', "none");
//opt.set('ss_plugin_opts', "");
if (param !== undefined) {
opt.set('remarks', decodeURI(param));
}
}
- } else if (ssu[0] === "trojan" || ssu[0] === "trojan-plus") {
+ }
+ if (ssu[0] === "trojan" || ssu[0] === "trojan-plus") {
+ flag = "trojan_plus_"
var stype = "Trojan-Plus";
var m = parseNodeUrl(ssrurl);
var password = m.passwd;
@@ -655,58 +717,20 @@ local has_xray = api.is_finded("xray")
}
}
if (queryParam.mux || queryParam.ws || queryParam.h2 || queryParam.ss || queryParam.plugin) {
- stype = "Trojan-Go";
+ ssu[0] = "trojan-go"
}
opt.set('type', stype);
- opt.set('address', m.hostname);
- opt.set('port', m.port || "443");
- opt.set(!opt.client && stype === "Trojan-Go" ? 'passwords' : 'password', decodeURIComponent(password));
- var tls = true;
- if (stype === "Trojan-Go") {
- tls = queryParam.plugin === undefined;
- }
- if (tls === false) { alert("TODO: plugin params for trojan-go."); }
- opt.set('tls', tls);
- if (tls) {
- opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
- opt.set('tls_allowInsecure', queryParam.allowinsecure === '1');
- }
-
- if (stype === "Trojan-Go") {
- var tran = 'original';
- var ws = null;
- var h2 = null;
- if (queryParam.type) {
- ws = queryParam.type.indexOf('ws') !== -1;
- h2 = queryParam.type.indexOf('h2') !== -1;
- }
- if (ws && h2) {
- tran = 'h2+ws'
- } else {
- if (ws) tran = 'ws';
- if (h2) tran = 'h2';
- }
- opt.set('trojan_transport', 'tran');
- if (ws) {
- opt.set('ws_host', queryParam.wshost || '');
- opt.set('ws_path', queryParam.wspath || '/');
- }
- if (h2) {
- opt.set('h2_host', queryParam.h2host || '');
- opt.set('h2_path', queryParam.h2path || '/');
- }
- var ss = queryParam.ss === '1';
- opt.set('ss_aead', ss);
- if (ss) {
- opt.set('ss_aead_method', queryParam.ssmethod.toLowerCase() || '');
- opt.set('ss_aead_pwd', queryParam.sspasswd || '');
- }
- }
- opt.set('mux', queryParam.mux === '1');
+ opt.set('trojan_plus_address', m.hostname);
+ opt.set('trojan_plus_port', m.port || "443");
+ opt.set('trojan_plus_password', decodeURIComponent(password));
+ opt.set('trojan_plus_tls', tls);
+ opt.set('trojan_plus_tls_serverName', queryParam.peer || queryParam.sni || '');
+ opt.set('trojan_plus_tls_allowInsecure', queryParam.allowinsecure === '1');
if (m.hash) {
opt.set('remarks', decodeURI(m.hash.substr(1)));
}
- } else if (ssu[0] === "trojan-go") {
+ } if (ssu[0] === "trojan-go") {
+ flag = "trojan_go_"
var m = parseNodeUrl(ssrurl);
var password = m.passwd;
if (password === "") {
@@ -724,22 +748,22 @@ local has_xray = api.is_finded("xray")
}
}
opt.set('type', 'Trojan-Go');
- opt.set('address', m.hostname);
- opt.set('port', m.port || "443");
- opt.set(opt.client ? 'password' : 'passwords', decodeURIComponent(password));
- opt.set('tls', '1');
- opt.set('tls_allowInsecure', '0');
- opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
+ opt.set('trojan_go_address', m.hostname);
+ opt.set('trojan_go_port', m.port || "443");
+ opt.set('trojan_go_password', decodeURIComponent(password));
+ opt.set('trojan_go_tls', '1');
+ opt.set('trojan_go_tls_allowInsecure', '0');
+ opt.set('trojan_go_tls_serverName', queryParam.peer || queryParam.sni || '');
var plugin = queryParam.plugin !== undefined;
if (plugin) {
- opt.set('trojan_transport', 'original');
+ opt.set('trojan_go_transport', 'original');
var plugin = JSON.parse(queryParam.plugin);
if (plugin) {
- opt.set('plugin_type', plugin.type);
+ opt.set('trojan_go_plugin_type', plugin.type);
if (plugin.type !== "plaintext") {
- opt.set('plugin_cmd', plugin.command);
- opt.set('plugin_option', plugin.option);
- opt.setlist('plugin_arg', plugin.arg);
+ opt.set('trojan_go_plugin_cmd', plugin.command);
+ opt.set('trojan_go_plugin_option', plugin.option);
+ opt.setlist('trojan_go_plugin_arg', plugin.arg);
}
} else
alert(queryParam.plugin);
@@ -758,14 +782,10 @@ local has_xray = api.is_finded("xray")
if (ws) tran = 'ws';
if (h2) tran = 'h2';
}
- opt.set('trojan_transport', tran);
+ opt.set('trojan_go_transport', tran);
if (ws) {
- opt.set('ws_host', queryParam.host || '');
- opt.set('ws_path', queryParam.path || '/');
- }
- if (h2){
- opt.set('h2_host', queryParam.host || '');
- opt.set('h2_path', queryParam.path || '/');
+ opt.set('trojan_go_ws_host', queryParam.host || '');
+ opt.set('trojan_go_ws_path', queryParam.path || '/');
}
var enc = {};
var ss = false;
@@ -774,16 +794,18 @@ local has_xray = api.is_finded("xray")
enc = {type: r[1], method: r[2], password: r[3]};
}
ss = enc.type === 'ss';
- opt.set('ss_aead', ss);
+ opt.set('trojan_go_ss_aead', ss);
if (ss) {
- opt.set('ss_aead_method', enc.method.toLowerCase() || '');
- opt.set('ss_aead_pwd', enc.password || '');
+ opt.set('trojan_go_ss_aead_method', enc.method.toLowerCase() || '');
+ opt.set('trojan_go_ss_aead_pwd', enc.password || '');
}
- opt.set('mux', '1');
+ opt.set('trojan_go_smux', '1');
if (m.hash) {
opt.set('remarks', decodeURI(m.hash.substr(1)));
}
- } else if (ssu[0] === "vmess") {
+ }
+ if (ssu[0] === "vmess") {
+ flag = "xray_"
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
<% if has_v2ray then %>
@@ -791,7 +813,7 @@ local has_xray = api.is_finded("xray")
<% elseif has_xray then %>
opt.set('type', "Xray");
<% end %>
- opt.set('protocol', "vmess");
+ opt.set('xray_protocol', "vmess");
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
@@ -799,10 +821,10 @@ local has_xray = api.is_finded("xray")
}
var ssm = JSON.parse(sstr);
opt.set('remarks', ssm.ps);
- opt.set('address', ssm.add);
- opt.set('port', ssm.port);
- opt.set('uuid', ssm.id);
- opt.set('tls', ssm.tls === "tls");
+ opt.set('xray_address', ssm.add);
+ opt.set('xray_port', ssm.port);
+ opt.set('xray_uuid', ssm.id);
+ opt.set('xray_tls', ssm.tls === "tls");
if (ssm.tls === "tls") {
var tls_serverName = ssm.host;
if (ssm.sni) {
@@ -822,7 +844,7 @@ local has_xray = api.is_finded("xray")
}
} else if (ssm.net === "ws") {
opt.set('ws_host', ssm.host);
- opt.set('ws_path', ssm.path);
+ opt.set('xray_ws_path', ssm.path);
} else if (ssm.net === "h2") {
opt.set('h2_host', ssm.host);
opt.set('h2_path', ssm.path);
@@ -834,22 +856,24 @@ local has_xray = api.is_finded("xray")
} else if (ssm.net === "grpc") {
opt.set('grpc_serviceName', ssm.path);
}
- } else if (ssu[0] === "vless") {
+ }
+ if (ssu[0] === "vless") {
+ flag = "xray_"
<% if has_xray then %>
opt.set('type', "Xray");
<% elseif has_v2ray then %>
opt.set('type', "V2ray");
<% end %>
- opt.set('protocol', "vless");
+ opt.set('xray_protocol', "vless");
var m = parseNodeUrl(ssrurl);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<%:Invalid Share URL Format%>";
return false;
}
- opt.set('uuid', password);
- opt.set('address', m.hostname);
- opt.set('port', m.port || "443");
+ opt.set('xray_uuid', password);
+ opt.set('xray_address', m.hostname);
+ opt.set('xray_port', m.port || "443");
var queryParam = {};
if (m.search.length > 1) {
var query = m.search.split('?');
@@ -865,9 +889,9 @@ local has_xray = api.is_finded("xray")
opt.set('encryption', queryParam.encryption);
if (queryParam.security) {
if (queryParam.security == "tls") {
- opt.set('tls', true);
+ opt.set('xray_tls', true);
opt.set('reality', false)
- opt.set('tlsflow', queryParam.flow || '');
+ opt.set('xray_tlsflow', queryParam.flow || '');
opt.set('tls_serverName', queryParam.sni || '');
opt.set('tls_allowInsecure', true);
if (queryParam.allowinsecure === '0') {
@@ -879,9 +903,9 @@ local has_xray = api.is_finded("xray")
}
if (queryParam.security == "reality") {
- opt.set('tls', true);
+ opt.set('xray_tls', true);
opt.set('reality', true)
- opt.set('tlsflow', queryParam.flow || '');
+ opt.set('xray_tlsflow', queryParam.flow || '');
opt.set('tls_serverName', queryParam.sni || '');
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set('reality_fingerprint', queryParam.fp);
@@ -907,7 +931,7 @@ local has_xray = api.is_finded("xray")
}
} else if (queryParam.type === "ws") {
opt.set('ws_host', queryParam.host || "");
- opt.set('ws_path', queryParam.path || "");
+ opt.set('xray_ws_path', queryParam.path || "");
} else if (queryParam.type === "h2" || queryParam.type === "http") {
opt.set('h2_host', queryParam.host || "");
opt.set('h2_path', queryParam.path || "");
@@ -925,7 +949,9 @@ local has_xray = api.is_finded("xray")
if (m.hash) {
opt.set('remarks', decodeURI(m.hash.substr(1)));
}
- } else if (ssu[0] === "brook") {
+ }
+ if (ssu[0] === "brook") {
+ flag = "brook_"
var stype = "Brook";
var m = parseNodeUrl(ssrurl);
@@ -952,7 +978,7 @@ local has_xray = api.is_finded("xray")
opt.set('type', stype);
opt.set('brook_protocol', protocol);
- opt.set('password', password);
+ opt.set('brook_password', password);
if (protocol == 'wsclient' || protocol == 'wssclient') {
opt.set('brook_protocol', 'wsclient');
@@ -960,9 +986,9 @@ local has_xray = api.is_finded("xray")
wsserver = wsserver[1].split('/');
var path = wsserver[1] && '/' + wsserver[1] || '/ws';
var server = wsserver[0].split(':');
- opt.set('address', server[0]);
- opt.set('port', server[1]);
- opt.set('ws_path', path);
+ opt.set('brook_address', server[0]);
+ opt.set('brook_port', server[1]);
+ opt.set('brook_ws_path', path);
if (protocol == 'wssclient') {
opt.set('brook_tls', true);
}
@@ -972,14 +998,16 @@ local has_xray = api.is_finded("xray")
s.innerHTML = "<%:Invalid Share URL Format%>";
return false;
}
- opt.set('address', server[0]);
- opt.set('port', server[1]);
+ opt.set('brook_address', server[0]);
+ opt.set('brook_port', server[1]);
}
if (m.hash) {
opt.set('remarks', decodeURI(m.hash.substr(1)));
}
- } else if (ssu[0] === "hysteria") {
+ }
+ if (ssu[0] === "hysteria") {
+ flag = "hysteria_"
var stype = "Hysteria";
var m = parseNodeUrl(ssrurl);
var queryParam = {};
@@ -993,16 +1021,16 @@ local has_xray = api.is_finded("xray")
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
- opt.set('address', m.hostname);
- opt.set('port', m.port || "443");
opt.set('type', stype);
+ opt.set('hysteria_address', m.hostname);
+ opt.set('hysteria_port', m.port || "443");
opt.set('hysteria_protocol', queryParam.protocol);
opt.set('hysteria_obfs', queryParam.obfsParam);
opt.set('hysteria_auth_type', "string");
opt.set('hysteria_auth_password', queryParam.auth);
- opt.set('tls_serverName', queryParam.peer);
+ opt.set('hysteria_tls_serverName', queryParam.peer);
if (queryParam.insecure && queryParam.insecure == "1") {
- opt.set('tls_allowInsecure', true);
+ opt.set('hysteria_tls_allowInsecure', true);
}
opt.set('hysteria_alpn', queryParam.alpn);
opt.set('hysteria_up_mbps', queryParam.upmbps);
@@ -1010,14 +1038,16 @@ local has_xray = api.is_finded("xray")
if (m.hash) {
opt.set('remarks', decodeURI(m.hash.substr(1)));
}
+ }
+ if (flag && flag != null) {
+ if (opt.get(flag + 'port').value) {
+ opt.get(flag + 'port').focus();
+ opt.get(flag + 'port').blur();
+ }
} else {
s.innerHTML = "<%:Invalid Share URL Format%>: " + ssu[0];
return false;
}
- if (opt.get('port').value) {
- opt.get('port').focus();
- opt.get('port').blur();
- }
s.innerHTML = "<%:Import Finished %>";
return false;
}