update 2025-08-26 09:20:45
This commit is contained in:
parent
e4e07d79b6
commit
893377a19f
@ -39,6 +39,13 @@ const callNikkiUpdateSubscription = rpc.declare({
|
|||||||
expect: { '': {} }
|
expect: { '': {} }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const callNikkiAPI = rpc.declare({
|
||||||
|
object: 'luci.nikki',
|
||||||
|
method: 'api',
|
||||||
|
params: ['method', 'path', 'query', 'body'],
|
||||||
|
expect: { '': {} }
|
||||||
|
});
|
||||||
|
|
||||||
const callNikkiGetIdentifiers = rpc.declare({
|
const callNikkiGetIdentifiers = rpc.declare({
|
||||||
object: 'luci.nikki',
|
object: 'luci.nikki',
|
||||||
method: 'get_identifiers',
|
method: 'get_identifiers',
|
||||||
@ -103,21 +110,8 @@ return baseclass.extend({
|
|||||||
return callNikkiUpdateSubscription(section_id);
|
return callNikkiUpdateSubscription(section_id);
|
||||||
},
|
},
|
||||||
|
|
||||||
api: async function (method, path, query, body) {
|
updateDashboard: function () {
|
||||||
const profile = await callNikkiProfile({ 'external-controller': null, 'secret': null });
|
return callNikkiAPI('POST', '/upgrade/ui');
|
||||||
const apiListen = profile['external-controller'];
|
|
||||||
const apiSecret = profile['secret'] ?? '';
|
|
||||||
if (!apiListen) {
|
|
||||||
return Promise.reject('API has not been configured');
|
|
||||||
}
|
|
||||||
const apiPort = apiListen.substring(apiListen.lastIndexOf(':') + 1);
|
|
||||||
const url = `http://${window.location.hostname}:${apiPort}${path}`;
|
|
||||||
return request.request(url, {
|
|
||||||
method: method,
|
|
||||||
headers: { 'Authorization': `Bearer ${apiSecret}` },
|
|
||||||
query: query,
|
|
||||||
content: body
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
openDashboard: async function () {
|
openDashboard: async function () {
|
||||||
@ -146,10 +140,6 @@ return baseclass.extend({
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
updateDashboard: function () {
|
|
||||||
return this.api('POST', '/upgrade/ui');
|
|
||||||
},
|
|
||||||
|
|
||||||
getIdentifiers: function () {
|
getIdentifiers: function () {
|
||||||
return callNikkiGetIdentifiers();
|
return callNikkiGetIdentifiers();
|
||||||
},
|
},
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { access, popen, writefile } from 'fs';
|
import { access, popen, writefile } from 'fs';
|
||||||
import { get_users, get_groups, get_cgroups } from '/etc/nikki/ucode/include.uc';
|
import { load_profile, get_users, get_groups, get_cgroups } from '/etc/nikki/ucode/include.uc';
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
version: {
|
version: {
|
||||||
@ -62,6 +62,35 @@ const methods = {
|
|||||||
return { success: success };
|
return { success: success };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
api: {
|
||||||
|
args: { method: 'method', path: 'path', query: 'query', body: 'body' },
|
||||||
|
call: function(req) {
|
||||||
|
let result = {};
|
||||||
|
|
||||||
|
const method = req.args?.method;
|
||||||
|
const path = req.args?.path;
|
||||||
|
const query = req.args?.query;
|
||||||
|
const body = req.args?.body;
|
||||||
|
|
||||||
|
const profile = load_profile();
|
||||||
|
const api_listen = profile['external-controller'];
|
||||||
|
const api_secret = profile['secret'];
|
||||||
|
|
||||||
|
if (!api_listen) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = api_listen + path;
|
||||||
|
|
||||||
|
const process = popen(`curl --request '${method}' --oauth2-bearer '${api_secret}' --url-query '${query}' --data '${body}' '${url}'`);
|
||||||
|
if (process) {
|
||||||
|
result = json(process);
|
||||||
|
process.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
get_identifiers: {
|
get_identifiers: {
|
||||||
call: function() {
|
call: function() {
|
||||||
const users = filter(get_users(), (x) => x != '');
|
const users = filter(get_users(), (x) => x != '');
|
||||||
|
@ -779,6 +779,10 @@ function gen_config(var)
|
|||||||
table.insert(outbounds, outbound)
|
table.insert(outbounds, outbound)
|
||||||
fallback_node_tag = outbound.tag
|
fallback_node_tag = outbound.tag
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
if gen_balancer(fallback_node) then
|
||||||
|
fallback_node_tag = fallback_node_id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=luci-app-passwall2
|
PKG_NAME:=luci-app-passwall2
|
||||||
PKG_VERSION:=25.8.22
|
PKG_VERSION:=25.8.25
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
|
@ -376,6 +376,9 @@ function clear_all_nodes()
|
|||||||
end)
|
end)
|
||||||
uci:foreach(appname, "subscribe_list", function(t)
|
uci:foreach(appname, "subscribe_list", function(t)
|
||||||
uci:delete(appname, t[".name"], "md5")
|
uci:delete(appname, t[".name"], "md5")
|
||||||
|
uci:delete(appname, t[".name"], "chain_proxy")
|
||||||
|
uci:delete(appname, t[".name"], "preproxy_node")
|
||||||
|
uci:delete(appname, t[".name"], "to_node")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
api.uci_save(uci, appname, true, true)
|
api.uci_save(uci, appname, true, true)
|
||||||
@ -440,6 +443,16 @@ function delete_select_nodes()
|
|||||||
uci:delete(appname, t[".name"], "fallback_node")
|
uci:delete(appname, t[".name"], "fallback_node")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
uci:foreach(appname, "subscribe_list", function(t)
|
||||||
|
if t["preproxy_node"] == w then
|
||||||
|
uci:delete(appname, t[".name"], "preproxy_node")
|
||||||
|
uci:delete(appname, t[".name"], "chain_proxy")
|
||||||
|
end
|
||||||
|
if t["to_node"] == w then
|
||||||
|
uci:delete(appname, t[".name"], "to_node")
|
||||||
|
uci:delete(appname, t[".name"], "chain_proxy")
|
||||||
|
end
|
||||||
|
end)
|
||||||
if (uci:get(appname, w, "add_mode") or "0") == "2" then
|
if (uci:get(appname, w, "add_mode") or "0") == "2" then
|
||||||
local add_from = uci:get(appname, w, "add_from") or ""
|
local add_from = uci:get(appname, w, "add_from") or ""
|
||||||
if add_from ~= "" then
|
if add_from ~= "" then
|
||||||
@ -531,20 +544,29 @@ function create_backup()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function restore_backup()
|
function restore_backup()
|
||||||
|
local result = { status = "error", message = "unknown error" }
|
||||||
local ok, err = pcall(function()
|
local ok, err = pcall(function()
|
||||||
local filename = http.formvalue("filename")
|
local filename = http.formvalue("filename")
|
||||||
local chunk = http.formvalue("chunk")
|
local chunk = http.formvalue("chunk")
|
||||||
local chunk_index = tonumber(http.formvalue("chunk_index") or "-1")
|
local chunk_index = tonumber(http.formvalue("chunk_index") or "-1")
|
||||||
local total_chunks = tonumber(http.formvalue("total_chunks") or "-1")
|
local total_chunks = tonumber(http.formvalue("total_chunks") or "-1")
|
||||||
if not filename or not chunk then
|
if not filename then
|
||||||
http_write_json({ status = "error", message = "Missing filename or chunk" })
|
result = { status = "error", message = "Missing filename" }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not chunk then
|
||||||
|
result = { status = "error", message = "Missing chunk data" }
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local file_path = "/tmp/" .. filename
|
local file_path = "/tmp/" .. filename
|
||||||
local decoded = nixio.bin.b64decode(chunk)
|
local decoded = nixio.bin.b64decode(chunk)
|
||||||
|
if not decoded then
|
||||||
|
result = { status = "error", message = "Base64 decode failed" }
|
||||||
|
return
|
||||||
|
end
|
||||||
local fp = io.open(file_path, "a+")
|
local fp = io.open(file_path, "a+")
|
||||||
if not fp then
|
if not fp then
|
||||||
http_write_json({ status = "error", message = "Failed to open file for writing: " .. file_path })
|
result = { status = "error", message = "Failed to open file: " .. file_path }
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
fp:write(decoded)
|
fp:write(decoded)
|
||||||
@ -552,7 +574,7 @@ function restore_backup()
|
|||||||
if chunk_index + 1 == total_chunks then
|
if chunk_index + 1 == total_chunks then
|
||||||
api.sys.call("echo '' > /tmp/log/passwall2.log")
|
api.sys.call("echo '' > /tmp/log/passwall2.log")
|
||||||
api.log(" * PassWall2 配置文件上传成功…")
|
api.log(" * PassWall2 配置文件上传成功…")
|
||||||
local temp_dir = '/tmp/passwall_bak'
|
local temp_dir = '/tmp/passwall2_bak'
|
||||||
api.sys.call("mkdir -p " .. temp_dir)
|
api.sys.call("mkdir -p " .. temp_dir)
|
||||||
if api.sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then
|
if api.sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then
|
||||||
for _, backup_file in ipairs(backup_files) do
|
for _, backup_file in ipairs(backup_files) do
|
||||||
@ -563,21 +585,23 @@ function restore_backup()
|
|||||||
end
|
end
|
||||||
api.log(" * PassWall2 配置还原成功…")
|
api.log(" * PassWall2 配置还原成功…")
|
||||||
api.log(" * 重启 PassWall2 服务中…\n")
|
api.log(" * 重启 PassWall2 服务中…\n")
|
||||||
api.sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &')
|
luci.sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &')
|
||||||
api.sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &')
|
luci.sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &')
|
||||||
|
result = { status = "success", message = "Upload completed", path = file_path }
|
||||||
else
|
else
|
||||||
api.log(" * PassWall2 配置文件解压失败,请重试!")
|
api.log(" * PassWall2 配置文件解压失败,请重试!")
|
||||||
|
result = { status = "error", message = "Decompression failed" }
|
||||||
end
|
end
|
||||||
api.sys.call("rm -rf " .. temp_dir)
|
api.sys.call("rm -rf " .. temp_dir)
|
||||||
fs.remove(file_path)
|
fs.remove(file_path)
|
||||||
http_write_json({ status = "success", message = "Upload completed", path = file_path })
|
|
||||||
else
|
else
|
||||||
http_write_json({ status = "success", message = "Chunk received" })
|
result = { status = "success", message = "Chunk received" }
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if not ok then
|
if not ok then
|
||||||
http_write_json({ status = "error", message = tostring(err) })
|
result = { status = "error", message = tostring(err) }
|
||||||
end
|
end
|
||||||
|
http_write_json(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
function geo_view()
|
function geo_view()
|
||||||
|
@ -84,6 +84,16 @@ function s.remove(e, t)
|
|||||||
m:del(s[".name"], "fallback_node")
|
m:del(s[".name"], "fallback_node")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
m.uci:foreach(appname, "subscribe_list", function(s)
|
||||||
|
if s["preproxy_node"] == t then
|
||||||
|
m:del(s[".name"], "preproxy_node")
|
||||||
|
m:del(s[".name"], "chain_proxy")
|
||||||
|
end
|
||||||
|
if s["to_node"] == t then
|
||||||
|
m:del(s[".name"], "to_node")
|
||||||
|
m:del(s[".name"], "chain_proxy")
|
||||||
|
end
|
||||||
|
end)
|
||||||
if (m:get(t, "add_mode") or "0") == "2" then
|
if (m:get(t, "add_mode") or "0") == "2" then
|
||||||
local add_from = m:get(t, "add_from") or ""
|
local add_from = m:get(t, "add_from") or ""
|
||||||
if add_from ~= "" then
|
if add_from ~= "" then
|
||||||
@ -157,6 +167,8 @@ o.cfgvalue = function(t, n)
|
|||||||
protocol = "HY2"
|
protocol = "HY2"
|
||||||
elseif protocol == "anytls" then
|
elseif protocol == "anytls" then
|
||||||
protocol = "AnyTLS"
|
protocol = "AnyTLS"
|
||||||
|
elseif protocol == "ssh" then
|
||||||
|
protocol = "SSH"
|
||||||
else
|
else
|
||||||
protocol = protocol:gsub("^%l",string.upper)
|
protocol = protocol:gsub("^%l",string.upper)
|
||||||
end
|
end
|
||||||
|
@ -51,6 +51,18 @@ if has_hysteria2 then
|
|||||||
local s = "hysteria2"
|
local s = "hysteria2"
|
||||||
table.insert(hysteria2_type, s)
|
table.insert(hysteria2_type, s)
|
||||||
end
|
end
|
||||||
|
local nodes_table = {}
|
||||||
|
for k, e in ipairs(api.get_valid_nodes()) do
|
||||||
|
if e.node_type == "normal" then
|
||||||
|
nodes_table[#nodes_table + 1] = {
|
||||||
|
id = e[".name"],
|
||||||
|
remark = e["remark"],
|
||||||
|
type = e["type"],
|
||||||
|
add_mode = e["add_mode"],
|
||||||
|
chain_proxy = e["chain_proxy"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
s = m:section(NamedSection, arg[1])
|
s = m:section(NamedSection, arg[1])
|
||||||
s.addremove = false
|
s.addremove = false
|
||||||
@ -205,4 +217,29 @@ o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, li
|
|||||||
o:value("Passwall2/OpenWrt", "PassWall2")
|
o:value("Passwall2/OpenWrt", "PassWall2")
|
||||||
o:value("v2rayN/9.99", "v2rayN")
|
o:value("v2rayN/9.99", "v2rayN")
|
||||||
|
|
||||||
|
o = s:option(ListValue, "chain_proxy", translate("Chain Proxy"))
|
||||||
|
o:value("", translate("Close(Not use)"))
|
||||||
|
o:value("1", translate("Preproxy Node"))
|
||||||
|
o:value("2", translate("Landing Node"))
|
||||||
|
|
||||||
|
local descrStr = "Chained proxy works only with Xray or Sing-box nodes.<br>"
|
||||||
|
descrStr = descrStr .. "The chained node must be the same type as your subscription node (Xray with Xray, Sing-box with Sing-box).<br>"
|
||||||
|
descrStr = descrStr .. "You can only use manual or imported nodes as chained nodes."
|
||||||
|
descrStr = translate(descrStr) .. "<br>" .. translate("Only support a layer of proxy.")
|
||||||
|
|
||||||
|
o = s:option(ListValue, "preproxy_node", translate("Preproxy Node"))
|
||||||
|
o:depends({ ["chain_proxy"] = "1" })
|
||||||
|
o.description = descrStr
|
||||||
|
|
||||||
|
o = s:option(ListValue, "to_node", translate("Landing Node"))
|
||||||
|
o:depends({ ["chain_proxy"] = "2" })
|
||||||
|
o.description = descrStr
|
||||||
|
|
||||||
|
for k, v in pairs(nodes_table) do
|
||||||
|
if (v.type == "Xray" or v.type == "sing-box") and (not v.chain_proxy or v.chain_proxy == "") and v.add_mode ~= "2" then
|
||||||
|
s.fields["preproxy_node"]:value(v.id, v.remark)
|
||||||
|
s.fields["to_node"]:value(v.id, v.remark)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
@ -65,6 +65,7 @@ end
|
|||||||
if version_ge_1_12_0 then
|
if version_ge_1_12_0 then
|
||||||
o:value("anytls", "AnyTLS")
|
o:value("anytls", "AnyTLS")
|
||||||
end
|
end
|
||||||
|
o:value("ssh", "SSH")
|
||||||
o:value("_urltest", translate("URLTest"))
|
o:value("_urltest", translate("URLTest"))
|
||||||
o:value("_shunt", translate("Shunt"))
|
o:value("_shunt", translate("Shunt"))
|
||||||
o:value("_iface", translate("Custom Interface"))
|
o:value("_iface", translate("Custom Interface"))
|
||||||
@ -258,6 +259,7 @@ end
|
|||||||
o = s:option(Value, _n("username"), translate("Username"))
|
o = s:option(Value, _n("username"), translate("Username"))
|
||||||
o:depends({ [_n("protocol")] = "http" })
|
o:depends({ [_n("protocol")] = "http" })
|
||||||
o:depends({ [_n("protocol")] = "socks" })
|
o:depends({ [_n("protocol")] = "socks" })
|
||||||
|
o:depends({ [_n("protocol")] = "ssh" })
|
||||||
|
|
||||||
o = s:option(Value, _n("password"), translate("Password"))
|
o = s:option(Value, _n("password"), translate("Password"))
|
||||||
o.password = true
|
o.password = true
|
||||||
@ -268,6 +270,7 @@ o:depends({ [_n("protocol")] = "shadowsocksr" })
|
|||||||
o:depends({ [_n("protocol")] = "trojan" })
|
o:depends({ [_n("protocol")] = "trojan" })
|
||||||
o:depends({ [_n("protocol")] = "tuic" })
|
o:depends({ [_n("protocol")] = "tuic" })
|
||||||
o:depends({ [_n("protocol")] = "anytls" })
|
o:depends({ [_n("protocol")] = "anytls" })
|
||||||
|
o:depends({ [_n("protocol")] = "ssh" })
|
||||||
|
|
||||||
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
|
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
|
||||||
for a, t in ipairs(security_list) do o:value(t) end
|
for a, t in ipairs(security_list) do o:value(t) end
|
||||||
@ -456,6 +459,24 @@ if singbox_tags:find("with_quic") then
|
|||||||
o:depends({ [_n("protocol")] = "hysteria2"})
|
o:depends({ [_n("protocol")] = "hysteria2"})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- [[ SSH config start ]] --
|
||||||
|
o = s:option(Value, _n("ssh_priv_key"), translate("Private Key"))
|
||||||
|
o:depends({ [_n("protocol")] = "ssh" })
|
||||||
|
|
||||||
|
o = s:option(Value, _n("ssh_priv_key_pp"), translate("Private Key Passphrase"))
|
||||||
|
o.password = true
|
||||||
|
o:depends({ [_n("protocol")] = "ssh" })
|
||||||
|
|
||||||
|
o = s:option(DynamicList, _n("ssh_host_key"), translate("Host Key"), translate("Accept any if empty."))
|
||||||
|
o:depends({ [_n("protocol")] = "ssh" })
|
||||||
|
|
||||||
|
o = s:option(DynamicList, _n("ssh_host_key_algo"), translate("Host Key Algorithms"))
|
||||||
|
o:depends({ [_n("protocol")] = "ssh" })
|
||||||
|
|
||||||
|
o = s:option(Value, _n("ssh_client_version"), translate("Client Version"), translate("Random version will be used if empty."))
|
||||||
|
o:depends({ [_n("protocol")] = "ssh" })
|
||||||
|
-- [[ SSH config end ]] --
|
||||||
|
|
||||||
o = s:option(Flag, _n("tls"), translate("TLS"))
|
o = s:option(Flag, _n("tls"), translate("TLS"))
|
||||||
o.default = 0
|
o.default = 0
|
||||||
o:depends({ [_n("protocol")] = "vmess" })
|
o:depends({ [_n("protocol")] = "vmess" })
|
||||||
|
@ -485,6 +485,8 @@ function get_valid_nodes()
|
|||||||
protocol = "HY2"
|
protocol = "HY2"
|
||||||
elseif protocol == "anytls" then
|
elseif protocol == "anytls" then
|
||||||
protocol = "AnyTLS"
|
protocol = "AnyTLS"
|
||||||
|
elseif protocol == "ssh" then
|
||||||
|
protocol = "SSH"
|
||||||
else
|
else
|
||||||
protocol = protocol:gsub("^%l",string.upper)
|
protocol = protocol:gsub("^%l",string.upper)
|
||||||
end
|
end
|
||||||
@ -530,6 +532,8 @@ function get_node_remarks(n)
|
|||||||
protocol = "HY2"
|
protocol = "HY2"
|
||||||
elseif protocol == "anytls" then
|
elseif protocol == "anytls" then
|
||||||
protocol = "AnyTLS"
|
protocol = "AnyTLS"
|
||||||
|
elseif protocol == "ssh" then
|
||||||
|
protocol = "SSH"
|
||||||
else
|
else
|
||||||
protocol = protocol:gsub("^%l",string.upper)
|
protocol = protocol:gsub("^%l",string.upper)
|
||||||
end
|
end
|
||||||
|
@ -439,6 +439,18 @@ function gen_outbound(flag, node, tag, proxy_table)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if node.protocol == "ssh" then
|
||||||
|
protocol_table = {
|
||||||
|
user = (node.username and node.username ~= "") and node.username or "root",
|
||||||
|
password = (node.password and node.password ~= "") and node.password or "",
|
||||||
|
private_key = node.ssh_priv_key,
|
||||||
|
private_key_passphrase = node.ssh_priv_key_pp,
|
||||||
|
host_key = node.ssh_host_key,
|
||||||
|
host_key_algorithms = node.ssh_host_key_algo,
|
||||||
|
client_version = node.ssh_client_version
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
if protocol_table then
|
if protocol_table then
|
||||||
for key, value in pairs(protocol_table) do
|
for key, value in pairs(protocol_table) do
|
||||||
result[key] = value
|
result[key] = value
|
||||||
|
@ -772,6 +772,10 @@ function gen_config(var)
|
|||||||
table.insert(outbounds, outbound)
|
table.insert(outbounds, outbound)
|
||||||
fallback_node_tag = outbound.tag
|
fallback_node_tag = outbound.tag
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
if gen_balancer(fallback_node) then
|
||||||
|
fallback_node_tag = fallback_node_id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -184,7 +184,6 @@ local api = require "luci.passwall2.api"
|
|||||||
const chunk = base64Data.substring(currentChunk * chunkSize, (currentChunk + 1) * chunkSize);
|
const chunk = base64Data.substring(currentChunk * chunkSize, (currentChunk + 1) * chunkSize);
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open("POST", '<%= api.url("restore_backup") %>', true);
|
xhr.open("POST", '<%= api.url("restore_backup") %>', true);
|
||||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState === 4) {
|
if (xhr.readyState === 4) {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
@ -203,12 +202,12 @@ local api = require "luci.passwall2.api"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.send(
|
const formData = new FormData();
|
||||||
"filename=" + encodeURIComponent(file.name) +
|
formData.append("filename", file.name);
|
||||||
"&chunk=" + encodeURIComponent(chunk) +
|
formData.append("chunk", chunk);
|
||||||
"&chunk_index=" + currentChunk +
|
formData.append("chunk_index", currentChunk);
|
||||||
"&total_chunks=" + totalChunks
|
formData.append("total_chunks", totalChunks);
|
||||||
);
|
xhr.send(formData);
|
||||||
} else {
|
} else {
|
||||||
//alert("Upload completed.");
|
//alert("Upload completed.");
|
||||||
document.getElementById("upload-btn").value = "<%:UL Restore%>";
|
document.getElementById("upload-btn").value = "<%:UL Restore%>";
|
||||||
|
@ -1645,6 +1645,15 @@ msgstr "落地节点"
|
|||||||
msgid "Only support a layer of proxy."
|
msgid "Only support a layer of proxy."
|
||||||
msgstr "仅支持一层代理。"
|
msgstr "仅支持一层代理。"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Chained proxy works only with Xray or Sing-box nodes.<br>"
|
||||||
|
"The chained node must be the same type as your subscription node (Xray with Xray, Sing-box with Sing-box).<br>"
|
||||||
|
"You can only use manual or imported nodes as chained nodes."
|
||||||
|
msgstr ""
|
||||||
|
"链式代理仅支持 Xray 与 Sing-box 节点。<br>"
|
||||||
|
"链式节点需与订阅节点类型一致(Xray 对应 Xray,Sing-box 对应 Sing-box)。<br>"
|
||||||
|
"仅支持手动添加或导入的节点用作链式节点。"
|
||||||
|
|
||||||
msgid "Set the default domain resolution strategy for the sing-box node."
|
msgid "Set the default domain resolution strategy for the sing-box node."
|
||||||
msgstr "为 sing-box 节点设置默认的域名解析策略。"
|
msgstr "为 sing-box 节点设置默认的域名解析策略。"
|
||||||
|
|
||||||
@ -1797,3 +1806,21 @@ msgstr "可以通过输入 GeoIP/Geosite,提取它们所包含的域名/IP。"
|
|||||||
|
|
||||||
msgid "Use the GeoIP/Geosite query function to verify if the entered Geo rules are correct."
|
msgid "Use the GeoIP/Geosite query function to verify if the entered Geo rules are correct."
|
||||||
msgstr "利用 GeoIP/Geosite 查询功能,可以验证输入的 Geo 规则是否正确。"
|
msgstr "利用 GeoIP/Geosite 查询功能,可以验证输入的 Geo 规则是否正确。"
|
||||||
|
|
||||||
|
msgid "Private Key Passphrase"
|
||||||
|
msgstr "私钥指纹"
|
||||||
|
|
||||||
|
msgid "Host Key"
|
||||||
|
msgstr "主机密钥"
|
||||||
|
|
||||||
|
msgid "Accept any if empty."
|
||||||
|
msgstr "留空则不校验。"
|
||||||
|
|
||||||
|
msgid "Host Key Algorithms"
|
||||||
|
msgstr "主机密钥算法"
|
||||||
|
|
||||||
|
msgid "Client Version"
|
||||||
|
msgstr "客户端版本"
|
||||||
|
|
||||||
|
msgid "Random version will be used if empty."
|
||||||
|
msgstr "如留空,则使用随机版本。"
|
||||||
|
@ -35,6 +35,7 @@ local vless_type_default = uci:get(appname, "@global_subscribe[0]", "vless_type"
|
|||||||
local hysteria2_type_default = uci:get(appname, "@global_subscribe[0]", "hysteria2_type") or "hysteria2"
|
local hysteria2_type_default = uci:get(appname, "@global_subscribe[0]", "hysteria2_type") or "hysteria2"
|
||||||
local domain_strategy_default = uci:get(appname, "@global_subscribe[0]", "domain_strategy") or ""
|
local domain_strategy_default = uci:get(appname, "@global_subscribe[0]", "domain_strategy") or ""
|
||||||
local domain_strategy_node = ""
|
local domain_strategy_node = ""
|
||||||
|
local preproxy_node_group, to_node_group, chain_node_type = "", "", ""
|
||||||
-- 判断是否过滤节点关键字
|
-- 判断是否过滤节点关键字
|
||||||
local filter_keyword_mode_default = uci:get(appname, "@global_subscribe[0]", "filter_keyword_mode") or "0"
|
local filter_keyword_mode_default = uci:get(appname, "@global_subscribe[0]", "filter_keyword_mode") or "0"
|
||||||
local filter_keyword_discard_list_default = uci:get(appname, "@global_subscribe[0]", "filter_discard_list") or {}
|
local filter_keyword_discard_list_default = uci:get(appname, "@global_subscribe[0]", "filter_discard_list") or {}
|
||||||
@ -1717,6 +1718,16 @@ local function update_node(manual)
|
|||||||
if kkk == "type" and vvv == "sing-box" then
|
if kkk == "type" and vvv == "sing-box" then
|
||||||
uci:set(appname, cfgid, "domain_strategy", domain_strategy_node)
|
uci:set(appname, cfgid, "domain_strategy", domain_strategy_node)
|
||||||
end
|
end
|
||||||
|
-- 订阅组链式代理
|
||||||
|
if chain_node_type ~= "" and kkk == "type" and vvv == chain_node_type then
|
||||||
|
if preproxy_node_group ~="" then
|
||||||
|
uci:set(appname, cfgid, "chain_proxy", "1")
|
||||||
|
uci:set(appname, cfgid, "preproxy_node", preproxy_node_group)
|
||||||
|
elseif to_node_group ~= "" then
|
||||||
|
uci:set(appname, cfgid, "chain_proxy", "2")
|
||||||
|
uci:set(appname, cfgid, "to_node", to_node_group)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1919,6 +1930,22 @@ local execute = function()
|
|||||||
else
|
else
|
||||||
domain_strategy_node = domain_strategy_default
|
domain_strategy_node = domain_strategy_default
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 订阅组链式代理
|
||||||
|
local function valid_chain_node(node)
|
||||||
|
if not node then return "" end
|
||||||
|
local cp = uci:get(appname, node, "chain_proxy") or ""
|
||||||
|
local am = uci:get(appname, node, "add_mode") or "0"
|
||||||
|
chain_node_type = (cp == "" and am ~= "2") and (uci:get(appname, node, "type") or "") or ""
|
||||||
|
if chain_node_type ~= "Xray" and chain_node_type ~= "sing-box" then
|
||||||
|
chain_node_type = ""
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
preproxy_node_group = (value.chain_proxy == "1") and valid_chain_node(value.preproxy_node) or ""
|
||||||
|
to_node_group = (value.chain_proxy == "2") and valid_chain_node(value.to_node) or ""
|
||||||
|
|
||||||
local ua = value.user_agent
|
local ua = value.user_agent
|
||||||
local access_mode = value.access_mode
|
local access_mode = value.access_mode
|
||||||
local result = (not access_mode) and "自动" or (access_mode == "direct" and "直连访问" or (access_mode == "proxy" and "通过代理" or "自动"))
|
local result = (not access_mode) and "自动" or (access_mode == "direct" and "直连访问" or (access_mode == "proxy" and "通过代理" or "自动"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user