660 lines
24 KiB
Dart
Executable File
660 lines
24 KiB
Dart
Executable File
import 'dart:convert';
|
||
import 'package:get/get.dart';
|
||
|
||
import '../response/kr_node_list.dart';
|
||
import 'package:flutter/foundation.dart';
|
||
|
||
/// 表示出站项的模型类
|
||
class KROutboundItem {
|
||
int selected = 0; // 是否选中(0=未选中,1=选中)
|
||
String id = ""; // 标签
|
||
String tag = ""; // 标签
|
||
String serverAddr = ""; // 服务器地址
|
||
|
||
/// 初始化配置
|
||
Map<String, dynamic> config = {}; // 配置项
|
||
|
||
String city = ""; // 城市
|
||
String country = ""; // 国家
|
||
|
||
double latitude = 0.0; // 节点纬度
|
||
double latitudeCountry = 0.0; // 国家中心纬度
|
||
double longitude = 0.0; // 节点经度
|
||
double longitudeCountry = 0.0; // 国家中心经度
|
||
String protocol = "";
|
||
|
||
/// 延迟
|
||
RxInt urlTestDelay = 0.obs;
|
||
|
||
/// URL
|
||
String url = "";
|
||
// ✅ 1. 将传入的 nodeListItem 保存为类的 final 成员变量
|
||
final KrNodeListItem nodeListItem;
|
||
/// 服务器类型
|
||
|
||
/// 构造函数,接受 KrItem 对象并初始化 KROutboundItem
|
||
KROutboundItem(this.nodeListItem) {
|
||
id = nodeListItem.id.toString();
|
||
protocol = nodeListItem.protocol;
|
||
latitude = nodeListItem.latitude;
|
||
latitudeCountry = nodeListItem.latitudeCountry;
|
||
longitude = nodeListItem.longitude;
|
||
longitudeCountry = nodeListItem.longitudeCountry;
|
||
|
||
tag = nodeListItem.name; // 设置标签
|
||
serverAddr = nodeListItem.serverAddr; // 设置服务器地址
|
||
city = nodeListItem.city; // 设置城市
|
||
country = nodeListItem.country; // 设置国家
|
||
|
||
// 🔧 优先使用直接字段构建配置(新API格式)
|
||
// 判断条件:如果有 port 和 serverAddr,说明是新格式
|
||
if (nodeListItem.port > 0 && nodeListItem.serverAddr.isNotEmpty) {
|
||
if (kDebugMode) {
|
||
print('ℹ️ 节点 ${nodeListItem.name} 使用直接字段构建配置');
|
||
}
|
||
_buildConfigFromFields(nodeListItem);
|
||
return;
|
||
}
|
||
|
||
// 兜底:尝试解析 config 字段(旧API格式)
|
||
if (nodeListItem.config.isEmpty) {
|
||
if (kDebugMode) {
|
||
print('❌ 节点 ${nodeListItem.name} 缺少配置信息(无port或config)');
|
||
}
|
||
config = {};
|
||
return;
|
||
}
|
||
|
||
late Map<String, dynamic> json;
|
||
try {
|
||
json = jsonDecode(nodeListItem.config) as Map<String, dynamic>;
|
||
} catch (e) {
|
||
if (kDebugMode) {
|
||
print('❌ 节点 ${nodeListItem.name} 的 config 解析失败: $e,尝试使用直接字段');
|
||
}
|
||
if (kDebugMode) {
|
||
print('📄 Config 内容: ${nodeListItem.config}');
|
||
}
|
||
_buildConfigFromFields(nodeListItem);
|
||
return;
|
||
}
|
||
switch (nodeListItem.protocol) {
|
||
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,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": json["port"],
|
||
"uuid": nodeListItem.uuid,
|
||
if (json["flow"] != null && json["flow"] != "none")
|
||
"flow": json["flow"],
|
||
if (json["transport"] != null && json["transport"] != "tcp")
|
||
"transport": _buildTransport(json),
|
||
"tls": {
|
||
"enabled": json["security"] == "tls",
|
||
"server_name": serverName,
|
||
"insecure": securityConfig["allow_insecure"] ?? true,
|
||
"utls": {
|
||
"enabled": true,
|
||
"fingerprint": securityConfig["fingerprint"] ?? "chrome"
|
||
}
|
||
}
|
||
};
|
||
break;
|
||
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,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": json["port"],
|
||
"uuid": nodeListItem.uuid,
|
||
"alter_id": 0,
|
||
"security": "auto",
|
||
if (json["transport"] != null && json["transport"] != "tcp")
|
||
"transport": _buildTransport(json),
|
||
"tls": {
|
||
"enabled": json["security"] == "tls",
|
||
"server_name": serverName,
|
||
"insecure": securityConfig["allow_insecure"] ?? true,
|
||
"utls": {"enabled": true, "fingerprint": "chrome"}
|
||
}
|
||
};
|
||
break;
|
||
case "shadowsocks":
|
||
config = {
|
||
"type": "shadowsocks",
|
||
"tag": nodeListItem.name,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": json["port"],
|
||
"method": json["method"],
|
||
"password": nodeListItem.uuid
|
||
};
|
||
break;
|
||
case "hysteria":
|
||
case "hysteria2":
|
||
// 后端的 "hysteria" 实际上是 Hysteria2 协议
|
||
final securityConfig =
|
||
json["security_config"] as Map<String, dynamic>? ?? {};
|
||
config = {
|
||
"type": "hysteria2",
|
||
"tag": nodeListItem.name,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": json["port"],
|
||
"password": nodeListItem.uuid,
|
||
"up_mbps": 100,
|
||
"down_mbps": 100,
|
||
"obfs": {
|
||
"type": "salamander",
|
||
"password": json["obfs_password"] ?? nodeListItem.uuid
|
||
},
|
||
"tls": {
|
||
"enabled": true,
|
||
"server_name": securityConfig["sni"] ?? "",
|
||
"insecure": securityConfig["allow_insecure"] ?? true
|
||
}
|
||
};
|
||
break;
|
||
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,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": json["port"],
|
||
"password": nodeListItem.uuid,
|
||
"tls": {
|
||
"enabled": json["security"] == "tls",
|
||
"server_name": serverName,
|
||
"insecure": securityConfig["allow_insecure"] ?? true,
|
||
"utls": {"enabled": true, "fingerprint": "chrome"}
|
||
}
|
||
};
|
||
break;
|
||
}
|
||
|
||
// 检查 relayNode 是否为 JSON 字符串并解析
|
||
if (nodeListItem.relayNode.isNotEmpty && nodeListItem.relayMode != "none") {
|
||
final relayNodeJson = jsonDecode(nodeListItem.relayNode);
|
||
if (relayNodeJson is List && nodeListItem.relayMode != "none") {
|
||
// 随机选择一个元素
|
||
final randomNode = (relayNodeJson..shuffle()).first;
|
||
config["server"] = randomNode["host"]; // 提取 host
|
||
config["server_port"] = randomNode["port"]; // 提取 port
|
||
}
|
||
}
|
||
// 解析配置
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
// 现在它可以正确地访问已保存的 nodeListItem 成员
|
||
return 'KROutboundItem(name: ${nodeListItem.name}, protocol: ${nodeListItem.protocol}, server: ${nodeListItem.serverAddr}, port: ${nodeListItem.port})';
|
||
}
|
||
/// 构建传输配置
|
||
Map<String, dynamic> _buildTransport(Map<String, dynamic> json) {
|
||
final transportType = json["transport"] as String?;
|
||
final transportConfig =
|
||
json["transport_config"] as Map<String, dynamic>? ?? {};
|
||
|
||
switch (transportType) {
|
||
case "ws":
|
||
return {
|
||
"type": "ws",
|
||
"path": transportConfig["path"] ?? "/",
|
||
if (transportConfig["host"] != null)
|
||
"headers": {"Host": transportConfig["host"]}
|
||
};
|
||
case "grpc":
|
||
return {
|
||
"type": "grpc",
|
||
"service_name": transportConfig["service_name"] ?? ""
|
||
};
|
||
case "http":
|
||
return {
|
||
"type": "http",
|
||
"host": [transportConfig["host"] ?? ""],
|
||
"path": transportConfig["path"] ?? "/"
|
||
};
|
||
default:
|
||
return {};
|
||
}
|
||
}
|
||
|
||
/// 直接从节点字段构建配置(新API格式)
|
||
void _buildConfigFromFields(KrNodeListItem nodeListItem) {
|
||
if (kDebugMode) {
|
||
print('🔧 开始构建节点配置 - 协议: ${nodeListItem.protocol}, 名称: ${nodeListItem.name}');
|
||
}
|
||
if (kDebugMode) {
|
||
print('📋 节点详细信息:');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - serverAddr: ${nodeListItem.serverAddr}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - port: ${nodeListItem.port}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - uuid: ${nodeListItem.uuid}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - method: ${nodeListItem.method}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - config: ${nodeListItem.config}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - protocols: ${nodeListItem.protocols}');
|
||
}
|
||
|
||
// 🔧 尝试从 config 字段解析 transport 配置
|
||
Map<String, dynamic>? transportConfig;
|
||
Map<String, dynamic>? securityConfig;
|
||
int actualPort = nodeListItem.port; // 🔧 使用局部变量存储实际端口
|
||
|
||
// 🔧 关键修复:优先从 protocols 字段解析配置
|
||
if (nodeListItem.protocols.isNotEmpty) {
|
||
try {
|
||
final protocolsList = jsonDecode(nodeListItem.protocols) as List;
|
||
if (kDebugMode) {
|
||
print('📄 解析到 protocols 数组,共 ${protocolsList.length} 个协议');
|
||
}
|
||
|
||
// 查找匹配当前协议类型的配置
|
||
Map<String, dynamic>? matchedProtocol;
|
||
for (var protocol in protocolsList) {
|
||
final protocolMap = protocol as Map<String, dynamic>;
|
||
final type = protocolMap['type']?.toString().toLowerCase() ?? '';
|
||
final enable = protocolMap['enable'] ?? true;
|
||
|
||
if (kDebugMode) {
|
||
print(' 📋 协议: type=$type, enable=$enable');
|
||
}
|
||
|
||
// 匹配协议类型(注意 hysteria 和 hysteria2 都匹配 hysteria)
|
||
if (type == nodeListItem.protocol.toLowerCase() ||
|
||
(nodeListItem.protocol == 'hysteria' && type == 'hysteria2') ||
|
||
(nodeListItem.protocol == 'hysteria2' && type == 'hysteria')) {
|
||
matchedProtocol = protocolMap;
|
||
if (kDebugMode) {
|
||
print(' ✅ 找到匹配的协议配置: $type');
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
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'];
|
||
if (kDebugMode) {
|
||
print(' 📡 传输协议: $network');
|
||
}
|
||
|
||
if (network == 'ws' || network == 'websocket') {
|
||
transportConfig = {
|
||
'type': 'ws',
|
||
'path': matchedProtocol['ws_path'] ?? matchedProtocol['path'] ?? '/',
|
||
};
|
||
final host = matchedProtocol['ws_host'] ?? matchedProtocol['host'];
|
||
if (host != null && host.toString().isNotEmpty) {
|
||
transportConfig['headers'] = {'Host': host.toString()};
|
||
}
|
||
if (kDebugMode) {
|
||
print(' ✅ WebSocket transport: $transportConfig');
|
||
}
|
||
} else if (network == 'grpc') {
|
||
transportConfig = {
|
||
'type': 'grpc',
|
||
'service_name': matchedProtocol['grpc_service_name'] ?? matchedProtocol['service_name'] ?? '',
|
||
};
|
||
if (kDebugMode) {
|
||
print(' ✅ gRPC transport: $transportConfig');
|
||
}
|
||
} else if (network == 'http' || network == 'h2') {
|
||
transportConfig = {
|
||
'type': 'http',
|
||
'host': [matchedProtocol['http_host'] ?? matchedProtocol['host'] ?? ''],
|
||
'path': matchedProtocol['http_path'] ?? matchedProtocol['path'] ?? '/',
|
||
};
|
||
if (kDebugMode) {
|
||
print(' ✅ HTTP transport: $transportConfig');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 提取 security 配置
|
||
if (matchedProtocol['tls'] != null || matchedProtocol['security'] != null) {
|
||
// 🔧 关键修复:读取 security 字段判断是否启用 TLS
|
||
final security = matchedProtocol['security']?.toString().toLowerCase() ?? '';
|
||
final tlsEnabled = security == 'tls' || security == 'reality';
|
||
|
||
securityConfig = {
|
||
'tls_enabled': tlsEnabled, // ← 新增:记录 TLS 是否启用
|
||
'sni': matchedProtocol['sni'] ?? matchedProtocol['server_name'],
|
||
'allow_insecure': matchedProtocol['allow_insecure'] ?? matchedProtocol['insecure'] ?? true,
|
||
'fingerprint': matchedProtocol['fingerprint'] ?? 'chrome',
|
||
};
|
||
if (kDebugMode) {
|
||
print(' ✅ Security config: security=$security, tls_enabled=$tlsEnabled, config=$securityConfig');
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
if (kDebugMode) {
|
||
print('⚠️ 解析 protocols 字段失败: $e');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 兜底:尝试从 config 字段解析(旧API格式)
|
||
if (transportConfig == null && nodeListItem.config.isNotEmpty) {
|
||
try {
|
||
final configJson = jsonDecode(nodeListItem.config) as Map<String, dynamic>;
|
||
if (kDebugMode) {
|
||
print('📄 解析到 config JSON: $configJson');
|
||
}
|
||
|
||
// 提取 transport 配置
|
||
if (configJson['transport'] != null && configJson['transport'] != 'tcp') {
|
||
transportConfig = _buildTransport(configJson);
|
||
if (kDebugMode) {
|
||
print('✅ 从 config 找到 transport 配置: $transportConfig');
|
||
}
|
||
}
|
||
|
||
// 提取 security_config
|
||
if (configJson['security_config'] != null) {
|
||
securityConfig = configJson['security_config'] as Map<String, dynamic>;
|
||
if (kDebugMode) {
|
||
print('✅ 从 config 找到 security_config: $securityConfig');
|
||
}
|
||
}
|
||
} catch (e) {
|
||
if (kDebugMode) {
|
||
print('⚠️ 解析 config 字段失败: $e');
|
||
}
|
||
}
|
||
}
|
||
|
||
switch (nodeListItem.protocol) {
|
||
case "shadowsocks":
|
||
// 优先使用 protocols 解析出来的 cipher,其次是 method 字段,最后才是默认值
|
||
String finalMethod = nodeListItem.method.isNotEmpty
|
||
? nodeListItem.method
|
||
: "2022-blake3-aes-256-gcm";
|
||
|
||
config = {
|
||
"type": "shadowsocks",
|
||
"tag": nodeListItem.name,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": actualPort,
|
||
"method": finalMethod,
|
||
"password": nodeListItem.uuid
|
||
};
|
||
if (kDebugMode) {
|
||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||
print('✅ Shadowsocks 节点配置构建成功: ${nodeListItem.name}');
|
||
print('📄 使用加密方法: $finalMethod');
|
||
print('📄 完整配置 JSON:');
|
||
print(jsonEncode(config));
|
||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||
}
|
||
break;
|
||
case "vless":
|
||
// 判断是否为域名(非IP地址)
|
||
final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||
.hasMatch(nodeListItem.serverAddr);
|
||
|
||
// 🔧 优先使用 security_config 中的 SNI
|
||
String serverName = nodeListItem.serverAddr;
|
||
if (securityConfig != null && securityConfig['sni'] != null && securityConfig['sni'].toString().isNotEmpty) {
|
||
serverName = securityConfig['sni'].toString();
|
||
}
|
||
|
||
// 🔧 关键修复:智能判断是否启用 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 (port=${nodeListItem.port}, isDomain=$isDomain)');
|
||
}
|
||
|
||
config = {
|
||
"type": "vless",
|
||
"tag": nodeListItem.name,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": actualPort,
|
||
"uuid": nodeListItem.uuid,
|
||
if (transportConfig != null) "transport": transportConfig,
|
||
if (vlessTlsEnabled) "tls": {
|
||
"enabled": true,
|
||
if (isDomain) "server_name": serverName,
|
||
"insecure": securityConfig?['allow_insecure'] ?? true,
|
||
"utls": {
|
||
"enabled": true,
|
||
"fingerprint": securityConfig?['fingerprint'] ?? "chrome"
|
||
}
|
||
}
|
||
};
|
||
if (kDebugMode) {
|
||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||
print('✅ VLESS 节点配置构建成功: ${nodeListItem.name}');
|
||
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地址)
|
||
final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||
.hasMatch(nodeListItem.serverAddr);
|
||
|
||
// 🔧 优先使用 security_config 中的 SNI
|
||
String serverName = nodeListItem.serverAddr;
|
||
if (securityConfig != null && securityConfig['sni'] != null && securityConfig['sni'].toString().isNotEmpty) {
|
||
serverName = securityConfig['sni'].toString();
|
||
}
|
||
|
||
// 🔧 关键修复:智能判断是否启用 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('🔐 VMess TLS 状态: enabled=$tlsEnabled (port=${nodeListItem.port}, isDomain=$isDomain)');
|
||
}
|
||
|
||
config = {
|
||
"type": "vmess",
|
||
"tag": nodeListItem.name,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": actualPort,
|
||
"uuid": nodeListItem.uuid,
|
||
"alter_id": 0,
|
||
"security": "auto",
|
||
if (transportConfig != null) "transport": transportConfig,
|
||
if (tlsEnabled) "tls": {
|
||
"enabled": true,
|
||
if (isDomain) "server_name": serverName,
|
||
"insecure": securityConfig?['allow_insecure'] ?? true,
|
||
"utls": {
|
||
"enabled": true,
|
||
"fingerprint": securityConfig?['fingerprint'] ?? "chrome"
|
||
}
|
||
}
|
||
};
|
||
if (kDebugMode) {
|
||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||
print('✅ VMess 节点配置构建成功: ${nodeListItem.name}');
|
||
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地址)
|
||
final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||
.hasMatch(nodeListItem.serverAddr);
|
||
|
||
// 🔧 优先使用 security_config 中的 SNI
|
||
String serverName = nodeListItem.serverAddr;
|
||
if (securityConfig != null && securityConfig['sni'] != null && securityConfig['sni'].toString().isNotEmpty) {
|
||
serverName = securityConfig['sni'].toString();
|
||
}
|
||
|
||
config = {
|
||
"type": "trojan",
|
||
"tag": nodeListItem.name,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": actualPort,
|
||
"password": nodeListItem.uuid,
|
||
if (transportConfig != null) "transport": transportConfig,
|
||
"tls": {
|
||
"enabled": true,
|
||
if (isDomain) "server_name": serverName,
|
||
"insecure": securityConfig?['allow_insecure'] ?? true,
|
||
"utls": {
|
||
"enabled": true,
|
||
"fingerprint": securityConfig?['fingerprint'] ?? "chrome"
|
||
}
|
||
}
|
||
};
|
||
if (kDebugMode) {
|
||
print('✅ Trojan 节点配置构建成功: ${nodeListItem.name}');
|
||
}
|
||
if (kDebugMode) {
|
||
print('📄 完整配置: $config');
|
||
}
|
||
break;
|
||
case "hysteria":
|
||
case "hysteria2":
|
||
// 后端的 "hysteria" 实际上是 Hysteria2 协议
|
||
if (kDebugMode) {
|
||
print('🔍 构建 Hysteria2 节点: ${nodeListItem.name}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - serverAddr: ${nodeListItem.serverAddr}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - port: ${nodeListItem.port}');
|
||
}
|
||
if (kDebugMode) {
|
||
print(' - uuid: ${nodeListItem.uuid}');
|
||
}
|
||
|
||
//判断是否为域名
|
||
final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||
.hasMatch(nodeListItem.serverAddr);
|
||
|
||
config = {
|
||
"type": "hysteria2",
|
||
"tag": nodeListItem.name,
|
||
"server": nodeListItem.serverAddr,
|
||
"server_port": nodeListItem.port,
|
||
"password": nodeListItem.uuid,
|
||
"tls": {
|
||
"enabled": true,
|
||
if (isDomain) "server_name": nodeListItem.serverAddr,
|
||
}
|
||
};
|
||
if (kDebugMode) {
|
||
print('✅ Hysteria2 节点配置构建成功');
|
||
}
|
||
if (kDebugMode) {
|
||
print('📄 完整配置: ${jsonEncode(config)}');
|
||
}
|
||
break;
|
||
default:
|
||
if (kDebugMode) {
|
||
print('⚠️ 不支持的协议类型: ${nodeListItem.protocol}');
|
||
}
|
||
config = {};
|
||
}
|
||
}
|
||
}
|