servers.lua 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. -- Licensed to the public under the GNU General Public License v3.
  2. require "luci.http"
  3. require "luci.sys"
  4. require "nixio.fs"
  5. require "luci.dispatcher"
  6. require "luci.model.uci"
  7. local uci = require "luci.model.uci".cursor()
  8. local m, s, o, node
  9. local server_count = 0
  10. -- 确保正确判断程序是否存在
  11. local function is_finded(e)
  12. return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
  13. end
  14. local has_ss_rust = is_finded("sslocal") or is_finded("ssserver")
  15. local has_ss_libev = is_finded("ss-redir") or is_finded("ss-local")
  16. local ss_type_list = {}
  17. if has_ss_rust then
  18. table.insert(ss_type_list, { id = "ss-rust", name = translate("ShadowSocks-rust Version") })
  19. end
  20. if has_ss_libev then
  21. table.insert(ss_type_list, { id = "ss-libev", name = translate("ShadowSocks-libev Version") })
  22. end
  23. -- 如果用户没有手动设置,则自动选择
  24. if ss_type == "" then
  25. if has_ss_rust then
  26. ss_type = "ss-rust"
  27. elseif has_ss_libev then
  28. ss_type = "ss-libev"
  29. end
  30. end
  31. uci:foreach("shadowsocksr", "servers", function(s)
  32. server_count = server_count + 1
  33. end)
  34. m = Map("shadowsocksr", translate("Servers subscription and manage"))
  35. -- Server Subscribe
  36. s = m:section(TypedSection, "server_subscribe")
  37. s.anonymous = true
  38. o = s:option(Flag, "auto_update", translate("Auto Update"))
  39. o.rmempty = false
  40. o.description = translate("Auto Update Server subscription, GFW list and CHN route")
  41. o = s:option(ListValue, "auto_update_week_time", translate("Update Time (Every Week)"))
  42. o:value('*', translate("Every Day"))
  43. o:value("1", translate("Every Monday"))
  44. o:value("2", translate("Every Tuesday"))
  45. o:value("3", translate("Every Wednesday"))
  46. o:value("4", translate("Every Thursday"))
  47. o:value("5", translate("Every Friday"))
  48. o:value("6", translate("Every Saturday"))
  49. o:value("0", translate("Every Sunday"))
  50. o.default = "*"
  51. o.rmempty = true
  52. o:depends("auto_update", "1")
  53. o = s:option(ListValue, "auto_update_day_time", translate("Update time (every day)"))
  54. for t = 0, 23 do
  55. o:value(t, t .. ":00")
  56. end
  57. o.default = 2
  58. o.rmempty = true
  59. o:depends("auto_update", "1")
  60. o = s:option(ListValue, "auto_update_min_time", translate("Update Interval (min)"))
  61. for i = 0, 59 do
  62. o:value(i, i .. ":00")
  63. end
  64. o.default = 30
  65. o.rmempty = true
  66. o:depends("auto_update", "1")
  67. -- 确保 ss_type_list 不为空
  68. if #ss_type_list > 0 then
  69. o = s:option(ListValue, "ss_type", string.format("<b><span style='color:red;'>%s</span></b>", translate("ShadowSocks Node Use Version")))
  70. o.description = translate("Selection ShadowSocks Node Use Version.")
  71. for _, v in ipairs(ss_type_list) do
  72. o:value(v.id, v.name) -- 存储 "ss-libev" / "ss-rust",但 UI 显示完整名称
  73. end
  74. o.default = ss_type -- 设置默认值
  75. o.write = function(self, section, value)
  76. -- 更新 Shadowsocks 节点的 has_ss_type
  77. uci:foreach("shadowsocksr", "servers", function(s)
  78. local node_type = uci:get("shadowsocksr", s[".name"], "type") -- 获取节点类型
  79. if node_type == "ss" then -- 仅修改 Shadowsocks 节点
  80. local old_value = uci:get("shadowsocksr", s[".name"], "has_ss_type")
  81. if old_value ~= value then
  82. uci:set("shadowsocksr", s[".name"], "has_ss_type", value)
  83. end
  84. end
  85. end)
  86. -- 更新当前 section 的 ss_type
  87. Value.write(self, section, value)
  88. end
  89. end
  90. o = s:option(DynamicList, "subscribe_url", translate("Subscribe URL"))
  91. o.rmempty = true
  92. o = s:option(Value, "filter_words", translate("Subscribe Filter Words"))
  93. o.rmempty = true
  94. o.description = translate("Filter Words splited by /")
  95. o = s:option(Value, "save_words", translate("Subscribe Save Words"))
  96. o.rmempty = true
  97. o.description = translate("Save Words splited by /")
  98. o = s:option(Button, "update_Sub", translate("Update Subscribe List"))
  99. o.inputstyle = "reload"
  100. o.description = translate("Update subscribe url list first")
  101. o.write = function()
  102. uci:commit("shadowsocksr")
  103. luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers"))
  104. end
  105. o = s:option(Flag, "allow_insecure", translate("Allow subscribe Insecure nodes By default"))
  106. o.rmempty = false
  107. o.description = translate("Subscribe nodes allows insecure connection as TLS client (insecure)")
  108. o.default = "0"
  109. o = s:option(Flag, "switch", translate("Subscribe Default Auto-Switch"))
  110. o.rmempty = false
  111. o.description = translate("Subscribe new add server default Auto-Switch on")
  112. o.default = "1"
  113. o = s:option(Flag, "proxy", translate("Through proxy update"))
  114. o.rmempty = false
  115. o.description = translate("Through proxy update list, Not Recommended ")
  116. o = s:option(Button, "subscribe", translate("Update All Subscribe Servers"))
  117. o.rawhtml = true
  118. o.template = "shadowsocksr/subscribe"
  119. o = s:option(Button, "delete", translate("Delete All Subscribe Servers"))
  120. o.inputstyle = "reset"
  121. o.description = string.format(translate("Server Count") .. ": %d", server_count)
  122. o.write = function()
  123. uci:delete_all("shadowsocksr", "servers", function(s)
  124. if s.hashkey or s.isSubscribe then
  125. return true
  126. else
  127. return false
  128. end
  129. end)
  130. uci:save("shadowsocksr")
  131. uci:commit("shadowsocksr")
  132. for file in nixio.fs.glob("/tmp/sub_md5_*") do
  133. nixio.fs.remove(file)
  134. end
  135. luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "delete"))
  136. return
  137. end
  138. o = s:option(Value, "user_agent", translate("User-Agent"))
  139. o.default = "v2rayN/9.99"
  140. o:value("curl", "Curl")
  141. o:value("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Linux")
  142. o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Windows")
  143. o:value("v2rayN/9.99", "v2rayN")
  144. -- [[ Servers Manage ]]--
  145. s = m:section(TypedSection, "servers")
  146. s.anonymous = true
  147. s.addremove = true
  148. s.template = "cbi/tblsection"
  149. s.sortable = true
  150. s.extedit = luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers", "%s")
  151. function s.create(...)
  152. local sid = TypedSection.create(...)
  153. if sid then
  154. luci.http.redirect(s.extedit % sid)
  155. return
  156. end
  157. end
  158. o = s:option(DummyValue, "type", translate("Type"))
  159. function o.cfgvalue(self, section)
  160. return m:get(section, "v2ray_protocol") or Value.cfgvalue(self, section) or translate("None")
  161. end
  162. o = s:option(DummyValue, "alias", translate("Alias"))
  163. function o.cfgvalue(...)
  164. return Value.cfgvalue(...) or translate("None")
  165. end
  166. o = s:option(DummyValue, "server_port", translate("Server Port"))
  167. function o.cfgvalue(...)
  168. return Value.cfgvalue(...) or "N/A"
  169. end
  170. o = s:option(DummyValue, "server_port", translate("Socket Connected"))
  171. o.template = "shadowsocksr/socket"
  172. o.width = "10%"
  173. o.render = function(self, section, scope)
  174. self.transport = s:cfgvalue(section).transport
  175. if self.transport == 'ws' then
  176. self.ws_path = s:cfgvalue(section).ws_path
  177. self.tls = s:cfgvalue(section).tls
  178. end
  179. DummyValue.render(self, section, scope)
  180. end
  181. o = s:option(DummyValue, "server", translate("Ping Latency"))
  182. o.template = "shadowsocksr/ping"
  183. o.width = "10%"
  184. local global_server = uci:get_first('shadowsocksr', 'global', 'global_server')
  185. node = s:option(Button, "apply_node", translate("Apply"))
  186. node.inputstyle = "apply"
  187. node.render = function(self, section, scope)
  188. if section == global_server then
  189. self.title = translate("Reapply")
  190. else
  191. self.title = translate("Apply")
  192. end
  193. Button.render(self, section, scope)
  194. end
  195. node.write = function(self, section)
  196. uci:set("shadowsocksr", '@global[0]', 'global_server', section)
  197. uci:save("shadowsocksr")
  198. uci:commit("shadowsocksr")
  199. luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "restart"))
  200. end
  201. o = s:option(Flag, "switch_enable", translate("Auto Switch"))
  202. o.rmempty = false
  203. function o.cfgvalue(...)
  204. return Value.cfgvalue(...) or 1
  205. end
  206. m:append(Template("shadowsocksr/server_list"))
  207. return m