From 436fc8133cf12edf35f51ed3c2d313f6a357643e Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Fri, 17 Nov 2023 23:22:12 +0330 Subject: [PATCH] Add error handling for geo assets --- assets/translations/strings_en.i18n.json | 2 + assets/translations/strings_fa.i18n.json | 4 +- assets/translations/strings_ru.i18n.json | 4 +- assets/translations/strings_tr.i18n.json | 4 +- assets/translations/strings_zh.i18n.json | 4 +- lib/data/local/dao/geo_assets_dao.dart | 6 ++ lib/data/repository/core_facade_impl.dart | 85 +++++++++++-------- lib/data/repository/exception_handlers.dart | 16 ++++ .../repository/geo_assets_repository.dart | 15 +++- lib/domain/core_service_failure.dart | 8 ++ lib/domain/rules/geo_assets_repository.dart | 2 + lib/services/files_editor_service.dart | 4 + libcore | 2 +- 13 files changed, 113 insertions(+), 43 deletions(-) diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index 98696753..45b59b37 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -269,6 +269,8 @@ "serviceNotRunning": "Service is not running", "missingPrivilege": "Missing Privilege", "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", "invalidConfig": "Invalid Configuration", "create": "Service creation error", diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index 426bc200..98e59148 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -272,7 +272,9 @@ "invalidConfigOptions": "تنظیمات کانفیگ نامعتبر", "invalidConfig": "کانفیگ غیر معتبر", "create": "در ایجاد سرویس خطایی رخ داده", - "start": "در راه‌اندازی سرویس خطایی رخ داده" + "start": "در راه‌اندازی سرویس خطایی رخ داده", + "missingGeoAssets": "دارایی های جغرافیایی از دست رفته", + "missingGeoAssetsMsg": "دارایی های جغرافیایی گم شده اند. تغییر دارایی فعال را در نظر بگیرید یا یکی را در تنظیمات دانلود کنید." }, "connectivity": { "unexpected": "خطای غیرمنتظره", diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index d278503c..30f211d3 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -272,7 +272,9 @@ "invalidConfigOptions": "Неправильные параметры конфигурации", "invalidConfig": "Неправильная конфигурация", "create": "Ошибка создания сервиса", - "start": "Ошибка запуска сервиса" + "start": "Ошибка запуска сервиса", + "missingGeoAssets": "Отсутствующие географические ресурсы", + "missingGeoAssetsMsg": "Георесурсы отсутствуют. рассмотрите возможность изменения активного актива или загрузите выбранный в настройках." }, "connectivity": { "unexpected": "Неожиданная ошибка", diff --git a/assets/translations/strings_tr.i18n.json b/assets/translations/strings_tr.i18n.json index 8b9199d2..ea1e802c 100644 --- a/assets/translations/strings_tr.i18n.json +++ b/assets/translations/strings_tr.i18n.json @@ -272,7 +272,9 @@ "invalidConfigOptions": "Geçersiz yapılandırma seçenekleri", "invalidConfig": "Geçersiz Yapılandırma", "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": { "unexpected": "Beklenmedik Hata", diff --git a/assets/translations/strings_zh.i18n.json b/assets/translations/strings_zh.i18n.json index 30ba3d30..035810f7 100644 --- a/assets/translations/strings_zh.i18n.json +++ b/assets/translations/strings_zh.i18n.json @@ -272,7 +272,9 @@ "invalidConfigOptions": "配置选项无效", "invalidConfig": "无效配置", "create": "服务创建错误", - "start": "服务启动错误" + "start": "服务启动错误", + "missingGeoAssets": "缺少地理资产", + "missingGeoAssetsMsg": "地理资产缺失。考虑更改活动资产或下载设置中选定的资产。" }, "connectivity": { "unexpected": "意外失败", diff --git a/lib/data/local/dao/geo_assets_dao.dart b/lib/data/local/dao/geo_assets_dao.dart index 3b27a9f6..13a04436 100644 --- a/lib/data/local/dao/geo_assets_dao.dart +++ b/lib/data/local/dao/geo_assets_dao.dart @@ -28,6 +28,12 @@ class GeoAssetsDao extends DatabaseAccessor Future edit(GeoAsset patch) async { await transaction( () 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))) .write(patch.toCompanion()); }, diff --git a/lib/data/repository/core_facade_impl.dart b/lib/data/repository/core_facade_impl.dart index 16220f5f..d254726c 100644 --- a/lib/data/repository/core_facade_impl.dart +++ b/lib/data/repository/core_facade_impl.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/data/api/clash_api.dart'; @@ -33,6 +34,21 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade { bool _initialized = false; + TaskEither _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 TaskEither setup() { if (_initialized) return TaskEither.of(unit); @@ -94,21 +110,17 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade { TaskEither generateConfig( String fileName, ) { - return exceptionHandler( - () async { + return TaskEither.Do( + ($) async { final configPath = filesEditor.configPath(fileName); - final options = await configOptions(); - return setup() - .andThen(() => changeConfigOptions(options)) - .andThen( - () => singbox - .generateConfig(configPath) - .mapLeft(CoreServiceFailure.other), - ) - .run(); + final options = await $(_getConfigOptions()); + await $(setup()); + await $(changeConfigOptions(options)); + return await $( + singbox.generateConfig(configPath).mapLeft(CoreServiceFailure.other), + ); }, - CoreServiceFailure.unexpected, - ); + ).handleExceptions(CoreServiceFailure.unexpected); } @override @@ -116,33 +128,35 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade { String fileName, bool disableMemoryLimit, ) { - return exceptionHandler( - () async { + return TaskEither.Do( + ($) async { final configPath = filesEditor.configPath(fileName); - final options = await configOptions(); + final options = await $(_getConfigOptions()); loggy.info( "config options: ${options.format()}\nMemory Limit: ${!disableMemoryLimit}", ); - if (options.enableTun) { - final hasPrivilege = await platformServices.hasPrivilege(); - if (!hasPrivilege) { - loggy.warning("missing privileges for tun mode"); - return left(const CoreMissingPrivilege()); - } - } - - return setup() - .andThen(() => changeConfigOptions(options)) - .andThen( - () => singbox - .start(configPath, disableMemoryLimit) - .mapLeft(CoreServiceFailure.start), - ) - .run(); + await $( + TaskEither(() async { + if (options.enableTun) { + final hasPrivilege = await platformServices.hasPrivilege(); + if (!hasPrivilege) { + loggy.warning("missing privileges for tun mode"); + return left(const CoreMissingPrivilege()); + } + } + return right(unit); + }), + ); + await $(setup()); + await $(changeConfigOptions(options)); + return await $( + singbox + .start(configPath, disableMemoryLimit) + .mapLeft(CoreServiceFailure.start), + ); }, - CoreServiceFailure.unexpected, - ); + ).handleExceptions(CoreServiceFailure.unexpected); } @override @@ -161,7 +175,8 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade { return exceptionHandler( () async { final configPath = filesEditor.configPath(fileName); - return changeConfigOptions(await configOptions()) + return _getConfigOptions() + .flatMap((options) => changeConfigOptions(options)) .andThen( () => singbox .restart(configPath, disableMemoryLimit) diff --git a/lib/data/repository/exception_handlers.dart b/lib/data/repository/exception_handlers.dart index 3805b584..34845072 100644 --- a/lib/data/repository/exception_handlers.dart +++ b/lib/data/repository/exception_handlers.dart @@ -30,3 +30,19 @@ extension StreamExceptionHandler on Stream { ); } } + +extension TaskEitherExceptionHandler on TaskEither { + TaskEither handleExceptions( + F Function(Object error, StackTrace stackTrace) onError, + ) { + return TaskEither( + () async { + try { + return await run(); + } catch (error, stackTrace) { + return Left(onError(error, stackTrace)); + } + }, + ); + } +} diff --git a/lib/data/repository/geo_assets_repository.dart b/lib/data/repository/geo_assets_repository.dart index c0488aa3..104c8677 100644 --- a/lib/data/repository/geo_assets_repository.dart +++ b/lib/data/repository/geo_assets_repository.dart @@ -68,9 +68,7 @@ class GeoAssetsRepositoryImpl filesEditor.geoAssetsDir.path, pollingDelay: const Duration(seconds: 1), ).events.asyncMap((event) async { - if (event.type == ChangeType.MODIFY) { - await _readGeoFiles(); - } + await _readGeoFiles(); return _geoFiles; }); } @@ -137,4 +135,15 @@ class GeoAssetsRepositoryImpl GeoAssetFailure.unexpected, ); } + + @override + TaskEither markAsActive(GeoAsset geoAsset) { + return exceptionHandler( + () async { + await geoAssetsDao.edit(geoAsset.copyWith(active: true)); + return right(unit); + }, + GeoAssetFailure.unexpected, + ); + } } diff --git a/lib/domain/core_service_failure.dart b/lib/domain/core_service_failure.dart index 8e2041dd..87ab9830 100644 --- a/lib/domain/core_service_failure.dart +++ b/lib/domain/core_service_failure.dart @@ -21,6 +21,9 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure { @With() const factory CoreServiceFailure.missingPrivilege() = CoreMissingPrivilege; + @With() + const factory CoreServiceFailure.missingGeoAssets() = CoreMissingGeoAssets; + const factory CoreServiceFailure.invalidConfigOptions([ String? message, ]) = InvalidConfigOptions; @@ -46,6 +49,7 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure { UnexpectedCoreServiceFailure() => null, CoreServiceNotRunning(:final message) => message, CoreMissingPrivilege() => null, + CoreMissingGeoAssets() => null, InvalidConfigOptions(:final message) => message, InvalidConfig(:final message) => message, CoreServiceCreateFailure(:final message) => message, @@ -68,6 +72,10 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure { type: t.failure.singbox.missingPrivilege, message: t.failure.singbox.missingPrivilegeMsg, ), + CoreMissingGeoAssets() => ( + type: t.failure.singbox.missingGeoAssets, + message: t.failure.singbox.missingGeoAssetsMsg, + ), InvalidConfigOptions(:final message) => ( type: t.failure.singbox.invalidConfigOptions, message: message diff --git a/lib/domain/rules/geo_assets_repository.dart b/lib/domain/rules/geo_assets_repository.dart index 1cd6f510..b063129a 100644 --- a/lib/domain/rules/geo_assets_repository.dart +++ b/lib/domain/rules/geo_assets_repository.dart @@ -9,4 +9,6 @@ abstract interface class GeoAssetsRepository { Stream>> watchAll(); TaskEither update(GeoAsset geoAsset); + + TaskEither markAsActive(GeoAsset geoAsset); } diff --git a/lib/services/files_editor_service.dart b/lib/services/files_editor_service.dart index 19a8bb21..db048e81 100644 --- a/lib/services/files_editor_service.dart +++ b/lib/services/files_editor_service.dart @@ -99,6 +99,10 @@ class FilesEditorService with InfraLogger { return p.relative(fullPath, from: workingDir.path); } + String resolveGeoAssetPath(String path) { + return p.absolute(workingDir.path, path); + } + String tempConfigPath(String fileName) => configPath("temp_$fileName"); Future deleteConfig(String fileName) { diff --git a/libcore b/libcore index 2c2504f9..2f865d5c 160000 --- a/libcore +++ b/libcore @@ -1 +1 @@ -Subproject commit 2c2504f97145453f4fc7866982172e95b533ea73 +Subproject commit 2f865d5c8a11966ab11e777bc0b67a999fa07126