613 lines
20 KiB
Dart
613 lines
20 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:archive/archive_io.dart';
|
|
import 'package:drift/drift.dart';
|
|
import 'package:drift_flutter/drift_flutter.dart';
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/database/database.dart';
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
import 'package:fl_clash/models/models.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:path/path.dart';
|
|
|
|
Future<T> decodeJSONTask<T>(String data) async {
|
|
return await compute<String, T>(_decodeJSON, data);
|
|
}
|
|
|
|
Future<T> _decodeJSON<T>(String content) async {
|
|
return json.decode(content);
|
|
}
|
|
|
|
Future<String> encodeJSONTask<T>(T data) async {
|
|
return await compute<T, String>(_encodeJSON, data);
|
|
}
|
|
|
|
Future<String> _encodeJSON<T>(T content) async {
|
|
return json.encode(content);
|
|
}
|
|
|
|
Future<String> encodeYamlTask<T>(T data) async {
|
|
return await compute<T, String>(_encodeYaml, data);
|
|
}
|
|
|
|
Future<String> _encodeYaml<T>(T content) async {
|
|
return yaml.encode(content);
|
|
}
|
|
|
|
Future<List<Group>> toGroupsTask(ComputeGroupsState data) async {
|
|
return await compute<ComputeGroupsState, List<Group>>(_toGroupsTask, data);
|
|
}
|
|
|
|
Future<List<Group>> _toGroupsTask(ComputeGroupsState state) async {
|
|
final proxiesData = state.proxiesData;
|
|
final all = proxiesData.all;
|
|
final sortType = state.sortType;
|
|
final delayMap = state.delayMap;
|
|
final selectedMap = state.selectedMap;
|
|
final defaultTestUrl = state.defaultTestUrl;
|
|
final proxies = proxiesData.proxies;
|
|
if (proxies.isEmpty) return [];
|
|
final groupsRaw = all
|
|
.where((name) {
|
|
final proxy = proxies[name] ?? {};
|
|
return GroupTypeExtension.valueList.contains(proxy['type']);
|
|
})
|
|
.map((groupName) {
|
|
final group = proxies[groupName];
|
|
group['all'] = ((group['all'] ?? []) as List)
|
|
.map((name) => proxies[name])
|
|
.where((proxy) => proxy != null)
|
|
.toList();
|
|
return group;
|
|
})
|
|
.toList();
|
|
final groups = groupsRaw.map((e) => Group.fromJson(e)).toList();
|
|
return computeSort(
|
|
groups: groups,
|
|
sortType: sortType,
|
|
delayMap: delayMap,
|
|
selectedMap: selectedMap,
|
|
defaultTestUrl: defaultTestUrl,
|
|
);
|
|
}
|
|
|
|
Future<Map<String, dynamic>> makeRealProfileTask(
|
|
MakeRealProfileState data,
|
|
) async {
|
|
return await compute<MakeRealProfileState, Map<String, dynamic>>(
|
|
_makeRealProfileTask,
|
|
data,
|
|
);
|
|
}
|
|
|
|
Future<Map<String, dynamic>> _makeRealProfileTask(
|
|
MakeRealProfileState data,
|
|
) async {
|
|
final rawConfig = Map.from(data.rawConfig);
|
|
final realPatchConfig = data.realPatchConfig;
|
|
final profilesPath = data.profilesPath;
|
|
final profileId = data.profileId;
|
|
final overrideDns = data.overrideDns;
|
|
final addedRules = data.addedRules;
|
|
final appendSystemDns = data.appendSystemDns;
|
|
final defaultUA = data.defaultUA;
|
|
String getProvidersFilePathInner(String type, String url) {
|
|
return join(
|
|
profilesPath,
|
|
'providers',
|
|
profileId.toString(),
|
|
type,
|
|
url.toMd5(),
|
|
);
|
|
}
|
|
|
|
rawConfig['external-controller'] = realPatchConfig.externalController.value;
|
|
rawConfig['external-ui'] = '';
|
|
rawConfig['interface-name'] = '';
|
|
rawConfig['external-ui-url'] = '';
|
|
rawConfig['tcp-concurrent'] = realPatchConfig.tcpConcurrent;
|
|
rawConfig['unified-delay'] = realPatchConfig.unifiedDelay;
|
|
rawConfig['ipv6'] = realPatchConfig.ipv6;
|
|
rawConfig['log-level'] = realPatchConfig.logLevel.name;
|
|
rawConfig['port'] = 0;
|
|
rawConfig['socks-port'] = 0;
|
|
rawConfig['keep-alive-interval'] = realPatchConfig.keepAliveInterval;
|
|
rawConfig['mixed-port'] = realPatchConfig.mixedPort;
|
|
rawConfig['port'] = realPatchConfig.port;
|
|
rawConfig['socks-port'] = realPatchConfig.socksPort;
|
|
rawConfig['redir-port'] = realPatchConfig.redirPort;
|
|
rawConfig['tproxy-port'] = realPatchConfig.tproxyPort;
|
|
rawConfig['find-process-mode'] = realPatchConfig.findProcessMode.name;
|
|
rawConfig['allow-lan'] = realPatchConfig.allowLan;
|
|
rawConfig['mode'] = realPatchConfig.mode.name;
|
|
if (rawConfig['tun'] == null) {
|
|
rawConfig['tun'] = {};
|
|
}
|
|
rawConfig['tun']['enable'] = realPatchConfig.tun.enable;
|
|
rawConfig['tun']['device'] = realPatchConfig.tun.device;
|
|
rawConfig['tun']['dns-hijack'] = realPatchConfig.tun.dnsHijack;
|
|
rawConfig['tun']['stack'] = realPatchConfig.tun.stack.name;
|
|
rawConfig['tun']['route-address'] = realPatchConfig.tun.routeAddress;
|
|
rawConfig['tun']['auto-route'] = realPatchConfig.tun.autoRoute;
|
|
rawConfig['geodata-loader'] = realPatchConfig.geodataLoader.name;
|
|
if (rawConfig['sniffer']?['sniff'] != null) {
|
|
for (final value in (rawConfig['sniffer']?['sniff'] as Map).values) {
|
|
if (value['ports'] != null && value['ports'] is List) {
|
|
value['ports'] =
|
|
value['ports']?.map((item) => item.toString()).toList() ?? [];
|
|
}
|
|
}
|
|
}
|
|
if (rawConfig['profile'] == null) {
|
|
rawConfig['profile'] = {};
|
|
}
|
|
if (rawConfig['proxy-providers'] != null) {
|
|
final proxyProviders = rawConfig['proxy-providers'] as Map;
|
|
for (final key in proxyProviders.keys) {
|
|
final proxyProvider = proxyProviders[key];
|
|
if (proxyProvider['type'] != 'http') {
|
|
continue;
|
|
}
|
|
if (proxyProvider['url'] != null) {
|
|
proxyProvider['path'] = getProvidersFilePathInner(
|
|
'proxies',
|
|
proxyProvider['url'],
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (rawConfig['rule-providers'] != null) {
|
|
final ruleProviders = rawConfig['rule-providers'] as Map;
|
|
for (final key in ruleProviders.keys) {
|
|
final ruleProvider = ruleProviders[key];
|
|
if (ruleProvider['type'] != 'http') {
|
|
continue;
|
|
}
|
|
if (ruleProvider['url'] != null) {
|
|
ruleProvider['path'] = getProvidersFilePathInner(
|
|
'rules',
|
|
ruleProvider['url'],
|
|
);
|
|
}
|
|
}
|
|
}
|
|
rawConfig['profile']['store-selected'] = false;
|
|
rawConfig['geox-url'] = realPatchConfig.geoXUrl.toJson();
|
|
rawConfig['global-ua'] = realPatchConfig.globalUa ?? defaultUA;
|
|
if (rawConfig['hosts'] == null) {
|
|
rawConfig['hosts'] = {};
|
|
}
|
|
for (final host in realPatchConfig.hosts.entries) {
|
|
rawConfig['hosts'][host.key] = host.value.splitByMultipleSeparators;
|
|
}
|
|
if (rawConfig['dns'] == null) {
|
|
rawConfig['dns'] = {};
|
|
}
|
|
final isEnableDns = rawConfig['dns']['enable'] == true;
|
|
final systemDns = 'system://';
|
|
if (overrideDns || !isEnableDns) {
|
|
final dns = switch (!isEnableDns) {
|
|
true => realPatchConfig.dns.copyWith(
|
|
nameserver: [...realPatchConfig.dns.nameserver, systemDns],
|
|
),
|
|
false => realPatchConfig.dns,
|
|
};
|
|
rawConfig['dns'] = dns.toJson();
|
|
rawConfig['dns']['nameserver-policy'] = {};
|
|
for (final entry in dns.nameserverPolicy.entries) {
|
|
rawConfig['dns']['nameserver-policy'][entry.key] =
|
|
entry.value.splitByMultipleSeparators;
|
|
}
|
|
}
|
|
if (appendSystemDns) {
|
|
final List<String> nameserver = List<String>.from(
|
|
rawConfig['dns']['nameserver'] ?? [],
|
|
);
|
|
if (!nameserver.contains(systemDns)) {
|
|
rawConfig['dns']['nameserver'] = [...nameserver, systemDns];
|
|
}
|
|
}
|
|
List<String> rules = [];
|
|
if (rawConfig['rules'] != null) {
|
|
rules = List<String>.from(rawConfig['rules']);
|
|
}
|
|
rawConfig.remove('rules');
|
|
if (addedRules.isNotEmpty) {
|
|
final parsedNewRules = addedRules
|
|
.map((item) => ParsedRule.parseString(item.value))
|
|
.toList();
|
|
final hasMatchPlaceholder = parsedNewRules.any(
|
|
(item) => item.ruleTarget?.toUpperCase() == 'MATCH',
|
|
);
|
|
String? replacementTarget;
|
|
|
|
if (hasMatchPlaceholder) {
|
|
for (int i = rules.length - 1; i >= 0; i--) {
|
|
final parsed = ParsedRule.parseString(rules[i]);
|
|
if (parsed.ruleAction == RuleAction.MATCH) {
|
|
final target = parsed.ruleTarget;
|
|
if (target != null && target.isNotEmpty) {
|
|
replacementTarget = target;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
final List<String> finalAddedRules;
|
|
|
|
if (replacementTarget?.isNotEmpty == true) {
|
|
finalAddedRules = [];
|
|
for (int i = 0; i < parsedNewRules.length; i++) {
|
|
final parsed = parsedNewRules[i];
|
|
if (parsed.ruleTarget?.toUpperCase() == 'MATCH') {
|
|
finalAddedRules.add(
|
|
parsed.copyWith(ruleTarget: replacementTarget).value,
|
|
);
|
|
} else {
|
|
finalAddedRules.add(addedRules[i].value);
|
|
}
|
|
}
|
|
} else {
|
|
finalAddedRules = addedRules.map((e) => e.value).toList();
|
|
}
|
|
rules = [...finalAddedRules, ...rules];
|
|
}
|
|
rawConfig['rules'] = rules;
|
|
return Map<String, dynamic>.from(rawConfig);
|
|
}
|
|
|
|
Future<List<String>> shakingProfileTask(
|
|
VM2<Iterable<int>, Iterable<int>> data,
|
|
) async {
|
|
return await compute<
|
|
VM3<Iterable<int>, Iterable<int>, RootIsolateToken>,
|
|
List<String>
|
|
>(_shakingProfileTask, VM3(data.a, data.b, RootIsolateToken.instance!));
|
|
}
|
|
|
|
Future<List<String>> _shakingProfileTask(
|
|
VM3<Iterable<int>, Iterable<int>, RootIsolateToken> data,
|
|
) async {
|
|
final profileIds = data.a;
|
|
final scriptIds = data.b;
|
|
final token = data.c;
|
|
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
|
final profilesDir = Directory(await appPath.profilesPath);
|
|
final scriptsDir = Directory(await appPath.scriptsDirPath);
|
|
final providersDir = Directory(await appPath.getProvidersRootPath());
|
|
final List<String> targets = [];
|
|
void scanDirectory(
|
|
Directory dir,
|
|
Iterable<int> baseNames, {
|
|
bool skipProvidersFolder = false,
|
|
}) {
|
|
if (!dir.existsSync()) return;
|
|
final entities = dir.listSync(recursive: false, followLinks: false);
|
|
|
|
for (final entity in entities) {
|
|
if (entity is File) {
|
|
final id = basenameWithoutExtension(entity.path);
|
|
if (!baseNames.contains(int.tryParse(id))) {
|
|
targets.add(entity.path);
|
|
}
|
|
} else if (skipProvidersFolder && entity is Directory) {
|
|
if (basename(entity.path) == 'providers') {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
scanDirectory(profilesDir, profileIds, skipProvidersFolder: true);
|
|
scanDirectory(providersDir, profileIds);
|
|
scanDirectory(scriptsDir, scriptIds);
|
|
return targets;
|
|
}
|
|
|
|
Future<String> encodeLogsTask(List<Log> data) async {
|
|
return await compute<List<Log>, String>(_encodeLogsTask, data);
|
|
}
|
|
|
|
Future<String> _encodeLogsTask(List<Log> data) async {
|
|
final logsRaw = data.map((item) => item.toString());
|
|
final logsRawString = logsRaw.join('\n');
|
|
return logsRawString;
|
|
}
|
|
|
|
Future<MigrationData> oldToNowTask(Map<String, Object?> data) async {
|
|
final homeDir = await appPath.homeDirPath;
|
|
return await compute<
|
|
VM3<Map<String, Object?>, String, String>,
|
|
MigrationData
|
|
>(_oldToNowTask, VM3(data, homeDir, homeDir));
|
|
}
|
|
|
|
Future<MigrationData> _oldToNowTask(
|
|
VM3<Map<String, Object?>, String, String> data,
|
|
) async {
|
|
final configMap = data.a;
|
|
final sourcePath = data.b;
|
|
final targetPath = data.c;
|
|
|
|
final accessControlMap = configMap['accessControl'];
|
|
final isAccessControl = configMap['isAccessControl'];
|
|
if (accessControlMap != null) {
|
|
(accessControlMap as Map)['enable'] = isAccessControl;
|
|
if (configMap['vpnProps'] != null) {
|
|
final vpnPropsRaw = configMap['vpnProps'] as Map;
|
|
vpnPropsRaw['accessControl'] = accessControlMap;
|
|
}
|
|
}
|
|
if (configMap['vpnProps'] != null) {
|
|
final vpnPropsRaw = configMap['vpnProps'] as Map;
|
|
vpnPropsRaw['accessControlProps'] = vpnPropsRaw['accessControl'];
|
|
}
|
|
configMap['davProps'] = configMap['dav'];
|
|
final appSettingProps = configMap['appSetting'] as Map? ?? {};
|
|
appSettingProps['restoreStrategy'] = appSettingProps['recoveryStrategy'];
|
|
configMap['appSettingProps'] = appSettingProps;
|
|
configMap['proxiesStyleProps'] = configMap['proxiesStyle'];
|
|
configMap['proxiesStyleProps'] = configMap['proxiesStyle'];
|
|
// final overwriteMap = configMap['overwrite'] as Map? ?? {};
|
|
// configMap['overwriteType'] = overwriteMap['type'];
|
|
// configMap['scriptId'] = overwriteMap['scriptOverwrite'];
|
|
List rawScripts = configMap['scripts'] as List<dynamic>? ?? [];
|
|
if (rawScripts.isEmpty) {
|
|
final scriptPropsJson = configMap['scriptProps'] as Map<String, dynamic>?;
|
|
if (scriptPropsJson != null) {
|
|
rawScripts = scriptPropsJson['scripts'] as List<dynamic>? ?? [];
|
|
}
|
|
}
|
|
final Map<String, int> idMap = {};
|
|
final List<Script> scripts = [];
|
|
for (final rawScript in rawScripts) {
|
|
final id = rawScript['id'] as String?;
|
|
final content = rawScript['content'] as String?;
|
|
final label = rawScript['label'] as String?;
|
|
if (id == null || content == null || label == null) {
|
|
continue;
|
|
}
|
|
final newId = idMap.updateCacheValue(rawScript['id'], () => snowflake.id);
|
|
final path = _getScriptPath(targetPath, newId.toString());
|
|
final file = File(path);
|
|
await file.safeWriteAsString(content);
|
|
scripts.add(
|
|
Script(id: newId, label: label, lastUpdateTime: DateTime.now()),
|
|
);
|
|
}
|
|
List rawRules = configMap['rules'] as List<dynamic>? ?? [];
|
|
final List<Rule> rules = [];
|
|
final List<ProfileRuleLink> links = [];
|
|
for (final rawRule in rawRules) {
|
|
final id = idMap.updateCacheValue(rawRule['id'], () => snowflake.id);
|
|
rawRule['id'] = id;
|
|
rules.add(Rule.fromJson(rawRule));
|
|
links.add(ProfileRuleLink(ruleId: id));
|
|
}
|
|
List rawProfiles = configMap['profiles'] as List<dynamic>? ?? [];
|
|
final List<Profile> profiles = [];
|
|
for (final rawProfile in rawProfiles) {
|
|
final rawId = rawProfile['id'] as String?;
|
|
if (rawId == null) {
|
|
continue;
|
|
}
|
|
final profileId = idMap.updateCacheValue(rawId, () => snowflake.id);
|
|
rawProfile['id'] = profileId;
|
|
final overwrite = rawProfile['overwrite'] as Map?;
|
|
if (overwrite != null) {
|
|
final standardOverwrite = overwrite['standardOverwrite'] as Map?;
|
|
if (standardOverwrite != null) {
|
|
final addedRules = standardOverwrite['addedRules'] as List? ?? [];
|
|
for (final addRule in addedRules) {
|
|
final id = idMap.updateCacheValue(addRule['id'], () => snowflake.id);
|
|
addRule['id'] = id;
|
|
rules.add(Rule.fromJson(addRule));
|
|
links.add(
|
|
ProfileRuleLink(
|
|
profileId: profileId,
|
|
ruleId: id,
|
|
scene: RuleScene.added,
|
|
),
|
|
);
|
|
}
|
|
final disabledRuleIds = standardOverwrite['disabledRuleIds'] as List?;
|
|
if (disabledRuleIds != null) {
|
|
for (final disabledRuleId in disabledRuleIds) {
|
|
final newDisabledRuleId = idMap[disabledRuleId];
|
|
if (newDisabledRuleId != null) {
|
|
links.add(
|
|
ProfileRuleLink(
|
|
profileId: profileId,
|
|
ruleId: newDisabledRuleId,
|
|
scene: RuleScene.disabled,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
final scriptOverwrite = overwrite['scriptOverwrite'] as Map?;
|
|
if (scriptOverwrite != null) {
|
|
final scriptId = scriptOverwrite['scriptId'] as String?;
|
|
rawProfile['scriptId'] = scriptId != null ? idMap[scriptId] : null;
|
|
}
|
|
rawProfile['overwriteType'] = overwrite['type'];
|
|
}
|
|
|
|
final sourceFile = File(_getProfilePath(sourcePath, rawId));
|
|
final targetFilePath = _getProfilePath(targetPath, profileId.toString());
|
|
await sourceFile.safeCopy(targetFilePath);
|
|
profiles.add(Profile.fromJson(rawProfile));
|
|
}
|
|
final currentProfileId = configMap['currentProfileId'];
|
|
configMap['currentProfileId'] = currentProfileId != null
|
|
? idMap[currentProfileId]
|
|
: null;
|
|
return MigrationData(
|
|
configMap: configMap,
|
|
profiles: profiles,
|
|
rules: rules,
|
|
scripts: scripts,
|
|
links: links,
|
|
);
|
|
}
|
|
|
|
Future<String> backupTask(
|
|
Map<String, dynamic> configMap,
|
|
Iterable<String> fileNames,
|
|
) async {
|
|
return await compute<
|
|
VM3<Map<String, dynamic>, Iterable<String>, RootIsolateToken>,
|
|
String
|
|
>(_backupTask, VM3(configMap, fileNames, RootIsolateToken.instance!));
|
|
}
|
|
|
|
Future<String> _backupTask<T>(
|
|
VM3<Map<String, dynamic>, Iterable<String>, RootIsolateToken> args,
|
|
) async {
|
|
final configMap = args.a;
|
|
final fileNames = args.b;
|
|
final token = args.c;
|
|
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
|
final dbPath = await appPath.databasePath;
|
|
final configStr = json.encode(configMap);
|
|
final profilesDir = Directory(await appPath.profilesPath);
|
|
final scriptsDir = Directory(await appPath.scriptsDirPath);
|
|
final tempZipFilePath = await appPath.tempFilePath;
|
|
final tempDBFile = File(await appPath.tempFilePath);
|
|
final tempConfigFile = File(await appPath.tempFilePath);
|
|
final dbFile = File(dbPath);
|
|
if (await dbFile.exists()) {
|
|
await dbFile.copy(tempDBFile.path);
|
|
}
|
|
final encoder = ZipFileEncoder();
|
|
encoder.create(tempZipFilePath);
|
|
await tempConfigFile.writeAsString(configStr);
|
|
await encoder.addFile(tempDBFile, backupDatabaseName);
|
|
await encoder.addFile(tempConfigFile, configJsonName);
|
|
if (await profilesDir.exists()) {
|
|
await encoder.addDirectory(
|
|
profilesDir,
|
|
filter: (file, _) {
|
|
if (!fileNames.contains(basename(file.path))) {
|
|
return ZipFileOperation.skip;
|
|
}
|
|
return ZipFileOperation.include;
|
|
},
|
|
);
|
|
}
|
|
if (await scriptsDir.exists()) {
|
|
await encoder.addDirectory(
|
|
scriptsDir,
|
|
filter: (file, _) {
|
|
if (!fileNames.contains(basename(file.path))) {
|
|
return ZipFileOperation.skip;
|
|
}
|
|
return ZipFileOperation.include;
|
|
},
|
|
);
|
|
}
|
|
encoder.close();
|
|
await tempConfigFile.safeDelete();
|
|
await tempDBFile.safeDelete();
|
|
return tempZipFilePath;
|
|
}
|
|
|
|
Future<MigrationData> restoreTask() async {
|
|
return await compute<RootIsolateToken, MigrationData>(
|
|
_restoreTask,
|
|
RootIsolateToken.instance!,
|
|
);
|
|
}
|
|
|
|
Future<MigrationData> _restoreTask(RootIsolateToken token) async {
|
|
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
|
final backupFilePath = await appPath.backupFilePath;
|
|
final restoreDirPath = await appPath.restoreDirPath;
|
|
final homeDirPath = await appPath.homeDirPath;
|
|
final zipDecoder = ZipDecoder();
|
|
final input = InputFileStream(backupFilePath);
|
|
final archive = zipDecoder.decodeStream(input);
|
|
final dir = Directory(restoreDirPath);
|
|
await dir.create(recursive: true);
|
|
for (final file in archive.files) {
|
|
final outPath = join(restoreDirPath, posix.normalize(file.name));
|
|
final outputStream = OutputFileStream(outPath);
|
|
file.writeContent(outputStream);
|
|
await outputStream.close();
|
|
}
|
|
await input.close();
|
|
final restoreConfigFile = File(join(restoreDirPath, configJsonName));
|
|
if (!await restoreConfigFile.exists()) {
|
|
throw appLocalizations.invalidBackupFile;
|
|
}
|
|
final restoreConfigMap =
|
|
json.decode(await restoreConfigFile.readAsString())
|
|
as Map<String, Object?>?;
|
|
final version = restoreConfigMap?['version'] ?? 0;
|
|
MigrationData migrationData = MigrationData(configMap: restoreConfigMap);
|
|
if (version == 0 && restoreConfigMap != null) {
|
|
migrationData = await _oldToNowTask(
|
|
VM3(restoreConfigMap, restoreDirPath, homeDirPath),
|
|
);
|
|
return migrationData;
|
|
}
|
|
final backupDatabaseFile = File(join(restoreDirPath, backupDatabaseName));
|
|
if (!await backupDatabaseFile.exists()) {
|
|
return migrationData;
|
|
}
|
|
final database = Database(
|
|
driftDatabase(
|
|
name: 'database',
|
|
native: DriftNativeOptions(
|
|
databaseDirectory: () async => Directory(restoreDirPath),
|
|
),
|
|
),
|
|
);
|
|
final results = await Future.wait([
|
|
database.profilesDao.all().get(),
|
|
database.scriptsDao.all().get(),
|
|
database.rules.all().map((item) => item.toRule()).get(),
|
|
database.profileRuleLinks.all().map((item) => item.toLink()).get(),
|
|
]);
|
|
final profiles = results[0].cast<Profile>();
|
|
final scripts = results[1].cast<Script>();
|
|
final profilesMigration = profiles.map(
|
|
(item) => VM2(
|
|
_getProfilePath(restoreDirPath, item.id.toString()),
|
|
_getProfilePath(homeDirPath, item.id.toString()),
|
|
),
|
|
);
|
|
final scriptsMigration = scripts.map(
|
|
(item) => VM2(
|
|
_getScriptPath(restoreDirPath, item.id.toString()),
|
|
_getScriptPath(homeDirPath, item.id.toString()),
|
|
),
|
|
);
|
|
await _copyWithMapList([...profilesMigration, ...scriptsMigration]);
|
|
migrationData = migrationData.copyWith(
|
|
profiles: profiles,
|
|
scripts: scripts,
|
|
rules: results[2].cast<Rule>(),
|
|
links: results[3].cast<ProfileRuleLink>(),
|
|
);
|
|
await database.close();
|
|
return migrationData;
|
|
}
|
|
|
|
Future<void> _copyWithMapList(List<VM2<String, String>> copyMapList) async {
|
|
await Future.wait(
|
|
copyMapList.map((item) => File(item.a).safeCopy(item.b)).toList(),
|
|
);
|
|
}
|
|
|
|
String _getScriptPath(String root, String fileName) {
|
|
return join(root, 'scripts', '$fileName.js');
|
|
}
|
|
|
|
String _getProfilePath(String root, String fileName) {
|
|
return join(root, 'profiles', '$fileName.yaml');
|
|
}
|