luci-app-passwall: bump to 4-1
This commit is contained in:
parent
c5811f6512
commit
98750d0a13
@ -6,9 +6,9 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=3.9
|
||||
PKG_RELEASE:=72
|
||||
PKG_DATE:=20201012
|
||||
PKG_VERSION:=4
|
||||
PKG_RELEASE:=1
|
||||
PKG_DATE:=20201204
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
|
||||
@ -33,9 +33,13 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server
|
||||
bool "Include ShadowsocksR Server"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Xray
|
||||
bool "Include Xray"
|
||||
default y if i386||x86_64||arm||aarch64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray
|
||||
bool "Include V2ray"
|
||||
default y if i386||x86_64||arm||aarch64
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus
|
||||
bool "Include Trojan_Plus"
|
||||
@ -48,7 +52,7 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Brook
|
||||
bool "Include Brook"
|
||||
default n
|
||||
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
|
||||
bool "Include NaiveProxy"
|
||||
default n
|
||||
@ -60,19 +64,19 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_kcptun
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_haproxy
|
||||
bool "Include haproxy"
|
||||
default y
|
||||
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG
|
||||
bool "Include ChinaDNS-NG"
|
||||
default n
|
||||
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_https_dns_proxy
|
||||
bool "Include Https DNS Proxy(DoH)"
|
||||
default y
|
||||
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_dns2socks
|
||||
bool "Include dns2socks"
|
||||
default n
|
||||
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_v2ray-plugin
|
||||
bool "Include v2ray-plugin (Shadowsocks plugin)"
|
||||
default y if i386||x86_64||arm||aarch64
|
||||
@ -101,6 +105,7 @@ define Package/$(PKG_NAME)
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR:shadowsocksr-libev-alt \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR:shadowsocksr-libev-ssr-local \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server:shadowsocksr-libev-server \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Xray:xray \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray:v2ray \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus:trojan-plus \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO:trojan-go \
|
||||
@ -137,21 +142,20 @@ define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./root/etc/config/passwall $(1)/etc/config/passwall
|
||||
$(INSTALL_CONF) ./root/etc/config/passwall_server $(1)/etc/config/passwall_server
|
||||
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./root/etc/init.d/passwall $(1)/etc/init.d/passwall
|
||||
$(INSTALL_BIN) ./root/etc/init.d/passwall_server $(1)/etc/init.d/passwall_server
|
||||
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_CONF) ./root/etc/uci-defaults/* $(1)/etc/uci-defaults
|
||||
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/passwall
|
||||
cp -pR ./root/usr/share/passwall/* $(1)/usr/share/passwall
|
||||
$(INSTALL_CONF) ./root/etc/config/passwall $(1)/usr/share/passwall/config.default
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
|
||||
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci/
|
||||
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
|
||||
po2lmo ./po/zh-cn/passwall.po $(1)/usr/lib/lua/luci/i18n/passwall.zh-cn.lmo
|
||||
endef
|
||||
|
||||
@ -4,8 +4,10 @@ local appname = "passwall"
|
||||
local ucic = luci.model.uci.cursor()
|
||||
local http = require "luci.http"
|
||||
local util = require "luci.util"
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local kcptun = require "luci.model.cbi.passwall.api.kcptun"
|
||||
local brook = require "luci.model.cbi.passwall.api.brook"
|
||||
local xray = require "luci.model.cbi.passwall.api.xray"
|
||||
local v2ray = require "luci.model.cbi.passwall.api.v2ray"
|
||||
local trojan_go = require "luci.model.cbi.passwall.api.trojan_go"
|
||||
|
||||
@ -65,6 +67,8 @@ function index()
|
||||
entry({"admin", "services", appname, "kcptun_update"}, call("kcptun_update")).leaf = true
|
||||
entry({"admin", "services", appname, "brook_check"}, call("brook_check")).leaf = true
|
||||
entry({"admin", "services", appname, "brook_update"}, call("brook_update")).leaf = true
|
||||
entry({"admin", "services", appname, "xray_check"}, call("xray_check")).leaf = true
|
||||
entry({"admin", "services", appname, "xray_update"}, call("xray_update")).leaf = true
|
||||
entry({"admin", "services", appname, "v2ray_check"}, call("v2ray_check")).leaf = true
|
||||
entry({"admin", "services", appname, "v2ray_update"}, call("v2ray_update")).leaf = true
|
||||
entry({"admin", "services", appname, "trojan_go_check"}, call("trojan_go_check")).leaf = true
|
||||
@ -239,10 +243,22 @@ function set_node()
|
||||
end
|
||||
|
||||
function copy_node()
|
||||
local e = {}
|
||||
local section = luci.http.formvalue("section")
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
local uuid = api.gen_uuid()
|
||||
ucic:section(appname, "nodes", uuid)
|
||||
for k, v in pairs(ucic:get_all(appname, section)) do
|
||||
local filter = k:find("%.")
|
||||
if filter and filter == 1 then
|
||||
else
|
||||
xpcall(function()
|
||||
ucic:set(appname, uuid, k, v)
|
||||
end,
|
||||
function(e)
|
||||
end)
|
||||
end
|
||||
end
|
||||
ucic:commit(appname)
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", appname, "node_config", uuid))
|
||||
end
|
||||
|
||||
function clear_all_nodes()
|
||||
@ -285,11 +301,11 @@ function check_port()
|
||||
ucic:foreach(appname, "nodes", function(s)
|
||||
local ret = ""
|
||||
local tcp_socket
|
||||
if (s.use_kcp and s.use_kcp == "1" and s.kcp_port) or
|
||||
(s.v2ray_transport and s.v2ray_transport == "mkcp" and s.port) then
|
||||
if (s.use_kcp and s.use_kcp == "1" and s.kcp_port) or (s.transport and s.transport == "mkcp" and s.port) then
|
||||
else
|
||||
local type = s.type
|
||||
if type and type ~= "V2ray_balancing" and type ~= "V2ray_shunt" and
|
||||
local protocol = s.protocol
|
||||
if type and (protocol and protocol ~= "_balancing" and protocol ~= "_shunt") and
|
||||
s.address and s.port and s.remarks then
|
||||
node_name = "%s:[%s] %s:%s" % {s.type, s.remarks, s.address, s.port}
|
||||
tcp_socket = nixio.socket("inet", "stream")
|
||||
@ -366,6 +382,25 @@ function brook_update()
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function xray_check()
|
||||
local json = xray.to_check("")
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function xray_update()
|
||||
local json = nil
|
||||
local task = http.formvalue("task")
|
||||
if task == "extract" then
|
||||
json = xray.to_extract(http.formvalue("file"), http.formvalue("subfix"))
|
||||
elseif task == "move" then
|
||||
json = xray.to_move(http.formvalue("file"))
|
||||
else
|
||||
json = xray.to_download(http.formvalue("url"))
|
||||
end
|
||||
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function v2ray_check()
|
||||
local json = v2ray.to_check("")
|
||||
http_write_json(json)
|
||||
|
||||
@ -19,7 +19,7 @@ function get_valid_nodes()
|
||||
local nodes = {}
|
||||
uci:foreach(appname, "nodes", function(e)
|
||||
if e.type and e.remarks then
|
||||
if e.type == "V2ray" and (e.protocol == "_balancing" or e.protocol == "_shunt") then
|
||||
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt") then
|
||||
e.remarks_name = "%s:[%s] " % {i18n.translatef(e.type .. e.protocol), e.remarks}
|
||||
e.node_type = "special"
|
||||
nodes[#nodes + 1] = e
|
||||
@ -79,19 +79,55 @@ function get_customed_path(e)
|
||||
end
|
||||
|
||||
function is_finded(e)
|
||||
return luci.sys.exec('type -t -p "%s/%s" -p "/usr/bin/v2ray/%s" "%s"' % {get_customed_path(e), e, e, e}) ~= "" and true or false
|
||||
return luci.sys.exec('type -t -p "%s/%s" "%s"' % {get_customed_path(e), e, e}) ~= "" and true or false
|
||||
end
|
||||
|
||||
function get_xray_path()
|
||||
local path = uci_get_type("global_app", "xray_file")
|
||||
return path
|
||||
end
|
||||
|
||||
function get_xray_version(file)
|
||||
if file == nil then file = get_xray_path() end
|
||||
chmod_755(file)
|
||||
if fs.access(file) then
|
||||
if file == get_xray_path() then
|
||||
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
|
||||
if fs.access("/tmp/psw_" .. md5) then
|
||||
return sys.exec("cat /tmp/psw_" .. md5)
|
||||
else
|
||||
local version = sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5)
|
||||
return version
|
||||
end
|
||||
else
|
||||
return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function get_v2ray_path()
|
||||
local path = uci_get_type("global_app", "v2ray_file")
|
||||
return path .. "/v2ray"
|
||||
return path
|
||||
end
|
||||
|
||||
function get_v2ray_version(file)
|
||||
if file == nil then file = get_v2ray_path() end
|
||||
chmod_755(file)
|
||||
if fs.access(file) then
|
||||
return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
if file == get_v2ray_path() then
|
||||
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
|
||||
if fs.access("/tmp/psw_" .. md5) then
|
||||
return sys.exec("cat /tmp/psw_" .. md5)
|
||||
else
|
||||
local version = sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5)
|
||||
return version
|
||||
end
|
||||
else
|
||||
return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
@ -105,7 +141,18 @@ function get_trojan_go_version(file)
|
||||
if file == nil then file = get_trojan_go_path() end
|
||||
chmod_755(file)
|
||||
if fs.access(file) then
|
||||
return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
if file == get_trojan_go_path() then
|
||||
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
|
||||
if fs.access("/tmp/psw_" .. md5) then
|
||||
return sys.exec("cat /tmp/psw_" .. md5)
|
||||
else
|
||||
local version = sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5)
|
||||
return version
|
||||
end
|
||||
else
|
||||
return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file)
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
@ -119,7 +166,18 @@ function get_kcptun_version(file)
|
||||
if file == nil then file = get_kcptun_path() end
|
||||
chmod_755(file)
|
||||
if fs.access(file) then
|
||||
return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file)
|
||||
if file == get_kcptun_path() then
|
||||
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
|
||||
if fs.access("/tmp/psw_" .. md5) then
|
||||
return sys.exec("cat /tmp/psw_" .. md5)
|
||||
else
|
||||
local version = sys.exec("echo -n $(%s -v | awk '{print $3}')" % file)
|
||||
sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5)
|
||||
return version
|
||||
end
|
||||
else
|
||||
return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file)
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
@ -133,11 +191,38 @@ function get_brook_version(file)
|
||||
if file == nil then file = get_brook_path() end
|
||||
chmod_755(file)
|
||||
if fs.access(file) then
|
||||
return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file)
|
||||
if file == get_brook_path() then
|
||||
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
|
||||
if fs.access("/tmp/psw_" .. md5) then
|
||||
return sys.exec("cat /tmp/psw_" .. md5)
|
||||
else
|
||||
local version = sys.exec("echo -n $(%s -v | awk '{print $3}')" % file)
|
||||
sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5)
|
||||
return version
|
||||
end
|
||||
else
|
||||
return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file)
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function get_free_space(dir)
|
||||
if dir == nil then dir = "/" end
|
||||
if sys.call("df -k " .. dir .. " >/dev/null") == 0 then
|
||||
return tonumber(sys.exec("echo -n $(df -k " .. dir .. " | awk 'NR>1' | awk '{print $4}')"))
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function get_file_space(file)
|
||||
if file == nil then return 0 end
|
||||
if fs.access(file) then
|
||||
return tonumber(sys.exec("echo -n $(du -k " .. file .. " | awk '{print $1}')"))
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function _unpack(t, i)
|
||||
i = i or 1
|
||||
if t[i] ~= nil then return t[i], _unpack(t, i + 1) end
|
||||
|
||||
@ -8,6 +8,13 @@ local api = require "luci.model.cbi.passwall.api.api"
|
||||
local brook_api = "https://api.github.com/repos/txthinking/brook/releases/latest"
|
||||
|
||||
function to_check(arch)
|
||||
local app_path = api.get_brook_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Brook")
|
||||
}
|
||||
end
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
@ -15,8 +22,7 @@ function to_check(arch)
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate(
|
||||
"Can't determine ARCH, or ARCH not supported.")
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
@ -66,6 +72,13 @@ function to_check(arch)
|
||||
end
|
||||
|
||||
function to_download(url)
|
||||
local app_path = api.get_brook_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Brook")
|
||||
}
|
||||
end
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
@ -88,13 +101,20 @@ function to_download(url)
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local app_path = api.get_brook_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Brook")
|
||||
}
|
||||
end
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local version = api.get_brook_version(file)
|
||||
if version == "" then
|
||||
local new_version = api.get_brook_version(file)
|
||||
if new_version == "" then
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
return {
|
||||
code = 1,
|
||||
@ -102,30 +122,29 @@ function to_move(file)
|
||||
}
|
||||
end
|
||||
|
||||
local client_file = api.get_brook_path()
|
||||
local client_file_bak
|
||||
local app_path_bak
|
||||
|
||||
if fs.access(client_file) then
|
||||
client_file_bak = client_file .. ".bak"
|
||||
api.exec("/bin/mv", {"-f", client_file, client_file_bak})
|
||||
if fs.access(app_path) then
|
||||
app_path_bak = app_path .. ".bak"
|
||||
api.exec("/bin/mv", {"-f", app_path, app_path_bak})
|
||||
end
|
||||
|
||||
local result = api.exec("/bin/mv", {"-f", file, client_file}, nil, api.command_timeout) == 0
|
||||
local result = api.exec("/bin/mv", {"-f", file, app_path}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result or not fs.access(client_file) then
|
||||
if not result or not fs.access(app_path) then
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
if client_file_bak then
|
||||
api.exec("/bin/mv", {"-f", client_file_bak, client_file})
|
||||
if app_path_bak then
|
||||
api.exec("/bin/mv", {"-f", app_path_bak, app_path})
|
||||
end
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", client_file)
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
api.exec("/bin/chmod", {"755", client_file})
|
||||
api.exec("/bin/chmod", {"755", app_path})
|
||||
|
||||
if client_file_bak then api.exec("/bin/rm", {"-f", client_file_bak}) end
|
||||
if app_path_bak then api.exec("/bin/rm", {"-f", app_path_bak}) end
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
|
||||
|
||||
@ -56,10 +56,14 @@ function gen_outbound(node, tag, relay_port)
|
||||
node.address = "127.0.0.1"
|
||||
end
|
||||
node.stream_security = "none"
|
||||
end
|
||||
|
||||
if node.transport == "mkcp" or node.transport == "quic" then
|
||||
node.stream_security = "none"
|
||||
else
|
||||
if node.tls and node.tls == "1" then
|
||||
node.stream_security = "tls"
|
||||
end
|
||||
|
||||
if node.transport == "mkcp" or node.transport == "quic" then
|
||||
node.stream_security = "none"
|
||||
end
|
||||
end
|
||||
|
||||
result = {
|
||||
@ -73,10 +77,6 @@ function gen_outbound(node, tag, relay_port)
|
||||
streamSettings = (node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
|
||||
network = node.transport,
|
||||
security = node.stream_security,
|
||||
xtlsSettings = (node.stream_security == "xtls") and {
|
||||
serverName = node.tls_serverName,
|
||||
allowInsecure = (node.tls_allowInsecure == "1") and true or false
|
||||
} or nil,
|
||||
tlsSettings = (node.stream_security == "tls") and {
|
||||
serverName = node.tls_serverName,
|
||||
allowInsecure = (node.tls_allowInsecure == "1") and true or false
|
||||
@ -265,7 +265,10 @@ if node then
|
||||
end
|
||||
end
|
||||
|
||||
routing = {domainStrategy = "IPOnDemand", rules = rules}
|
||||
routing = {
|
||||
domainStrategy = node.domainStrategy or "AsIs",
|
||||
rules = rules
|
||||
}
|
||||
|
||||
elseif node.protocol == "_balancing" then
|
||||
if node.balancing_node then
|
||||
@ -277,7 +280,7 @@ if node then
|
||||
if outbound then table.insert(outbounds, outbound) end
|
||||
end
|
||||
routing = {
|
||||
domainStrategy = "IPOnDemand",
|
||||
domainStrategy = node.domainStrategy or "AsIs",
|
||||
balancers = {{tag = "balancer", selector = nodes}},
|
||||
rules = {
|
||||
{type = "field", network = "tcp,udp", balancerTag = "balancer"}
|
||||
|
||||
@ -0,0 +1,318 @@
|
||||
module("luci.model.cbi.passwall.api.gen_xray", package.seeall)
|
||||
local ucursor = require"luci.model.uci".cursor()
|
||||
local sys = require "luci.sys"
|
||||
local json = require "luci.jsonc"
|
||||
local appname = "passwall"
|
||||
local inbounds = {}
|
||||
local outbounds = {}
|
||||
local routing = nil
|
||||
|
||||
local node_section = arg[1] or "nil"
|
||||
local proto = arg[2]
|
||||
local redir_port = arg[3]
|
||||
local socks_proxy_port = arg[4]
|
||||
local node = ucursor:get_all(appname, node_section)
|
||||
local network = proto
|
||||
local new_port
|
||||
|
||||
local function get_new_port()
|
||||
if new_port then
|
||||
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port %s tcp)", appname, new_port + 1)))
|
||||
else
|
||||
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port auto tcp)", appname)))
|
||||
end
|
||||
return new_port
|
||||
end
|
||||
|
||||
function gen_outbound(node, tag, relay_port)
|
||||
local result = nil
|
||||
if node then
|
||||
local node_id = node[".name"]
|
||||
if tag == nil then
|
||||
tag = node_id
|
||||
end
|
||||
if node.type ~= "Xray" then
|
||||
if node.type == "Socks" then
|
||||
node.protocol = "socks"
|
||||
node.transport = "tcp"
|
||||
else
|
||||
local node_type = (proto and proto ~= "nil") and proto or "socks"
|
||||
new_port = get_new_port()
|
||||
node.port = new_port
|
||||
sys.call(string.format('/usr/share/%s/app.sh run_socks "%s" "%s" "%s" "%s" "%s" "%s" "%s" "%s"> /dev/null',
|
||||
appname,
|
||||
node_id,
|
||||
"127.0.0.1",
|
||||
new_port,
|
||||
string.format("/var/etc/%s/v2_%s_%s.json", appname, node_type, node_id),
|
||||
"0",
|
||||
"nil",
|
||||
"4",
|
||||
relay_port and tostring(relay_port) or ""
|
||||
)
|
||||
)
|
||||
node.protocol = "socks"
|
||||
node.transport = "tcp"
|
||||
node.address = "127.0.0.1"
|
||||
end
|
||||
node.stream_security = "none"
|
||||
else
|
||||
if node.tls and node.tls == "1" then
|
||||
node.stream_security = "tls"
|
||||
if node.xtls and node.xtls == "1" then
|
||||
node.stream_security = "xtls"
|
||||
end
|
||||
end
|
||||
|
||||
if node.transport == "mkcp" or node.transport == "quic" then
|
||||
node.stream_security = "none"
|
||||
end
|
||||
end
|
||||
|
||||
result = {
|
||||
tag = tag,
|
||||
protocol = node.protocol,
|
||||
mux = (node.stream_security ~= "xtls") and {
|
||||
enabled = (node.mux == "1") and true or false,
|
||||
concurrency = (node.mux_concurrency) and tonumber(node.mux_concurrency) or 8
|
||||
} or nil,
|
||||
-- 底层传输配置
|
||||
streamSettings = (node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
|
||||
network = node.transport,
|
||||
security = node.stream_security,
|
||||
xtlsSettings = (node.stream_security == "xtls") and {
|
||||
serverName = node.tls_serverName,
|
||||
allowInsecure = (node.tls_allowInsecure == "1") and true or false
|
||||
} or nil,
|
||||
tlsSettings = (node.stream_security == "tls") and {
|
||||
serverName = node.tls_serverName,
|
||||
allowInsecure = (node.tls_allowInsecure == "1") and true or false
|
||||
} or nil,
|
||||
tcpSettings = (node.transport == "tcp" and node.protocol ~= "socks") and {
|
||||
header = {
|
||||
type = node.tcp_guise,
|
||||
request = (node.tcp_guise == "http") and {
|
||||
path = node.tcp_guise_http_path or {"/"},
|
||||
headers = {
|
||||
Host = node.tcp_guise_http_host or {}
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
} or nil,
|
||||
kcpSettings = (node.transport == "mkcp") and {
|
||||
mtu = tonumber(node.mkcp_mtu),
|
||||
tti = tonumber(node.mkcp_tti),
|
||||
uplinkCapacity = tonumber(node.mkcp_uplinkCapacity),
|
||||
downlinkCapacity = tonumber(node.mkcp_downlinkCapacity),
|
||||
congestion = (node.mkcp_congestion == "1") and true or false,
|
||||
readBufferSize = tonumber(node.mkcp_readBufferSize),
|
||||
writeBufferSize = tonumber(node.mkcp_writeBufferSize),
|
||||
seed = (node.mkcp_seed and node.mkcp_seed ~= "") and node.mkcp_seed or nil,
|
||||
header = {type = node.mkcp_guise}
|
||||
} or nil,
|
||||
wsSettings = (node.transport == "ws") and {
|
||||
path = node.ws_path or "",
|
||||
headers = (node.ws_host ~= nil) and
|
||||
{Host = node.ws_host} or nil
|
||||
} or nil,
|
||||
httpSettings = (node.transport == "h2") and
|
||||
{path = node.h2_path, host = node.h2_host} or
|
||||
nil,
|
||||
dsSettings = (node.transport == "ds") and
|
||||
{path = node.ds_path} or nil,
|
||||
quicSettings = (node.transport == "quic") and {
|
||||
security = node.quic_security,
|
||||
key = node.quic_key,
|
||||
header = {type = node.quic_guise}
|
||||
} or nil
|
||||
} or nil,
|
||||
settings = {
|
||||
vnext = (node.protocol == "vmess" or node.protocol == "vless") and {
|
||||
{
|
||||
address = node.address,
|
||||
port = tonumber(node.port),
|
||||
users = {
|
||||
{
|
||||
id = node.uuid,
|
||||
alterId = tonumber(node.alter_id),
|
||||
level = node.level and tonumber(node.level) or 0,
|
||||
security = (node.protocol == "vmess") and node.security or nil,
|
||||
encryption = node.encryption or "none",
|
||||
flow = node.flow or nil
|
||||
}
|
||||
}
|
||||
}
|
||||
} or nil,
|
||||
servers = (node.protocol == "socks" or node.protocol == "http" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
|
||||
{
|
||||
address = node.address,
|
||||
port = tonumber(node.port),
|
||||
method = node.method or nil,
|
||||
password = node.password or "",
|
||||
users = (node.username and node.password) and
|
||||
{{user = node.username, pass = node.password}} or nil
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
}
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
if node then
|
||||
if socks_proxy_port ~= "nil" then
|
||||
table.insert(inbounds, {
|
||||
listen = "0.0.0.0",
|
||||
port = tonumber(socks_proxy_port),
|
||||
protocol = "socks",
|
||||
settings = {auth = "noauth", udp = true, ip = "127.0.0.1"}
|
||||
})
|
||||
network = "tcp,udp"
|
||||
end
|
||||
|
||||
if redir_port ~= "nil" then
|
||||
table.insert(inbounds, {
|
||||
port = tonumber(redir_port),
|
||||
protocol = "dokodemo-door",
|
||||
settings = {network = proto, followRedirect = true},
|
||||
sniffing = {enabled = true, destOverride = {"http", "tls"}}
|
||||
})
|
||||
if proto == "tcp" and node.tcp_socks == "1" then
|
||||
table.insert(inbounds, {
|
||||
listen = "0.0.0.0",
|
||||
port = tonumber(node.tcp_socks_port),
|
||||
protocol = "socks",
|
||||
settings = {
|
||||
auth = node.tcp_socks_auth,
|
||||
accounts = (node.tcp_socks_auth == "password") and {
|
||||
{
|
||||
user = node.tcp_socks_auth_username,
|
||||
pass = node.tcp_socks_auth_password
|
||||
}
|
||||
} or nil,
|
||||
udp = true
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if node.protocol == "_shunt" then
|
||||
local rules = {}
|
||||
ucursor:foreach(appname, "shunt_rules", function(e)
|
||||
local name = e[".name"]
|
||||
local _node_id = node[name] or nil
|
||||
if _node_id and _node_id ~= "nil" then
|
||||
local _node = ucursor:get_all(appname, _node_id)
|
||||
local is_proxy = node[name .. "_proxy"]
|
||||
local relay_port
|
||||
if is_proxy and is_proxy == "1" then
|
||||
new_port = get_new_port()
|
||||
relay_port = new_port
|
||||
table.insert(inbounds, {
|
||||
tag = "proxy_" .. name,
|
||||
listen = "127.0.0.1",
|
||||
port = new_port,
|
||||
protocol = "dokodemo-door",
|
||||
settings = {network = "tcp,udp", address = _node.address, port = tonumber(_node.port)}
|
||||
})
|
||||
if _node.tls_serverName == nil then
|
||||
_node.tls_serverName = _node.address
|
||||
end
|
||||
_node.address = "127.0.0.1"
|
||||
_node.port = new_port
|
||||
end
|
||||
local _outbound = gen_outbound(_node, name, relay_port)
|
||||
if _outbound then
|
||||
table.insert(outbounds, _outbound)
|
||||
if is_proxy and is_proxy == "1" then
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
inboundTag = {"proxy_" .. name},
|
||||
outboundTag = "default"
|
||||
})
|
||||
end
|
||||
if e.domain_list then
|
||||
local _domain = {}
|
||||
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_domain, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = name,
|
||||
domain = _domain
|
||||
})
|
||||
end
|
||||
if e.ip_list then
|
||||
local _ip = {}
|
||||
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_ip, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = name,
|
||||
ip = _ip
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local default_node_id = node.default_node or nil
|
||||
if default_node_id and default_node_id ~= "nil" then
|
||||
local default_node = ucursor:get_all(appname, default_node_id)
|
||||
local default_outbound = gen_outbound(default_node, "default")
|
||||
if default_outbound then
|
||||
table.insert(outbounds, default_outbound)
|
||||
local rule = {
|
||||
type = "field",
|
||||
outboundTag = "default",
|
||||
network = network
|
||||
}
|
||||
table.insert(rules, rule)
|
||||
end
|
||||
end
|
||||
|
||||
routing = {
|
||||
domainStrategy = node.domainStrategy or "AsIs",
|
||||
rules = rules
|
||||
}
|
||||
|
||||
elseif node.protocol == "_balancing" then
|
||||
if node.balancing_node then
|
||||
local nodes = node.balancing_node
|
||||
local length = #nodes
|
||||
for i = 1, length do
|
||||
local node = ucursor:get_all(appname, nodes[i])
|
||||
local outbound = gen_outbound(node)
|
||||
if outbound then table.insert(outbounds, outbound) end
|
||||
end
|
||||
routing = {
|
||||
domainStrategy = node.domainStrategy or "AsIs",
|
||||
balancers = {{tag = "balancer", selector = nodes}},
|
||||
rules = {
|
||||
{type = "field", network = "tcp,udp", balancerTag = "balancer"}
|
||||
}
|
||||
}
|
||||
end
|
||||
else
|
||||
local outbound = gen_outbound(node)
|
||||
if outbound then table.insert(outbounds, outbound) end
|
||||
end
|
||||
|
||||
-- 额外传出连接
|
||||
table.insert(outbounds, {protocol = "freedom", tag = "direct", settings = {keep = ""}})
|
||||
|
||||
local xray = {
|
||||
log = {
|
||||
-- error = string.format("/var/etc/passwall/%s.log", node[".name"]),
|
||||
loglevel = "warning"
|
||||
},
|
||||
-- 传入连接
|
||||
inbounds = inbounds,
|
||||
-- 传出连接
|
||||
outbounds = outbounds,
|
||||
-- 路由
|
||||
routing = routing
|
||||
}
|
||||
print(json.stringify(xray, 1))
|
||||
end
|
||||
@ -0,0 +1,80 @@
|
||||
local json = require "luci.jsonc"
|
||||
local inbounds = {}
|
||||
local outbounds = {}
|
||||
local routing = nil
|
||||
|
||||
local local_proto = arg[1]
|
||||
local local_address = arg[2]
|
||||
local local_port = arg[3]
|
||||
local server_proto = arg[4]
|
||||
local server_address = arg[5]
|
||||
local server_port = arg[6]
|
||||
local server_username = arg[7] or "nil"
|
||||
local server_password = arg[8] or "nil"
|
||||
|
||||
function gen_outbound(proto, address, port, username, password)
|
||||
local result = {
|
||||
protocol = proto,
|
||||
streamSettings = {
|
||||
network = "tcp",
|
||||
security = "none"
|
||||
},
|
||||
settings = {
|
||||
servers = {
|
||||
{
|
||||
address = address,
|
||||
port = tonumber(port),
|
||||
users = (username ~= "nil" and password ~= "nil") and {
|
||||
{
|
||||
user = username,
|
||||
pass = password
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
end
|
||||
|
||||
if local_proto ~= "nil" and local_address ~= "nil" and local_port ~= "nil" then
|
||||
local inbound = {
|
||||
listen = local_address,
|
||||
port = tonumber(local_port),
|
||||
protocol = local_proto,
|
||||
settings = {
|
||||
accounts = nil
|
||||
}
|
||||
}
|
||||
if local_proto == "socks" then
|
||||
inbound.settings.auth = "noauth"
|
||||
inbound.settings.udp = true
|
||||
elseif local_proto == "http" then
|
||||
inbound.settings.allowTransparent = false
|
||||
end
|
||||
table.insert(inbounds, inbound)
|
||||
end
|
||||
|
||||
if server_proto ~= "nil" and server_address ~= "nil" and server_port ~= "nil" then
|
||||
local outbound = gen_outbound(server_proto, server_address, server_port, server_username, server_password)
|
||||
if outbound then table.insert(outbounds, outbound) end
|
||||
end
|
||||
|
||||
-- 额外传出连接
|
||||
table.insert(outbounds, {
|
||||
protocol = "freedom", tag = "direct", settings = {keep = ""}
|
||||
})
|
||||
|
||||
local xray = {
|
||||
log = {
|
||||
-- error = string.format("/var/etc/passwall/%s.log", node[".name"]),
|
||||
loglevel = "warning"
|
||||
},
|
||||
-- 传入连接
|
||||
inbounds = inbounds,
|
||||
-- 传出连接
|
||||
outbounds = outbounds,
|
||||
-- 路由
|
||||
routing = routing
|
||||
}
|
||||
print(json.stringify(xray, 1))
|
||||
@ -7,30 +7,14 @@ local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local kcptun_api = "https://api.github.com/repos/xtaci/kcptun/releases/latest"
|
||||
|
||||
function get_kcptun_file_path()
|
||||
return api.uci_get_type("global_app", "kcptun_client_file")
|
||||
end
|
||||
|
||||
function get_kcptun_version(file)
|
||||
if file == nil then file = get_kcptun_file_path() end
|
||||
|
||||
if file and file ~= "" then
|
||||
if not fs.access(file, "rwx", "rx", "rx") then
|
||||
fs.chmod(file, 755)
|
||||
end
|
||||
|
||||
local info = util.trim(sys.exec("%s -v 2>/dev/null" % file))
|
||||
|
||||
if info ~= "" then
|
||||
local tb = util.split(info, "%s+", nil, true)
|
||||
return tb[1] == "kcptun" and tb[3] or ""
|
||||
end
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
function to_check(arch)
|
||||
local app_path = api.get_kcptun_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun")
|
||||
}
|
||||
end
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
@ -38,8 +22,7 @@ function to_check(arch)
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate(
|
||||
"Can't determine ARCH, or ARCH not supported.")
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
@ -52,12 +35,9 @@ function to_check(arch)
|
||||
}
|
||||
end
|
||||
|
||||
local now_version = api.get_kcptun_version()
|
||||
local remote_version = json.tag_name:match("[^v]+")
|
||||
|
||||
local client_file = get_kcptun_file_path()
|
||||
|
||||
local needs_update = api.compare_versions(get_kcptun_version(client_file),
|
||||
"<", remote_version)
|
||||
local needs_update = api.compare_versions(now_version, "<", remote_version)
|
||||
local html_url, download_url
|
||||
|
||||
if needs_update then
|
||||
@ -73,24 +53,30 @@ function to_check(arch)
|
||||
if needs_update and not download_url then
|
||||
return {
|
||||
code = 1,
|
||||
now_version = get_kcptun_version(client_file),
|
||||
now_version = now_version,
|
||||
version = remote_version,
|
||||
html_url = html_url,
|
||||
error = i18n.translate(
|
||||
"New version found, but failed to get new version download url.")
|
||||
error = i18n.translate("New version found, but failed to get new version download url.")
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
code = 0,
|
||||
update = needs_update,
|
||||
now_version = get_kcptun_version(client_file),
|
||||
now_version = now_version,
|
||||
version = remote_version,
|
||||
url = {html = html_url, download = download_url}
|
||||
}
|
||||
end
|
||||
|
||||
function to_download(url)
|
||||
local app_path = api.get_kcptun_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun")
|
||||
}
|
||||
end
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
@ -113,6 +99,13 @@ function to_download(url)
|
||||
end
|
||||
|
||||
function to_extract(file, subfix)
|
||||
local app_path = api.get_kcptun_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun")
|
||||
}
|
||||
end
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
return {code = 1, error = i18n.translate("File path required.")}
|
||||
end
|
||||
@ -157,47 +150,50 @@ function to_extract(file, subfix)
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local app_path = api.get_kcptun_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun")
|
||||
}
|
||||
end
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local version = get_kcptun_version(file)
|
||||
if version == "" then
|
||||
local new_version = api.get_kcptun_version(file)
|
||||
if new_version == "" then
|
||||
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate(
|
||||
"The client file is not suitable for current device.")
|
||||
error = i18n.translate("The client file is not suitable for current device.")
|
||||
}
|
||||
end
|
||||
|
||||
local client_file = get_kcptun_file_path()
|
||||
local client_file_bak
|
||||
local app_path_bak
|
||||
|
||||
if fs.access(client_file) then
|
||||
client_file_bak = client_file .. ".bak"
|
||||
api.exec("/bin/mv", {"-f", client_file, client_file_bak})
|
||||
if fs.access(app_path) then
|
||||
app_path_bak = app_path .. ".bak"
|
||||
api.exec("/bin/mv", {"-f", app_path, app_path_bak})
|
||||
end
|
||||
|
||||
local result = api.exec("/bin/mv", {"-f", file, client_file}, nil,
|
||||
api.command_timeout) == 0
|
||||
local result = api.exec("/bin/mv", {"-f", file, app_path}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result or not fs.access(client_file) then
|
||||
if not result or not fs.access(app_path) then
|
||||
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
|
||||
if client_file_bak then
|
||||
api.exec("/bin/mv", {"-f", client_file_bak, client_file})
|
||||
if app_path_bak then
|
||||
api.exec("/bin/mv", {"-f", app_path_bak, app_path})
|
||||
end
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s",
|
||||
client_file)
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
api.exec("/bin/chmod", {"755", client_file})
|
||||
api.exec("/bin/chmod", {"755", app_path})
|
||||
|
||||
if client_file_bak then api.exec("/bin/rm", {"-f", client_file_bak}) end
|
||||
if app_path_bak then api.exec("/bin/rm", {"-f", app_path_bak}) end
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
|
||||
|
||||
|
||||
@ -3,12 +3,18 @@ local fs = require "nixio.fs"
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local i18n = require "luci.i18n"
|
||||
local ipkg = require("luci.model.ipkg")
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local trojan_go_api = api.uci_get_type("global_app", "trojan_go_latest", "https://api.github.com/repos/trojan-gfw/trojan-go/releases/latest")
|
||||
|
||||
function to_check(arch)
|
||||
local app_path = api.get_trojan_go_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO")
|
||||
}
|
||||
end
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
@ -16,8 +22,7 @@ function to_check(arch)
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate(
|
||||
"Can't determine ARCH, or ARCH not supported.")
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
@ -59,8 +64,7 @@ function to_check(arch)
|
||||
now_version = now_version,
|
||||
version = remote_version,
|
||||
html_url = html_url,
|
||||
error = i18n.translate(
|
||||
"New version found, but failed to get new version download url.") .. " [linux-" .. file_tree .. ".zip]"
|
||||
error = i18n.translate("New version found, but failed to get new version download url.") .. " [linux-" .. file_tree .. ".zip]"
|
||||
}
|
||||
end
|
||||
|
||||
@ -74,6 +78,13 @@ function to_check(arch)
|
||||
end
|
||||
|
||||
function to_download(url)
|
||||
local app_path = api.get_trojan_go_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO")
|
||||
}
|
||||
end
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
@ -96,10 +107,19 @@ function to_download(url)
|
||||
end
|
||||
|
||||
function to_extract(file, subfix)
|
||||
local isinstall_unzip = ipkg.installed("unzip")
|
||||
if isinstall_unzip == nil then
|
||||
ipkg.update()
|
||||
ipkg.install("unzip")
|
||||
local app_path = api.get_trojan_go_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO")
|
||||
}
|
||||
end
|
||||
if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Not installed unzip, Can't unzip!")
|
||||
}
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
@ -121,29 +141,35 @@ function to_extract(file, subfix)
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local app_path = api.get_trojan_go_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO")
|
||||
}
|
||||
end
|
||||
if not file or file == "" then
|
||||
sys.call("/bin/rm -rf /tmp/trojan-go_extract.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local client_file = api.get_trojan_go_path()
|
||||
local client_file_bak
|
||||
local app_path_bak
|
||||
|
||||
if fs.access(client_file) then
|
||||
client_file_bak = client_file .. ".bak"
|
||||
api.exec("/bin/mv", {"-f", client_file, client_file_bak})
|
||||
if fs.access(app_path) then
|
||||
app_path_bak = app_path .. ".bak"
|
||||
api.exec("/bin/mv", {"-f", app_path, app_path_bak})
|
||||
end
|
||||
|
||||
local result = api.exec("/bin/mv", { "-f", file .. "/trojan-go", client_file }, nil, api.command_timeout) == 0
|
||||
local result = api.exec("/bin/mv", { "-f", file .. "/trojan-go", app_path }, nil, api.command_timeout) == 0
|
||||
sys.call("/bin/rm -rf /tmp/trojan-go_extract.*")
|
||||
if not result or not fs.access(client_file) then
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", client_file)
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
api.exec("/bin/chmod", {"-R", "755", client_file})
|
||||
api.exec("/bin/chmod", {"-R", "755", app_path})
|
||||
|
||||
return {code = 0}
|
||||
end
|
||||
|
||||
@ -3,26 +3,19 @@ local fs = require "nixio.fs"
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local i18n = require "luci.i18n"
|
||||
local ipkg = require("luci.model.ipkg")
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local v2ray_api = "https://api.github.com/repos/v2fly/v2ray-core/releases/latest"
|
||||
local is_armv7 = false
|
||||
|
||||
function get_v2ray_file_path()
|
||||
return api.uci_get_type("global_app", "v2ray_file")
|
||||
end
|
||||
|
||||
function get_v2ray_version()
|
||||
if get_v2ray_file_path() and get_v2ray_file_path() ~= "" then
|
||||
if fs.access(get_v2ray_file_path() .. "/v2ray") then
|
||||
return sys.exec("echo -n $(" .. get_v2ray_file_path() .. "/v2ray -version | awk '{print $2}' | sed -n 1P" .. ")")
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function to_check(arch)
|
||||
local app_path = api.get_v2ray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray")
|
||||
}
|
||||
end
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
@ -31,8 +24,7 @@ function to_check(arch)
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate(
|
||||
"Can't determine ARCH, or ARCH not supported.")
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
@ -48,8 +40,9 @@ function to_check(arch)
|
||||
}
|
||||
end
|
||||
|
||||
local now_version = api.get_v2ray_version()
|
||||
local remote_version = json.tag_name:match("[^v]+")
|
||||
local needs_update = api.compare_versions(get_v2ray_version(), "<", remote_version)
|
||||
local needs_update = api.compare_versions(now_version, "<", remote_version)
|
||||
local html_url, download_url
|
||||
|
||||
if needs_update then
|
||||
@ -65,24 +58,30 @@ function to_check(arch)
|
||||
if needs_update and not download_url then
|
||||
return {
|
||||
code = 1,
|
||||
now_version = get_v2ray_version(),
|
||||
now_version = now_version,
|
||||
version = remote_version,
|
||||
html_url = html_url,
|
||||
error = i18n.translate(
|
||||
"New version found, but failed to get new version download url.")
|
||||
error = i18n.translate("New version found, but failed to get new version download url.")
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
code = 0,
|
||||
update = needs_update,
|
||||
now_version = get_v2ray_version(),
|
||||
now_version = now_version,
|
||||
version = remote_version,
|
||||
url = {html = html_url, download = download_url}
|
||||
}
|
||||
end
|
||||
|
||||
function to_download(url)
|
||||
local app_path = api.get_v2ray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray")
|
||||
}
|
||||
end
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
@ -105,16 +104,26 @@ function to_download(url)
|
||||
end
|
||||
|
||||
function to_extract(file, subfix)
|
||||
local isinstall_unzip = ipkg.installed("unzip")
|
||||
if isinstall_unzip == nil then
|
||||
ipkg.update()
|
||||
ipkg.install("unzip")
|
||||
local app_path = api.get_v2ray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray")
|
||||
}
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
return {code = 1, error = i18n.translate("File path required.")}
|
||||
end
|
||||
|
||||
if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Not installed unzip, Can't unzip!")
|
||||
}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
local tmp_dir = util.trim(util.exec("mktemp -d -t v2ray_extract.XXXXXX"))
|
||||
|
||||
@ -130,37 +139,35 @@ function to_extract(file, subfix)
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local app_path = api.get_v2ray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray")
|
||||
}
|
||||
end
|
||||
if not file or file == "" then
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local client_file = get_v2ray_file_path()
|
||||
|
||||
sys.call("mkdir -p " .. client_file)
|
||||
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
if sub_version == "7" then is_armv7 = true end
|
||||
local t = ""
|
||||
sys.call("/etc/init.d/passwall stop")
|
||||
local result = nil
|
||||
if is_armv7 and is_armv7 == true then
|
||||
result = api.exec("/bin/mv", {
|
||||
"-f", file .. "/v2ray_armv7", file .. "/v2ctl_armv7", client_file
|
||||
}, nil, api.command_timeout) == 0
|
||||
else
|
||||
result = api.exec("/bin/mv", {
|
||||
"-f", file .. "/v2ray", file .. "/v2ctl", client_file
|
||||
}, nil, api.command_timeout) == 0
|
||||
end
|
||||
if sub_version and sub_version == "7" then t = "_armv7" end
|
||||
result = api.exec("/bin/mv", { "-f", file .. "/v2ray" .. t, app_path }, nil, api.command_timeout) == 0
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
if not result or not fs.access(client_file) then
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", client_file)
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
api.exec("/bin/chmod", {"-R", "755", client_file})
|
||||
|
||||
api.chmod_755(app_path)
|
||||
sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &")
|
||||
|
||||
return {code = 0}
|
||||
end
|
||||
|
||||
@ -0,0 +1,173 @@
|
||||
module("luci.model.cbi.passwall.api.xray", package.seeall)
|
||||
local fs = require "nixio.fs"
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local i18n = require "luci.i18n"
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local xray_api = "https://api.github.com/repos/XTLS/Xray-core/releases/latest"
|
||||
local is_armv7 = false
|
||||
|
||||
function to_check(arch)
|
||||
local app_path = api.get_xray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray")
|
||||
}
|
||||
end
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
if sub_version == "7" then is_armv7 = true end
|
||||
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
if file_tree == "amd64" then file_tree = "64" end
|
||||
if file_tree == "386" then file_tree = "32" end
|
||||
|
||||
local json = api.get_api_json(xray_api)
|
||||
|
||||
if json.tag_name == nil then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Get remote version info failed.")
|
||||
}
|
||||
end
|
||||
|
||||
local now_version = api.get_xray_version()
|
||||
local remote_version = json.tag_name:match("[^v]+")
|
||||
local needs_update = api.compare_versions(now_version, "<", remote_version)
|
||||
local html_url, download_url
|
||||
|
||||
if needs_update then
|
||||
html_url = json.html_url
|
||||
for _, v in ipairs(json.assets) do
|
||||
if v.name and v.name:match("linux%-" .. file_tree) then
|
||||
download_url = v.browser_download_url
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if needs_update and not download_url then
|
||||
return {
|
||||
code = 1,
|
||||
now_version = now_version,
|
||||
version = remote_version,
|
||||
html_url = html_url,
|
||||
error = i18n.translate("New version found, but failed to get new version download url.")
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
code = 0,
|
||||
update = needs_update,
|
||||
now_version = now_version,
|
||||
version = remote_version,
|
||||
url = {html = html_url, download = download_url}
|
||||
}
|
||||
end
|
||||
|
||||
function to_download(url)
|
||||
local app_path = api.get_xray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray")
|
||||
}
|
||||
end
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -f /tmp/xray_download.*")
|
||||
|
||||
local tmp_file = util.trim(util.exec("mktemp -u -t xray_download.XXXXXX"))
|
||||
|
||||
local result = api.exec(api.curl, {api._unpack(api.curl_args), "-o", tmp_file, url}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result then
|
||||
api.exec("/bin/rm", {"-f", tmp_file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("File download failed or timed out: %s", url)
|
||||
}
|
||||
end
|
||||
|
||||
return {code = 0, file = tmp_file}
|
||||
end
|
||||
|
||||
function to_extract(file, subfix)
|
||||
local app_path = api.get_xray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray")
|
||||
}
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
return {code = 1, error = i18n.translate("File path required.")}
|
||||
end
|
||||
|
||||
if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Not installed unzip, Can't unzip!")
|
||||
}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
local tmp_dir = util.trim(util.exec("mktemp -d -t xray_extract.XXXXXX"))
|
||||
|
||||
local output = {}
|
||||
api.exec("/usr/bin/unzip", {"-o", file, "-d", tmp_dir},
|
||||
function(chunk) output[#output + 1] = chunk end)
|
||||
|
||||
local files = util.split(table.concat(output))
|
||||
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
|
||||
return {code = 0, file = tmp_dir}
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local app_path = api.get_xray_path() or ""
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray")
|
||||
}
|
||||
end
|
||||
if not file or file == "" then
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
local t = ""
|
||||
sys.call("/etc/init.d/passwall stop")
|
||||
local result = nil
|
||||
if sub_version and sub_version == "7" then t = "_armv7" end
|
||||
result = api.exec("/bin/mv", { "-f", file .. "/xray" .. t, app_path }, nil, api.command_timeout) == 0
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
api.chmod_755(app_path)
|
||||
sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &")
|
||||
|
||||
return {code = 0}
|
||||
end
|
||||
@ -9,39 +9,39 @@ s = m:section(TypedSection, "global_app", translate("App Update"),
|
||||
translate("Please confirm that your firmware supports FPU.") ..
|
||||
"</font>")
|
||||
s.anonymous = true
|
||||
s:append(Template(appname .. "/app_update/xray_version"))
|
||||
s:append(Template(appname .. "/app_update/v2ray_version"))
|
||||
s:append(Template(appname .. "/app_update/trojan_go_version"))
|
||||
s:append(Template(appname .. "/app_update/kcptun_version"))
|
||||
s:append(Template(appname .. "/app_update/brook_version"))
|
||||
|
||||
---- V2ray Path
|
||||
o = s:option(Value, "v2ray_file", translate("V2ray Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/v2ray/"))
|
||||
o.default = "/usr/bin/v2ray/"
|
||||
o = s:option(Value, "xray_file", translatef("%s App Path", "Xray"))
|
||||
o.default = "/usr/bin/xray"
|
||||
o.rmempty = false
|
||||
|
||||
---- Trojan-Go Path
|
||||
o = s:option(Value, "trojan_go_file", translate("Trojan-Go Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/trojan-go"))
|
||||
o = s:option(Value, "v2ray_file", translatef("%s App Path", "V2ray"))
|
||||
o.default = "/usr/bin/v2ray"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "trojan_go_file", translatef("%s App Path", "Trojan-Go"))
|
||||
o.default = "/usr/bin/trojan-go"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "trojan_go_latest", translate("Trojan-Go Version API"), translate("alternate API URL for version checking"))
|
||||
o = s:option(Value, "trojan_go_latest", translatef("Trojan-Go Version API"), translate("alternate API URL for version checking"))
|
||||
o.default = "https://api.github.com/repos/peter-tank/trojan-go/releases/latest"
|
||||
|
||||
---- Kcptun client Path
|
||||
o = s:option(Value, "kcptun_client_file", translate("Kcptun Client Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/kcptun-client"))
|
||||
o = s:option(Value, "kcptun_client_file", translatef("%s Client App Path", "Kcptun"))
|
||||
o.default = "/usr/bin/kcptun-client"
|
||||
o.rmempty = false
|
||||
|
||||
--[[
|
||||
o = s:option(Button, "_check_kcptun", translate("Manually update"), translatef("Make sure there is enough space to install %s", "kcptun"))
|
||||
o.template = appname .. "/kcptun"
|
||||
o.inputstyle = "apply"
|
||||
o.btnclick = "onBtnClick_kcptun(this);"
|
||||
o.id = "_kcptun-check_btn"]] --
|
||||
|
||||
---- Brook Path
|
||||
o = s:option(Value, "brook_file", translate("Brook Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/brook"))
|
||||
o = s:option(Value, "brook_file", translatef("%s App Path", "Brook"))
|
||||
o.default = "/usr/bin/brook"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(DummyValue, "tips", " ")
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
return string.format('<font color="red">%s</font>', translate("if you want to run from memory, change the path, /tmp beginning then save the application and update it manually."))
|
||||
end
|
||||
|
||||
return m
|
||||
|
||||
@ -39,6 +39,8 @@ for i = 1, tcp_node_num, 1 do
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
|
||||
o = s:option(Flag, "restore_switch" .. i, "TCP " .. i .. " " .. translate("Restore Switch"), translate("When detects main node is available, switch back to the main node."))
|
||||
end
|
||||
|
||||
return m
|
||||
|
||||
@ -76,17 +76,17 @@ o.rmempty = false
|
||||
---- TCP Node
|
||||
local tcp_node_num = tonumber(m:get("@global_other[0]", "tcp_node_num") or 1)
|
||||
for i = 1, tcp_node_num, 1 do
|
||||
o = s:taboption("Main", ListValue, "tcp_node" .. i, translate("TCP Node") .. " " .. i)
|
||||
if i == 1 then
|
||||
o = s:taboption("Main", ListValue, "tcp_node" .. i, translate("TCP Node"))
|
||||
o.description = translate("For proxy specific list.")
|
||||
|
||||
o.title = translate("TCP Node")
|
||||
o.description = translate("For proxy specific list.") .. o.description
|
||||
if tonumber(m:get("@auto_switch[0]", "enable") or 0) == 1 then
|
||||
local now_node = luci.sys.exec(string.format("[ -f '/var/etc/%s/id/TCP_%s' ] && echo -n $(cat /var/etc/%s/id/TCP_%s)", appname, i, appname, i))
|
||||
if now_node and now_node ~= "" then
|
||||
local e = uci:get_all(appname, now_node)
|
||||
local current_node = luci.sys.exec(string.format("[ -f '/var/etc/%s/id/TCP_%s' ] && echo -n $(cat /var/etc/%s/id/TCP_%s)", appname, i, appname, i))
|
||||
if current_node and current_node ~= "" and current_node ~= "nil" then
|
||||
local e = uci:get_all(appname, current_node)
|
||||
if e then
|
||||
local remarks = ""
|
||||
if e.type == "V2ray" and (e.protocol == "_balancing" or e.protocol == "_shunt") then
|
||||
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt") then
|
||||
remarks = "%s:[%s] " % {translatef(e.type .. e.protocol), e.remarks}
|
||||
else
|
||||
if e.use_kcp and e.use_kcp == "1" then
|
||||
@ -95,12 +95,10 @@ for i = 1, tcp_node_num, 1 do
|
||||
remarks = "%s:[%s] %s:%s" % {e.type, e.remarks, e.address, e.port}
|
||||
end
|
||||
end
|
||||
o.description = o.description .. "<br />" ..translatef("Current node: %s", remarks)
|
||||
o.description = translate("For proxy specific list.") .. "<br />" .. translatef("Current node: %s", '<a href="node_config/' .. current_node .. '">' .. remarks .. '</a>')
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
o = s:taboption("Main", ListValue, "tcp_node" .. i, translate("TCP Node") .. " " .. i)
|
||||
end
|
||||
o:value("nil", translate("Close"))
|
||||
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
|
||||
@ -109,16 +107,14 @@ end
|
||||
---- UDP Node
|
||||
local udp_node_num = tonumber(m:get("@global_other[0]", "udp_node_num") or 1)
|
||||
for i = 1, udp_node_num, 1 do
|
||||
o = s:taboption("Main", ListValue, "udp_node" .. i, translate("UDP Node") .. " " .. i)
|
||||
o:value("nil", translate("Close"))
|
||||
if i == 1 then
|
||||
o = s:taboption("Main", ListValue, "udp_node" .. i, translate("UDP Node"))
|
||||
o.description = translate("For proxy game network, DNS hijack etc.") .. translate(" The selected server will not use Kcptun.")
|
||||
o:value("nil", translate("Close"))
|
||||
o.title = translate("UDP Node")
|
||||
o.description = translate("For proxy game network, DNS hijack etc.") .. o.description .. "<br />" .. translate("The selected server will not use Kcptun.")
|
||||
o:value("tcp_", translate("Same as the tcp node"))
|
||||
--o:value("tcp", translate("Same as the tcp node"))
|
||||
--o:value("tcp_", translate("Same as the tcp node") .. "(" .. translate("New process") .. ")")
|
||||
else
|
||||
o = s:taboption("Main", ListValue, "udp_node" .. i, translate("UDP Node") .. " " .. i)
|
||||
o:value("nil", translate("Close"))
|
||||
end
|
||||
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
|
||||
end
|
||||
@ -334,7 +330,7 @@ o.default = 9050
|
||||
o.datatype = "port"
|
||||
o.rmempty = false
|
||||
|
||||
if api.is_finded("v2ray") then
|
||||
if api.is_finded("xray") or api.is_finded("v2ray") then
|
||||
o = s:option(Value, "http_port", "HTTP" .. translate("Listen Port") .. " " .. translate("0 is not use"))
|
||||
o.default = 0
|
||||
o.datatype = "port"
|
||||
|
||||
@ -47,7 +47,10 @@ o:depends("balancing_enable", 1)
|
||||
|
||||
-- [[ Balancing Settings ]]--
|
||||
s = m:section(TypedSection, "haproxy_config", "",
|
||||
"<font color='red'>" .. translate("Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group.").."</font>")
|
||||
"<font color='red'>" ..
|
||||
translate("Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group.") ..
|
||||
"\n" .. translate("Note that the node configuration parameters for load balancing must be consistent, otherwise problems can arise!") ..
|
||||
"</font>")
|
||||
s.template = "cbi/tblsection"
|
||||
s.sortable = true
|
||||
s.anonymous = true
|
||||
|
||||
@ -79,6 +79,10 @@ end
|
||||
if api.is_finded("ssr-redir") then
|
||||
type:value("SSR", translate("ShadowsocksR"))
|
||||
end
|
||||
if api.is_finded("xray") then
|
||||
type:value("Xray", translate("Xray"))
|
||||
type.description = translate("Xray is currently directly compatible with V2ray and used.")
|
||||
end
|
||||
if api.is_finded("v2ray") then
|
||||
type:value("V2ray", translate("V2ray"))
|
||||
end
|
||||
@ -110,6 +114,7 @@ protocol:value("trojan", translate("Trojan"))
|
||||
protocol:value("_balancing", translate("Balancing"))
|
||||
protocol:value("_shunt", translate("Shunt"))
|
||||
protocol:depends("type", "V2ray")
|
||||
protocol:depends("type", "Xray")
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
@ -128,7 +133,7 @@ balancing_node:depends("protocol", "_balancing")
|
||||
|
||||
-- 分流
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
o = s:option(ListValue, e[".name"], translate(e.remarks))
|
||||
o = s:option(ListValue, e[".name"], '<a href="../shunt_rules/' .. e[".name"] .. '">' .. translate(e.remarks) .. "</a>")
|
||||
o:value("nil", translate("Close"))
|
||||
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
|
||||
o:depends("protocol", "_shunt")
|
||||
@ -138,11 +143,29 @@ uci:foreach(appname, "shunt_rules", function(e)
|
||||
o:depends("protocol", "_shunt")
|
||||
end)
|
||||
|
||||
shunt_tips = s:option(DummyValue, "shunt_tips", " ")
|
||||
shunt_tips.rawhtml = true
|
||||
shunt_tips.cfgvalue = function(t, n)
|
||||
return string.format('<a style="color: red" href="../rule">%s</a>', translate("No shunt rules? Click me to go to add."))
|
||||
end
|
||||
shunt_tips:depends("protocol", "_shunt")
|
||||
|
||||
default_node = s:option(ListValue, "default_node", translate("Default") .. " " .. translate("Node"))
|
||||
default_node:value("nil", translate("Close"))
|
||||
for k, v in pairs(nodes_table) do default_node:value(v.id, v.remarks) end
|
||||
default_node:depends("protocol", "_shunt")
|
||||
|
||||
domainStrategy = s:option(ListValue, "domainStrategy", translate("Domain Strategy"))
|
||||
domainStrategy:value("AsIs")
|
||||
domainStrategy:value("IPIfNonMatch")
|
||||
domainStrategy:value("IPOnDemand")
|
||||
domainStrategy.description = "<br /><ul><li>" .. translate("'AsIs': Only use domain for routing. Default value.")
|
||||
.. "</li><li>" .. translate("'IPIfNonMatch': When no rule matches current domain, resolves it into IP addresses (A or AAAA records) and try all rules again.")
|
||||
.. "</li><li>" .. translate("'IPOnDemand': As long as there is a IP-based rule, resolves the domain into IP immediately.")
|
||||
.. "</li></ul>"
|
||||
domainStrategy:depends("protocol", "_balancing")
|
||||
domainStrategy:depends("protocol", "_shunt")
|
||||
|
||||
-- Brook协议
|
||||
brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol"))
|
||||
brook_protocol:value("client", translate("Brook"))
|
||||
@ -180,6 +203,12 @@ address:depends("type", "Trojan")
|
||||
address:depends("type", "Trojan-Plus")
|
||||
address:depends("type", "Trojan-Go")
|
||||
address:depends("type", "Naiveproxy")
|
||||
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 = "V2ray", protocol = "vmess" })
|
||||
address:depends({ type = "V2ray", protocol = "vless" })
|
||||
address:depends({ type = "V2ray", protocol = "http" })
|
||||
@ -197,6 +226,12 @@ use_ipv6:depends("type", "Brook")
|
||||
use_ipv6:depends("type", "Trojan")
|
||||
use_ipv6:depends("type", "Trojan-Plus")
|
||||
use_ipv6:depends("type", "Trojan-Go")
|
||||
use_ipv6:depends({ type = "Xray", protocol = "vmess" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "vless" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "http" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "socks" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "trojan" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "vmess" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "vless" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "http" })
|
||||
@ -216,6 +251,12 @@ port:depends("type", "Trojan")
|
||||
port:depends("type", "Trojan-Plus")
|
||||
port:depends("type", "Trojan-Go")
|
||||
port:depends("type", "Naiveproxy")
|
||||
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 = "V2ray", protocol = "vmess" })
|
||||
port:depends({ type = "V2ray", protocol = "vless" })
|
||||
port:depends({ type = "V2ray", protocol = "http" })
|
||||
@ -226,6 +267,8 @@ port:depends({ type = "V2ray", protocol = "trojan" })
|
||||
username = s:option(Value, "username", translate("Username"))
|
||||
username:depends("type", "Socks")
|
||||
username:depends("type", "Naiveproxy")
|
||||
username:depends({ type = "Xray", protocol = "http" })
|
||||
username:depends({ type = "Xray", protocol = "socks" })
|
||||
username:depends({ type = "V2ray", protocol = "http" })
|
||||
username:depends({ type = "V2ray", protocol = "socks" })
|
||||
|
||||
@ -239,6 +282,10 @@ password:depends("type", "Trojan")
|
||||
password:depends("type", "Trojan-Plus")
|
||||
password:depends("type", "Trojan-Go")
|
||||
password:depends("type", "Naiveproxy")
|
||||
password:depends({ type = "Xray", protocol = "http" })
|
||||
password:depends({ type = "Xray", protocol = "socks" })
|
||||
password:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
password:depends({ type = "Xray", protocol = "trojan" })
|
||||
password:depends({ type = "V2ray", protocol = "http" })
|
||||
password:depends({ type = "V2ray", protocol = "socks" })
|
||||
password:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
@ -266,10 +313,12 @@ end
|
||||
|
||||
security = s:option(ListValue, "security", translate("Encrypt Method"))
|
||||
for a, t in ipairs(security_list) do security:value(t) end
|
||||
security:depends({ type = "Xray", protocol = "vmess" })
|
||||
security:depends({ type = "V2ray", protocol = "vmess" })
|
||||
|
||||
encryption = s:option(Value, "encryption", translate("Encrypt Method"))
|
||||
encryption.default = "none"
|
||||
encryption:depends({ type = "Xray", protocol = "vless" })
|
||||
encryption:depends({ type = "V2ray", protocol = "vless" })
|
||||
|
||||
v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
|
||||
@ -360,6 +409,8 @@ kcp_opts:depends("use_kcp", "1")
|
||||
|
||||
uuid = s:option(Value, "uuid", translate("ID"))
|
||||
uuid.password = true
|
||||
uuid:depends({ type = "Xray", protocol = "vmess" })
|
||||
uuid:depends({ type = "Xray", protocol = "vless" })
|
||||
uuid:depends({ type = "V2ray", protocol = "vmess" })
|
||||
uuid:depends({ type = "V2ray", protocol = "vless" })
|
||||
|
||||
@ -368,64 +419,77 @@ alter_id:depends("protocol", "vmess")
|
||||
|
||||
level = s:option(Value, "level", translate("User Level"))
|
||||
level.default = 1
|
||||
level:depends({ type = "Xray", protocol = "vmess" })
|
||||
level:depends({ type = "Xray", protocol = "vless" })
|
||||
level:depends({ type = "Xray", protocol = "trojan" })
|
||||
level:depends({ type = "V2ray", protocol = "vmess" })
|
||||
level:depends({ type = "V2ray", protocol = "vless" })
|
||||
level:depends({ type = "V2ray", protocol = "trojan" })
|
||||
|
||||
flow = s:option(Value, "flow", translate("flow"))
|
||||
flow.placeholder = "xtls-rprx-origin"
|
||||
--flow:value("xtls-rprx-origin")
|
||||
--flow:value("xtls-rprx-origin-udp443")
|
||||
--flow:value("xtls-rprx-direct")
|
||||
--flow:value("xtls-rprx-direct-udp443")
|
||||
flow:depends({ type = "V2ray", protocol = "vless" })
|
||||
|
||||
stream_security = s:option(ListValue, "stream_security", translate("Transport Layer Encryption"), translate('Whether or not transport layer encryption is enabled, "none" for unencrypted, "tls" for using TLS, "xtls" for using XTLS.'))
|
||||
stream_security:value("none", "none")
|
||||
stream_security:value("tls", "tls")
|
||||
stream_security:value("xtls", "xtls")
|
||||
stream_security.default = "tls"
|
||||
stream_security:depends({ type = "V2ray", protocol = "vmess" })
|
||||
stream_security:depends({ type = "V2ray", protocol = "vless" })
|
||||
stream_security:depends({ type = "V2ray", protocol = "socks" })
|
||||
stream_security:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
stream_security:depends({ type = "V2ray", protocol = "trojan" })
|
||||
stream_security:depends("type", "Trojan")
|
||||
stream_security:depends("type", "Trojan-Plus")
|
||||
stream_security:depends("type", "Trojan-Go")
|
||||
stream_security.validate = function(self, value)
|
||||
if value == "none" and (type:formvalue(arg[1]) == "Trojan" or type:formvalue(arg[1]) == "Trojan-Plus") then
|
||||
return nil, translate("'none' not supported for original Trojan, please choose 'tls'.")
|
||||
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
|
||||
return value
|
||||
end
|
||||
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 = "V2ray", protocol = "vmess" })
|
||||
tls:depends({ type = "V2ray", protocol = "vless" })
|
||||
tls:depends({ type = "V2ray", protocol = "socks" })
|
||||
tls:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
tls:depends({ type = "Xray", protocol = "trojan" })
|
||||
tls:depends("type", "Trojan")
|
||||
tls:depends("type", "Trojan-Plus")
|
||||
tls:depends("type", "Trojan-Go")
|
||||
|
||||
xtls = s:option(Flag, "xtls", translate("XTLS"))
|
||||
xtls.default = 0
|
||||
xtls:depends({ type = "Xray", protocol = "vless", tls = "1" })
|
||||
|
||||
flow = s:option(Value, "flow", translate("flow"))
|
||||
flow:value("xtls-rprx-origin")
|
||||
flow:value("xtls-rprx-origin-udp443")
|
||||
flow:value("xtls-rprx-direct")
|
||||
flow:value("xtls-rprx-direct-udp443")
|
||||
flow:value("xtls-rprx-splice")
|
||||
flow:value("xtls-rprx-splice-udp443")
|
||||
flow:depends("xtls", "1")
|
||||
|
||||
-- [[ TLS部分 ]] --
|
||||
tls_sessionTicket = s:option(Flag, "tls_sessionTicket", translate("Session Ticket"))
|
||||
tls_sessionTicket.default = "0"
|
||||
tls_sessionTicket:depends({ type = "Trojan", stream_security = "tls" })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Plus", stream_security = "tls" })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Go", stream_security = "tls" })
|
||||
tls_sessionTicket:depends({ type = "Trojan", tls = "1" })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Plus", tls = "1" })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Go", tls = "1" })
|
||||
|
||||
-- [[ Trojan TLS ]]--
|
||||
trojan_force_fp = s:option(ListValue, "fingerprint", translate("Finger Print"))
|
||||
for a, t in ipairs(force_fp) do trojan_force_fp:value(t) end
|
||||
trojan_force_fp.default = "firefox"
|
||||
trojan_force_fp:depends({ type = "Trojan-Go", stream_security = "tls" })
|
||||
trojan_force_fp:depends({ type = "Trojan-Go", tls = "1" })
|
||||
|
||||
tls_serverName = s:option(Value, "tls_serverName", translate("Domain"))
|
||||
tls_serverName:depends("stream_security", "tls")
|
||||
tls_serverName:depends("stream_security", "xtls")
|
||||
tls_serverName:depends("tls", "1")
|
||||
tls_serverName:depends("xtls", "1")
|
||||
|
||||
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("stream_security", "tls")
|
||||
tls_allowInsecure:depends("stream_security", "xtls")
|
||||
tls_allowInsecure:depends("tls", "1")
|
||||
tls_allowInsecure:depends("xtls", "1")
|
||||
|
||||
-- [[ Trojan Cert ]]--
|
||||
trojan_cert_path = s:option(Value, "trojan_cert_path", translate("Trojan Cert Path"))
|
||||
trojan_cert_path.default = ""
|
||||
trojan_cert_path:depends({ stream_security = "tls", tls_allowInsecure = false })
|
||||
trojan_cert_path:depends({ tls = "1", tls_allowInsecure = false })
|
||||
|
||||
trojan_transport = s:option(ListValue, "trojan_transport", translate("Transport"))
|
||||
trojan_transport:value("original", "Original")
|
||||
@ -440,7 +504,7 @@ trojan_plugin:value("plaintext", "Plain Text")
|
||||
trojan_plugin:value("shadowsocks", "ShadowSocks")
|
||||
trojan_plugin:value("other", "Other")
|
||||
trojan_plugin.default = "plaintext"
|
||||
trojan_plugin:depends({ stream_security = "none", trojan_transport = "original" })
|
||||
trojan_plugin:depends({ tls = "0", trojan_transport = "original" })
|
||||
|
||||
trojan_plugin_cmd = s:option(Value, "plugin_cmd", translate("Plugin Binary"))
|
||||
trojan_plugin_cmd.placeholder = "eg: /usr/bin/v2ray-plugin"
|
||||
@ -464,6 +528,11 @@ transport:value("ws", "WebSocket")
|
||||
transport:value("h2", "HTTP/2")
|
||||
transport:value("ds", "DomainSocket")
|
||||
transport:value("quic", "QUIC")
|
||||
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" })
|
||||
transport:depends({ type = "V2ray", protocol = "vmess" })
|
||||
transport:depends({ type = "V2ray", protocol = "vless" })
|
||||
transport:depends({ type = "V2ray", protocol = "socks" })
|
||||
@ -475,6 +544,7 @@ 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 = "Xray", protocol = "shadowsocks" })
|
||||
ss_transport:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
]]--
|
||||
|
||||
@ -557,7 +627,7 @@ h2_path:depends("trojan_transport", "h2+ws")
|
||||
h2_path:depends("trojan_transport", "h2")
|
||||
|
||||
-- [[ DomainSocket部分 ]]--
|
||||
ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running V2Ray."))
|
||||
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部分 ]]--
|
||||
@ -590,6 +660,11 @@ ss_aead_pwd:depends("ss_aead", "1")
|
||||
|
||||
-- [[ Mux ]]--
|
||||
mux = s:option(Flag, "mux", translate("Mux"))
|
||||
mux:depends({ type = "Xray", protocol = "vmess" })
|
||||
mux:depends({ type = "Xray", protocol = "vless", xtls = false })
|
||||
mux:depends({ type = "Xray", protocol = "http" })
|
||||
mux:depends({ type = "Xray", protocol = "socks" })
|
||||
mux:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
mux:depends({ type = "V2ray", protocol = "vmess" })
|
||||
mux:depends({ type = "V2ray", protocol = "vless" })
|
||||
mux:depends({ type = "V2ray", protocol = "http" })
|
||||
@ -605,6 +680,7 @@ mux_concurrency:depends("mux", "1")
|
||||
--[[
|
||||
tcp_socks = s:option(Flag, "tcp_socks", translate("TCP Open Socks"), translate("When using this TCP node, whether to open the socks proxy at the same time"))
|
||||
tcp_socks.default = 0
|
||||
tcp_socks:depends("type", "Xray")
|
||||
tcp_socks:depends("type", "V2ray")
|
||||
|
||||
tcp_socks_port = s:option(Value, "tcp_socks_port", "Socks " .. translate("Port"), translate("Do not conflict with other ports"))
|
||||
|
||||
@ -63,7 +63,7 @@ if nodes_display:find("compact_display_nodes") then
|
||||
local remarks = m:get(n, "remarks") or ""
|
||||
local type = m:get(n, "type") or ""
|
||||
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.type' value='%s'>", appname, n, type)
|
||||
if type == "V2ray" then
|
||||
if type == "Xray" or type == "V2ray" then
|
||||
local protocol = m:get(n, "protocol")
|
||||
if protocol == "_balancing" then
|
||||
type = type .. " 负载均衡"
|
||||
@ -75,7 +75,11 @@ if nodes_display:find("compact_display_nodes") then
|
||||
local port = m:get(n, "port") or ""
|
||||
str = str .. translate(type) .. ":" .. remarks
|
||||
if address ~= "" and port ~= "" then
|
||||
str = str .. string.format("(%s:%s)", address, port)
|
||||
if datatypes.ip6addr(address) then
|
||||
str = str .. string.format("([%s]:%s)", address, port)
|
||||
else
|
||||
str = str .. string.format("(%s:%s)", address, port)
|
||||
end
|
||||
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.address' value='%s'>", appname, n, address)
|
||||
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.port' value='%s'>", appname, n, port)
|
||||
end
|
||||
@ -102,7 +106,7 @@ else
|
||||
local v = Value.cfgvalue(t, n)
|
||||
if v then
|
||||
result = translate(v)
|
||||
if v == "V2ray" then
|
||||
if v == "Xray" or v == "V2ray" then
|
||||
local protocol = m:get(n, "protocol")
|
||||
if protocol == "_balancing" then
|
||||
result = result .. " 负载均衡"
|
||||
|
||||
@ -49,7 +49,7 @@ for e = 0, 23 do o:value(e, e .. translate("oclock")) end
|
||||
o.default = 0
|
||||
o:depends("auto_update", 1)
|
||||
|
||||
s = m:section(TypedSection, "shunt_rules", "V2ray" .. translate("Shunt") .. translate("Rule"))
|
||||
s = m:section(TypedSection, "shunt_rules", "Xray/V2ray" .. translate("Shunt") .. translate("Rule"))
|
||||
s.template = "cbi/tblsection"
|
||||
s.anonymous = false
|
||||
s.addremove = true
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
local d = require "luci.dispatcher"
|
||||
local appname = "passwall"
|
||||
|
||||
m = Map(appname, "V2ray" .. translate("Shunt") .. translate("Rule"))
|
||||
m = Map(appname, "Xray/V2ray" .. translate("Shunt") .. translate("Rule"))
|
||||
m.redirect = d.build_url("admin", "services", appname)
|
||||
|
||||
s = m:section(NamedSection, arg[1], "shunt_rules", "")
|
||||
@ -13,31 +13,61 @@ remarks.default = arg[1]
|
||||
remarks.rmempty = false
|
||||
|
||||
domain_list = s:option(TextValue, "domain_list", translate("Domain"))
|
||||
domain_list.rows = 15
|
||||
domain_list.rows = 10
|
||||
domain_list.wrap = "off"
|
||||
domain_list.validate = function(self, value)
|
||||
local hosts= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(hosts, w) end)
|
||||
for index, host in ipairs(hosts) do
|
||||
if not datatypes.hostname(host) then
|
||||
return nil, host .. " " .. translate("Not valid domain name, please re-enter!")
|
||||
local flag = 1
|
||||
local tmp_host = host
|
||||
if host:find("regexp:") and host:find("regexp:") == 1 then
|
||||
flag = 0
|
||||
elseif host:find("domain:.") and host:find("domain:.") == 1 then
|
||||
tmp_host = host:gsub("domain:", "")
|
||||
elseif host:find("full:.") and host:find("full:.") == 1 then
|
||||
tmp_host = host:gsub("full:", "")
|
||||
elseif host:find("geosite:") and host:find("geosite:") == 1 then
|
||||
flag = 0
|
||||
elseif host:find("ext:") and host:find("ext:") == 1 then
|
||||
flag = 0
|
||||
end
|
||||
if flag == 1 then
|
||||
if not datatypes.hostname(tmp_host) then
|
||||
return nil, tmp_host .. " " .. translate("Not valid domain name, please re-enter!")
|
||||
end
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
domain_list.description = "<br /><ul><li>" .. translate("Plaintext: If this string matches any part of the targeting domain, this rule takes effet. Example: rule 'sina.com' matches targeting domain 'sina.com', 'sina.com.cn' and 'www.sina.com', but not 'sina.cn'.")
|
||||
.. "</li><li>" .. translate("Regular expression: Begining with 'regexp:', the rest is a regular expression. When the regexp matches targeting domain, this rule takes effect. Example: rule 'regexp:\\.goo.*\\.com$' matches 'www.google.com' and 'fonts.googleapis.com', but not 'google.com'.")
|
||||
.. "</li><li>" .. translate("Subdomain (recommended): Begining with 'domain:' and the rest is a domain. When the targeting domain is exactly the value, or is a subdomain of the value, this rule takes effect. Example: rule 'domain:v2ray.com' matches 'www.v2ray.com', 'v2ray.com', but not 'xv2ray.com'.")
|
||||
.. "</li><li>" .. translate("Full domain: Begining with 'full:' and the rest is a domain. When the targeting domain is exactly the value, the rule takes effect. Example: rule 'domain:v2ray.com' matches 'v2ray.com', but not 'www.v2ray.com'.")
|
||||
.. "</li><li>" .. translate("Pre-defined domain list: Begining with 'geosite:' and the rest is a name, such as geosite:google or geosite:cn.")
|
||||
.. "</li><li>" .. translate("Domains from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geosite.dat. The tag must exist in the file.")
|
||||
.. "</li></ul>"
|
||||
ip_list = s:option(TextValue, "ip_list", "IP")
|
||||
ip_list.rows = 15
|
||||
ip_list.rows = 10
|
||||
ip_list.wrap = "off"
|
||||
ip_list.validate = function(self, value)
|
||||
local ipmasks= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end)
|
||||
for index, ipmask in ipairs(ipmasks) do
|
||||
if not datatypes.ipmask4(ipmask) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!")
|
||||
if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 then
|
||||
elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 then
|
||||
else
|
||||
if not datatypes.ipmask4(ipmask) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!")
|
||||
end
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
ip_list.description = "<br /><ul><li>" .. translate("IP: such as '127.0.0.1'.")
|
||||
.. "</li><li>" .. translate("CIDR: such as '127.0.0.0/8'.")
|
||||
.. "</li><li>" .. translate("GeoIP: such as 'geoip:cn'. It begins with geoip: (lower case) and followed by two letter of country code.")
|
||||
.. "</li><li>" .. translate("IPs from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geoip.dat. The tag must exist in the file.")
|
||||
.. "</li></ul>"
|
||||
|
||||
return m
|
||||
|
||||
3
package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua
Normal file → Executable file
3
package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua
Normal file → Executable file
@ -89,6 +89,9 @@ local function start()
|
||||
end
|
||||
type = type:lower()
|
||||
bin = ln_start("/usr/bin/" .. type .. "-server", type .. "-server", "-c " .. config_file .. " " .. udp_param)
|
||||
elseif type == "Xray" then
|
||||
config = require("luci.model.cbi.passwall.server.api.xray").gen_config(user)
|
||||
bin = ln_start(_api.get_xray_path(), "xray", "-config=" .. config_file)
|
||||
elseif type == "V2ray" then
|
||||
config = require("luci.model.cbi.passwall.server.api.v2ray").gen_config(user)
|
||||
bin = ln_start(_api.get_v2ray_path(), "v2ray", "-config=" .. config_file)
|
||||
|
||||
@ -14,7 +14,7 @@ function gen_config(user)
|
||||
for i = 1, #user.uuid do
|
||||
clients[i] = {
|
||||
id = user.uuid[i],
|
||||
flow = (user.xtls and user.xtls == "1") and user.flow or nil,
|
||||
flow = user.flow or nil,
|
||||
level = tonumber(user.level),
|
||||
alterId = tonumber(user.alter_id)
|
||||
}
|
||||
@ -109,16 +109,6 @@ function gen_config(user)
|
||||
streamSettings = {
|
||||
network = user.transport,
|
||||
security = "none",
|
||||
xtlsSettings = (user.tls and user.tls == "1" and user.xtls and user.xtls == "1") and {
|
||||
--alpn = {"http/1.1"},
|
||||
disableSystemRoot = false,
|
||||
certificates = {
|
||||
{
|
||||
certificateFile = user.tls_certificateFile,
|
||||
keyFile = user.tls_keyFile
|
||||
}
|
||||
}
|
||||
} or nil,
|
||||
tlsSettings = (user.tls and user.tls == "1") and {
|
||||
disableSystemRoot = false,
|
||||
certificates = {
|
||||
@ -176,10 +166,6 @@ function gen_config(user)
|
||||
|
||||
if user.tls and user.tls == "1" then
|
||||
config.inbounds[1].streamSettings.security = "tls"
|
||||
if user.xtls and user.xtls == "1" then
|
||||
config.inbounds[1].streamSettings.security = "xtls"
|
||||
config.inbounds[1].streamSettings.tlsSettings = nil
|
||||
end
|
||||
end
|
||||
|
||||
if user.transport == "mkcp" or user.transport == "quic" then
|
||||
|
||||
@ -0,0 +1,191 @@
|
||||
module("luci.model.cbi.passwall.server.api.xray", package.seeall)
|
||||
local ucic = require"luci.model.uci".cursor()
|
||||
|
||||
function gen_config(user)
|
||||
local settings = nil
|
||||
local routing = nil
|
||||
local outbounds = {
|
||||
{protocol = "freedom", tag = "direct"}, {protocol = "blackhole", tag = "blocked"}
|
||||
}
|
||||
|
||||
if user.protocol == "vmess" or user.protocol == "vless" then
|
||||
if user.uuid then
|
||||
local clients = {}
|
||||
for i = 1, #user.uuid do
|
||||
clients[i] = {
|
||||
id = user.uuid[i],
|
||||
flow = (user.xtls and user.xtls == "1") and user.flow or nil,
|
||||
level = tonumber(user.level),
|
||||
alterId = tonumber(user.alter_id)
|
||||
}
|
||||
end
|
||||
settings = {
|
||||
clients = clients,
|
||||
decryption = user.decryption or "none"
|
||||
}
|
||||
end
|
||||
elseif user.protocol == "socks" then
|
||||
settings = {
|
||||
auth = (user.auth and user.auth == "1") and "password" or "noauth",
|
||||
accounts = (user.auth and user.auth == "1") and {
|
||||
{
|
||||
user = user.username,
|
||||
pass = user.password
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif user.protocol == "http" then
|
||||
settings = {
|
||||
allowTransparent = false,
|
||||
accounts = (user.auth and user.auth == "1") and {
|
||||
{
|
||||
user = user.username,
|
||||
pass = user.password
|
||||
}
|
||||
}
|
||||
}
|
||||
user.transport = "tcp"
|
||||
user.tcp_guise = "none"
|
||||
elseif user.protocol == "shadowsocks" then
|
||||
settings = {
|
||||
method = user.method,
|
||||
password = user.password,
|
||||
level = tonumber(user.level) or 1,
|
||||
network = user.ss_network or "TCP,UDP"
|
||||
}
|
||||
elseif user.protocol == "trojan" then
|
||||
if user.uuid then
|
||||
local clients = {}
|
||||
for i = 1, #user.uuid do
|
||||
clients[i] = {
|
||||
password = user.uuid[i],
|
||||
level = tonumber(user.level)
|
||||
}
|
||||
end
|
||||
settings = {
|
||||
clients = clients
|
||||
}
|
||||
end
|
||||
elseif user.protocol == "mtproto" then
|
||||
settings = {
|
||||
users = {
|
||||
{
|
||||
level = tonumber(user.level) or 1,
|
||||
secret = (user.password == nil) and "" or user.password
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
routing = {
|
||||
domainStrategy = "IPOnDemand",
|
||||
rules = {
|
||||
{
|
||||
type = "field",
|
||||
ip = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"},
|
||||
outboundTag = (user.accept_lan == nil or user.accept_lan == "0") and "blocked" or "direct"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if user.transit_node and user.transit_node ~= "nil" then
|
||||
local gen_xray = require("luci.model.cbi.passwall.api.gen_xray")
|
||||
local client = gen_xray.gen_outbound(ucic:get_all("passwall", user.transit_node), "transit")
|
||||
table.insert(outbounds, 1, client)
|
||||
end
|
||||
|
||||
local config = {
|
||||
log = {
|
||||
-- error = "/var/etc/passwall_server/log/" .. user[".name"] .. ".log",
|
||||
loglevel = "warning"
|
||||
},
|
||||
-- 传入连接
|
||||
inbounds = {
|
||||
{
|
||||
listen = (user.bind_local == "1") and "127.0.0.1" or nil,
|
||||
port = tonumber(user.port),
|
||||
protocol = user.protocol,
|
||||
settings = settings,
|
||||
streamSettings = {
|
||||
network = user.transport,
|
||||
security = "none",
|
||||
xtlsSettings = (user.tls and user.tls == "1" and user.xtls and user.xtls == "1") and {
|
||||
--alpn = {"http/1.1"},
|
||||
disableSystemRoot = false,
|
||||
certificates = {
|
||||
{
|
||||
certificateFile = user.tls_certificateFile,
|
||||
keyFile = user.tls_keyFile
|
||||
}
|
||||
}
|
||||
} or nil,
|
||||
tlsSettings = (user.tls and user.tls == "1") and {
|
||||
disableSystemRoot = false,
|
||||
certificates = {
|
||||
{
|
||||
certificateFile = user.tls_certificateFile,
|
||||
keyFile = user.tls_keyFile
|
||||
}
|
||||
}
|
||||
} or nil,
|
||||
tcpSettings = (user.transport == "tcp") and {
|
||||
header = {
|
||||
type = user.tcp_guise,
|
||||
request = (user.tcp_guise == "http") and {
|
||||
path = user.tcp_guise_http_path or {"/"},
|
||||
headers = {
|
||||
Host = user.tcp_guise_http_host or {}
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
} or nil,
|
||||
kcpSettings = (user.transport == "mkcp") and {
|
||||
mtu = tonumber(user.mkcp_mtu),
|
||||
tti = tonumber(user.mkcp_tti),
|
||||
uplinkCapacity = tonumber(user.mkcp_uplinkCapacity),
|
||||
downlinkCapacity = tonumber(user.mkcp_downlinkCapacity),
|
||||
congestion = (user.mkcp_congestion == "1") and true or false,
|
||||
readBufferSize = tonumber(user.mkcp_readBufferSize),
|
||||
writeBufferSize = tonumber(user.mkcp_writeBufferSize),
|
||||
seed = (user.mkcp_seed and user.mkcp_seed ~= "") and user.mkcp_seed or nil,
|
||||
header = {type = user.mkcp_guise}
|
||||
} or nil,
|
||||
wsSettings = (user.transport == "ws") and {
|
||||
acceptProxyProtocol = false,
|
||||
headers = (user.ws_host) and {Host = user.ws_host} or nil,
|
||||
path = user.ws_path
|
||||
} or nil,
|
||||
httpSettings = (user.transport == "h2") and {
|
||||
path = user.h2_path, host = user.h2_host
|
||||
} or nil,
|
||||
dsSettings = (user.transport == "ds") and {
|
||||
path = user.ds_path
|
||||
} or nil,
|
||||
quicSettings = (user.transport == "quic") and {
|
||||
security = user.quic_security,
|
||||
key = user.quic_key,
|
||||
header = {type = user.quic_guise}
|
||||
} or nil
|
||||
}
|
||||
}
|
||||
},
|
||||
-- 传出连接
|
||||
outbounds = outbounds,
|
||||
routing = routing
|
||||
}
|
||||
|
||||
if user.tls and user.tls == "1" then
|
||||
config.inbounds[1].streamSettings.security = "tls"
|
||||
if user.xtls and user.xtls == "1" then
|
||||
config.inbounds[1].streamSettings.security = "xtls"
|
||||
config.inbounds[1].streamSettings.tlsSettings = nil
|
||||
end
|
||||
end
|
||||
|
||||
if user.transport == "mkcp" or user.transport == "quic" then
|
||||
config.inbounds[1].streamSettings.security = "none"
|
||||
config.inbounds[1].streamSettings.tlsSettings = nil
|
||||
end
|
||||
|
||||
return config
|
||||
end
|
||||
@ -47,7 +47,7 @@ e = t:option(DummyValue, "type", translate("Type"))
|
||||
e.cfgvalue = function(t, n)
|
||||
local v = Value.cfgvalue(t, n)
|
||||
if v then
|
||||
if v == "V2ray" then
|
||||
if v == "Xray" or v == "V2ray" then
|
||||
local protocol = m:get(n, "protocol")
|
||||
return v .. " -> " .. protocol
|
||||
end
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
local d = require "luci.dispatcher"
|
||||
local uci = require"luci.model.uci".cursor()
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local ss_encrypt_method_list = {
|
||||
@ -78,6 +77,9 @@ end
|
||||
if api.is_finded("ssr-server") then
|
||||
type:value("SSR", translate("ShadowsocksR"))
|
||||
end
|
||||
if api.is_finded("xray") then
|
||||
type:value("Xray", translate("Xray"))
|
||||
end
|
||||
if api.is_finded("v2ray") then
|
||||
type:value("V2ray", translate("V2ray"))
|
||||
end
|
||||
@ -104,6 +106,7 @@ protocol:value("socks", "Socks")
|
||||
protocol:value("shadowsocks", "Shadowsocks")
|
||||
protocol:value("trojan", "Trojan")
|
||||
protocol:value("mtproto", "MTProto")
|
||||
protocol:depends("type", "Xray")
|
||||
protocol:depends("type", "V2ray")
|
||||
|
||||
-- Brook协议
|
||||
@ -137,6 +140,8 @@ auth.validate = function(self, value, t)
|
||||
return value
|
||||
end
|
||||
auth:depends("type", "Socks")
|
||||
auth:depends({ type = "Xray", protocol = "socks" })
|
||||
auth:depends({ type = "Xray", protocol = "http" })
|
||||
auth:depends({ type = "V2ray", protocol = "socks" })
|
||||
auth:depends({ type = "V2ray", protocol = "http" })
|
||||
|
||||
@ -149,9 +154,11 @@ password:depends("auth", "1")
|
||||
password:depends("type", "SS")
|
||||
password:depends("type", "SSR")
|
||||
password:depends("type", "Brook")
|
||||
password:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
password:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
|
||||
mtproto_password = s:option(Value, "mtproto_password", translate("Password"), translate("The MTProto protocol must be 32 characters and can only contain characters from 0 to 9 and a to f."))
|
||||
mtproto_password:depends({ type = "Xray", protocol = "mtproto" })
|
||||
mtproto_password:depends({ type = "V2ray", protocol = "mtproto" })
|
||||
mtproto_password.default = arg[1]
|
||||
function mtproto_password.cfgvalue(self, section)
|
||||
@ -163,6 +170,7 @@ end
|
||||
|
||||
decryption = s:option(Value, "decryption", translate("Encrypt Method"))
|
||||
decryption.default = "none"
|
||||
decryption:depends({ type = "Xray", protocol = "vless" })
|
||||
decryption:depends({ type = "V2ray", protocol = "vless" })
|
||||
|
||||
ss_encrypt_method = s:option(ListValue, "ss_encrypt_method", translate("Encrypt Method"))
|
||||
@ -187,6 +195,7 @@ 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 = "Xray", protocol = "shadowsocks" })
|
||||
v_ss_encrypt_method:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
function v_ss_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
@ -200,6 +209,7 @@ 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 = "Xray", protocol = "shadowsocks" })
|
||||
ss_network:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
|
||||
ssr_protocol = s:option(ListValue, "ssr_protocol", translate("Protocol"))
|
||||
@ -237,6 +247,9 @@ uuid = s:option(DynamicList, "uuid", translate("ID") .. "/" .. translate("Passwo
|
||||
for i = 1, 3 do
|
||||
uuid:value(api.gen_uuid(1))
|
||||
end
|
||||
uuid:depends({ type = "Xray", protocol = "vmess" })
|
||||
uuid:depends({ type = "Xray", protocol = "vless" })
|
||||
uuid:depends({ type = "Xray", protocol = "trojan" })
|
||||
uuid:depends({ type = "V2ray", protocol = "vmess" })
|
||||
uuid:depends({ type = "V2ray", protocol = "vless" })
|
||||
uuid:depends({ type = "V2ray", protocol = "trojan" })
|
||||
@ -246,10 +259,16 @@ uuid:depends("type", "Trojan-Plus")
|
||||
|
||||
alter_id = s:option(Value, "alter_id", translate("Alter ID"))
|
||||
alter_id.default = 16
|
||||
alter_id:depends({ type = "Xray", protocol = "vmess" })
|
||||
alter_id:depends({ type = "V2ray", protocol = "vmess" })
|
||||
|
||||
level = s:option(Value, "level", translate("User Level"))
|
||||
level.default = 1
|
||||
level:depends({ type = "Xray", protocol = "vmess" })
|
||||
level:depends({ type = "Xray", protocol = "vless" })
|
||||
level:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
level:depends({ type = "Xray", protocol = "trojan" })
|
||||
level:depends({ type = "Xray", protocol = "mtproto" })
|
||||
level:depends({ type = "V2ray", protocol = "vmess" })
|
||||
level:depends({ type = "V2ray", protocol = "vless" })
|
||||
level:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
@ -274,6 +293,10 @@ tls.validate = function(self, value, t)
|
||||
return value
|
||||
end
|
||||
end
|
||||
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 = "V2ray", protocol = "vmess" })
|
||||
tls:depends({ type = "V2ray", protocol = "vless" })
|
||||
tls:depends({ type = "V2ray", protocol = "socks" })
|
||||
@ -284,12 +307,16 @@ tls:depends("type", "Trojan-Go")
|
||||
|
||||
xtls = s:option(Flag, "xtls", translate("XTLS"))
|
||||
xtls.default = 0
|
||||
xtls:depends({ type = "V2ray", protocol = "vless", tls = "1" })
|
||||
xtls:depends({ type = "Xray", protocol = "vless", tls = "1" })
|
||||
|
||||
flow = s:option(Value, "flow", translate("flow"))
|
||||
flow.default = "xtls-rprx-origin"
|
||||
flow:value("xtls-rprx-origin")
|
||||
flow:value("xtls-rprx-origin-udp443")
|
||||
flow:value("xtls-rprx-direct")
|
||||
flow:value("xtls-rprx-direct-udp443")
|
||||
flow:value("xtls-rprx-splice")
|
||||
flow:value("xtls-rprx-splice-udp443")
|
||||
flow:depends("xtls", "1")
|
||||
|
||||
-- [[ TLS部分 ]] --
|
||||
@ -342,6 +369,11 @@ transport:value("ws", "WebSocket")
|
||||
transport:value("h2", "HTTP/2")
|
||||
transport:value("ds", "DomainSocket")
|
||||
transport:value("quic", "QUIC")
|
||||
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" })
|
||||
transport:depends({ type = "V2ray", protocol = "vmess" })
|
||||
transport:depends({ type = "V2ray", protocol = "vless" })
|
||||
transport:depends({ type = "V2ray", protocol = "socks" })
|
||||
@ -460,7 +492,7 @@ 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 V2Ray."))
|
||||
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部分 ]]--
|
||||
@ -480,6 +512,7 @@ quic_guise:depends("transport", "quic")
|
||||
-- [[ VLESS Fallback部分 ]]--
|
||||
--[[
|
||||
fallback = s:option(Flag, "fallback", translate("Fallback"))
|
||||
fallback:depends({ type = "Xray", protocol = "vless", transport = "tcp", tls = "1" })
|
||||
fallback:depends({ type = "V2ray", protocol = "vless", transport = "tcp", tls = "1" })
|
||||
|
||||
fallback_alpn = s:option(Value, "fallback_alpn", "Fallback alpn")
|
||||
@ -517,7 +550,7 @@ 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 WebSocket and more."))
|
||||
remote_enable = s:option(Flag, "remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/Xray/V2ray WebSocket and more."))
|
||||
remote_enable.default = "1"
|
||||
remote_enable.rmempty = false
|
||||
remote_enable:depends("type", "Trojan")
|
||||
@ -535,16 +568,18 @@ 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."))
|
||||
bind_local.default = "0"
|
||||
bind_local:depends("type", "Xray")
|
||||
bind_local:depends("type", "V2ray")
|
||||
|
||||
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.rmempty = false
|
||||
accept_lan:depends("type", "Xray")
|
||||
accept_lan:depends("type", "V2ray")
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" and e.type == "V2ray" then
|
||||
if e.node_type == "normal" and e.type == "Xray" or e.type == "V2ray" then
|
||||
nodes_table[#nodes_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e.remarks_name
|
||||
@ -556,6 +591,7 @@ transit_node = s:option(ListValue, "transit_node", translate("transit node"))
|
||||
transit_node:value("nil", translate("Close"))
|
||||
for k, v in pairs(nodes_table) do transit_node:value(v.id, v.remarks) end
|
||||
transit_node.default = "nil"
|
||||
transit_node:depends("type", "Xray")
|
||||
transit_node:depends("type", "V2ray")
|
||||
|
||||
return m
|
||||
|
||||
@ -6,6 +6,7 @@ local brook_version = require "luci.model.cbi.passwall.api.api".get_brook_versio
|
||||
//<![CDATA[
|
||||
var brookInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
@ -48,7 +49,7 @@ local brook_version = require "luci.model.cbi.passwall.api.api".get_brook_versio
|
||||
|
||||
function onRequestError_brook(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = btn.placeholder;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if(errorMessage) {
|
||||
alert(errorMessage);
|
||||
|
||||
@ -6,6 +6,7 @@ local kcptun_version = require "luci.model.cbi.passwall.api.api".get_kcptun_vers
|
||||
//<![CDATA[
|
||||
var kcptunInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
@ -48,7 +49,7 @@ local kcptun_version = require "luci.model.cbi.passwall.api.api".get_kcptun_vers
|
||||
|
||||
function onRequestError_kcptun(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = btn.placeholder;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if(errorMessage) {
|
||||
alert(errorMessage);
|
||||
|
||||
@ -6,6 +6,7 @@ local trojan_go_version = require "luci.model.cbi.passwall.api.api".get_trojan_g
|
||||
//<![CDATA[
|
||||
var trojanInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
@ -48,7 +49,7 @@ local trojan_go_version = require "luci.model.cbi.passwall.api.api".get_trojan_g
|
||||
|
||||
function onRequestError_trojan(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = btn.placeholder;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if(errorMessage) {
|
||||
alert(errorMessage);
|
||||
|
||||
@ -6,6 +6,7 @@ local v2ray_version = require "luci.model.cbi.passwall.api.api".get_v2ray_versio
|
||||
//<![CDATA[
|
||||
var v2rayInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
@ -48,7 +49,7 @@ local v2ray_version = require "luci.model.cbi.passwall.api.api".get_v2ray_versio
|
||||
|
||||
function onRequestError_v2ray(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = btn.placeholder;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if(errorMessage) {
|
||||
alert(errorMessage);
|
||||
|
||||
@ -0,0 +1,172 @@
|
||||
<%
|
||||
local xray_version = require "luci.model.cbi.passwall.api.api".get_xray_version()
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var xrayInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
var inProgressText = '<%:Updating...%>';
|
||||
var unexpectedErrorText = '<%:Unexpected error%>';
|
||||
var updateInProgressNotice = '<%:Updating, are you sure to close?%>';
|
||||
var downloadingText = '<%:Downloading...%>';
|
||||
var decompressioningText = '<%:Unpacking...%>';
|
||||
var movingText = '<%:Moving...%>';
|
||||
|
||||
window.onload = function() {
|
||||
var xrayCheckBtn = document.getElementById('_xray-check_btn');
|
||||
var xrayDetailElm = document.getElementById('_xray-check_btn-detail');
|
||||
};
|
||||
|
||||
function addPageNotice_xray() {
|
||||
window.onbeforeunload = function(e) {
|
||||
e.returnValue = updateInProgressNotice;
|
||||
return updateInProgressNotice;
|
||||
};
|
||||
}
|
||||
|
||||
function removePageNotice_xray() {
|
||||
window.onbeforeunload = undefined;
|
||||
}
|
||||
|
||||
function onUpdateSuccess_xray(btn) {
|
||||
alert(updateSuccessText);
|
||||
|
||||
if(btn) {
|
||||
btn.value = updateSuccessText;
|
||||
btn.placeholder = updateSuccessText;
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
window.setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onRequestError_xray(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if(errorMessage) {
|
||||
alert(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onBtnClick_xray(btn) {
|
||||
if(xrayInfo === undefined) {
|
||||
checkUpdate_xray(btn);
|
||||
} else {
|
||||
doUpdate_xray(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate_xray(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = inProgressText;
|
||||
|
||||
addPageNotice_xray();
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
|
||||
XHR.get('<%=url([[admin]], [[services]], [[passwall]], [[xray_check]])%>', {
|
||||
token: tokenStr,
|
||||
arch: ''
|
||||
}, function(x,json) {
|
||||
removePageNotice_xray();
|
||||
|
||||
if(json.code) {
|
||||
xrayInfo = undefined;
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
if(json.update) {
|
||||
xrayInfo = json;
|
||||
btn.disabled = false;
|
||||
btn.value = clickToUpdateText;
|
||||
btn.placeholder = clickToUpdateText;
|
||||
|
||||
if(ckeckDetailElm) {
|
||||
var urlNode = '';
|
||||
if(json.version) {
|
||||
urlNode = '<em style="color:red;">最新版本号:' + json.version + '</em>';
|
||||
if(json.url && json.url.html) {
|
||||
urlNode = '<a href="' + json.url.html + '" target="_blank">' + urlNode + '</a>';
|
||||
}
|
||||
}
|
||||
ckeckDetailElm.innerHTML = urlNode;
|
||||
}
|
||||
} else {
|
||||
btn.disabled = true;
|
||||
btn.value = noUpdateText;
|
||||
}
|
||||
}
|
||||
},300);
|
||||
}
|
||||
|
||||
function doUpdate_xray(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = downloadingText;
|
||||
|
||||
addPageNotice_xray();
|
||||
|
||||
var xrayUpdateUrl = '<%=url([[admin]], [[services]], [[passwall]], [[xray_update]])%>';
|
||||
// Download file
|
||||
XHR.get(xrayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
url: xrayInfo ? xrayInfo.url.download : ''
|
||||
}, function(x,json) {
|
||||
if(json.code) {
|
||||
removePageNotice_xray();
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
btn.value = decompressioningText;
|
||||
|
||||
// Extract file
|
||||
XHR.get(xrayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'extract',
|
||||
file: json.file,
|
||||
subfix: xrayInfo ? xrayInfo.type : ''
|
||||
}, function(x,json) {
|
||||
if(json.code) {
|
||||
removePageNotice_xray();
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
btn.value = movingText;
|
||||
|
||||
// Move file to target dir
|
||||
XHR.get(xrayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'move',
|
||||
file: json.file
|
||||
}, function(x,json) {
|
||||
removePageNotice_xray();
|
||||
if(json.code) {
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
onUpdateSuccess_xray(btn);
|
||||
}
|
||||
},300)
|
||||
}
|
||||
},300)
|
||||
}
|
||||
},300)
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title">Xray
|
||||
<%:Version%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=xray_version%> 】</span>
|
||||
<input class="cbi-button cbi-input-apply" type="button" id="_xray-check_btn" onclick="onBtnClick_xray(this);" value="<%:Manually update%>" />
|
||||
<span id="_xray-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,3 +1,9 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local tcp_node_num = api.uci_get_type("global_other", "tcp_node_num", 1)
|
||||
local udp_node_num = api.uci_get_type("global_other", "udp_node_num", 1)
|
||||
local auto_switch = api.uci_get_type("auto_switch", "enable", 0)
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var _status = document.getElementsByClassName('_status');
|
||||
@ -29,5 +35,40 @@
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var global = document.getElementById("cbi-passwall-global");
|
||||
if (global) {
|
||||
var node = global.getElementsByClassName("cbi-section-node")[0];
|
||||
var node_id = node.getAttribute("id");
|
||||
|
||||
for (var i = 0; i <= 1; i++) {
|
||||
var proto = "udp";
|
||||
var num = <%=udp_node_num%>;
|
||||
if (i == 0) {
|
||||
if (<%=auto_switch%> > 0) {
|
||||
continue;
|
||||
}
|
||||
proto = "tcp";
|
||||
num = <%=tcp_node_num%>;
|
||||
}
|
||||
if (num >= 1) {
|
||||
for (var j = 0; j < num; j++) {
|
||||
var index = j + 1;
|
||||
var node = document.getElementById(node_id + "-" + proto + "_node" + index);
|
||||
var node_select = document.getElementById(node.id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), "."));
|
||||
var node_select_value = node_select.value;
|
||||
if (node_select_value && node_select_value != "nil") {
|
||||
var new_a = document.createElement("a");
|
||||
new_a.setAttribute("href","node_config/" + node_select_value);
|
||||
new_a.innerHTML = "<%:Edit Current Node%>";
|
||||
|
||||
node_select.outerHTML = node_select.outerHTML + "  " + new_a.outerHTML;
|
||||
//node_select.parentNode.insertBefore(new_a, node_select.nextSibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
@ -172,14 +172,14 @@ local dsp = require "luci.dispatcher"
|
||||
"&protoparam=" + b64encsafe(v_protocol_param.value) +
|
||||
"&remarks=" + b64encutf8safe(v_alias.value);
|
||||
url = b64encsafe(ssr_str);
|
||||
} else if (v_type === "Trojan" || v_type === "Trojan-Go") {
|
||||
} else if (v_type === "Trojan" || v_type === "Trojan-Plus" || v_type === "Trojan-Go") {
|
||||
var v_password = opt.get(!opt.client && v_type === "Trojan-Go" ? "passwords" : "password");
|
||||
var v_server = opt.get("address");
|
||||
var v_port = opt.get("port");
|
||||
url = encodeURIComponent(v_password.value) +
|
||||
"@" + v_server.value +
|
||||
":" + v_port.value + "/?";
|
||||
if (opt.get("stream_security").value === "tls") {
|
||||
if (opt.get("tls").checked) {
|
||||
url += "tls=1";
|
||||
url += opt.query("sni", "tls_serverName");
|
||||
url += opt.query("allowinsecure", "tls_allowInsecure");
|
||||
@ -187,7 +187,7 @@ local dsp = require "luci.dispatcher"
|
||||
url += "tls=0";
|
||||
}
|
||||
if (v_type === "Trojan-Go") {
|
||||
if (opt.get("stream_security").value === "none" && opt.get("trojan_transport").value === "original") {
|
||||
if (!opt.get("tls").checked && opt.get("trojan_transport").value === "original") {
|
||||
var plugin = {};
|
||||
plugin.type = opt.get("plugin_type").value;
|
||||
if (plugin.type !== "plaintext") {
|
||||
@ -432,8 +432,8 @@ local dsp = require "luci.dispatcher"
|
||||
tls = queryParam.plugin === undefined;
|
||||
}
|
||||
if (tls === false) { alert("TODO: plugin params for trojan-go."); }
|
||||
opt.set('stream_security', tls ? "tls" : "none");
|
||||
opt.get('stream_security').dispatchEvent(event);
|
||||
opt.set('tls', tls);
|
||||
opt.get('tls').dispatchEvent(event);
|
||||
if (tls) {
|
||||
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
|
||||
opt.set('tls_allowInsecure', queryParam.allowinsecure === '1');
|
||||
@ -490,8 +490,8 @@ local dsp = require "luci.dispatcher"
|
||||
opt.set('address', m.hostname);
|
||||
opt.set('port', m.port || "443");
|
||||
opt.set(opt.client ? 'password' : 'passwords', decodeURIComponent(password));
|
||||
opt.set('stream_security', (queryParam.tls && queryParam.tls === '1') ? 'tls' : 'none');
|
||||
opt.get('stream_security').dispatchEvent(event);
|
||||
opt.set('tls', (queryParam.tls && queryParam.tls === '1'));
|
||||
opt.get('tls').dispatchEvent(event);
|
||||
var plugin = queryParam.plugin !== undefined;
|
||||
if (plugin) {
|
||||
opt.set('trojan_transport', 'original');
|
||||
@ -509,8 +509,8 @@ local dsp = require "luci.dispatcher"
|
||||
alert(queryParam.plugin);
|
||||
}
|
||||
var tls = !plugin && queryParam.tls === '1';
|
||||
opt.set('stream_security', tls ? "tls" : "none");
|
||||
opt.get('stream_security').dispatchEvent(event);
|
||||
opt.set('tls', tls);
|
||||
opt.get('tls').dispatchEvent(event);
|
||||
if (tls) {
|
||||
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
|
||||
opt.set('tls_allowInsecure', queryParam.allowinsecure === '1');
|
||||
@ -562,13 +562,14 @@ local dsp = require "luci.dispatcher"
|
||||
param = sstr.substr(ploc + 2);
|
||||
}
|
||||
var ssm = JSON.parse(sstr);
|
||||
console.log(ssm);
|
||||
opt.set('remarks', ssm.ps);
|
||||
opt.set('address', ssm.add);
|
||||
opt.set('port', ssm.port);
|
||||
opt.set('alter_id', ssm.aid);
|
||||
opt.set('uuid', ssm.id);
|
||||
opt.set('stream_security', ssm.tls === "tls" ? "tls" : "none");
|
||||
opt.get('stream_security').dispatchEvent(event);
|
||||
opt.set('tls', ssm.tls === "tls");
|
||||
opt.get('tls').dispatchEvent(event);
|
||||
if (ssm.tls === "tls") {
|
||||
opt.set('tls_serverName', ssm.host);
|
||||
}
|
||||
|
||||
@ -97,6 +97,10 @@ table td, .table .td {
|
||||
}
|
||||
}
|
||||
|
||||
function copy_node(cbi_id) {
|
||||
window.location.href = '<%=dsp.build_url("admin/services/passwall/copy_node")%>' + "?section=" + cbi_id;
|
||||
}
|
||||
|
||||
var section = "";
|
||||
function open_set_node_div(cbi_id) {
|
||||
section = cbi_id;
|
||||
@ -212,7 +216,7 @@ table td, .table .td {
|
||||
catch(err){}
|
||||
//判断是否含有汉字
|
||||
var reg = new RegExp("[\\u4E00-\\u9FFF]+","g");
|
||||
if ((address != null && address != "") && (port != null && port != "") && reg.test(address) == false && (address.indexOf(".") != -1 && address.charAt(address.address - 1) != ".")) {
|
||||
if ((address != null && address != "") && (port != null && port != "") && reg.test(address) == false) {
|
||||
return { address: address, port: port };
|
||||
} else {
|
||||
return null;
|
||||
@ -408,12 +412,14 @@ table td, .table .td {
|
||||
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1);
|
||||
var td = edit_btn[i].parentNode;
|
||||
var new_div = "";
|
||||
//添加"置顶"按钮到"修改"按钮前
|
||||
//添加"置顶"按钮
|
||||
new_div += '<input class="cbi-button" type="button" value="<%:To Top%>" onclick="_cbi_row_top(\'' + id + '\')"/> ';
|
||||
//添加"选择"按钮到"修改"按钮前
|
||||
//添加"选择"按钮
|
||||
new_div += '<input class="cbi-button cbi-button-add" type="button" value="<%:Select%>" id="select_' + id + '" onclick="select_node_div(this, \'' + id + '\')"/> ';
|
||||
//添加"应用"按钮到"修改"按钮前
|
||||
//添加"应用"按钮
|
||||
new_div += '<input class="cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_' + id + '" onclick="open_set_node_div(\'' + id + '\')"/> ';
|
||||
//添加"复制"按钮
|
||||
new_div += '<input class="cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node(\'' + id + '\')"/> ';
|
||||
td.innerHTML = new_div + td.innerHTML;
|
||||
}
|
||||
catch(err) {
|
||||
|
||||
@ -112,6 +112,9 @@ msgstr "TCP节点"
|
||||
msgid "UDP Node"
|
||||
msgstr "UDP节点"
|
||||
|
||||
msgid "Edit Current Node"
|
||||
msgstr "编辑当前节点"
|
||||
|
||||
msgid "Socks Config"
|
||||
msgstr "Socks配置"
|
||||
|
||||
@ -391,12 +394,18 @@ msgstr "类型"
|
||||
msgid "Balancing"
|
||||
msgstr "负载均衡"
|
||||
|
||||
msgid "Xray_balancing"
|
||||
msgstr "Xray 负载均衡"
|
||||
|
||||
msgid "V2ray_balancing"
|
||||
msgstr "V2ray 负载均衡"
|
||||
|
||||
msgid "Shunt"
|
||||
msgstr "分流"
|
||||
|
||||
msgid "Xray_shunt"
|
||||
msgstr "Xray 分流"
|
||||
|
||||
msgid "V2ray_shunt"
|
||||
msgstr "V2ray 分流"
|
||||
|
||||
@ -406,6 +415,21 @@ msgstr "前置代理"
|
||||
msgid "Use the default node for the transit."
|
||||
msgstr "使用默认节点代理转发。"
|
||||
|
||||
msgid "No shunt rules? Click me to go to add."
|
||||
msgstr "没有分流规则?点我前往去添加。"
|
||||
|
||||
msgid "Domain Strategy"
|
||||
msgstr "域名解析策略"
|
||||
|
||||
msgid "'AsIs': Only use domain for routing. Default value."
|
||||
msgstr "AsIs:只使用域名进行路由选择。默认值。"
|
||||
|
||||
msgid "'IPIfNonMatch': When no rule matches current domain, resolves it into IP addresses (A or AAAA records) and try all rules again."
|
||||
msgstr "IPIfNonMatch:当域名没有匹配任何规则时,将域名解析成 IP(A 记录或 AAAA 记录)再次进行匹配。"
|
||||
|
||||
msgid "'IPOnDemand': As long as there is a IP-based rule, resolves the domain into IP immediately."
|
||||
msgstr "IPOnDemand:当匹配时碰到任何基于 IP 的规则,将域名立即解析为 IP 进行匹配。"
|
||||
|
||||
msgid "Load balancing node list"
|
||||
msgstr "负载均衡节点列表"
|
||||
|
||||
@ -505,6 +529,9 @@ msgstr "应用"
|
||||
msgid "Use"
|
||||
msgstr "使用"
|
||||
|
||||
msgid "Copy"
|
||||
msgstr "复制"
|
||||
|
||||
msgid "Delay Settings"
|
||||
msgstr "定时配置"
|
||||
|
||||
@ -613,6 +640,12 @@ msgstr "主节点"
|
||||
msgid "List of backup nodes"
|
||||
msgstr "备用节点的列表"
|
||||
|
||||
msgid "Restore Switch"
|
||||
msgstr "恢复切换"
|
||||
|
||||
msgid "When detects main node is available, switch back to the main node."
|
||||
msgstr "当检测到主节点可用时,切换回主节点。"
|
||||
|
||||
msgid "Configure this node with 127.0.0.1: this port"
|
||||
msgstr "使用127.0.0.1和此端口配置节点"
|
||||
|
||||
@ -637,6 +670,9 @@ msgstr "负载均衡端口"
|
||||
msgid "Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group."
|
||||
msgstr "添加节点,指定出口功能是为多WAN用户准备的。负载比重范围1-256。多个主服务器可以负载均衡,备用只有在主服务器离线时才会启用!可以设置多个组,负载均衡端口相同则为一组。"
|
||||
|
||||
msgid "Note that the node configuration parameters for load balancing must be consistent, otherwise problems can arise!"
|
||||
msgstr "注意,负载均衡的节点配置参数必须一致,否则会出问题!"
|
||||
|
||||
msgid "Node"
|
||||
msgstr "节点"
|
||||
|
||||
@ -739,23 +775,20 @@ msgstr "组件更新"
|
||||
msgid "Please confirm that your firmware supports FPU."
|
||||
msgstr "请确认你的固件支持FPU。"
|
||||
|
||||
msgid "if you want to run from memory, change the path, such as %s, Then save the application and update it manually."
|
||||
msgstr "如果你希望从内存中运行,请更改路径,例如%s,然后保存应用后,再手动更新。"
|
||||
msgid "if you want to run from memory, change the path, /tmp beginning then save the application and update it manually."
|
||||
msgstr "如果你希望从内存中运行,请更改路径,/tmp 开头,然后保存应用后,再手动更新。"
|
||||
|
||||
msgid "Make sure there is enough space to install %s"
|
||||
msgstr "确保有足够的空间安装%s"
|
||||
msgstr "确保有足够的空间安装 %s"
|
||||
|
||||
msgid "V2ray Path"
|
||||
msgstr "V2ray 路径"
|
||||
msgid "App Path"
|
||||
msgstr "程序路径"
|
||||
|
||||
msgid "Trojan-Go Path"
|
||||
msgstr "Trojan-Go 路径"
|
||||
msgid "%s App Path"
|
||||
msgstr "%s 程序路径"
|
||||
|
||||
msgid "Kcptun Client Path"
|
||||
msgstr "Kcptun 客户端路径"
|
||||
|
||||
msgid "Brook Path"
|
||||
msgstr "Brook 路径"
|
||||
msgid "%s Client App Path"
|
||||
msgstr "%s 客户端程序路径"
|
||||
|
||||
msgid "Trojan-Go Version API"
|
||||
msgstr "Trojan-Go 版本 API"
|
||||
@ -838,6 +871,36 @@ msgstr "不是有效域名,请重新输入!"
|
||||
msgid "Not valid IP format, please re-enter!"
|
||||
msgstr "不是有效IP格式,请重新输入!"
|
||||
|
||||
msgid "Plaintext: If this string matches any part of the targeting domain, this rule takes effet. Example: rule 'sina.com' matches targeting domain 'sina.com', 'sina.com.cn' and 'www.sina.com', but not 'sina.cn'."
|
||||
msgstr "纯字符串: 当此字符串匹配目标域名中任意部分,该规则生效。比如'sina.com'可以匹配'sina.com'、'sina.com.cn'和'www.sina.com',但不匹配'sina.cn'。"
|
||||
|
||||
msgid "Regular expression: Begining with 'regexp:', the rest is a regular expression. When the regexp matches targeting domain, this rule takes effect. Example: rule 'regexp:\\.goo.*\\.com$' matches 'www.google.com' and 'fonts.googleapis.com', but not 'google.com'."
|
||||
msgstr "正则表达式: 由'regexp:'开始,余下部分是一个正则表达式。当此正则表达式匹配目标域名时,该规则生效。例如'regexp:\\.goo.*\\.com$'匹配'www.google.com'、'fonts.googleapis.com',但不匹配'google.com'。"
|
||||
|
||||
msgid "Subdomain (recommended): Begining with 'domain:' and the rest is a domain. When the targeting domain is exactly the value, or is a subdomain of the value, this rule takes effect. Example: rule 'domain:v2ray.com' matches 'www.v2ray.com', 'v2ray.com', but not 'xv2ray.com'."
|
||||
msgstr "子域名 (推荐): 由'domain:'开始,余下部分是一个域名。当此域名是目标域名或其子域名时,该规则生效。例如'domain:v2ray.com'匹配'www.v2ray.com'、'v2ray.com',但不匹配'xv2ray.com'。"
|
||||
|
||||
msgid "Full domain: Begining with 'full:' and the rest is a domain. When the targeting domain is exactly the value, the rule takes effect. Example: rule 'domain:v2ray.com' matches 'v2ray.com', but not 'www.v2ray.com'."
|
||||
msgstr "完整匹配: 由'full:'开始,余下部分是一个域名。当此域名完整匹配目标域名时,该规则生效。例如'full:v2ray.com'匹配'v2ray.com'但不匹配'www.v2ray.com'。"
|
||||
|
||||
msgid "Pre-defined domain list: Begining with 'geosite:' and the rest is a name, such as geosite:google or geosite:cn."
|
||||
msgstr "预定义域名列表:由'geosite:'开头,余下部分是一个名称,如geosite:google或者geosite:cn。"
|
||||
|
||||
msgid "Domains from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geosite.dat. The tag must exist in the file."
|
||||
msgstr "从文件中加载域名: 形如'ext:file:tag',必须以ext:(小写)开头,后面跟文件名和标签,文件存放在资源目录中,文件格式与geosite.dat相同,标签必须在文件中存在。"
|
||||
|
||||
msgid "IP: such as '127.0.0.1'."
|
||||
msgstr "IP: 形如'127.0.0.1'。"
|
||||
|
||||
msgid "CIDR: such as '127.0.0.0/8'."
|
||||
msgstr "CIDR: 形如'10.0.0.0/8'."
|
||||
|
||||
msgid "GeoIP: such as 'geoip:cn'. It begins with geoip: (lower case) and followed by two letter of country code."
|
||||
msgstr "GeoIP: 形如'geoip:cn',必须以geoip:(小写)开头,后面跟双字符国家代码,支持几乎所有可以上网的国家。"
|
||||
|
||||
msgid "IPs from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geoip.dat. The tag must exist in the file."
|
||||
msgstr "从文件中加载 IP: 形如'ext:file:tag',必须以ext:(小写)开头,后面跟文件名和标签,文件存放在资源目录中,文件格式与geoip.dat相同标签必须在文件中存在。"
|
||||
|
||||
msgid "Clear logs"
|
||||
msgstr "清空日志"
|
||||
|
||||
@ -868,6 +931,9 @@ msgstr "插件"
|
||||
msgid "opts"
|
||||
msgstr "插件选项"
|
||||
|
||||
msgid "Xray is currently directly compatible with V2ray and used."
|
||||
msgstr "Xray 目前可直接兼容 V2ray 并使用。"
|
||||
|
||||
msgid "Protocol"
|
||||
msgstr "协议名称"
|
||||
|
||||
@ -955,8 +1021,8 @@ msgstr "是否允许不安全连接。当勾选时,将跳过证书验证。"
|
||||
msgid "<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)"
|
||||
msgstr "<br />none:默认值,不进行伪装,发送的数据是没有特征的数据包。<br />srtp:伪装成 SRTP 数据包,会被识别为视频通话数据(如 FaceTime)。<br />utp:伪装成 uTP 数据包,会被识别为 BT 下载数据。<br />wechat-video:伪装成微信视频通话的数据包。<br />dtls:伪装成 DTLS 1.2 数据包。<br />wireguard:伪装成 WireGuard 数据包。(并不是真正的 WireGuard 协议)"
|
||||
|
||||
msgid "A legal file path. This file must not exist before running V2Ray."
|
||||
msgstr "一个合法的文件路径。在运行 V2Ray 之前,这个文件必须不存在。"
|
||||
msgid "A legal file path. This file must not exist before running."
|
||||
msgstr "一个合法的文件路径。在运行之前,这个文件必须不存在。"
|
||||
|
||||
msgid "TCP Open Socks"
|
||||
msgstr "开启Socks"
|
||||
@ -1069,6 +1135,12 @@ msgstr "日志"
|
||||
msgid "UDP Forward"
|
||||
msgstr "UDP转发"
|
||||
|
||||
msgid "You did not fill in the %s path. Please save and apply then update manually."
|
||||
msgstr "您没有填写 %s 路径。请保存应用后再手动更新。"
|
||||
|
||||
msgid "Not installed unzip, Can't unzip!"
|
||||
msgstr "未安装unzip,无法解压。"
|
||||
|
||||
msgid "Can't determine ARCH, or ARCH not supported."
|
||||
msgstr "无法确认ARCH架构,或是不支持。"
|
||||
|
||||
|
||||
@ -48,6 +48,7 @@ config global_rules
|
||||
option chnroute6_url 'https://ispip.clang.cn/all_cn_ipv6.txt'
|
||||
|
||||
config global_app
|
||||
option xray_file '/usr/bin/xray/'
|
||||
option v2ray_file '/usr/bin/v2ray/'
|
||||
option trojan_go_file '/usr/bin/trojan-go'
|
||||
option kcptun_client_file '/usr/bin/kcptun-client'
|
||||
|
||||
@ -25,5 +25,25 @@ uci -q batch <<-EOF >/dev/null
|
||||
commit uhttpd
|
||||
EOF
|
||||
|
||||
tmp=$(uci -q get passwall.@global_app[0].xray_file)
|
||||
if [ -z "$tmp" ]; then
|
||||
uci set passwall.@global_app[0].xray_file="/usr/bin/xray"
|
||||
else
|
||||
[ -n "$(echo $tmp | grep -E "^.*/xray/$")" ] && {
|
||||
uci set passwall.@global_app[0].xray_file="${tmp%?}"
|
||||
}
|
||||
fi
|
||||
tmp=$(uci -q get passwall.@global_app[0].v2ray_file)
|
||||
if [ -z "$tmp" ]; then
|
||||
uci set passwall.@global_app[0].v2ray_file="/usr/bin/v2ray"
|
||||
else
|
||||
[ -n "$(echo $tmp | grep -E "^.*/v2ray/$")" ] && {
|
||||
uci set passwall.@global_app[0].v2ray_file="${tmp%?}"
|
||||
}
|
||||
fi
|
||||
uci commit passwall
|
||||
|
||||
/etc/init.d/https-dns-proxy stop >/dev/null 2>&1 &
|
||||
/etc/init.d/https-dns-proxy disable >/dev/null 2>&1 &
|
||||
rm -rf /tmp/luci-*cache
|
||||
exit 0
|
||||
|
||||
@ -26,6 +26,8 @@ use_tcp_node_resolve_dns=0
|
||||
use_udp_node_resolve_dns=0
|
||||
LUA_API_PATH=/usr/lib/lua/luci/model/cbi/$CONFIG/api
|
||||
API_GEN_SS=$LUA_API_PATH/gen_shadowsocks.lua
|
||||
API_GEN_XRAY=$LUA_API_PATH/gen_xray.lua
|
||||
API_GEN_XRAY_PROTO=$LUA_API_PATH/gen_xray_proto.lua
|
||||
API_GEN_V2RAY=$LUA_API_PATH/gen_v2ray.lua
|
||||
API_GEN_V2RAY_PROTO=$LUA_API_PATH/gen_v2ray_proto.lua
|
||||
API_GEN_TROJAN=$LUA_API_PATH/gen_trojan.lua
|
||||
@ -209,7 +211,7 @@ get_new_port() {
|
||||
|
||||
first_type() {
|
||||
local path_name=${1}
|
||||
type -t -p "/bin/${path_name}" -p "${TMP_BIN_PATH}/${path_name}" -p "${path_name}" -p "/usr/bin/v2ray/{path_name}" "$@" | head -n1
|
||||
type -t -p "/bin/${path_name}" -p "${TMP_BIN_PATH}/${path_name}" -p "${path_name}" "$@" | head -n1
|
||||
}
|
||||
|
||||
ln_start_bin() {
|
||||
@ -333,7 +335,7 @@ run_socks() {
|
||||
msg="某种原因,此 Socks 服务的相关配置已失联,启动中止!"
|
||||
fi
|
||||
|
||||
if [ "$type" == "v2ray" ] && ([ -n "$(config_n_get $node balancing_node)" ] || [ "$(config_n_get $node default_node)" != "nil" ]); then
|
||||
if [ "$type" == "xray" -o "$type" == "v2ray" ] && ([ -n "$(config_n_get $node balancing_node)" ] || [ "$(config_n_get $node default_node)" != "nil" ]); then
|
||||
unset msg
|
||||
fi
|
||||
|
||||
@ -350,6 +352,10 @@ run_socks() {
|
||||
[ -n "$_username" ] && [ -n "$_password" ] && local _auth="--uname $_username --passwd $_password"
|
||||
ln_start_bin "$(first_type ssocks)" ssocks_SOCKS_$id --listen $socks_port --socks $server_host:$port $_auth
|
||||
;;
|
||||
xray)
|
||||
lua $API_GEN_XRAY $node nil nil $socks_port > $config_file
|
||||
ln_start_bin "$(first_type $(config_t_get global_app xray_file notset)/xray xray)" xray -config="$config_file"
|
||||
;;
|
||||
v2ray)
|
||||
lua $API_GEN_V2RAY $node nil nil $socks_port > $config_file
|
||||
ln_start_bin "$(first_type $(config_t_get global_app v2ray_file notset)/v2ray v2ray)" v2ray -config="$config_file"
|
||||
@ -423,6 +429,10 @@ run_redir() {
|
||||
eval port=\$UDP_REDIR_PORT$6
|
||||
ln_start_bin "$(first_type ipt2socks)" "ipt2socks_udp_$6" -U -l "$port" -b 0.0.0.0 -s "$node_address" -p "$node_port" -R
|
||||
;;
|
||||
xray)
|
||||
lua $API_GEN_XRAY $node udp $local_port nil > $config_file
|
||||
ln_start_bin "$(first_type $(config_t_get global_app xray_file notset)/xray xray)" xray -config="$config_file"
|
||||
;;
|
||||
v2ray)
|
||||
lua $API_GEN_V2RAY $node udp $local_port nil > $config_file
|
||||
ln_start_bin "$(first_type $(config_t_get global_app v2ray_file notset)/v2ray v2ray)" v2ray -config="$config_file"
|
||||
@ -480,6 +490,12 @@ run_redir() {
|
||||
_socks_username=$(config_n_get $node username)
|
||||
_socks_password=$(config_n_get $node password)
|
||||
;;
|
||||
xray)
|
||||
local extra_param="tcp"
|
||||
[ "$6" == 1 ] && [ "$UDP_NODE1" == "tcp" ] && extra_param="tcp,udp"
|
||||
lua $API_GEN_XRAY $node $extra_param $local_port nil > $config_file
|
||||
ln_start_bin "$(first_type $(config_t_get global_app xray_file notset)/xray xray)" xray -config="$config_file"
|
||||
;;
|
||||
v2ray)
|
||||
local extra_param="tcp"
|
||||
[ "$6" == 1 ] && [ "$UDP_NODE1" == "tcp" ] && extra_param="tcp,udp"
|
||||
@ -603,15 +619,22 @@ start_socks() {
|
||||
|
||||
clean_log() {
|
||||
logsnum=$(cat $LOG_FILE 2>/dev/null | wc -l)
|
||||
[ "$logsnum" -gt 300 ] && {
|
||||
[ "$logsnum" -gt 1000 ] && {
|
||||
echo "" > $LOG_FILE
|
||||
echolog "日志文件过长,清空处理!"
|
||||
}
|
||||
}
|
||||
|
||||
start_crontab() {
|
||||
clean_crontab() {
|
||||
touch /etc/crontabs/root
|
||||
sed -i "/$CONFIG/d" /etc/crontabs/root >/dev/null 2>&1 &
|
||||
#sed -i "/${CONFIG}/d" /etc/crontabs/root >/dev/null 2>&1 &
|
||||
sed -i "/$(echo "/etc/init.d/${CONFIG}" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 &
|
||||
sed -i "/$(echo "lua ${APP_PATH}/rule_update.lua log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 &
|
||||
sed -i "/$(echo "lua ${APP_PATH}/subscribe.lua start log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 &
|
||||
}
|
||||
|
||||
start_crontab() {
|
||||
clean_crontab
|
||||
auto_on=$(config_t_get global_delay auto_on 0)
|
||||
if [ "$auto_on" = "1" ]; then
|
||||
time_off=$(config_t_get global_delay time_off)
|
||||
@ -666,8 +689,7 @@ start_crontab() {
|
||||
}
|
||||
|
||||
stop_crontab() {
|
||||
touch /etc/crontabs/root
|
||||
sed -i "/$CONFIG/d" /etc/crontabs/root >/dev/null 2>&1 &
|
||||
clean_crontab
|
||||
ps | grep "$APP_PATH/test.sh" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
|
||||
/etc/init.d/cron restart
|
||||
#echolog "清除定时执行命令。"
|
||||
@ -1118,7 +1140,7 @@ start_haproxy() {
|
||||
unset msg
|
||||
failcount=0
|
||||
while [ "$failcount" -lt "3" ]; do
|
||||
ubus list network.interface.${export} >/dev/null 2>&1
|
||||
ip route show dev ${export} >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
let "failcount++"
|
||||
echolog " - 找不到出口接口:$export,1分钟后再重试(${failcount}/3),${bip}"
|
||||
|
||||
0
package/lienol/luci-app-passwall/root/usr/share/passwall/rule_update.lua
Normal file → Executable file
0
package/lienol/luci-app-passwall/root/usr/share/passwall/rule_update.lua
Normal file → Executable file
77
package/lienol/luci-app-passwall/root/usr/share/passwall/subscribe.lua
Normal file → Executable file
77
package/lienol/luci-app-passwall/root/usr/share/passwall/subscribe.lua
Normal file → Executable file
@ -8,7 +8,7 @@ require 'luci.model.uci'
|
||||
require 'luci.util'
|
||||
require 'luci.jsonc'
|
||||
require 'luci.sys'
|
||||
local _api = require "luci.model.cbi.passwall.api.api"
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
-- these global functions are accessed all the time by the event handler
|
||||
-- so caching them is worth the effort
|
||||
@ -135,7 +135,7 @@ do
|
||||
end
|
||||
|
||||
ucic2:foreach(application, uciType, function(node)
|
||||
if node.type == 'V2ray' and node.protocol == '_shunt' then
|
||||
if node.protocol and node.protocol == '_shunt' then
|
||||
local node_id = node[".name"]
|
||||
ucic2:foreach(application, "shunt_rules", function(e)
|
||||
local _node_id = node[e[".name"]] or nil
|
||||
@ -146,7 +146,7 @@ do
|
||||
CONFIG[#CONFIG + 1] = {
|
||||
log = false,
|
||||
currentNode = _node,
|
||||
remarks = "V2ray分流" .. e.remarks .. "节点",
|
||||
remarks = "分流" .. e.remarks .. "节点",
|
||||
set = function(server)
|
||||
ucic2:set(application, node_id, e[".name"], server)
|
||||
end
|
||||
@ -161,12 +161,12 @@ do
|
||||
CONFIG[#CONFIG + 1] = {
|
||||
log = false,
|
||||
currentNode = default_node,
|
||||
remarks = "V2ray分流默认节点",
|
||||
remarks = "分流默认节点",
|
||||
set = function(server)
|
||||
ucic2:set(application, node_id, "default_node", server)
|
||||
end
|
||||
}
|
||||
elseif node.type == 'V2ray' and node.protocol == '_balancing' then
|
||||
elseif node.protocol and node.protocol == '_balancing' then
|
||||
local node_id = node[".name"]
|
||||
local nodes = {}
|
||||
local new_nodes = {}
|
||||
@ -184,7 +184,7 @@ do
|
||||
remarks = node,
|
||||
set = function(server)
|
||||
for kk, vv in pairs(CONFIG) do
|
||||
if (vv.remarks == "V2ray负载均衡节点列表" .. node_id) then
|
||||
if (vv.remarks == "负载均衡节点列表" .. node_id) then
|
||||
table.insert(vv.new_nodes, server)
|
||||
end
|
||||
end
|
||||
@ -193,13 +193,13 @@ do
|
||||
end
|
||||
end
|
||||
CONFIG[#CONFIG + 1] = {
|
||||
remarks = "V2ray负载均衡节点列表" .. node_id,
|
||||
remarks = "负载均衡节点列表" .. node_id,
|
||||
nodes = nodes,
|
||||
new_nodes = new_nodes,
|
||||
set = function()
|
||||
for kk, vv in pairs(CONFIG) do
|
||||
if (vv.remarks == "V2ray负载均衡节点列表" .. node_id) then
|
||||
log("刷新V2ray负载均衡节点列表")
|
||||
if (vv.remarks == "负载均衡节点列表" .. node_id) then
|
||||
log("刷新负载均衡节点列表")
|
||||
ucic2:foreach(application, uciType, function(node2)
|
||||
if node2[".name"] == node[".name"] then
|
||||
local index = node2[".index"]
|
||||
@ -315,7 +315,7 @@ local function base64Decode(text)
|
||||
end
|
||||
-- 处理数据
|
||||
local function processData(szType, content, add_mode)
|
||||
log(content, add_mode)
|
||||
--log(content, add_mode)
|
||||
local result = {
|
||||
timeout = 60,
|
||||
add_mode = add_mode,
|
||||
@ -344,6 +344,9 @@ local function processData(szType, content, add_mode)
|
||||
elseif szType == 'vmess' then
|
||||
local info = jsonParse(content)
|
||||
result.type = 'V2ray'
|
||||
if api.is_finded("xray") then
|
||||
result.type = 'Xray'
|
||||
end
|
||||
result.address = info.add
|
||||
result.port = info.port
|
||||
result.protocol = 'vmess'
|
||||
@ -385,11 +388,11 @@ local function processData(szType, content, add_mode)
|
||||
end
|
||||
if not info.security then result.security = "auto" end
|
||||
if info.tls == "tls" or info.tls == "1" then
|
||||
result.stream_security = "tls"
|
||||
result.tls = "1"
|
||||
result.tls_serverName = info.host
|
||||
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
|
||||
else
|
||||
result.stream_security = "none"
|
||||
result.tls = "0"
|
||||
end
|
||||
elseif szType == "ss" then
|
||||
local idx_sp = 0
|
||||
@ -506,7 +509,7 @@ local function processData(szType, content, add_mode)
|
||||
result.type = "Trojan-Go"
|
||||
result.fingerprint = "firefox"
|
||||
end
|
||||
result.stream_security = 'tls'
|
||||
result.tls = '1'
|
||||
result.tls_serverName = peer and peer or sni
|
||||
result.tls_allowInsecure = allowInsecure and "1" or "0"
|
||||
end
|
||||
@ -567,7 +570,7 @@ local function processData(szType, content, add_mode)
|
||||
end
|
||||
result.port = port
|
||||
result.fingerprint = "firefox"
|
||||
result.stream_security = 'tls'
|
||||
result.tls = '1'
|
||||
result.tls_serverName = peer and peer or sni
|
||||
result.tls_allowInsecure = allowInsecure and "1" or "0"
|
||||
end
|
||||
@ -647,21 +650,21 @@ end
|
||||
local function select_node(nodes, config)
|
||||
local server
|
||||
if config.currentNode then
|
||||
-- 特别优先级 V2ray分流 + 备注
|
||||
if config.currentNode.type == 'V2ray' and config.currentNode.protocol == '_shunt' then
|
||||
-- 特别优先级 分流 + 备注
|
||||
if config.currentNode.protocol and config.currentNode.protocol == '_shunt' then
|
||||
for id, node in pairs(nodes) do
|
||||
if node.remarks == config.currentNode.remarks then
|
||||
log('选择【' .. config.remarks .. '】V2ray分流匹配节点:' .. node.remarks)
|
||||
log('选择【' .. config.remarks .. '】分流匹配节点:' .. node.remarks)
|
||||
server = id
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 特别优先级 V2ray负载均衡 + 备注
|
||||
if config.currentNode.type == 'V2ray' and config.currentNode.protocol == '_balancing' then
|
||||
-- 特别优先级 负载均衡 + 备注
|
||||
if config.currentNode.protocol and config.currentNode.protocol == '_balancing' then
|
||||
for id, node in pairs(nodes) do
|
||||
if node.remarks == config.currentNode.remarks then
|
||||
log('选择【' .. config.remarks .. '】V2ray负载均衡匹配节点:' .. node.remarks)
|
||||
log('选择【' .. config.remarks .. '】负载均衡匹配节点:' .. node.remarks)
|
||||
server = id
|
||||
break
|
||||
end
|
||||
@ -679,11 +682,11 @@ local function select_node(nodes, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 第二优先级 IP + 端口
|
||||
-- 第二优先级 类型 + IP + 端口
|
||||
if not server then
|
||||
for id, node in pairs(nodes) do
|
||||
if node.address and node.port then
|
||||
if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then
|
||||
if node.type and node.address and node.port then
|
||||
if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then
|
||||
if config.log == nil or config.log == true then
|
||||
log('选择【' .. config.remarks .. '】第二匹配节点:' .. node.remarks)
|
||||
end
|
||||
@ -693,11 +696,11 @@ local function select_node(nodes, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 第三优先级 IP
|
||||
-- 第三优先级 IP + 端口
|
||||
if not server then
|
||||
for id, node in pairs(nodes) do
|
||||
if node.address then
|
||||
if node.address == config.currentNode.address then
|
||||
if node.address and node.port then
|
||||
if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then
|
||||
if config.log == nil or config.log == true then
|
||||
log('选择【' .. config.remarks .. '】第三匹配节点:' .. node.remarks)
|
||||
end
|
||||
@ -707,13 +710,27 @@ local function select_node(nodes, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 第四优先级备注
|
||||
-- 第四优先级 IP
|
||||
if not server then
|
||||
for id, node in pairs(nodes) do
|
||||
if node.address then
|
||||
if node.address == config.currentNode.address then
|
||||
if config.log == nil or config.log == true then
|
||||
log('选择【' .. config.remarks .. '】第四匹配节点:' .. node.remarks)
|
||||
end
|
||||
server = id
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 第五优先级备注
|
||||
if not server then
|
||||
for id, node in pairs(nodes) do
|
||||
if node.remarks then
|
||||
if node.remarks == config.currentNode.remarks then
|
||||
if config.log == nil or config.log == true then
|
||||
log('选择【' .. config.remarks .. '】第四匹配节点:' .. node.remarks)
|
||||
log('选择【' .. config.remarks .. '】第五匹配节点:' .. node.remarks)
|
||||
end
|
||||
server = id
|
||||
break
|
||||
@ -751,7 +768,7 @@ local function update_node(manual)
|
||||
end)
|
||||
for _, v in ipairs(nodeResult) do
|
||||
for _, vv in ipairs(v) do
|
||||
local uuid = _api.gen_uuid()
|
||||
local uuid = api.gen_uuid()
|
||||
local cfgid = ucic2:section(application, uciType, uuid)
|
||||
cfgid = uuid
|
||||
for kkk, vvv in pairs(vv) do
|
||||
@ -858,7 +875,7 @@ local function parse_link(raw, remark, manual)
|
||||
not result.address:find("%.") or -- 虽然没有.也算域,不过应该没有人会这样干吧
|
||||
result.address:sub(#result.address) == "." -- 结尾是.
|
||||
then
|
||||
log('丢弃无效节点: ' .. result.type .. ' 节点, ' .. result.remarks)
|
||||
log('丢弃过滤节点: ' .. result.type .. ' 节点, ' .. result.remarks)
|
||||
else
|
||||
tinsert(all_nodes, result)
|
||||
end
|
||||
|
||||
@ -67,40 +67,15 @@ test_auto_switch() {
|
||||
fi
|
||||
|
||||
status=$(test_proxy)
|
||||
if [ "$status" == 0 ]; then
|
||||
echolog "自动切换检测:${type}_${index}节点$(config_n_get $now_node type) $(config_n_get $now_node address) $(config_n_get $now_node port)正常。"
|
||||
#检测主节点是否能使用
|
||||
local main_node=$(config_t_get auto_switch tcp_main1)
|
||||
if [ "$now_node" != "$main_node" ]; then
|
||||
local node_type=$(echo $(config_n_get $main_node type) | tr 'A-Z' 'a-z')
|
||||
if [ "$node_type" == "socks" ]; then
|
||||
local node_address=$(config_n_get $main_node address)
|
||||
local node_port=$(config_n_get $main_node port)
|
||||
[ -n "$node_address" ] && [ -n "$node_port" ] && local curlx="socks5h://$node_address:$node_port"
|
||||
else
|
||||
local tmp_port=$(/usr/share/passwall/app.sh get_new_port 61080 tcp)
|
||||
/usr/share/passwall/app.sh run_socks "$main_node" "127.0.0.1" "$tmp_port" "/var/etc/passwall/auto_switch_$index.json" "10"
|
||||
local curlx="socks5h://127.0.0.1:$tmp_port"
|
||||
fi
|
||||
sleep 10s
|
||||
proxy_status=$(test_url "https://www.google.com/generate_204" 3 3 "-x $curlx")
|
||||
ps -w | grep -v "grep" | grep "/var/etc/passwall/auto_switch_$index.json" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
if [ "$proxy_status" -eq 200 ]; then
|
||||
#主节点正常,切换到主节点
|
||||
echolog "自动切换检测:${type}_${index}主节点正常,切换到主节点!"
|
||||
/usr/share/passwall/app.sh node_switch $type $2 $index $main_node
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
elif [ "$status" == 2 ]; then
|
||||
if [ "$status" == 2 ]; then
|
||||
echolog "自动切换检测:无法连接到网络,请检查网络是否正常!"
|
||||
return 2
|
||||
elif [ "$status" == 1 ]; then
|
||||
echolog "自动切换检测:${type}_${index}节点异常,开始切换节点!"
|
||||
|
||||
fi
|
||||
|
||||
local restore_switch=$(config_t_get auto_switch restore_switch${index} 0)
|
||||
if [ "$restore_switch" == "1" ]; then
|
||||
#检测主节点是否能使用
|
||||
local main_node=$(config_t_get auto_switch tcp_main1)
|
||||
local main_node=$(config_t_get auto_switch tcp_main${index})
|
||||
if [ "$now_node" != "$main_node" ]; then
|
||||
local node_type=$(echo $(config_n_get $main_node type) | tr 'A-Z' 'a-z')
|
||||
if [ "$node_type" == "socks" ]; then
|
||||
@ -122,7 +97,13 @@ test_auto_switch() {
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [ "$status" == 0 ]; then
|
||||
echolog "自动切换检测:${type}_${index}节点$(config_n_get $now_node type) $(config_n_get $now_node address) $(config_n_get $now_node port)正常。"
|
||||
return 0
|
||||
elif [ "$status" == 1 ]; then
|
||||
echolog "自动切换检测:${type}_${index}节点异常,开始切换节点!"
|
||||
local new_node
|
||||
in_backup_nodes=$(echo $b_tcp_nodes | grep $now_node)
|
||||
# 判断当前节点是否存在于备用节点列表里
|
||||
|
||||
Loading…
Reference in New Issue
Block a user