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 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) { _initFromNodeListItem(); } /// 静态工厂:构造虚拟 urltest 节点(用于 ${country}-auto) factory KROutboundItem.fromVirtual(String tag, String country, Map config) { // 构造一个虚拟 KrNodeListItem,仅填充必要字段 final virtualNode = KrNodeListItem( id: 0, name: tag, protocol: 'urltest', serverAddr: '', port: 0, uuid: '', config: jsonEncode(config), city: '', country: country, tags: [], latitude: 0, longitude: 0, latitudeCountry: 0, longitudeCountry: 0, relayNode: '', relayMode: 'none', protocols: '', method: '', speedLimit: 0, traffic: 0, trafficRatio: 0, upload: 0, download: 0, startTime: '', expireTime: '', ); return KROutboundItem._virtual(virtualNode); } /// 私有构造:用于虚拟节点,避免重复解析 KROutboundItem._virtual(this.nodeListItem) { // 直接填充虚拟节点所需字段 id = nodeListItem.id.toString(); protocol = nodeListItem.protocol; tag = nodeListItem.name; serverAddr = nodeListItem.serverAddr; city = nodeListItem.city; country = nodeListItem.country; latitude = nodeListItem.latitude; latitudeCountry = nodeListItem.latitudeCountry; longitude = nodeListItem.longitude; longitudeCountry = nodeListItem.longitudeCountry; config = jsonDecode(nodeListItem.config); // 已知 config 有效 } /// 初始化逻辑提取,供主构造调用 void _initFromNodeListItem() { 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.isNotEmpty) { try { final json = jsonDecode(nodeListItem.config) as Map; if (kDebugMode) { print('📄 解析到 config JSON: $json'); } // 提取 transport 配置 Map? transportConfig; if (json['transport'] != null && json['transport'] != 'tcp') { transportConfig = _buildTransport(json); if (kDebugMode) { print('✅ 找到 transport 配置: $transportConfig'); } } // 提取 security_config Map? securityConfig; if (json['security_config'] != null) { securityConfig = json['security_config'] as Map; if (kDebugMode) { print('✅ 找到 security_config: $securityConfig'); } } // 根据协议类型构建配置 switch (nodeListItem.protocol) { case "shadowsocks": config = { "type": "shadowsocks", "tag": nodeListItem.name, "server": nodeListItem.serverAddr, "server_port": json["port"], "method": json["method"], "password": nodeListItem.uuid }; break; case "vless": final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$') .hasMatch(nodeListItem.serverAddr); config = { "type": "vless", "tag": nodeListItem.name, "server": nodeListItem.serverAddr, "server_port": json["port"], "uuid": nodeListItem.uuid, if (transportConfig != null) "transport": transportConfig, if (json["security"] == "tls") "tls": { "enabled": true, if (isDomain) "server_name": securityConfig?["sni"] ?? nodeListItem.serverAddr, "insecure": securityConfig?["allow_insecure"] ?? true, "utls": { "enabled": true, "fingerprint": securityConfig?["fingerprint"] ?? "chrome" } } }; break; case "vmess": final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$') .hasMatch(nodeListItem.serverAddr); config = { "type": "vmess", "tag": nodeListItem.name, "server": nodeListItem.serverAddr, "server_port": json["port"], "uuid": nodeListItem.uuid, "alter_id": 0, "security": "auto", if (transportConfig != null) "transport": transportConfig, if (json["security"] == "tls") "tls": { "enabled": true, if (isDomain) "server_name": securityConfig?["sni"] ?? nodeListItem.serverAddr, "insecure": securityConfig?["allow_insecure"] ?? true, "utls": { "enabled": true, "fingerprint": securityConfig?["fingerprint"] ?? "chrome" } } }; break; case "trojan": final bool isDomain = !RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$') .hasMatch(nodeListItem.serverAddr); config = { "type": "trojan", "tag": nodeListItem.name, "server": nodeListItem.serverAddr, "server_port": json["port"], "password": nodeListItem.uuid, if (transportConfig != null) "transport": transportConfig, "tls": { "enabled": json["security"] == "tls", if (isDomain) "server_name": securityConfig?["sni"] ?? nodeListItem.serverAddr, "insecure": securityConfig?["allow_insecure"] ?? true, "utls": { "enabled": true, "fingerprint": securityConfig?["fingerprint"] ?? "chrome" } } }; break; case "hysteria": case "hysteria2": final securityConfig = json["security_config"] as Map? ?? {}; 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; default: if (kDebugMode) { print('⚠️ 不支持的协议类型: ${nodeListItem.protocol}'); } config = {}; } // 检查 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 } } } catch (e) { if (kDebugMode) { print('⚠️ 解析 config 字段失败: $e'); } config = {}; } } } @override String toString() { return 'KROutboundItem(name: ${nodeListItem.name}, protocol: ${nodeListItem.protocol}, server: ${nodeListItem.serverAddr}, port: ${nodeListItem.port})'; } /// 构建传输配置 Map _buildTransport(Map json) { final transportType = json["transport"] as String?; final transportConfig = json["transport_config"] as Map? ?? {}; 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}'); } // 🔧 尝试从 config 字段解析 transport 配置 Map? transportConfig; Map? 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? matchedProtocol; for (var protocol in protocolsList) { final protocolMap = protocol as Map; 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; 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; 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 = {}; } } }