hi-client/lib/app/model/business/kr_outbound_item.dart
Rust 4c5763647d refactor: 优化日志输出,仅在调试模式下启用
- 为所有 print 语句添加 kDebugMode 检查
- 更新 KRLogUtil 工具类,Release 模式下禁用日志输出
- 优化 18 个文件中的 335+ 条日志语句
- 提升 Release 版本性能并增强安全性

(cherry picked from commit 301f1510ba81fe94fb08e013ca80b3ca708a2e15)
2025-11-01 23:31:11 -07:00

593 lines
20 KiB
Dart
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 = "";
@override
String toString() {
return 'KROutboundItem(tag: $tag, country: $country, delay: ${urlTestDelay.value}ms)';
}
/// 服务器类型
/// 构造函数,接受 KrNodeListItem 对象并初始化 KROutboundItem
KROutboundItem(KrNodeListItem 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
}
}
// 解析配置
}
/// 构建传输配置
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;
// 🔧 关键修复:优先从 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) {
// 提取 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": nodeListItem.port,
"method": finalMethod,
"password": nodeListItem.uuid
};
if (kDebugMode) {
print('✅ Shadowsocks 节点配置构建成功: ${nodeListItem.name}');
}
if (kDebugMode) {
print('📄 使用加密方法: $finalMethod');
}
if (kDebugMode) {
print('📄 完整配置: $config');
}
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();
}
// 🔧 关键修复:根据 security_config 判断是否启用 TLS
final bool vlessTlsEnabled = securityConfig?['tls_enabled'] ?? false;
if (kDebugMode) {
print('🔐 VLESS TLS 状态: enabled=$vlessTlsEnabled');
}
config = {
"type": "vless",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": nodeListItem.port,
"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('✅ VLESS 节点配置构建成功: ${nodeListItem.name}');
}
if (kDebugMode) {
print('📄 完整配置: $config');
}
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();
}
// 🔧 关键修复:根据 security_config 判断是否启用 TLS
final bool tlsEnabled = securityConfig?['tls_enabled'] ?? false;
if (kDebugMode) {
print('🔐 TLS 状态: enabled=$tlsEnabled');
}
config = {
"type": "vmess",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": nodeListItem.port,
"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('✅ VMess 节点配置构建成功: ${nodeListItem.name}');
}
if (kDebugMode) {
print('📄 完整配置: $config');
}
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": nodeListItem.port,
"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 = {};
}
}
}