Files
Archive/openclash/luci-app-openclash/luasrc/model/cbi/openclash/groups-config.lua
T
2026-03-13 19:57:41 +01:00

335 lines
9.7 KiB
Lua

local m, s, o
local openclash = "openclash"
local uci = luci.model.uci.cursor()
local fs = require "luci.openclash"
local sys = require "luci.sys"
local sid = arg[1]
local file_path = ""
for i = 2, #(arg) do
file_path = file_path .. "/" .. luci.http.urlencode(arg[i])
end
if not fs.isfile(file_path) and file_path ~= "" then
file_path = luci.http.urldecode(file_path)
end
font_red = [[<b style=color:red>]]
font_off = [[</b>]]
bold_on = [[<strong>]]
bold_off = [[</strong>]]
function IsYamlFile(e)
e=e or""
local e=string.lower(string.sub(e,-5,-1))
return e == ".yaml"
end
function IsYmlFile(e)
e=e or""
local e=string.lower(string.sub(e,-4,-1))
return e == ".yml"
end
m = Map(openclash, translate("Edit Group"))
m.pageaction = false
m.redirect = luci.dispatcher.build_url("admin/services/openclash/servers%s" % file_path)
if m.uci:get(openclash, sid) ~= "groups" then
luci.http.redirect(m.redirect)
return
end
-- [[ Groups Setting ]]--
s = m:section(NamedSection, sid, "groups")
s.anonymous = true
s.addremove = false
o = s:option(ListValue, "config", translate("Config File"))
o:value("all", translate("Use For All Config File"))
local e,a={}
for t,f in ipairs(fs.glob("/etc/openclash/config/*"))do
a=fs.stat(f)
if a then
e[t]={}
e[t].name=fs.basename(f)
if IsYamlFile(e[t].name) or IsYmlFile(e[t].name) then
o:value(e[t].name)
end
end
end
o = s:option(ListValue, "type", translate("Group Type"))
o.rmempty = false
o.description = translate("Choose The Operation Mode")
o:value("select", translate("Manual-Select"))
o:value("smart", translate("Smart-Select"))
o:value("url-test", translate("URL-Test"))
o:value("fallback", translate("Fallback"))
o:value("load-balance", translate("Load-Balance"))
o = s:option(Value, "name", translate("Group Name"))
o.rmempty = false
o.default = "Group - "..sid
o = s:option(ListValue, "disable_udp", translate("Disable UDP"))
o:value("false", translate("Disable"))
o:value("true", translate("Enable"))
o.default = "false"
o.rmempty = true
o = s:option(Value, "test_url", translate("Test URL"))
o:value("http://cp.cloudflare.com/generate_204")
o:value("http://www.gstatic.com/generate_204")
o:value("https://cp.cloudflare.com/generate_204")
o.rmempty = true
o:depends("type", "url-test")
o:depends("type", "fallback")
o:depends("type", "load-balance")
o:depends("type", "smart")
o = s:option(Value, "test_interval", translate("Test Interval(s)"))
o.default = "300"
o.rmempty = true
o:depends("type", "url-test")
o:depends("type", "fallback")
o:depends("type", "load-balance")
o:depends("type", "smart")
o = s:option(ListValue, "strategy", translate("Strategy Type"))
o.rmempty = true
o.description = translate("Choose The Load-Balance's Strategy Type")
o:value("round-robin", translate("Round-robin"))
o:value("consistent-hashing", translate("Consistent-hashing"))
o:value("sticky-sessions", translate("Sticky-sessions"))
o:depends("type", "load-balance")
o = s:option(ListValue, "uselightgbm", translate("Uselightgbm"))
o.description = translate("Use LightGBM Model For Smart Group Weight Prediction")
o:value("false", translate("Disable"))
o:value("true", translate("Enable"))
o.default = "false"
o.rmempty = true
o:depends("type", "smart")
o = s:option(ListValue, "collectdata", translate("Collectdata"))
o.description = translate("Collect Datas For Smart Group Model Training")
o:value("false", translate("Disable"))
o:value("true", translate("Enable"))
o.default = "false"
o.rmempty = true
o:depends("type", "smart")
o = s:option(Value, "policy_priority", translate("Policy Priority"))
o.description = translate("The Priority Of The Nodes, The Higher Than 1, The More Likely It Is To Be Selected, The Default Is 1, Support Regex")
o.rmempty = true
o.placeholder = "Premium:0.9;SG:1.3"
o.rmempty = true
o:depends("type", "smart")
o = s:option(Value, "tolerance", translate("Tolerance(ms)"))
o.default = "150"
o.rmempty = true
o:depends("type", "url-test")
o = s:option(Value, "policy_filter", translate("Provider Filter"))
o.rmempty = true
o.placeholder = "bgp|sg"
o = s:option(Value, "icon", translate("Icon"))
o.rmempty = true
-- [[ other-setting ]]--
o = s:option(Value, "other_parameters", translate("Other Parameters"))
o.template = "cbi/tvalue"
o.rows = 20
o.wrap = "off"
o.description = font_red..bold_on..translate("Edit Your Other Parameters Here")..bold_off..font_off
o.rmempty = true
function o.cfgvalue(self, section)
if self.map:get(section, "other_parameters") == nil then
return "# Example:\n"..
"# Only support YAML, four spaces need to be reserved at the beginning of each line to maintain formatting alignment\n"..
"# 示例:\n"..
"# 仅支持 YAML, 每行行首需要多保留四个空格以使脚本处理后能够与上方配置保持格式对齐\n"..
"# type: select\n"..
"# proxies:\n"..
"# - DIRECT\n"..
"# - ss\n"..
"# use:\n"..
"# - provider1\n"..
"# - provider1\n"..
"# url: 'https://www.gstatic.com/generate_204'\n"..
"# interval: 300\n"..
"# lazy: true\n"..
"# timeout: 5000\n"..
"# max-failed-times: 5\n"..
"# disable-udp: true\n"..
"# interface-name: en0\n"..
"# routing-mark: 11451\n"..
"# include-all: false\n"..
"# include-all-proxies: false\n"..
"# include-all-providers: false\n"..
"# filter: \"(?i)港|hk|hongkong|hong kong\"\n"..
"# exclude-filter: \"美|日\"\n"..
"# exclude-type: \"Shadowsocks|Http\"\n"..
"# expected-status: 204\n"..
"# hidden: true\n"..
"# icon: xxx"
else
return Value.cfgvalue(self, section)
end
end
function o.validate(self, value)
if value then
value = value:gsub("\r\n?", "\n")
value = value:gsub("%c*$", "")
end
return value
end
o = s:option(DynamicList, "other_group", translate("Other Group (Support Regex)"))
o.description = font_red..bold_on..translate("The Added Proxy Groups Must Exist Except 'DIRECT' & 'REJECT' & 'REJECT-DROP' & 'PASS' & 'GLOBAL'")..bold_off..font_off
o:value("all", translate("All Groups"))
uci:foreach("openclash", "groups",
function(s)
if s.name ~= "" and s.name ~= nil and s.name ~= m.uci:get(openclash, sid, "name") and (s.config == m.uci:get(openclash, sid, "config") or s.config == "all") then
o:value(s.name)
end
end)
o:value("DIRECT")
o:value("REJECT")
o:value("REJECT-DROP")
o:value("PASS")
o:value("GLOBAL")
o.rmempty = true
local function sync_group_name(section, old_name, new_name)
if old_name == "" or old_name == nil or old_name == new_name then
return
end
local function matches(value, pattern)
if not value or not pattern then
return false
end
local ok, result = pcall(function() return string.match(value, pattern) end)
if ok and result then
return true
end
return value == pattern
end
-- servers 的 groups 列表
uci:foreach(openclash, "servers", function(s)
local groups = uci:get(openclash, s[".name"], "groups")
if groups then
local new_groups = {}
local changed = false
for _, item in ipairs(groups) do
if matches(old_name, item) then
table.insert(new_groups, new_name)
changed = true
else
table.insert(new_groups, item)
end
end
if changed then
uci:delete(openclash, s[".name"], "groups")
uci:set_list(openclash, s[".name"], "groups", new_groups)
end
end
end)
-- proxy-provider 的 groups 列表
uci:foreach(openclash, "proxy-provider", function(s)
local groups = uci:get(openclash, s[".name"], "groups")
if groups then
local new_groups = {}
local changed = false
for _, item in ipairs(groups) do
if matches(old_name, item) then
table.insert(new_groups, new_name)
changed = true
else
table.insert(new_groups, item)
end
end
if changed then
uci:delete(openclash, s[".name"], "groups")
uci:set_list(openclash, s[".name"], "groups", new_groups)
end
end
end)
-- groups 的 other_group 列表
uci:foreach(openclash, "groups", function(s)
if s[".name"] ~= section then
local other_group = uci:get(openclash, s[".name"], "other_group")
if other_group then
local new_other = {}
local changed = false
for _, item in ipairs(other_group) do
if matches(old_name, item) then
table.insert(new_other, new_name)
changed = true
else
table.insert(new_other, item)
end
end
if changed then
uci:delete(openclash, s[".name"], "other_group")
uci:set_list(openclash, s[".name"], "other_group", new_other)
end
end
end
end)
-- dns_servers 的 specific_group 选项
uci:foreach(openclash, "dns_servers", function(s)
local specific_group = uci:get(openclash, s[".name"], "specific_group")
if matches(old_name, specific_group) then
uci:set(openclash, s[".name"], "specific_group", new_name)
end
end)
-- servers 的 dialer_proxy 选项
uci:foreach(openclash, "servers", function(s)
local dialer_proxy = uci:get(openclash, s[".name"], "dialer_proxy")
if matches(old_name, dialer_proxy) then
uci:set(openclash, s[".name"], "dialer_proxy", new_name)
end
end)
uci:commit(openclash)
end
local t = {
{Commit, Back}
}
a = m:section(Table, t)
o = a:option(Button,"Commit", " ")
o.inputtitle = translate("Commit Settings")
o.inputstyle = "apply"
o.write = function()
local old_name = m.uci:get(openclash, sid, "old_name") or ""
local new_name = luci.http.formvalue("cbid.openclash." .. sid .. ".name") or m.uci:get(openclash, sid, "name")
sync_group_name(sid, old_name, new_name)
m.uci:set(openclash, sid, "old_name", new_name)
m.uci:commit(openclash)
luci.http.redirect(m.redirect)
end
o = a:option(Button,"Back", " ")
o.inputtitle = translate("Back Settings")
o.inputstyle = "reset"
o.write = function()
m.uci:revert(openclash, sid)
luci.http.redirect(m.redirect)
end
m:append(Template("openclash/toolbar_show"))
m:append(Template("openclash/config_editor"))
return m