feat: 多端邀请接入
This commit is contained in:
@@ -76,4 +76,168 @@ class KRDeviceUtil {
|
||||
_kr_cachedDeviceId = null;
|
||||
_kr_storage.kr_deleteData(key: _kr_deviceIdKey);
|
||||
}
|
||||
}
|
||||
|
||||
/// 从桌面平台提取邀请码 (深度检测)
|
||||
/// 1. 检测执行路径 (支持重命名的 .app 或二进制)
|
||||
/// 2. (MacOS) 检测挂载源 (支持重命名的 DMG)
|
||||
/// 3. (MacOS) 检测来源元数据 (支持从下载链接中识别)
|
||||
Future<String> kr_getDesktopInviteCode() async {
|
||||
if (!Platform.isMacOS && !Platform.isWindows) return '';
|
||||
try {
|
||||
final String executablePath = Platform.resolvedExecutable;
|
||||
KRLogUtil.kr_i('🔍 [DEBUG] 桌面端开始深度解析邀请码, 当前路径: $executablePath', tag: 'DeviceUtil');
|
||||
|
||||
// 策略 1: 直接匹配路径 (最快)
|
||||
String? code = _kr_extractCode(executablePath);
|
||||
if (code != null) return code;
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
// 策略 2: 如果在 /Volumes 下运行,尝试找 DMG 原文件名
|
||||
if (executablePath.contains('/Volumes/')) {
|
||||
code = await _kr_getInviteCodeFromHdiutil(executablePath);
|
||||
if (code != null) {
|
||||
KRLogUtil.kr_i('🎯 从 hdiutil (挂载源) 获取到邀请码: $code', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
// 策略 3: 检查文件的来源元数据 (kMDItemWhereFroms)
|
||||
code = await _kr_getInviteCodeFromMetadata(executablePath);
|
||||
if (code != null) {
|
||||
KRLogUtil.kr_i('🎯 从 mdls (文件元数据) 获取到邀请码: $code', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
|
||||
// 策略 4: 在下载目录下寻找最近的带有 ic- 的 DMG
|
||||
code = await _kr_searchDownloadsForInviteCode();
|
||||
if (code != null) {
|
||||
KRLogUtil.kr_i('🎯 从下载目录搜索获取到邀请码: $code', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
|
||||
KRLogUtil.kr_i('⚠️ 深度检测完成,未发现有效的邀请码标识', tag: 'DeviceUtil');
|
||||
}
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('解析桌面邀请码异常: $e', tag: 'DeviceUtil');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/// (MacOS) 在下载目录下搜寻最近的、符合命名规范的 DMG
|
||||
Future<String?> _kr_searchDownloadsForInviteCode() async {
|
||||
try {
|
||||
final String home = Platform.environment['HOME'] ?? '';
|
||||
if (home.isEmpty) return null;
|
||||
|
||||
final Directory downloads = Directory('$home/Downloads');
|
||||
if (!await downloads.exists()) return null;
|
||||
|
||||
final List<FileSystemEntity> files = await downloads.list().toList();
|
||||
String? bestMatch;
|
||||
DateTime? latestDate;
|
||||
|
||||
for (var file in files) {
|
||||
if (file is File && file.path.contains('ic-') && (file.path.endsWith('.dmg') || file.path.endsWith('.exe'))) {
|
||||
// 提取代码
|
||||
final code = _kr_extractCode(file.path);
|
||||
if (code != null) {
|
||||
final stat = await file.stat();
|
||||
if (latestDate == null || stat.modified.isAfter(latestDate)) {
|
||||
latestDate = stat.modified;
|
||||
bestMatch = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestMatch;
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// 正则提取 ic- 模式
|
||||
String? _kr_extractCode(String source) {
|
||||
final RegExp regExp = RegExp(r'ic-([A-Za-z0-9-_]+)');
|
||||
final Match? match = regExp.firstMatch(source);
|
||||
if (match != null && match.groupCount >= 1) {
|
||||
final String code = match.group(1) ?? '';
|
||||
KRLogUtil.kr_i('✅ [DEBUG] 成功匹配到邀请码: $code (源: $source)', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// (MacOS) 通过 hdiutil 查找挂载卷对应的原始 DMG 路径
|
||||
Future<String?> _kr_getInviteCodeFromHdiutil(String execPath) async {
|
||||
try {
|
||||
final ProcessResult result = await Process.run('hdiutil', ['info']);
|
||||
if (result.exitCode == 0) {
|
||||
final String output = result.stdout.toString();
|
||||
// 寻找每个条目包含的 image-path 和 实际挂载路径
|
||||
final List<String> segments = output.split('================================================');
|
||||
for (var segment in segments) {
|
||||
if (!segment.contains('image-path')) continue;
|
||||
|
||||
String? imagePath;
|
||||
String? mountPoint;
|
||||
|
||||
final List<String> lines = segment.split('\n');
|
||||
for (var line in lines) {
|
||||
final String trimmedLine = line.trim();
|
||||
if (trimmedLine.isEmpty) continue;
|
||||
|
||||
if (trimmedLine.contains('image-path')) {
|
||||
final int colonIndex = trimmedLine.indexOf(':');
|
||||
if (colonIndex != -1) {
|
||||
imagePath = trimmedLine.substring(colonIndex + 1).trim();
|
||||
}
|
||||
}
|
||||
// 匹配类似: /dev/disk8s1 7C3457EF... /Volumes/HiFastVPN Installation
|
||||
// 或者是正常的 mount-point 键值对
|
||||
else if (trimmedLine.contains('/Volumes/')) {
|
||||
if (trimmedLine.contains('mount-point')) {
|
||||
final int colonIndex = trimmedLine.indexOf(':');
|
||||
if (colonIndex != -1) {
|
||||
mountPoint = trimmedLine.substring(colonIndex + 1).trim();
|
||||
}
|
||||
} else if (trimmedLine.startsWith('/dev/')) {
|
||||
// 提取路径:通常在最后一个制表符或空格序列之后
|
||||
final int volumesIndex = trimmedLine.indexOf('/Volumes/');
|
||||
if (volumesIndex != -1) {
|
||||
mountPoint = trimmedLine.substring(volumesIndex).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imagePath != null && mountPoint != null) {
|
||||
final normalizedMount = mountPoint.endsWith('/') ? mountPoint.substring(0, mountPoint.length - 1) : mountPoint;
|
||||
if (execPath.startsWith(normalizedMount)) {
|
||||
KRLogUtil.kr_i('🎯 [DEBUG] 成功匹配到挂载源: $imagePath', tag: 'DeviceUtil');
|
||||
return _kr_extractCode(imagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('❌ [DEBUG] hdiutil 追溯异常: $e', tag: 'DeviceUtil');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// (MacOS) 通过 mdls 检查文件的来源下载链接
|
||||
Future<String?> _kr_getInviteCodeFromMetadata(String execPath) async {
|
||||
try {
|
||||
// 提取 .app 的路径
|
||||
final int appIndex = execPath.indexOf('.app');
|
||||
final String targetPath = appIndex != -1
|
||||
? execPath.substring(0, appIndex + 4)
|
||||
: execPath;
|
||||
|
||||
final ProcessResult result = await Process.run('mdls', ['-name', 'kMDItemWhereFroms', targetPath]);
|
||||
if (result.exitCode == 0) {
|
||||
return _kr_extractCode(result.stdout.toString());
|
||||
}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user