Add error handling for geo assets
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -272,7 +272,9 @@
|
|||||||
"invalidConfigOptions": "تنظیمات کانفیگ نامعتبر",
|
"invalidConfigOptions": "تنظیمات کانفیگ نامعتبر",
|
||||||
"invalidConfig": "کانفیگ غیر معتبر",
|
"invalidConfig": "کانفیگ غیر معتبر",
|
||||||
"create": "در ایجاد سرویس خطایی رخ داده",
|
"create": "در ایجاد سرویس خطایی رخ داده",
|
||||||
"start": "در راهاندازی سرویس خطایی رخ داده"
|
"start": "در راهاندازی سرویس خطایی رخ داده",
|
||||||
|
"missingGeoAssets": "دارایی های جغرافیایی از دست رفته",
|
||||||
|
"missingGeoAssetsMsg": "دارایی های جغرافیایی گم شده اند. تغییر دارایی فعال را در نظر بگیرید یا یکی را در تنظیمات دانلود کنید."
|
||||||
},
|
},
|
||||||
"connectivity": {
|
"connectivity": {
|
||||||
"unexpected": "خطای غیرمنتظره",
|
"unexpected": "خطای غیرمنتظره",
|
||||||
|
|||||||
@@ -272,7 +272,9 @@
|
|||||||
"invalidConfigOptions": "Неправильные параметры конфигурации",
|
"invalidConfigOptions": "Неправильные параметры конфигурации",
|
||||||
"invalidConfig": "Неправильная конфигурация",
|
"invalidConfig": "Неправильная конфигурация",
|
||||||
"create": "Ошибка создания сервиса",
|
"create": "Ошибка создания сервиса",
|
||||||
"start": "Ошибка запуска сервиса"
|
"start": "Ошибка запуска сервиса",
|
||||||
|
"missingGeoAssets": "Отсутствующие географические ресурсы",
|
||||||
|
"missingGeoAssetsMsg": "Георесурсы отсутствуют. рассмотрите возможность изменения активного актива или загрузите выбранный в настройках."
|
||||||
},
|
},
|
||||||
"connectivity": {
|
"connectivity": {
|
||||||
"unexpected": "Неожиданная ошибка",
|
"unexpected": "Неожиданная ошибка",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -272,7 +272,9 @@
|
|||||||
"invalidConfigOptions": "配置选项无效",
|
"invalidConfigOptions": "配置选项无效",
|
||||||
"invalidConfig": "无效配置",
|
"invalidConfig": "无效配置",
|
||||||
"create": "服务创建错误",
|
"create": "服务创建错误",
|
||||||
"start": "服务启动错误"
|
"start": "服务启动错误",
|
||||||
|
"missingGeoAssets": "缺少地理资产",
|
||||||
|
"missingGeoAssetsMsg": "地理资产缺失。考虑更改活动资产或下载设置中选定的资产。"
|
||||||
},
|
},
|
||||||
"connectivity": {
|
"connectivity": {
|
||||||
"unexpected": "意外失败",
|
"unexpected": "意外失败",
|
||||||
|
|||||||
@@ -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());
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
2
libcore
2
libcore
Submodule libcore updated: 2c2504f971...2f865d5c8a
Reference in New Issue
Block a user