Update On Fri Jul 11 20:40:25 CEST 2025

This commit is contained in:
github-action[bot]
2025-07-11 20:40:26 +02:00
parent e116426a2d
commit 4e5d06b4cd
44 changed files with 1644 additions and 912 deletions

View File

@@ -8,6 +8,7 @@ local http = require "luci.http"
local util = require "luci.util"
local i18n = require "luci.i18n"
local fs = api.fs
local jsonStringify = luci.jsonc.stringify
function index()
if not nixio.fs.access("/etc/config/passwall2") then
@@ -40,13 +41,14 @@ function index()
end
entry({"admin", "services", appname, "app_update"}, cbi(appname .. "/client/app_update"), _("App Update"), 95).leaf = true
entry({"admin", "services", appname, "rule"}, cbi(appname .. "/client/rule"), _("Rule Manage"), 96).leaf = true
entry({"admin", "services", appname, "geoview"}, form(appname .. "/client/geoview"), _("Geo View"), 97).leaf = true
entry({"admin", "services", appname, "node_subscribe_config"}, cbi(appname .. "/client/node_subscribe_config")).leaf = true
entry({"admin", "services", appname, "node_config"}, cbi(appname .. "/client/node_config")).leaf = true
entry({"admin", "services", appname, "shunt_rules"}, cbi(appname .. "/client/shunt_rules")).leaf = true
entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true
entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true
entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true
entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Log Maint"), 999).leaf = true
entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Watch Logs"), 999).leaf = true
--[[ Server ]]
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true
@@ -90,40 +92,44 @@ function index()
end
--[[Backup]]
entry({"admin", "services", appname, "backup"}, call("create_backup")).leaf = true
entry({"admin", "services", appname, "create_backup"}, call("create_backup")).leaf = true
entry({"admin", "services", appname, "restore_backup"}, call("restore_backup")).leaf = true
--[[geoview]]
entry({"admin", "services", appname, "geo_view"}, call("geo_view")).leaf = true
end
local function http_write_json(content)
http.prepare_content("application/json")
http.write_json(content or {code = 1})
http.write(jsonStringify(content or {code = 1}))
end
function reset_config()
luci.sys.call('/etc/init.d/passwall2 stop')
luci.sys.call('[ -f "/usr/share/passwall2/0_default_config" ] && cp -f /usr/share/passwall2/0_default_config /etc/config/passwall2')
luci.http.redirect(api.url())
http.redirect(api.url())
end
function show_menu()
api.sh_uci_del(appname, "@global[0]", "hide_from_luci", true)
luci.sys.call("rm -rf /tmp/luci-*")
luci.sys.call("/etc/init.d/rpcd restart >/dev/null")
luci.http.redirect(api.url())
http.redirect(api.url())
end
function hide_menu()
api.sh_uci_set(appname, "@global[0]", "hide_from_luci", "1", true)
luci.sys.call("rm -rf /tmp/luci-*")
luci.sys.call("/etc/init.d/rpcd restart >/dev/null")
luci.http.redirect(luci.dispatcher.build_url("admin", "status", "overview"))
http.redirect(luci.dispatcher.build_url("admin", "status", "overview"))
end
function link_add_node()
-- 分片接收以突破uhttpd的限制
local tmp_file = "/tmp/links.conf"
local chunk = luci.http.formvalue("chunk")
local chunk_index = tonumber(luci.http.formvalue("chunk_index"))
local total_chunks = tonumber(luci.http.formvalue("total_chunks"))
local chunk = http.formvalue("chunk")
local chunk_index = tonumber(http.formvalue("chunk_index"))
local total_chunks = tonumber(http.formvalue("total_chunks"))
if chunk and chunk_index ~= nil and total_chunks ~= nil then
-- 按顺序拼接到文件
@@ -144,8 +150,8 @@ function link_add_node()
end
function socks_autoswitch_add_node()
local id = luci.http.formvalue("id")
local key = luci.http.formvalue("key")
local id = http.formvalue("id")
local key = http.formvalue("key")
if id and id ~= "" and key and key ~= "" then
uci:set(appname, id, "enable_autoswitch", "1")
local new_list = uci:get(appname, id, "autoswitch_backup_node") or {}
@@ -162,12 +168,12 @@ function socks_autoswitch_add_node()
uci:set_list(appname, id, "autoswitch_backup_node", new_list)
api.uci_save(uci, appname)
end
luci.http.redirect(api.url("socks_config", id))
http.redirect(api.url("socks_config", id))
end
function socks_autoswitch_remove_node()
local id = luci.http.formvalue("id")
local key = luci.http.formvalue("key")
local id = http.formvalue("id")
local key = http.formvalue("key")
if id and id ~= "" and key and key ~= "" then
uci:set(appname, id, "enable_autoswitch", "1")
local new_list = uci:get(appname, id, "autoswitch_backup_node") or {}
@@ -179,19 +185,19 @@ function socks_autoswitch_remove_node()
uci:set_list(appname, id, "autoswitch_backup_node", new_list)
api.uci_save(uci, appname)
end
luci.http.redirect(api.url("socks_config", id))
http.redirect(api.url("socks_config", id))
end
function gen_client_config()
local id = luci.http.formvalue("id")
local id = http.formvalue("id")
local config_file = api.TMP_PATH .. "/config_" .. id
luci.sys.call(string.format("/usr/share/passwall2/app.sh run_socks flag=config_%s node=%s bind=127.0.0.1 socks_port=1080 config_file=%s no_run=1", id, id, config_file))
if nixio.fs.access(config_file) then
luci.http.prepare_content("application/json")
luci.http.write(luci.sys.exec("cat " .. config_file))
http.prepare_content("application/json")
http.write(luci.sys.exec("cat " .. config_file))
luci.sys.call("rm -f " .. config_file)
else
luci.http.redirect(api.url("node_list"))
http.redirect(api.url("node_list"))
end
end
@@ -201,38 +207,37 @@ function get_now_use_node()
if node then
e["global"] = node
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
http_write_json(e)
end
function get_redir_log()
local id = luci.http.formvalue("id")
local name = luci.http.formvalue("name")
local id = http.formvalue("id")
local name = http.formvalue("name")
local file_path = "/tmp/etc/passwall2/acl/" .. id .. "/" .. name .. ".log"
if nixio.fs.access(file_path) then
local content = luci.sys.exec("tail -n 19999 '" .. file_path .. "'")
content = content:gsub("\n", "<br />")
luci.http.write(content)
http.write(content)
else
luci.http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
end
end
function get_socks_log()
local name = luci.http.formvalue("name")
local name = http.formvalue("name")
local path = "/tmp/etc/passwall2/SOCKS_" .. name .. ".log"
if nixio.fs.access(path) then
local content = luci.sys.exec("tail -n 5000 ".. path)
content = content:gsub("\n", "<br />")
luci.http.write(content)
http.write(content)
else
luci.http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
end
end
function get_log()
-- luci.sys.exec("[ -f /tmp/log/passwall2.log ] && sed '1!G;h;$!d' /tmp/log/passwall2.log > /tmp/log/passwall2_show.log")
luci.http.write(luci.sys.exec("[ -f '/tmp/log/passwall2.log' ] && cat /tmp/log/passwall2.log"))
http.write(luci.sys.exec("[ -f '/tmp/log/passwall2.log' ] && cat /tmp/log/passwall2.log"))
end
function clear_log()
@@ -242,20 +247,18 @@ end
function index_status()
local e = {}
e["global_status"] = luci.sys.call("/bin/busybox top -bn1 | grep -v 'grep' | grep '/tmp/etc/passwall2/bin/' | grep 'default' | grep 'global' >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
http_write_json(e)
end
function haproxy_status()
local e = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
http_write_json(e)
end
function socks_status()
local e = {}
local index = luci.http.formvalue("index")
local id = luci.http.formvalue("id")
local index = http.formvalue("index")
local id = http.formvalue("id")
e.index = index
e.socks_status = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v -E 'grep|acl/|acl_' | grep '%s/bin/' | grep '%s' | grep 'SOCKS_' > /dev/null", appname, id)) == 0
local use_http = uci:get(appname, id, "http_port") or 0
@@ -264,14 +267,13 @@ function socks_status()
e.use_http = 1
e.http_status = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v -E 'grep|acl/|acl_' | grep '%s/bin/' | grep '%s' | grep -E 'HTTP_|HTTP2SOCKS' > /dev/null", appname, id)) == 0
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
http_write_json(e)
end
function connect_status()
local e = {}
e.use_time = ""
local url = luci.http.formvalue("url")
local url = http.formvalue("url")
local result = luci.sys.exec('curl --connect-timeout 3 -o /dev/null -I -sk -w "%{http_code}:%{time_appconnect}" ' .. url)
local code = tonumber(luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $1}'") or "0")
if code ~= 0 then
@@ -283,15 +285,14 @@ function connect_status()
end
e.ping_type = "curl"
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
http_write_json(e)
end
function ping_node()
local index = luci.http.formvalue("index")
local address = luci.http.formvalue("address")
local port = luci.http.formvalue("port")
local type = luci.http.formvalue("type") or "icmp"
local index = http.formvalue("index")
local address = http.formvalue("address")
local port = http.formvalue("port")
local type = http.formvalue("type") or "icmp"
local e = {}
e.index = index
if type == "tcping" and luci.sys.exec("echo -n $(command -v tcping)") ~= "" then
@@ -302,13 +303,12 @@ function ping_node()
else
e.ping = luci.sys.exec("echo -n $(ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null" % address)
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
http_write_json(e)
end
function urltest_node()
local index = luci.http.formvalue("index")
local id = luci.http.formvalue("id")
local index = http.formvalue("index")
local id = http.formvalue("id")
local e = {}
e.index = index
local result = luci.sys.exec(string.format("/usr/share/passwall2/test.sh url_test_node %s %s", id, "urltest_node"))
@@ -321,21 +321,20 @@ function urltest_node()
e.use_time = string.format("%.2f", use_time / 1000)
end
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
http_write_json(e)
end
function set_node()
local type = luci.http.formvalue("type")
local config = luci.http.formvalue("config")
local section = luci.http.formvalue("section")
local type = http.formvalue("type")
local config = http.formvalue("config")
local section = http.formvalue("section")
uci:set(appname, type, config, section)
api.uci_save(uci, appname, true, true)
luci.http.redirect(api.url("log"))
http.redirect(api.url("log"))
end
function copy_node()
local section = luci.http.formvalue("section")
local section = http.formvalue("section")
local uuid = api.gen_short_uuid()
uci:section(appname, "nodes", uuid)
for k, v in pairs(uci:get_all(appname, section)) do
@@ -352,11 +351,13 @@ function copy_node()
uci:delete(appname, uuid, "add_from")
uci:set(appname, uuid, "add_mode", 1)
api.uci_save(uci, appname)
luci.http.redirect(api.url("node_config", uuid))
http.redirect(api.url("node_config", uuid))
end
function clear_all_nodes()
uci:set(appname, '@global[0]', "enabled", "0")
uci:set(appname, '@global[0]', "socks_enabled", "0")
uci:set(appname, '@haproxy_config[0]', "balancing_enable", "0")
uci:delete(appname, '@global[0]', "node")
uci:foreach(appname, "socks", function(t)
uci:delete(appname, t[".name"])
@@ -371,12 +372,15 @@ function clear_all_nodes()
uci:foreach(appname, "nodes", function(node)
uci:delete(appname, node['.name'])
end)
api.uci_save(uci, appname, true)
luci.sys.call("/etc/init.d/" .. appname .. " stop")
uci:foreach(appname, "subscribe_list", function(t)
uci:delete(appname, t[".name"], "md5")
end)
api.uci_save(uci, appname, true, true)
end
function delete_select_nodes()
local ids = luci.http.formvalue("ids")
local ids = http.formvalue("ids")
string.gsub(ids, '[^' .. "," .. ']+', function(w)
if (uci:get(appname, "@global[0]", "node") or "") == w then
uci:delete(appname, '@global[0]', "node")
@@ -413,38 +417,47 @@ function delete_select_nodes()
uci:delete(appname, t[".name"], "chain_proxy")
end
end)
if (uci:get(appname, w, "add_mode") or "0") == "2" then
local add_from = uci:get(appname, w, "add_from") or ""
if add_from ~= "" then
uci:foreach(appname, "subscribe_list", function(t)
if t["remark"] == add_from then
uci:delete(appname, t[".name"], "md5")
end
end)
end
end
uci:delete(appname, w)
end)
api.uci_save(uci, appname, true)
luci.sys.call("/etc/init.d/" .. appname .. " restart > /dev/null 2>&1 &")
api.uci_save(uci, appname, true, true)
end
function update_rules()
local update = luci.http.formvalue("update")
local update = http.formvalue("update")
luci.sys.call("lua /usr/share/passwall2/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &")
http_write_json()
end
function server_user_status()
local e = {}
e.index = luci.http.formvalue("index")
e.status = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep -i '%s' >/dev/null", appname .. "_server", luci.http.formvalue("id"))) == 0
e.index = http.formvalue("index")
e.status = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep -i '%s' >/dev/null", appname .. "_server", http.formvalue("id"))) == 0
http_write_json(e)
end
function server_user_log()
local id = luci.http.formvalue("id")
local id = http.formvalue("id")
if nixio.fs.access("/tmp/etc/passwall2_server/" .. id .. ".log") then
local content = luci.sys.exec("cat /tmp/etc/passwall2_server/" .. id .. ".log")
content = content:gsub("\n", "<br />")
luci.http.write(content)
http.write(content)
else
luci.http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
end
end
function server_get_log()
luci.http.write(luci.sys.exec("[ -f '/tmp/log/passwall2_server.log' ] && cat /tmp/log/passwall2_server.log"))
http.write(luci.sys.exec("[ -f '/tmp/log/passwall2_server.log' ] && cat /tmp/log/passwall2_server.log"))
end
function server_clear_log()
@@ -475,12 +488,13 @@ function com_update(comname)
http_write_json(json)
end
local backup_files = {
"/etc/config/passwall2",
"/etc/config/passwall2_server",
"/usr/share/passwall2/domains_excluded"
}
function create_backup()
local backup_files = {
"/etc/config/passwall2",
"/etc/config/passwall2_server",
"/usr/share/passwall2/domains_excluded"
}
local date = os.date("%y%m%d%H%M")
local tar_file = "/tmp/passwall2-" .. date .. "-backup.tar.gz"
fs.remove(tar_file)
@@ -493,15 +507,115 @@ function create_backup()
fs.remove(tar_file)
end
function restore_backup()
local ok, err = pcall(function()
local filename = http.formvalue("filename")
local chunk = http.formvalue("chunk")
local chunk_index = tonumber(http.formvalue("chunk_index") or "-1")
local total_chunks = tonumber(http.formvalue("total_chunks") or "-1")
if not filename or not chunk then
http_write_json({ status = "error", message = "Missing filename or chunk" })
return
end
local file_path = "/tmp/" .. filename
local decoded = nixio.bin.b64decode(chunk)
local fp = io.open(file_path, "a+")
if not fp then
http_write_json({ status = "error", message = "Failed to open file for writing: " .. file_path })
return
end
fp:write(decoded)
fp:close()
if chunk_index + 1 == total_chunks then
api.sys.call("echo '' > /tmp/log/passwall2.log")
api.log(" * PassWall2 配置文件上传成功…")
local temp_dir = '/tmp/passwall_bak'
api.sys.call("mkdir -p " .. temp_dir)
if api.sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then
for _, backup_file in ipairs(backup_files) do
local temp_file = temp_dir .. backup_file
if fs.access(temp_file) then
api.sys.call("cp -f " .. temp_file .. " " .. backup_file)
end
end
api.log(" * PassWall2 配置还原成功…")
api.log(" * 重启 PassWall2 服务中…\n")
api.sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &')
api.sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &')
else
api.log(" * PassWall2 配置文件解压失败,请重试!")
end
api.sys.call("rm -rf " .. temp_dir)
fs.remove(file_path)
http_write_json({ status = "success", message = "Upload completed", path = file_path })
else
http_write_json({ status = "success", message = "Chunk received" })
end
end)
if not ok then
http_write_json({ status = "error", message = tostring(err) })
end
end
function geo_view()
local action = luci.http.formvalue("action")
local value = luci.http.formvalue("value")
if not value or value == "" then
http.prepare_content("text/plain")
http.write(i18n.translate("Please enter query content!"))
return
end
local geo_dir = (uci:get(appname, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"):match("^(.*)/")
local geosite_path = geo_dir .. "/geosite.dat"
local geoip_path = geo_dir .. "/geoip.dat"
local geo_type, file_path, cmd
local geo_string = ""
if action == "lookup" then
if api.datatypes.ipaddr(value) or api.datatypes.ip6addr(value) then
geo_type, file_path = "geoip", geoip_path
else
geo_type, file_path = "geosite", geosite_path
end
cmd = string.format("geoview -type %s -action lookup -input '%s' -value '%s' -lowmem=true", geo_type, file_path, value)
geo_string = luci.sys.exec(cmd):lower()
if geo_string ~= "" then
local lines = {}
for line in geo_string:gmatch("([^\n]*)\n?") do
if line ~= "" then
table.insert(lines, geo_type .. ":" .. line)
end
end
geo_string = table.concat(lines, "\n")
end
elseif action == "extract" then
local prefix, list = value:match("^(geoip:)(.*)$")
if not prefix then
prefix, list = value:match("^(geosite:)(.*)$")
end
if prefix and list and list ~= "" then
geo_type = prefix:sub(1, -2)
file_path = (geo_type == "geoip") and geoip_path or geosite_path
cmd = string.format("geoview -type %s -action extract -input '%s' -list '%s' -lowmem=true", geo_type, file_path, list)
geo_string = luci.sys.exec(cmd)
end
end
http.prepare_content("text/plain")
if geo_string and geo_string ~="" then
http.write(geo_string)
else
http.write(i18n.translate("No results were found!"))
end
end
function subscribe_del_node()
local remark = luci.http.formvalue("remark")
local remark = http.formvalue("remark")
if remark and remark ~= "" then
luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate " .. luci.util.shellquote(remark) .. " > /dev/null 2>&1")
end
luci.http.status(200, "OK")
http.status(200, "OK")
end
function subscribe_del_all()
luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate > /dev/null 2>&1")
luci.http.status(200, "OK")
http.status(200, "OK")
end