feat: 初始化并发问题处理
This commit is contained in:
@@ -28,13 +28,10 @@ class KROutboundItem {
|
||||
|
||||
/// URL
|
||||
String url = "";
|
||||
@override
|
||||
String toString() {
|
||||
return 'KROutboundItem(tag: $tag, country: $country, delay: ${urlTestDelay.value}ms)';
|
||||
}
|
||||
|
||||
/// 服务器类型
|
||||
|
||||
/// 构造函数,接受 KrNodeListItem 对象并初始化 KROutboundItem
|
||||
/// 构造函数,接受 KrItem 对象并初始化 KROutboundItem
|
||||
KROutboundItem(KrNodeListItem nodeListItem) {
|
||||
id = nodeListItem.id.toString();
|
||||
protocol = nodeListItem.protocol;
|
||||
@@ -84,13 +81,13 @@ class KROutboundItem {
|
||||
case "vless":
|
||||
final securityConfig =
|
||||
json["security_config"] as Map<String, dynamic>? ?? {};
|
||||
|
||||
|
||||
// 智能设置 server_name
|
||||
String serverName = securityConfig["sni"] ?? "";
|
||||
if (serverName.isEmpty) {
|
||||
serverName = nodeListItem.serverAddr;
|
||||
}
|
||||
|
||||
|
||||
config = {
|
||||
"type": "vless",
|
||||
"tag": nodeListItem.name,
|
||||
@@ -115,13 +112,13 @@ class KROutboundItem {
|
||||
case "vmess":
|
||||
final securityConfig =
|
||||
json["security_config"] as Map<String, dynamic>? ?? {};
|
||||
|
||||
|
||||
// 智能设置 server_name
|
||||
String serverName = securityConfig["sni"] ?? "";
|
||||
if (serverName.isEmpty) {
|
||||
serverName = nodeListItem.serverAddr;
|
||||
}
|
||||
|
||||
|
||||
config = {
|
||||
"type": "vmess",
|
||||
"tag": nodeListItem.name,
|
||||
@@ -152,7 +149,7 @@ class KROutboundItem {
|
||||
break;
|
||||
case "hysteria":
|
||||
case "hysteria2":
|
||||
// 后端的 "hysteria" 实际上是 Hysteria2 协议
|
||||
// 后端的 "hysteria" 实际上是 Hysteria2 协议
|
||||
final securityConfig =
|
||||
json["security_config"] as Map<String, dynamic>? ?? {};
|
||||
config = {
|
||||
@@ -177,14 +174,14 @@ class KROutboundItem {
|
||||
case "trojan":
|
||||
final securityConfig =
|
||||
json["security_config"] as Map<String, dynamic>? ?? {};
|
||||
|
||||
|
||||
// 智能设置 server_name
|
||||
String serverName = securityConfig["sni"] ?? "";
|
||||
if (serverName.isEmpty) {
|
||||
// 如果没有配置 SNI,使用服务器地址
|
||||
serverName = nodeListItem.serverAddr;
|
||||
}
|
||||
|
||||
|
||||
config = {
|
||||
"type": "trojan",
|
||||
"tag": nodeListItem.name,
|
||||
@@ -274,6 +271,7 @@ class KROutboundItem {
|
||||
// 🔧 尝试从 config 字段解析 transport 配置
|
||||
Map<String, dynamic>? transportConfig;
|
||||
Map<String, dynamic>? securityConfig;
|
||||
int actualPort = nodeListItem.port; // 🔧 使用局部变量存储实际端口
|
||||
|
||||
// 🔧 关键修复:优先从 protocols 字段解析配置
|
||||
if (nodeListItem.protocols.isNotEmpty) {
|
||||
@@ -307,6 +305,28 @@ class KROutboundItem {
|
||||
}
|
||||
|
||||
if (matchedProtocol != null) {
|
||||
// 🔧 关键修复:只在顶层端口为0时,才使用 protocols 中的端口
|
||||
// 这样可以保留顶层的正确端口(如 53441),不被 protocols 数组中的端口(如 287)覆盖
|
||||
if (actualPort == 0 && matchedProtocol['port'] != null) {
|
||||
// 安全解析端口号
|
||||
int protocolPort = 0;
|
||||
final portValue = matchedProtocol['port'];
|
||||
if (portValue is int) {
|
||||
protocolPort = portValue;
|
||||
} else if (portValue is String) {
|
||||
protocolPort = int.tryParse(portValue) ?? 0;
|
||||
}
|
||||
|
||||
if (protocolPort > 0) {
|
||||
actualPort = protocolPort;
|
||||
if (kDebugMode) {
|
||||
print(' ✅ 从 protocols 使用端口: $protocolPort (顶层端口为0)');
|
||||
}
|
||||
}
|
||||
} else if (kDebugMode && matchedProtocol['port'] != null) {
|
||||
print(' ✅ 保留顶层端口: $actualPort (protocols中的端口: ${matchedProtocol['port']})');
|
||||
}
|
||||
|
||||
// 提取 transport 配置
|
||||
if (matchedProtocol['network'] != null || matchedProtocol['transport'] != null) {
|
||||
final network = matchedProtocol['network'] ?? matchedProtocol['transport'];
|
||||
@@ -402,7 +422,7 @@ class KROutboundItem {
|
||||
|
||||
switch (nodeListItem.protocol) {
|
||||
case "shadowsocks":
|
||||
// 优先使用 protocols 解析出来的 cipher,其次是 method 字段,最后才是默认值
|
||||
// 优先使用 protocols 解析出来的 cipher,其次是 method 字段,最后才是默认值
|
||||
String finalMethod = nodeListItem.method.isNotEmpty
|
||||
? nodeListItem.method
|
||||
: "2022-blake3-aes-256-gcm";
|
||||
@@ -411,22 +431,21 @@ class KROutboundItem {
|
||||
"type": "shadowsocks",
|
||||
"tag": nodeListItem.name,
|
||||
"server": nodeListItem.serverAddr,
|
||||
"server_port": nodeListItem.port,
|
||||
"server_port": actualPort,
|
||||
"method": finalMethod,
|
||||
"password": nodeListItem.uuid
|
||||
};
|
||||
if (kDebugMode) {
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
print('✅ Shadowsocks 节点配置构建成功: ${nodeListItem.name}');
|
||||
}
|
||||
if (kDebugMode) {
|
||||
print('📄 使用加密方法: $finalMethod');
|
||||
}
|
||||
if (kDebugMode) {
|
||||
print('📄 完整配置: $config');
|
||||
print('📄 完整配置 JSON:');
|
||||
print(jsonEncode(config));
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
}
|
||||
break;
|
||||
case "vless":
|
||||
// 判断是否为域名(非IP地址)
|
||||
// 判断是否为域名(非IP地址)
|
||||
final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||||
.hasMatch(nodeListItem.serverAddr);
|
||||
|
||||
@@ -436,17 +455,25 @@ class KROutboundItem {
|
||||
serverName = securityConfig['sni'].toString();
|
||||
}
|
||||
|
||||
// 🔧 关键修复:根据 security_config 判断是否启用 TLS
|
||||
final bool vlessTlsEnabled = securityConfig?['tls_enabled'] ?? false;
|
||||
// 🔧 关键修复:智能判断是否启用 TLS
|
||||
// 1. 优先使用 securityConfig['tls_enabled']
|
||||
// 2. 如果没有明确配置,根据端口和域名智能判断
|
||||
bool vlessTlsEnabled = securityConfig?['tls_enabled'] ?? true; // 默认启用 TLS
|
||||
|
||||
// 如果端口是标准非TLS端口(80, 8080等),则禁用 TLS
|
||||
if (nodeListItem.port == 80 || nodeListItem.port == 8080) {
|
||||
vlessTlsEnabled = false;
|
||||
}
|
||||
|
||||
if (kDebugMode) {
|
||||
print('🔐 VLESS TLS 状态: enabled=$vlessTlsEnabled');
|
||||
print('🔐 VLESS TLS 状态: enabled=$vlessTlsEnabled (port=${nodeListItem.port}, isDomain=$isDomain)');
|
||||
}
|
||||
|
||||
config = {
|
||||
"type": "vless",
|
||||
"tag": nodeListItem.name,
|
||||
"server": nodeListItem.serverAddr,
|
||||
"server_port": nodeListItem.port,
|
||||
"server_port": actualPort,
|
||||
"uuid": nodeListItem.uuid,
|
||||
if (transportConfig != null) "transport": transportConfig,
|
||||
if (vlessTlsEnabled) "tls": {
|
||||
@@ -460,14 +487,27 @@ class KROutboundItem {
|
||||
}
|
||||
};
|
||||
if (kDebugMode) {
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
print('✅ VLESS 节点配置构建成功: ${nodeListItem.name}');
|
||||
}
|
||||
if (kDebugMode) {
|
||||
print('📄 完整配置: $config');
|
||||
print('📋 原始节点信息:');
|
||||
print(' - serverAddr: ${nodeListItem.serverAddr}');
|
||||
print(' - port: ${nodeListItem.port}');
|
||||
print(' - uuid: ${nodeListItem.uuid}');
|
||||
print(' - protocols: ${nodeListItem.protocols}');
|
||||
print('🔐 安全配置:');
|
||||
print(' - TLS 启用: $vlessTlsEnabled');
|
||||
print(' - 是域名: $isDomain');
|
||||
print(' - server_name: $serverName');
|
||||
print(' - securityConfig: $securityConfig');
|
||||
print('📡 传输配置:');
|
||||
print(' - transportConfig: $transportConfig');
|
||||
print('📄 最终生成的完整配置 JSON:');
|
||||
print(jsonEncode(config));
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
}
|
||||
break;
|
||||
case "vmess":
|
||||
// 判断是否为域名(非IP地址)
|
||||
// 判断是否为域名(非IP地址)
|
||||
final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||||
.hasMatch(nodeListItem.serverAddr);
|
||||
|
||||
@@ -477,17 +517,25 @@ class KROutboundItem {
|
||||
serverName = securityConfig['sni'].toString();
|
||||
}
|
||||
|
||||
// 🔧 关键修复:根据 security_config 判断是否启用 TLS
|
||||
final bool tlsEnabled = securityConfig?['tls_enabled'] ?? false;
|
||||
// 🔧 关键修复:智能判断是否启用 TLS
|
||||
// 1. 优先使用 securityConfig['tls_enabled']
|
||||
// 2. 如果没有明确配置,根据端口和域名智能判断
|
||||
bool tlsEnabled = securityConfig?['tls_enabled'] ?? true; // 默认启用 TLS
|
||||
|
||||
// 如果端口是标准非TLS端口(80, 8080等),则禁用 TLS
|
||||
if (nodeListItem.port == 80 || nodeListItem.port == 8080) {
|
||||
tlsEnabled = false;
|
||||
}
|
||||
|
||||
if (kDebugMode) {
|
||||
print('🔐 TLS 状态: enabled=$tlsEnabled');
|
||||
print('🔐 VMess TLS 状态: enabled=$tlsEnabled (port=${nodeListItem.port}, isDomain=$isDomain)');
|
||||
}
|
||||
|
||||
config = {
|
||||
"type": "vmess",
|
||||
"tag": nodeListItem.name,
|
||||
"server": nodeListItem.serverAddr,
|
||||
"server_port": nodeListItem.port,
|
||||
"server_port": actualPort,
|
||||
"uuid": nodeListItem.uuid,
|
||||
"alter_id": 0,
|
||||
"security": "auto",
|
||||
@@ -503,14 +551,27 @@ class KROutboundItem {
|
||||
}
|
||||
};
|
||||
if (kDebugMode) {
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
print('✅ VMess 节点配置构建成功: ${nodeListItem.name}');
|
||||
}
|
||||
if (kDebugMode) {
|
||||
print('📄 完整配置: $config');
|
||||
print('📋 原始节点信息:');
|
||||
print(' - serverAddr: ${nodeListItem.serverAddr}');
|
||||
print(' - port: ${nodeListItem.port}');
|
||||
print(' - uuid: ${nodeListItem.uuid}');
|
||||
print(' - protocols: ${nodeListItem.protocols}');
|
||||
print('🔐 安全配置:');
|
||||
print(' - TLS 启用: $tlsEnabled');
|
||||
print(' - 是域名: $isDomain');
|
||||
print(' - server_name: $serverName');
|
||||
print(' - securityConfig: $securityConfig');
|
||||
print('📡 传输配置:');
|
||||
print(' - transportConfig: $transportConfig');
|
||||
print('📄 最终生成的完整配置 JSON:');
|
||||
print(jsonEncode(config));
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
}
|
||||
break;
|
||||
case "trojan":
|
||||
// 判断是否为域名(非IP地址)
|
||||
// 判断是否为域名(非IP地址)
|
||||
final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||||
.hasMatch(nodeListItem.serverAddr);
|
||||
|
||||
@@ -524,7 +585,7 @@ class KROutboundItem {
|
||||
"type": "trojan",
|
||||
"tag": nodeListItem.name,
|
||||
"server": nodeListItem.serverAddr,
|
||||
"server_port": nodeListItem.port,
|
||||
"server_port": actualPort,
|
||||
"password": nodeListItem.uuid,
|
||||
if (transportConfig != null) "transport": transportConfig,
|
||||
"tls": {
|
||||
@@ -546,7 +607,7 @@ class KROutboundItem {
|
||||
break;
|
||||
case "hysteria":
|
||||
case "hysteria2":
|
||||
// 后端的 "hysteria" 实际上是 Hysteria2 协议
|
||||
// 后端的 "hysteria" 实际上是 Hysteria2 协议
|
||||
if (kDebugMode) {
|
||||
print('🔍 构建 Hysteria2 节点: ${nodeListItem.name}');
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:convert';
|
||||
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
||||
|
||||
class KRNodeList {
|
||||
final List<KrNodeListItem> list;
|
||||
final List<KrNodeListItem> list;
|
||||
final String subscribeId;
|
||||
final String startTime;
|
||||
final String expireTime;
|
||||
@@ -35,8 +35,8 @@ class KRNodeList {
|
||||
// 查找 id 匹配的订阅项
|
||||
try {
|
||||
subscribeData = listData.firstWhere(
|
||||
(item) => (item as Map<String, dynamic>)['id']?.toString() == requestSubscribeId,
|
||||
orElse: () => listData[0] as Map<String, dynamic>
|
||||
(item) => (item as Map<String, dynamic>)['id']?.toString() == requestSubscribeId,
|
||||
orElse: () => listData[0] as Map<String, dynamic>
|
||||
) as Map<String, dynamic>;
|
||||
KRLogUtil.kr_i('✅ 找到匹配的订阅项: id=$requestSubscribeId', tag: 'NodeList');
|
||||
} catch (e) {
|
||||
@@ -70,7 +70,7 @@ class KRNodeList {
|
||||
|
||||
class KrNodeListItem {
|
||||
final int id;
|
||||
String name;
|
||||
String name;
|
||||
final String uuid;
|
||||
final String protocol;
|
||||
final String relayMode;
|
||||
@@ -136,25 +136,68 @@ class KrNodeListItem {
|
||||
String method = json['method']?.toString() ?? ''; // 加密方法(Shadowsocks等)
|
||||
final protocols = json['protocols']?.toString() ?? ''; // 协议配置JSON
|
||||
|
||||
// 🔧 如果有 protocols 字段,从中解析 port 和 cipher
|
||||
// 🔧 打印原始节点 JSON(用于调试)
|
||||
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'NodeList');
|
||||
KRLogUtil.kr_i('📥 收到节点 API 原始数据:', tag: 'NodeList');
|
||||
KRLogUtil.kr_i('节点名称: ${json['name']}', tag: 'NodeList');
|
||||
KRLogUtil.kr_i('协议类型: ${json['protocol']}', tag: 'NodeList');
|
||||
KRLogUtil.kr_i('完整 JSON:', tag: 'NodeList');
|
||||
KRLogUtil.kr_i(jsonEncode(json), tag: 'NodeList');
|
||||
|
||||
// 🔧 如果有 protocols 字段,从中解析 cipher(但不覆盖顶层的 port)
|
||||
if (protocols.isNotEmpty) {
|
||||
try {
|
||||
final protocolsList = jsonDecode(protocols) as List;
|
||||
final currentProtocol = json['protocol']?.toString().toLowerCase() ?? '';
|
||||
|
||||
KRLogUtil.kr_i('📋 protocols 字段内容 (${protocolsList.length} 个协议):', tag: 'NodeList');
|
||||
for (var i = 0; i < protocolsList.length; i++) {
|
||||
KRLogUtil.kr_i(' 协议 $i: ${jsonEncode(protocolsList[i])}', tag: 'NodeList');
|
||||
}
|
||||
|
||||
if (protocolsList.isNotEmpty) {
|
||||
final firstProtocol = protocolsList[0] as Map<String, dynamic>;
|
||||
// 优先使用 protocols 中的配置
|
||||
if (firstProtocol['port'] != null) {
|
||||
port = _parseIntSafely(firstProtocol['port']);
|
||||
// 🔧 修复:查找与当前协议类型匹配的配置,而不是直接使用第一个
|
||||
Map<String, dynamic>? matchedProtocolConfig;
|
||||
|
||||
// 尝试找到协议类型匹配的配置
|
||||
for (var protocolConfig in protocolsList) {
|
||||
final configMap = protocolConfig as Map<String, dynamic>;
|
||||
// 🔧 修复:API 使用的字段名是 'type',不是 'protocol'
|
||||
final protocolType = (configMap['type'] ?? configMap['protocol'])?.toString().toLowerCase() ?? '';
|
||||
|
||||
// 检查是否匹配(支持 shadowsocks/ss, vmess, vless, trojan 等)
|
||||
if (protocolType == currentProtocol ||
|
||||
(currentProtocol == 'shadowsocks' && protocolType == 'ss') ||
|
||||
(currentProtocol == 'ss' && protocolType == 'shadowsocks')) {
|
||||
matchedProtocolConfig = configMap;
|
||||
KRLogUtil.kr_i('🎯 找到匹配的协议配置: $protocolType', tag: 'NodeList');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstProtocol['cipher'] != null && firstProtocol['cipher'].toString().isNotEmpty) {
|
||||
method = firstProtocol['cipher'].toString();
|
||||
|
||||
// 如果没找到匹配的,使用第一个配置(兼容旧API)
|
||||
final targetProtocol = matchedProtocolConfig ?? (protocolsList[0] as Map<String, dynamic>);
|
||||
|
||||
// 🔧 关键修复:只在顶层没有 port 字段时,才使用 protocols 中的端口
|
||||
// 这样可以保留顶层的正确端口(如 53441),不被 protocols 数组中的端口(如 287)覆盖
|
||||
if (port == 0 && targetProtocol['port'] != null) {
|
||||
port = _parseIntSafely(targetProtocol['port']);
|
||||
KRLogUtil.kr_i('✅ 从 protocols 解析端口: $port', tag: 'NodeList');
|
||||
} else {
|
||||
KRLogUtil.kr_i('✅ 保留顶层端口: $port (protocols中的端口: ${targetProtocol['port']})', tag: 'NodeList');
|
||||
}
|
||||
|
||||
// 提取 cipher(加密方法)
|
||||
if (targetProtocol['cipher'] != null && targetProtocol['cipher'].toString().isNotEmpty) {
|
||||
method = targetProtocol['cipher'].toString();
|
||||
KRLogUtil.kr_i('✅ 从 protocols 解析 cipher: $method', tag: 'NodeList');
|
||||
}
|
||||
KRLogUtil.kr_i('从 protocols 解析: port=$port, cipher=$method', tag: 'NodeList');
|
||||
}
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_w('解析 protocols 字段失败: $e', tag: 'NodeList');
|
||||
KRLogUtil.kr_w('⚠️ 解析 protocols 字段失败: $e', tag: 'NodeList');
|
||||
}
|
||||
}
|
||||
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'NodeList');
|
||||
|
||||
return KrNodeListItem(
|
||||
id: _parseIntSafely(json['id']),
|
||||
|
||||
Reference in New Issue
Block a user