浏览代码

luci-app-ssr-plus: Add `SS` and `SS-Rust` version selection to address default use of `SS-Rust` programs.

zxlhhyccc 7 月之前
父节点
当前提交
80d870df89

+ 1 - 1
luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua

@@ -96,7 +96,7 @@ function check_port()
 	local retstring = "<br /><br />"
 	local s
 	local server_name = ""
-	local uci = luci.model.uci.cursor()
+	local uci = require "luci.model.uci".cursor()
 	local iret = 1
 	uci:foreach("shadowsocksr", "servers", function(s)
 		if s.alias then

+ 4 - 2
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua

@@ -1,9 +1,9 @@
 local m, s, o
-local uci = luci.model.uci.cursor()
+local uci = require "luci.model.uci".cursor()
 local server_table = {}
 local type_table = {}
 local function is_finded(e)
-	return luci.sys.exec('type -t -p "%s"' % e) ~= "" and true or false
+	return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
 end
 
 uci:foreach("shadowsocksr", "servers", function(s)
@@ -203,6 +203,7 @@ for key, server_type in pairs(type_table) do
         o:depends("server", key)
     end
 end
+o:depends({server = "same", disable = true}) 
 
 -- Socks User
 o = s:option(Value, "socks5_user", translate("Socks5 User"), translate("Only when Socks5 Auth Mode is password valid, Mandatory."))
@@ -225,6 +226,7 @@ for key, server_type in pairs(type_table) do
         o:depends("server", key)
     end
 end
+o:depends({server = "same", disable = true}) 
 end
 
 -- Local Port

+ 55 - 12
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua

@@ -6,19 +6,29 @@ require "luci.sys"
 require "luci.http"
 require "luci.jsonc"
 require "luci.model.ipkg"
+require "luci.model.uci"
+local uci = require "luci.model.uci".cursor()
 
 local m, s, o
+
 local sid = arg[1]
 local uuid = luci.sys.exec("cat /proc/sys/kernel/random/uuid")
 
+-- 确保正确判断程序是否存在
 local function is_finded(e)
-	return luci.sys.exec('type -t -p "%s"' % e) ~= "" and true or false
+	return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
 end
 
 local function is_installed(e)
 	return luci.model.ipkg.installed(e)
 end
 
+local has_ss_rust = is_finded("sslocal") or is_finded("ssserver")
+local has_ss_libev = is_finded("ss-redir") or is_finded("ss-local")
+
+-- 读取当前存储的 ss_type
+local ss_type = uci:get_first("shadowsocksr", "server_subscribe", "ss_type")
+
 local server_table = {}
 local encrypt_methods = {
 	-- ssr
@@ -79,7 +89,7 @@ local encrypt_methods_ss = {
 	"camellia-256-cfb",
 	"salsa20",
 	"chacha20",
-	"chacha20-ietf" ]]
+	"chacha20-ietf" ]]--
 }
 
 local protocol = {
@@ -146,8 +156,8 @@ end
 if is_finded("ssr-redir") then
 	o:value("ssr", translate("ShadowsocksR"))
 end
-if is_finded("ss-local") or is_finded("ss-redir") or is_finded("sslocal") or is_finded("ssmanager") then
-    o:value("ss", translate("Shadowsocks"))
+if has_ss_rust or has_ss_libev then
+    o:value("ss", translate("ShadowSocks"))
 end
 if is_finded("trojan") then
 	o:value("trojan", translate("Trojan"))
@@ -185,16 +195,44 @@ o:depends("type", "tun")
 o.description = translate("Redirect traffic to this network interface")
 
 -- 新增一个选择框,用于选择 Shadowsocks 版本
-o = s:option(ListValue, "ss_variant", translate("Shadowsocks Variant"))
-local isSSRust = is_finded("sslocal") or is_finded("ssmanager")
-local isSSLibev = is_finded("ss-local") or is_finded("ss-redir")
-if isSSRust then
-    o:value("isSSRust", translate("Shadowsocks-rust Version"))
+o = s:option(ListValue, "has_ss_type", string.format("<b><span style='color:red;'>%s</span></b>", translate("ShadowSocks Node Use Version")))
+o.description = translate("Selection ShadowSocks Node Use Version.")
+-- 设置默认 Shadowsocks 版本
+-- 动态添加选项
+if has_ss_rust then
+    o:value("ss-rust", translate("ShadowSocks-rust Version"))
 end
-if isSSLibev then
-    o:value("isSSLibev", translate("Shadowsocks-libev Version"))
+if has_ss_libev then
+    o:value("ss-libev", translate("ShadowSocks-libev Version"))
+end
+-- 设置默认值
+if ss_type == "ss-rust" then
+    o.default = "ss-rust"
+elseif ss_type == "ss-libev" then
+    o.default = "ss-libev"
 end
 o:depends("type", "ss")
+o.write = function(self, section, value)
+    -- 更新 Shadowsocks 节点的 has_ss_type
+    uci:foreach("shadowsocksr", "servers", function(s)
+        local node_type = uci:get("shadowsocksr", s[".name"], "type")  -- 获取节点类型
+        if node_type == "ss" then  -- 仅修改 Shadowsocks 节点
+            local old_value = uci:get("shadowsocksr", s[".name"], "has_ss_type")
+            if old_value ~= value then
+                uci:set("shadowsocksr", s[".name"], "has_ss_type", value)
+            end
+        end
+    end)
+
+    -- 更新 server_subscribe 的 ss_type
+    local old_value = uci:get("shadowsocksr", "server_subscribe", "ss_type")
+    if old_value ~= value then
+        uci:set("shadowsocksr", "@server_subscribe[0]", "ss_type", value)
+    end
+
+    -- 更新当前 section 的 has_ss_type
+    Value.write(self, section, value)
+end
 
 o = s:option(ListValue, "v2ray_protocol", translate("V2Ray/XRay protocol"))
 o:value("vless", translate("VLESS"))
@@ -271,7 +309,12 @@ o:depends("type", "ssr")
 
 o = s:option(ListValue, "encrypt_method_ss", translate("Encrypt Method"))
 for _, v in ipairs(encrypt_methods_ss) do
-	o:value(v)
+	if v == "none" then
+	   o.default = "none"
+	   o:value("none", translate("none"))
+	else
+	    o:value(v, translate(v))
+	end
 end
 o.rmempty = true
 o:depends("type", "ss")

+ 2 - 2
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua

@@ -3,11 +3,11 @@
 -- Licensed to the public under the GNU General Public License v3.
 
 local m, s, sec, o
-local uci = luci.model.uci.cursor()
+local uci = require "luci.model.uci".cursor()
 
 local validation = require "luci.cbi.datatypes"
 local function is_finded(e)
-	return luci.sys.exec('type -t -p "%s"' % e) ~= "" and true or false
+	return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
 end
 
 m = Map("shadowsocksr", translate("ShadowSocksR Plus+ Settings"), translate("<h3>Support SS/SSR/V2RAY/XRAY/TROJAN/NAIVEPROXY/SOCKS5/TUN etc.</h3>"))

+ 3 - 9
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua

@@ -120,15 +120,9 @@ function o.cfgvalue(...)
 end
 
 o = sec:option(DummyValue, "encrypt_method", translate("Encrypt Method"))
-function o.cfgvalue(...)
-	local v = Value.cfgvalue(...)
-	return v and v:upper() or "-"
-end
-
-o = sec:option(DummyValue, "encrypt_method_ss", translate("Encrypt Method"))
-function o.cfgvalue(...)
-	local v = Value.cfgvalue(...)
-	return v and v:upper() or "-"
+function o.cfgvalue(self, section)
+	local method = self.map:get(section, "encrypt_method") or self.map:get(section, "encrypt_method_ss")
+	return method and method:upper() or "-"
 end
 
 o = sec:option(DummyValue, "protocol", translate("Protocol"))

+ 55 - 2
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua

@@ -1,10 +1,39 @@
 -- Licensed to the public under the GNU General Public License v3.
 require "luci.http"
+require "luci.sys"
 require "luci.dispatcher"
 require "luci.model.uci"
-local m, s, o
-local uci = luci.model.uci.cursor()
+local uci = require "luci.model.uci".cursor()
+
+local m, s, o, node
 local server_count = 0
+
+-- 确保正确判断程序是否存在
+local function is_finded(e)
+    return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
+end
+
+local has_ss_rust = is_finded("sslocal") or is_finded("ssserver")
+local has_ss_libev = is_finded("ss-redir") or is_finded("ss-local")
+
+local ss_type_list = {}
+
+if has_ss_rust then
+    table.insert(ss_type_list, { id = "ss-rust", name = translate("ShadowSocks-rust Version") })
+end
+if has_ss_libev then
+    table.insert(ss_type_list, { id = "ss-libev", name = translate("ShadowSocks-libev Version") })
+end
+
+-- 如果用户没有手动设置,则自动选择
+if ss_type == "" then
+    if has_ss_rust then
+        ss_type = "ss-rust"
+    elseif has_ss_libev then
+        ss_type = "ss-libev"
+    end
+end
+
 uci:foreach("shadowsocksr", "servers", function(s)
 	server_count = server_count + 1
 end)
@@ -48,6 +77,30 @@ o.default = 30
 o.rmempty = true
 o:depends("auto_update", "1")
 
+-- 确保 ss_type_list 不为空
+if #ss_type_list > 0 then
+    o = s:option(ListValue, "ss_type", string.format("<b><span style='color:red;'>%s</span></b>", translate("ShadowSocks Node Use Version")))
+    o.description = translate("Selection ShadowSocks Node Use Version.")
+    for _, v in ipairs(ss_type_list) do
+        o:value(v.id, v.name) -- 存储 "ss-libev" / "ss-rust",但 UI 显示完整名称
+    end
+    o.default = ss_type  -- 设置默认值
+    o.write = function(self, section, value)
+        -- 更新 Shadowsocks 节点的 has_ss_type
+        uci:foreach("shadowsocksr", "servers", function(s)
+            local node_type = uci:get("shadowsocksr", s[".name"], "type")  -- 获取节点类型
+            if node_type == "ss" then  -- 仅修改 Shadowsocks 节点
+                local old_value = uci:get("shadowsocksr", s[".name"], "has_ss_type")
+                if old_value ~= value then
+                    uci:set("shadowsocksr", s[".name"], "has_ss_type", value)
+                end
+            end
+        end)
+        -- 更新当前 section 的 ss_type
+        Value.write(self, section, value)
+    end
+end
+
 o = s:option(DynamicList, "subscribe_url", translate("Subscribe URL"))
 o.rmempty = true
 

+ 1 - 1
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua

@@ -15,7 +15,7 @@ local ad_count = 0
 local ip_count = 0
 local nfip_count = 0
 local Process_list = luci.sys.exec("busybox ps -w")
-local uci = luci.model.uci.cursor()
+local uci = require "luci.model.uci".cursor()
 -- html constants
 font_blue = [[<b style=color:green>]]
 style_blue = [[<b style=color:red>]]

+ 15 - 5
luci-app-ssr-plus/luasrc/view/shadowsocksr/server_list.htm

@@ -87,8 +87,18 @@
 	}
 
 	// set tr draggable
-	function enableDragForTable(table_selecter, store) {
-		var trs = document.querySelectorAll(table_selecter + " tr");
+	function enableDragForTable(table_selector, store) {
+		// 添加 CSS 样式
+		const style = document.createElement("style");
+		style.textContent = `
+			tr[draggable="true"] {
+				cursor: move;
+				user-select: none;
+			}
+			`;
+		document.head.appendChild(style);
+		
+		var trs = document.querySelectorAll(table_selector + " tr");
 		if (!trs || trs.length.length < 3) {
 			return;
 		}
@@ -104,12 +114,12 @@
 			ev.dataTransfer.dropEffect = "move";
 		}
 		function moveToTop(id) {
-			var top = document.querySelectorAll(table_selecter + " tr")[2];
+			var top = document.querySelectorAll(table_selector + " tr")[2];
 			cbi_row_drop(id, top.id, store);
 		}
 		function moveToBottom(id) {
-			console.log('moveToBottom:', id);
-			var trList = document.querySelectorAll(table_selecter + " tr");
+			//console.log('moveToBottom:', id);
+			var trList = document.querySelectorAll(table_selector + " tr");
 			var bottom = trList[trList.length - 1];
 			cbi_row_drop(id, bottom.id, store, true);
 		}

+ 30 - 12
luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm

@@ -1,6 +1,12 @@
 <%+cbi/valueheader%>
+<%
+local map = self.map
+local ss_type = map:get("@server_subscribe[0]", "ss_type")
+-%>
 <script type="text/javascript">
 //<![CDATA[
+let ss_type = "<%=ss_type%>"
+
 function padright(str, cnt, pad) {
 	return str + Array(cnt + 1).join(pad);
 }
@@ -133,14 +139,17 @@ function import_ssr_url(btn, urlname, sid) {
 			if (sipIndex != -1) {
 				// SIP002
 				var userInfo = b64decsafe(url0.substr(0, sipIndex));
+				// console.log("userInfo:", userInfo); // 打印解析后的 userInfo
 				var temp = url0.substr(sipIndex + 1).split("/?");
 				var serverInfo = temp[0].split(":");
 				var server = serverInfo[0];
 				var port = serverInfo[1].replace("/","");
 				var method, password, enable_plugin, plugin, pluginOpts;
+
+				// 解析 plugin 参数
 				if (temp[1]) {
 					var pluginInfo = decodeURIComponent(temp[1]);
-					// 使用正則匹配 plugin 參數
+					// 使用正则匹配 plugin 参数
 					var pluginNameInfo = pluginInfo.match(/plugin=([^&]+)/);
 					if (pluginNameInfo) {
 						var pluginParams = pluginNameInfo[1].split(";");
@@ -148,20 +157,25 @@ function import_ssr_url(btn, urlname, sid) {
 						pluginOpts = pluginParams.length > 0 ? pluginParams.join(";") : "";
 					}
 				}
+				// 解析 userInfo(解析加密方法和密码)
 				var userInfoSplitIndex = userInfo.indexOf(":");
-				if (userInfoSplitIndex != -1) {
-					method = userInfo.substr(0, userInfoSplitIndex);
-					password = userInfo.substr(userInfoSplitIndex + 1);
+				if (userInfoSplitIndex !== -1) {
+					method = userInfo.substr(0, userInfoSplitIndex);  // 提取加密方法
+					password = userInfo.substr(userInfoSplitIndex + 1);  // 提取密码
+					if (!method || method.trim() === "") {
+					    method = "none";  // 如果加密方法为空,设置为 "none"
+					}
 				}
+				var has_ss_type = (ss_type === "ss-rust") ? "ss-rust" : "ss-libev";
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = ssu[0];
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].dispatchEvent(event);
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.ss_variant')[0].value = 
-					(ssu[0] === "ss") ? "isSSRust" : "isSSLibev";
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.ss_variant')[0].dispatchEvent(event);
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.has_ss_type')[0].value = has_ss_type;
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.has_ss_type')[0].dispatchEvent(event);
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.server')[0].value = server;
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.server_port')[0].value = port;
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.password')[0].value = password || "";
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.encrypt_method_ss')[0].value = method || "";
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.encrypt_method_ss')[0].value = method;
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.encrypt_method_ss')[0].dispatchEvent(event);
 				if (plugin && plugin !== "none") {
 					document.getElementsByName('cbid.shadowsocksr.' + sid + '.enable_plugin')[0].checked = true; // 设置 enable_plugin 为 true
 					document.getElementsByName('cbid.shadowsocksr.' + sid + '.enable_plugin')[0].dispatchEvent(event); // 触发事件
@@ -185,10 +199,14 @@ function import_ssr_url(btn, urlname, sid) {
 				var team = sstr.split('@');
 				var part1 = team[0].split(':');
 				var part2 = team[1].split(':');
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.server')[0].value = part2[0];
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.server_port')[0].value = part2[1];
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.password')[0].value = part1[1];
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.encrypt_method_ss')[0].value = part1[0];
+				var method = (part1[0] && part1[0].trim() !== "") ? part1[0].trim() : "none";
+				var password = part1[1] || "";
+				var server = part2[0];
+				var port = part2[1];
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.server')[0].value = server;
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.server_port')[0].value = port;
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.password')[0].value = password;
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.encrypt_method_ss')[0].value = method;
 				if (param != undefined) {
 					document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = decodeURI(param);
 				}

+ 9 - 6
luci-app-ssr-plus/po/zh_Hans/ssr-plus.po

@@ -222,14 +222,17 @@ msgstr "服务器名称指示"
 msgid "Shadow-TLS ChainPoxy type"
 msgstr "代理链类型"
 
-msgid "Shadowsocks Variant"
-msgstr "shadowsocks 变体"
+msgid "ShadowSocks Node Use Version"
+msgstr "ShadowSocks 节点使用版本"
 
-msgid "Shadowsocks-rust Version"
-msgstr "shadowsocks rust 版本"
+msgid "Selection ShadowSocks Node Use Version."
+msgstr "选择 ShadowSocks 节点使用版本。"
 
-msgid "Shadowsocks-libev Version"
-msgstr "Shadowsocks-libev 版本"
+msgid "ShadowSocks-libev Version"
+msgstr "ShadowSocks-libev 版本"
+
+msgid "ShadowSocks-rust Version"
+msgstr "ShadowSocks-rust 版本"
 
 msgid "Vmess Protocol"
 msgstr "VMESS 协议"

+ 88 - 12
luci-app-ssr-plus/root/etc/init.d/shadowsocksr

@@ -166,7 +166,7 @@ _exit() {
 }
 
 first_type() {
-	type -t -p "/bin/${1}" -p "${TMP_BIN_PATH}/${1}" -p "${1}" "$@" | head -n1
+	type -t -p "/bin/${1}" -p "/usr/bin/${1}" -p "${TMP_BIN_PATH}/${1}" -p "${1}" "$@" | head -n1
 }
 
 ln_start_bin() {
@@ -258,7 +258,7 @@ start_dns() {
 			fi
 		fi
 	fi
-	
+
 	if [ "$(uci_get_by_type global apple_optimization 1)" == "1" ]; then
 		local new_appledns="$(uci_get_by_type global apple_dns)"
 		if [ -n "$new_appledns" ]; then		
@@ -320,7 +320,7 @@ gen_service_file() { #1-server.type 2-cfgname 3-file_path
 
 get_name() {
 	case "$1" in
-	ss) echo "Shadowsocks" ;;
+	ss) echo "ShadowSocks" ;;
 	ssr) echo "ShadowsocksR" ;;
 	esac
 }
@@ -432,11 +432,23 @@ gen_config_file() { #server1 type2 code3 local_port4 socks_port5 chain6 threads5
 
 start_udp() {
 	local type=$(uci_get_by_name $UDP_RELAY_SERVER type)
+	local has_ss_type=$(uci_get_by_type server_subscribe ss_type)
 	redir_udp=1
 	case "$type" in
 	ss | ssr)
 		gen_config_file $UDP_RELAY_SERVER $type 2 $tmp_udp_port
-		ss_program="$(first_type ${type}local ${type}-redir)"
+		if [ "$has_ss_type" = "ss-libev" -o "$type" = "ssr" ]; then
+			ss_program="$(first_type ${type}-redir)"
+		elif [ "$has_ss_type" = "ss-rust" ]; then
+			ss_program="$(first_type ${type}local)"
+		fi
+		echolog "$(get_name $type) program is: $ss_program"
+		# 获取当前软链接指向的执行文件路径
+		old_ss_program=$(readlink -f "$TMP_PATH/bin/${type}-redir" 2>/dev/null)
+		# **当新旧执行文件路径不同时,删除旧链接**
+		if [ "$old_ss_program" != "$ss_program" ]; then
+			rm -rf "$TMP_PATH/bin/${type}-redir"
+		fi
 		ln_start_bin $ss_program ${type}-redir -c $udp_config_file
 		echolog "UDP TPROXY Relay:$(get_name $type) Started!"
 		;;
@@ -540,7 +552,7 @@ shunt_dns_config_file_port() {
             # 全局socks 有密码,NetFlix 不能使用 auth 验证,需更换为新端口并使用无密码的 socks 配置用于分流
             # 新增NetFlix dns 使用端口
             local port=$tmp_shunt_local_port
-                   jq --arg port "$port" '.inbounds |= .[0:1] + [{"protocol":"socks","port":($port | tonumber),"settings":{"udp":true,"auth":"noauth"}}] + .[1:]' "$shunt_config_file" > "$shunt_config_file.tmp" && mv "$shunt_config_file.tmp" $shunt_config_file
+            jq --arg port "$port" '.inbounds |= .[0:1] + [{"protocol":"socks","port":($port | tonumber),"settings":{"udp":true,"auth":"noauth"}}] + .[1:]' "$shunt_config_file" > "$shunt_config_file.tmp" && mv "$shunt_config_file.tmp" $shunt_config_file
             echo $port  # 返回端口号
             return 0  # 成功返回
         else
@@ -566,16 +578,38 @@ shunt_dns_config_file_port() {
 
 start_shunt() {
 	local type=$(uci_get_by_name $SHUNT_SERVER type)
+	local has_ss_type=$(uci_get_by_type server_subscribe ss_type)
 	case "$type" in
 	ss | ssr)
 		gen_config_file $SHUNT_SERVER $type 3 $tmp_shunt_port
-		ss_program="$(first_type ${type}local ${type}-redir)"
+		if [ "$has_ss_type" = "ss-libev" -o "$type" = "ssr" ]; then
+			ss_program="$(first_type ${type}-redir)"
+		elif [ "$has_ss_type" = "ss-rust" ]; then
+			ss_program="$(first_type ${type}local)"
+		fi
+		echolog "$(get_name $type) program is: $ss_program"
+		# 获取当前软链接指向的执行文件路径
+		old_ss_program=$(readlink -f "$TMP_PATH/bin/${type}-redir" 2>/dev/null)
+		# **当新旧执行文件路径不同时,删除旧链接**
+		if [ "$old_ss_program" != "$ss_program" ]; then
+			rm -rf "$TMP_PATH/bin/${type}-redir"
+		fi
 		ln_start_bin $ss_program ${type}-redir -c $shunt_config_file
 		if [ -n "$tmp_local_port" ]; then
 			local tmp_port=$tmp_local_port
 		else
 			local tmp_port=$tmp_shunt_local_port
-			ln_start_bin $(first_type ${type}local ${type}-local) ${type}-local -c $shunt_dns_config_file
+			if [ "$has_ss_type" = "ss-libev" -o "$type" = "ssr" ]; then
+				dns_ss_program="$(first_type ${type}-local)"
+			elif [ "$has_ss_type" = "ss-rust" ]; then
+				dns_ss_program="$(first_type ${type}local)"
+			fi
+			# 获取当前软链接指向的执行文件路径
+			old_dns_ss_program=$(readlink -f "$TMP_PATH/bin/${type}-local" 2>/dev/null)
+			if [ "$old_dns_ss_program" != "$dns_ss_program" ]; then
+				rm -rf "$TMP_PATH/bin/${type}-local"
+			fi
+			ln_start_bin $dns_ss_program ${type}-local -c $shunt_dns_config_file
 		fi
 		shunt_dns_command $tmp_port
 		echolog "shunt:$(get_name $type) Started!"
@@ -695,10 +729,22 @@ start_local() {
 	local local_port=$(uci_get_by_type socks5_proxy local_port)
 	[ "$LOCAL_SERVER" == "$SHUNT_SERVER" ] && tmp_local_port=$local_port
 	local type=$(uci_get_by_name $LOCAL_SERVER type)
+	local has_ss_type=$(uci_get_by_type server_subscribe ss_type)
 	case "$type" in
 	ss | ssr)
 		gen_config_file $LOCAL_SERVER $type 4 $local_port
-		ss_program="$(first_type ${type}local ${type}-local)"
+		if [ "$has_ss_type" = "ss-libev" -o "$type" = "ssr" ]; then
+			ss_program="$(first_type ${type}-local)"
+		elif [ "$has_ss_type" = "ss-rust" ]; then
+			ss_program="$(first_type ${type}local)"
+		fi
+		echolog "$(get_name $type) program is: $ss_program"
+		# 获取当前软链接指向的执行文件路径
+		old_ss_program=$(readlink -f "$TMP_PATH/bin/${type}-local" 2>/dev/null)
+		# **当 新旧执行文件路径不同时,删除旧链接**
+		if [ "$old_ss_program" != "$ss_program" ]; then
+			rm -rf "$TMP_PATH/bin/${type}-local"
+		fi
 		ln_start_bin $ss_program ${type}-local -c $local_config_file
 		echolog "Global_Socks5:$(get_name $type) Started!"
 		;;
@@ -789,12 +835,24 @@ Start_Run() {
 	fi
 	local tcp_port=$(uci_get_by_name $GLOBAL_SERVER local_port)
 	local type=$(uci_get_by_name $GLOBAL_SERVER type)
+	local has_ss_type=$(uci_get_by_type server_subscribe ss_type)
 	case "$type" in
 	ss | ssr)
 		gen_config_file $GLOBAL_SERVER $type 1 $tcp_port
-		ss_program="$(first_type ${type}local ${type}-redir)"
+		if [ "$has_ss_type" = "ss-libev" -o "$type" = "ssr" ]; then
+			ss_program="$(first_type ${type}-redir)"
+		elif [ "$has_ss_type" = "ss-rust" ]; then
+			ss_program="$(first_type ${type}local)"
+		fi
+		echolog "$(get_name $type) program is: $ss_program"
+		# 获取当前软链接指向的执行文件路径
+		old_ss_program=$(readlink -f "$TMP_PATH/bin/${type}-redir" 2>/dev/null)
+		# **当新旧执行文件路径不同时,删除旧链接**
+		if [ "$old_ss_program" != "$ss_program" ]; then
+			rm -rf "$TMP_PATH/bin/${type}-redir"
+		fi
 		for i in $(seq 1 $threads); do
-			ln_start_bin "$ss_program" ${type}-redir -c $tcp_config_file
+			ln_start_bin $ss_program ${type}-redir -c $tcp_config_file
 		done
 		echolog "Main node:$(get_name $type) $threads Threads Started!"
 		;;
@@ -1000,10 +1058,22 @@ start_server() {
 			fi
 		fi
 		local type=$(uci_get_by_name $1 type)
+		local has_ss_type=$(uci_get_by_type server_subscribe ss_type)
 		case "$type" in
 		ss | ssr)
 			gen_service_file ${type} $1 $TMP_PATH/ssr-server$server_count.json
-			ln_start_bin $(first_type ${type}server ${type}-server) ${type}-server -c $TMP_PATH/ssr-server$server_count.json
+			if [ "$has_ss_type" = "ss-libev" -o "$type" = "ssr" ]; then
+				ss_program="$(first_type ${type}-server)"
+			elif [ "$has_ss_type" = "ss-rust" ]; then
+				ss_program="$(first_type ${type}server)"
+			fi
+			# 获取当前软链接指向的执行文件路径
+			old_ss_program=$(readlink -f "$TMP_PATH/bin/${type}-server" 2>/dev/null)
+			# **当新旧执行文件路径不同时,删除旧链接**
+			if [ "$old_ss_program" != "$ss_program" ]; then
+				rm -rf "$TMP_PATH/bin/${type}-server" 
+			fi
+			ln_start_bin $ss_program ${type}-server -c $TMP_PATH/ssr-server$server_count.json
 			echolog "Server: $(get_name ${type}) Server$server_count Started!"
 			;;
 		socks5)
@@ -1201,7 +1271,12 @@ stop() {
 		uci -q commit "dhcp"
 	fi
 	if [ -f "$DNSMASQ_CONF_DIR/dnsmasq-ssrplus.conf" ]; then
-		rm -rf $DNSMASQ_CONF_DIR/dnsmasq-ssrplus.conf $TMP_DNSMASQ_PATH $TMP_PATH/*-ssr-*.json $TMP_PATH/ssr-server*.json
+		rm -rf $DNSMASQ_CONF_DIR/dnsmasq-ssrplus.conf \
+			$TMP_DNSMASQ_PATH \
+			$TMP_PATH/*-ssr-*.json \
+			$TMP_PATH/ssr-server*.json \
+			$TMP_PATH/*-config-*.json
+
 		/etc/init.d/dnsmasq restart >/dev/null 2>&1
 	fi
 	del_cron
@@ -1216,3 +1291,4 @@ reset() {
 	cp /usr/share/shadowsocksr/shadowsocksr.config /etc/config/shadowsocksr
 	unset_lock
 }
+

+ 15 - 4
luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua

@@ -10,6 +10,7 @@ require "luci.util"
 require "luci.sys"
 require "luci.jsonc"
 require "luci.model.ipkg"
+
 -- these global functions are accessed all the time by the event handler
 -- so caching them is worth the effort
 local tinsert = table.insert
@@ -21,15 +22,24 @@ local cache = {}
 local nodeResult = setmetatable({}, {__index = cache}) -- update result
 local name = 'shadowsocksr'
 local uciType = 'servers'
-local ucic = luci.model.uci.cursor()
+local ucic = require "luci.model.uci".cursor()
 local proxy = ucic:get_first(name, 'server_subscribe', 'proxy', '0')
 local switch = ucic:get_first(name, 'server_subscribe', 'switch', '1')
 local allow_insecure = ucic:get_first(name, 'server_subscribe', 'allow_insecure', '0')
 local subscribe_url = ucic:get_first(name, 'server_subscribe', 'subscribe_url', {})
 local filter_words = ucic:get_first(name, 'server_subscribe', 'filter_words', '过期时间/剩余流量')
 local save_words = ucic:get_first(name, 'server_subscribe', 'save_words', '')
-local v2_ss = luci.sys.exec('type -t -p sslocal ss-redir') ~= "" and "ss" or "v2ray"
-local ss_variant = luci.sys.exec('type -t -p sslocal') ~= "" and "isSSRust" or luci.sys.exec('type -t -p ss-redir') ~= "" and "isSSLibev"
+-- 读取 ss_type 设置
+local ss_type = ucic:get_first(name, 'server_subscribe', 'ss_type')
+-- 根据 ss_type 选择对应的程序
+local ss_program = ""
+if ss_type == "ss-rust" then
+    ss_program = "sslocal"  -- Rust 版本使用 sslocal
+elseif ss_type == "ss-libev" then
+    ss_program = "ss-redir"  -- Libev 版本使用 ss-redir
+end
+local v2_ss = luci.sys.exec('type -t -p ' .. ss_program .. ' 2>/dev/null') ~= "" and "ss" or "v2ray"
+local has_ss_type = luci.sys.exec('type -t -p ' .. ss_program .. ' 2>/dev/null') ~= "" and ss_type
 local v2_tj = luci.sys.exec('type -t -p trojan') ~= "" and "trojan" or "v2ray"
 local log = function(...)
 	print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({...}, " "))
@@ -279,7 +289,7 @@ local function processData(szType, content)
 		result.alias = UrlDecode(alias)
 		result.type = v2_ss
 		result.v2ray_protocol = (v2_ss == "v2ray") and "shadowsocks" or nil
-		result.ss_variant = ss_variant
+		result.has_ss_type = has_ss_type
 		result.encrypt_method_ss = method
 		result.password = password
 		result.server = host[1]
@@ -771,3 +781,4 @@ if subscribe_url and #subscribe_url > 0 then
 		end
 	end)
 end
+

+ 1 - 1
luci-app-ssr-plus/root/usr/share/shadowsocksr/update.lua

@@ -8,7 +8,7 @@ require "luci.sys"
 require "luci.model.uci"
 local icount = 0
 local args = arg[1]
-local uci = luci.model.uci.cursor()
+local uci = require "luci.model.uci".cursor()
 
 -- 以下设置更新数据库至 DNSMASQ 配置路径
 -- 获取 DNSMASQ 配置 ID