Add error handling for geo assets

This commit is contained in:
problematicconsumer
2023-11-17 23:22:12 +03:30
parent 4a6b112087
commit 436fc8133c
13 changed files with 113 additions and 43 deletions

View File

@@ -269,6 +269,8 @@
"serviceNotRunning": "Service is not running", "serviceNotRunning": "Service is not running",
"missingPrivilege": "Missing Privilege", "missingPrivilege": "Missing Privilege",
"missingPrivilegeMsg": "VPN mode requires administrator privileges. Either relaunch the app as administrator or change service mode.", "missingPrivilegeMsg": "VPN mode requires administrator privileges. Either relaunch the app as administrator or change service mode.",
"missingGeoAssets": "Missing Geo Assets",
"missingGeoAssetsMsg": "Geo assets are missing. consider changing active asset or download selected one in the settings.",
"invalidConfigOptions": "Invalid configuration options", "invalidConfigOptions": "Invalid configuration options",
"invalidConfig": "Invalid Configuration", "invalidConfig": "Invalid Configuration",
"create": "Service creation error", "create": "Service creation error",

View File

@@ -272,7 +272,9 @@
"invalidConfigOptions": "تنظیمات کانفیگ نامعتبر", "invalidConfigOptions": "تنظیمات کانفیگ نامعتبر",
"invalidConfig": "کانفیگ غیر معتبر", "invalidConfig": "کانفیگ غیر معتبر",
"create": "در ایجاد سرویس خطایی رخ داده", "create": "در ایجاد سرویس خطایی رخ داده",
"start": "در راه‌اندازی سرویس خطایی رخ داده" "start": "در راه‌اندازی سرویس خطایی رخ داده",
"missingGeoAssets": "دارایی های جغرافیایی از دست رفته",
"missingGeoAssetsMsg": "دارایی های جغرافیایی گم شده اند. تغییر دارایی فعال را در نظر بگیرید یا یکی را در تنظیمات دانلود کنید."
}, },
"connectivity": { "connectivity": {
"unexpected": "خطای غیرمنتظره", "unexpected": "خطای غیرمنتظره",

View File

@@ -272,7 +272,9 @@
"invalidConfigOptions": "Неправильные параметры конфигурации", "invalidConfigOptions": "Неправильные параметры конфигурации",
"invalidConfig": "Неправильная конфигурация", "invalidConfig": "Неправильная конфигурация",
"create": "Ошибка создания сервиса", "create": "Ошибка создания сервиса",
"start": "Ошибка запуска сервиса" "start": "Ошибка запуска сервиса",
"missingGeoAssets": "Отсутствующие географические ресурсы",
"missingGeoAssetsMsg": "Георесурсы отсутствуют. рассмотрите возможность изменения активного актива или загрузите выбранный в настройках."
}, },
"connectivity": { "connectivity": {
"unexpected": "Неожиданная ошибка", "unexpected": "Неожиданная ошибка",

View File

@@ -272,7 +272,9 @@
"invalidConfigOptions": "Geçersiz yapılandırma seçenekleri", "invalidConfigOptions": "Geçersiz yapılandırma seçenekleri",
"invalidConfig": "Geçersiz Yapılandırma", "invalidConfig": "Geçersiz Yapılandırma",
"create": "Servis oluşturma hatası", "create": "Servis oluşturma hatası",
"start": "Servis başlatma hatası" "start": "Servis başlatma hatası",
"missingGeoAssets": "Eksik Coğrafi Varlıklar",
"missingGeoAssetsMsg": "Coğrafi öğeler eksik. Aktif varlığı değiştirmeyi veya ayarlarda seçileni indirmeyi düşünün."
}, },
"connectivity": { "connectivity": {
"unexpected": "Beklenmedik Hata", "unexpected": "Beklenmedik Hata",

View File

@@ -272,7 +272,9 @@
"invalidConfigOptions": "配置选项无效", "invalidConfigOptions": "配置选项无效",
"invalidConfig": "无效配置", "invalidConfig": "无效配置",
"create": "服务创建错误", "create": "服务创建错误",
"start": "服务启动错误" "start": "服务启动错误",
"missingGeoAssets": "缺少地理资产",
"missingGeoAssetsMsg": "地理资产缺失。考虑更改活动资产或下载设置中选定的资产。"
}, },
"connectivity": { "connectivity": {
"unexpected": "意外失败", "unexpected": "意外失败",

View File

@@ -28,6 +28,12 @@ class GeoAssetsDao extends DatabaseAccessor<AppDatabase>
Future<void> edit(GeoAsset patch) async { Future<void> edit(GeoAsset patch) async {
await transaction( await transaction(
() async { () async {
if (patch.active) {
await (update(geoAssetEntries)
..where((tbl) => tbl.active.equals(true))
..where((tbl) => tbl.type.equalsValue(patch.type)))
.write(const GeoAssetEntriesCompanion(active: Value(false)));
}
await (update(geoAssetEntries)..where((tbl) => tbl.id.equals(patch.id))) await (update(geoAssetEntries)..where((tbl) => tbl.id.equals(patch.id)))
.write(patch.toCompanion()); .write(patch.toCompanion());
}, },

View File

@@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
import 'package:hiddify/data/api/clash_api.dart'; import 'package:hiddify/data/api/clash_api.dart';
@@ -33,6 +34,21 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
bool _initialized = false; bool _initialized = false;
TaskEither<CoreServiceFailure, ConfigOptions> _getConfigOptions() {
return exceptionHandler(
() async {
final options = await configOptions();
final geoip = filesEditor.resolveGeoAssetPath(options.geoipPath);
final geosite = filesEditor.resolveGeoAssetPath(options.geositePath);
if (!await File(geoip).exists() || !await File(geosite).exists()) {
return left(const CoreMissingGeoAssets());
}
return right(options);
},
CoreServiceFailure.unexpected,
);
}
@override @override
TaskEither<CoreServiceFailure, Unit> setup() { TaskEither<CoreServiceFailure, Unit> setup() {
if (_initialized) return TaskEither.of(unit); if (_initialized) return TaskEither.of(unit);
@@ -94,21 +110,17 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
TaskEither<CoreServiceFailure, String> generateConfig( TaskEither<CoreServiceFailure, String> generateConfig(
String fileName, String fileName,
) { ) {
return exceptionHandler( return TaskEither<CoreServiceFailure, String>.Do(
() async { ($) async {
final configPath = filesEditor.configPath(fileName); final configPath = filesEditor.configPath(fileName);
final options = await configOptions(); final options = await $(_getConfigOptions());
return setup() await $(setup());
.andThen(() => changeConfigOptions(options)) await $(changeConfigOptions(options));
.andThen( return await $(
() => singbox singbox.generateConfig(configPath).mapLeft(CoreServiceFailure.other),
.generateConfig(configPath) );
.mapLeft(CoreServiceFailure.other),
)
.run();
}, },
CoreServiceFailure.unexpected, ).handleExceptions(CoreServiceFailure.unexpected);
);
} }
@override @override
@@ -116,33 +128,35 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
String fileName, String fileName,
bool disableMemoryLimit, bool disableMemoryLimit,
) { ) {
return exceptionHandler( return TaskEither<CoreServiceFailure, Unit>.Do(
() async { ($) async {
final configPath = filesEditor.configPath(fileName); final configPath = filesEditor.configPath(fileName);
final options = await configOptions(); final options = await $(_getConfigOptions());
loggy.info( loggy.info(
"config options: ${options.format()}\nMemory Limit: ${!disableMemoryLimit}", "config options: ${options.format()}\nMemory Limit: ${!disableMemoryLimit}",
); );
if (options.enableTun) { await $(
final hasPrivilege = await platformServices.hasPrivilege(); TaskEither(() async {
if (!hasPrivilege) { if (options.enableTun) {
loggy.warning("missing privileges for tun mode"); final hasPrivilege = await platformServices.hasPrivilege();
return left(const CoreMissingPrivilege()); if (!hasPrivilege) {
} loggy.warning("missing privileges for tun mode");
} return left(const CoreMissingPrivilege());
}
return setup() }
.andThen(() => changeConfigOptions(options)) return right(unit);
.andThen( }),
() => singbox );
.start(configPath, disableMemoryLimit) await $(setup());
.mapLeft(CoreServiceFailure.start), await $(changeConfigOptions(options));
) return await $(
.run(); singbox
.start(configPath, disableMemoryLimit)
.mapLeft(CoreServiceFailure.start),
);
}, },
CoreServiceFailure.unexpected, ).handleExceptions(CoreServiceFailure.unexpected);
);
} }
@override @override
@@ -161,7 +175,8 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
return exceptionHandler( return exceptionHandler(
() async { () async {
final configPath = filesEditor.configPath(fileName); final configPath = filesEditor.configPath(fileName);
return changeConfigOptions(await configOptions()) return _getConfigOptions()
.flatMap((options) => changeConfigOptions(options))
.andThen( .andThen(
() => singbox () => singbox
.restart(configPath, disableMemoryLimit) .restart(configPath, disableMemoryLimit)

View File

@@ -30,3 +30,19 @@ extension StreamExceptionHandler<R extends Object?> on Stream<R> {
); );
} }
} }
extension TaskEitherExceptionHandler<F, R> on TaskEither<F, R> {
TaskEither<F, R> handleExceptions(
F Function(Object error, StackTrace stackTrace) onError,
) {
return TaskEither(
() async {
try {
return await run();
} catch (error, stackTrace) {
return Left(onError(error, stackTrace));
}
},
);
}
}

View File

@@ -68,9 +68,7 @@ class GeoAssetsRepositoryImpl
filesEditor.geoAssetsDir.path, filesEditor.geoAssetsDir.path,
pollingDelay: const Duration(seconds: 1), pollingDelay: const Duration(seconds: 1),
).events.asyncMap((event) async { ).events.asyncMap((event) async {
if (event.type == ChangeType.MODIFY) { await _readGeoFiles();
await _readGeoFiles();
}
return _geoFiles; return _geoFiles;
}); });
} }
@@ -137,4 +135,15 @@ class GeoAssetsRepositoryImpl
GeoAssetFailure.unexpected, GeoAssetFailure.unexpected,
); );
} }
@override
TaskEither<GeoAssetFailure, Unit> markAsActive(GeoAsset geoAsset) {
return exceptionHandler(
() async {
await geoAssetsDao.edit(geoAsset.copyWith(active: true));
return right(unit);
},
GeoAssetFailure.unexpected,
);
}
} }

View File

@@ -21,6 +21,9 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
@With<ExpectedFailure>() @With<ExpectedFailure>()
const factory CoreServiceFailure.missingPrivilege() = CoreMissingPrivilege; const factory CoreServiceFailure.missingPrivilege() = CoreMissingPrivilege;
@With<ExpectedFailure>()
const factory CoreServiceFailure.missingGeoAssets() = CoreMissingGeoAssets;
const factory CoreServiceFailure.invalidConfigOptions([ const factory CoreServiceFailure.invalidConfigOptions([
String? message, String? message,
]) = InvalidConfigOptions; ]) = InvalidConfigOptions;
@@ -46,6 +49,7 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
UnexpectedCoreServiceFailure() => null, UnexpectedCoreServiceFailure() => null,
CoreServiceNotRunning(:final message) => message, CoreServiceNotRunning(:final message) => message,
CoreMissingPrivilege() => null, CoreMissingPrivilege() => null,
CoreMissingGeoAssets() => null,
InvalidConfigOptions(:final message) => message, InvalidConfigOptions(:final message) => message,
InvalidConfig(:final message) => message, InvalidConfig(:final message) => message,
CoreServiceCreateFailure(:final message) => message, CoreServiceCreateFailure(:final message) => message,
@@ -68,6 +72,10 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
type: t.failure.singbox.missingPrivilege, type: t.failure.singbox.missingPrivilege,
message: t.failure.singbox.missingPrivilegeMsg, message: t.failure.singbox.missingPrivilegeMsg,
), ),
CoreMissingGeoAssets() => (
type: t.failure.singbox.missingGeoAssets,
message: t.failure.singbox.missingGeoAssetsMsg,
),
InvalidConfigOptions(:final message) => ( InvalidConfigOptions(:final message) => (
type: t.failure.singbox.invalidConfigOptions, type: t.failure.singbox.invalidConfigOptions,
message: message message: message

View File

@@ -9,4 +9,6 @@ abstract interface class GeoAssetsRepository {
Stream<Either<GeoAssetFailure, List<GeoAssetWithFileSize>>> watchAll(); Stream<Either<GeoAssetFailure, List<GeoAssetWithFileSize>>> watchAll();
TaskEither<GeoAssetFailure, Unit> update(GeoAsset geoAsset); TaskEither<GeoAssetFailure, Unit> update(GeoAsset geoAsset);
TaskEither<GeoAssetFailure, Unit> markAsActive(GeoAsset geoAsset);
} }

View File

@@ -99,6 +99,10 @@ class FilesEditorService with InfraLogger {
return p.relative(fullPath, from: workingDir.path); return p.relative(fullPath, from: workingDir.path);
} }
String resolveGeoAssetPath(String path) {
return p.absolute(workingDir.path, path);
}
String tempConfigPath(String fileName) => configPath("temp_$fileName"); String tempConfigPath(String fileName) => configPath("temp_$fileName");
Future<void> deleteConfig(String fileName) { Future<void> deleteConfig(String fileName) {

Submodule libcore updated: 2c2504f971...2f865d5c8a