提交 559a8cdb 编写于 作者: L LEAN-ESX

luci-app-ssr-plus: keep subscribe order

上级 1c448705
......@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-ssr-plus
PKG_VERSION:=165
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
......
......@@ -15,48 +15,18 @@ local tinsert = table.insert
local ssub, slen, schar, sbyte, sformat, sgsub = string.sub, string.len, string.char, string.byte, string.format, string.gsub
local jsonParse, jsonStringify = luci.jsonc.parse, luci.jsonc.stringify
local b64decode = nixio.bin.b64decode
local nodeResult = {} -- update result
local application = 'shadowsocksr'
local cache = {}
local nodeResult = setmetatable({}, { __index = cache }) -- update result
local name = 'shadowsocksr'
local uciType = 'servers'
local ucic2 = uci.cursor()
local proxy = ucic2:get(application, '@server_subscribe[0]', 'proxy') or '0'
local switch = ucic2:get(application, '@server_subscribe[0]', 'switch') or '1'
local subscribe_url = ucic2:get(application, '@server_subscribe[0]', 'subscribe_url') or {}
ucic2:revert(application)
local ucic = uci.cursor()
local proxy = ucic:get_first(name, 'server_subscribe', 'proxy', '0')
local switch = ucic:get_first(name, 'server_subscribe', 'switch', '1')
local subscribe_url = ucic:get_first(name, 'server_subscribe', 'subscribe_url', {})
local log = function(...)
print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({ ... }, " "))
end
-- 获取各项动态配置的当前服务器,可以用 get 和 set, get必须要获取到节点表
local CONFIG = {
GLOBAL_SERVER = {
remarks = '主节点',
type = "global", option = "global_server",
set = function(server)
ucic2:set(application, '@global[0]', "global_server", server)
end
}
}
do
for k, v in pairs(CONFIG) do
local currentNode
if v.get then
currentNode = v.get()
else
local cfgid = ucic2:get(application, '@' .. v.type .. '[0]', v.option)
if cfgid then
currentNode = ucic2:get_all(application, cfgid)
end
end
if currentNode then
CONFIG[k].currentNode = currentNode
else
CONFIG[k] = nil
end
end
end
-- 分割字符串
local function split(full, sep)
full = full:gsub("%z", "") -- 这里不是很清楚 有时候结尾带个\0
......@@ -77,15 +47,15 @@ local function split(full, sep)
return result
end
-- urlencode
-- local function get_urlencode(c)
-- return sformat("%%%02X", sbyte(c))
-- end
local function get_urlencode(c)
return sformat("%%%02X", sbyte(c))
end
-- local function urlEncode(szText)
-- local str = szText:gsub("([^0-9a-zA-Z ])", get_urlencode)
-- str = str:gsub(" ", "+")
-- return str
-- end
local function urlEncode(szText)
local str = szText:gsub("([^0-9a-zA-Z ])", get_urlencode)
str = str:gsub(" ", "+")
return str
end
local function get_urldecode(h)
return schar(tonumber(h, 16))
......@@ -101,7 +71,12 @@ local function trim(text)
end
return (sgsub(text, "^%s*(.-)%s*$", "%1"))
end
-- md5
local function md5(content)
local stdout = luci.sys.exec('echo \"' .. urlEncode(content) .. '\" | md5sum | cut -d \" \" -f1')
-- assert(nixio.errno() == 0)
return trim(stdout)
end
-- base64
local function base64Decode(text)
local raw = text
......@@ -121,10 +96,9 @@ end
-- 处理数据
local function processData(szType, content)
local result = {
type = szType,
local_port = 1234,
kcp_param = '--nocomp',
isSubscribe = 1,
type = szType,
local_port = 1234,
kcp_param = '--nocomp'
}
if szType == 'ssr' then
local dat = split(content, "/%?")
......@@ -269,12 +243,12 @@ local function processData(szType, content)
local t = split(v, '=')
params[t[1]] = t[2]
end
if params.peer then
-- 未指定peer(sni)默认使用remote addr
result.tls_host = params.peer
end
if params.allowInsecure == "1" then
result.insecure = "1"
else
......@@ -288,6 +262,14 @@ local function processData(szType, content)
if not result.alias then
result.alias = result.server .. ':' .. result.server_port
end
-- alias 不参与 hashkey 计算
local alias = result.alias
result.alias = nil
local switch_enable = result.switch_enable
result.switch_enable = nil
result.hashkey = md5(jsonStringify(result))
result.alias = alias
result.switch_enable = switch_enable
return result
end
-- wget
......@@ -296,71 +278,20 @@ local function wget(url)
return trim(stdout)
end
local function select_node(nodes, config)
local server
-- 第一优先级 IP + 端口
for id, node in pairs(nodes) do
if node.server .. ':' .. node.server_port == config.currentNode.server .. ':' .. config.currentNode.server_port then
log('选择【' .. config.remarks .. '】第一匹配节点:' .. node.alias)
server = id
break
end
end
-- 第二优先级 IP
if not server then
for id, node in pairs(nodes) do
if node.server == config.currentNode.server then
log('选择【' .. config.remarks .. '】第二匹配节点:' .. node.alias)
server = id
break
end
end
end
-- 第三优先级备注
if not server then
for id, node in pairs(nodes) do
if node.alias == config.currentNode.alias then
log('选择【' .. config.remarks .. '】第三匹配节点:' .. node.alias)
server = id
break
end
end
end
-- 第四 cfgid
if not server then
for id, node in pairs(nodes) do
if id == config.currentNode['.name'] then
log('选择【' .. config.remarks .. '】第四匹配节点:' .. node.alias)
server = id
break
end
end
end
-- 还不行 随便找一个
if not server then
server = ucic2:get(application, '@'.. uciType .. '[0]')
if server then
log('无法找到最匹配的节点,当前已更换为' .. ucic2:get_all(application, server).alias)
end
end
if server then
config.set(server)
end
end
local execute = function()
-- exec
do
if proxy == '0' then -- 不使用代理更新的话先暂停
log('服务正在暂停')
luci.sys.init.stop(application)
luci.sys.init.stop(name)
end
for k, url in ipairs(subscribe_url) do
local raw = wget(url)
if #raw > 0 then
local nodes, szType
local all_odes = {}
tinsert(nodeResult, all_odes)
local groupHash = md5(url)
cache[groupHash] = {}
tinsert(nodeResult, {})
local index = #nodeResult
-- SSD 似乎是这种格式 ssd:// 开头的
if raw:find('ssd://') then
......@@ -410,12 +341,14 @@ local execute = function()
result.alias:find("官网") or
result.alias:find("防失联地址") or
not result.server or
result.server:match("[^0-9a-zA-Z%_%-%.%s]") -- 中文做地址的 也没有人拿中文域名搞,就算中文域也有Puny Code SB 机场
result.server:match("[^0-9a-zA-Z%-%.%s]") -- 中文做地址的 也没有人拿中文域名搞,就算中文域也有Puny Code SB 机场
then
log('丢弃无效节点: ' .. result.type ..' 节点, ' .. result.alias)
else
log('成功解析: ' .. result.type ..' 节点, ' .. result.alias)
tinsert(all_odes, result)
result.grouphashkey = groupHash
tinsert(nodeResult[index], result)
cache[groupHash][result.hashkey] = nodeResult[index][#nodeResult[index]]
end
end
end
......@@ -428,45 +361,61 @@ local execute = function()
end
-- diff
do
assert(next(nodeResult), '更新失败,没有可用的节点信息')
-- delete all for subscribe nodes
ucic2:foreach(application, uciType, function(node)
if node.isSubscribe or node.hashkey then -- 兼容之前的hashkey
ucic2:delete(application, node['.name'])
if next(nodeResult) == nil then
log("更新失败,没有可用的节点信息")
return
end
local add, del = 0, 0
ucic:foreach(name, uciType, function(old)
if old.grouphashkey or old.hashkey then -- 没有 hash 的不参与删除
if not nodeResult[old.grouphashkey] or not nodeResult[old.grouphashkey][old.hashkey] then
ucic:delete(name, old['.name'])
del = del + 1
else
local dat = nodeResult[old.grouphashkey][old.hashkey]
ucic:tset(name, old['.name'], dat)
-- 标记一下
setmetatable(nodeResult[old.grouphashkey][old.hashkey], { __index = { _ignore = true } })
end
else
if not old.alias then
old.alias = old.server .. ':' .. old.server_port
end
log('忽略手动添加的节点: ' .. old.alias)
end
end)
for _, v in ipairs(nodeResult) do
for _, vv in ipairs(v) do
vv.switch_enable = switch
local cfgid = ucic2:add(application, uciType)
for kkk, vvv in pairs(vv) do
ucic2:set(application, cfgid, kkk, vvv)
for k, v in ipairs(nodeResult) do
for kk, vv in ipairs(v) do
if not vv._ignore then
local section = ucic:add(name, uciType)
ucic:tset(name, section, vv)
ucic:set(name, section, "switch_enable", switch)
add = add + 1
end
end
end
ucic2:commit(application)
local ucic3 = uci.cursor()
-- repair configuration
if next(CONFIG) then
local nodes = {}
ucic3:foreach(application, uciType, function(node)
if node.server and node.server_port and node.alias then
nodes[node['.name']] = node
end
end)
for _, config in pairs(CONFIG) do
select_node(nodes, config)
ucic:commit(name)
-- 如果原有服务器节点已经不见了就尝试换为第一个节点
local globalServer = ucic:get_first(name, 'global', 'global_server', '')
local firstServer = ucic:get_first(name, uciType)
if firstServer then
if not ucic:get(name, globalServer) then
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
ucic:commit(name)
ucic:set(name, ucic:get_first(name, 'global'), 'global_server', ucic:get_first(name, uciType))
ucic:commit(name)
log('当前主服务器节点已被删除,正在自动更换为第一个节点。')
luci.sys.call("/etc/init.d/" .. name .. " start > /dev/null 2>&1 &")
else
log('维持当前主服务器节点。')
luci.sys.call("/etc/init.d/" .. name .." restart > /dev/null 2>&1 &")
end
ucic3:commit(application)
end
-- select first server
local globalServer = ucic3:get(application, '@global[0]', 'global_server') or ''
if not globalServer or not ucic3:get_all(application, globalServer) then
ucic3:set(application, '@global[0]', 'global_server', select(2, ucic3:get(application, '@' .. uciType .. '[0]')))
ucic3:commit(application)
log('当前没有主节点,自动选择第一个节点开启服务。')
else
log('没有服务器节点了,停止服务')
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
end
luci.sys.call("/etc/init.d/" .. application .." restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
log('新增节点数量: ' ..add, '删除节点数量: ' .. del)
log('订阅更新成功')
end
end
......@@ -476,13 +425,13 @@ if subscribe_url and #subscribe_url > 0 then
log(e)
log(debug.traceback())
log('发生错误, 正在恢复服务')
if CONFIG.GLOBAL_SERVER and CONFIG.GLOBAL_SERVER.currentNode then
luci.sys.call("/etc/init.d/" .. application .." restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
local firstServer = ucic:get_first(name, uciType)
if firstServer then
luci.sys.call("/etc/init.d/" .. name .." restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
log('重启服务成功')
else
luci.sys.call("/etc/init.d/" .. application .." stop > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
luci.sys.call("/etc/init.d/" .. name .." stop > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
log('停止服务成功')
end
end)
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册