diff --git a/.gitignore b/.gitignore index b21277a5..ccae89b2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ migrate_working_dir/ # generated files **/*.g.dart **/*.freezed.dart +**/*.mapper.dart **/*.gen.dart **/libclash.so **/libclash.h diff --git a/lib/features/proxy/data/proxy_data_providers.dart b/lib/features/proxy/data/proxy_data_providers.dart index 1c0c0823..bfc29735 100644 --- a/lib/features/proxy/data/proxy_data_providers.dart +++ b/lib/features/proxy/data/proxy_data_providers.dart @@ -1,3 +1,4 @@ +import 'package:hiddify/core/http_client/http_client_provider.dart'; import 'package:hiddify/features/proxy/data/proxy_repository.dart'; import 'package:hiddify/singbox/service/singbox_service_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -8,5 +9,6 @@ part 'proxy_data_providers.g.dart'; ProxyRepository proxyRepository(ProxyRepositoryRef ref) { return ProxyRepositoryImpl( singbox: ref.watch(singboxServiceProvider), + client: ref.watch(httpClientProvider), ); } diff --git a/lib/features/proxy/data/proxy_repository.dart b/lib/features/proxy/data/proxy_repository.dart index 13b0b576..9a3781b3 100644 --- a/lib/features/proxy/data/proxy_repository.dart +++ b/lib/features/proxy/data/proxy_repository.dart @@ -1,5 +1,7 @@ import 'package:fpdart/fpdart.dart'; +import 'package:hiddify/core/http_client/dio_http_client.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; +import 'package:hiddify/features/proxy/model/ip_info_entity.dart'; import 'package:hiddify/features/proxy/model/proxy_entity.dart'; import 'package:hiddify/features/proxy/model/proxy_failure.dart'; import 'package:hiddify/singbox/service/singbox_service.dart'; @@ -7,6 +9,7 @@ import 'package:hiddify/utils/custom_loggers.dart'; abstract interface class ProxyRepository { Stream>> watchProxies(); + TaskEither getCurrentIpInfo(); TaskEither selectProxy( String groupTag, String outboundTag, @@ -17,13 +20,18 @@ abstract interface class ProxyRepository { class ProxyRepositoryImpl with ExceptionHandler, InfraLogger implements ProxyRepository { - ProxyRepositoryImpl({required this.singbox}); + ProxyRepositoryImpl({ + required this.singbox, + required this.client, + }); final SingboxService singbox; + final DioHttpClient client; @override Stream>> watchProxies() { return singbox.watchOutbounds().map((event) { + print("outbounds: $event"); final groupWithSelected = { for (final group in event) group.tag: group.selected, }; @@ -76,4 +84,32 @@ class ProxyRepositoryImpl ProxyUnexpectedFailure.new, ); } + + final Map response)> + _ipInfoSources = { + "https://ipapi.co/json/": IpInfo.fromIpApiCoJson, + "https://ipinfo.io/json/": IpInfo.fromIpInfoIoJson, + }; + + @override + TaskEither getCurrentIpInfo() { + return TaskEither.tryCatch( + () async { + for (final source in _ipInfoSources.entries) { + try { + loggy.debug("getting current ip info using [${source.key}]"); + final response = await client.get>(source.key); + if (response.statusCode == 200 && response.data != null) { + return source.value(response.data!); + } + } catch (e) { + loggy.debug("failed getting ip info using [${source.key}]", e); + continue; + } + } + throw const ProxyFailure.unexpected(); + }, + ProxyUnexpectedFailure.new, + ); + } } diff --git a/lib/features/proxy/model/ip_info_entity.dart b/lib/features/proxy/model/ip_info_entity.dart new file mode 100644 index 00000000..6a5536d5 --- /dev/null +++ b/lib/features/proxy/model/ip_info_entity.dart @@ -0,0 +1,70 @@ +import 'package:dart_mappable/dart_mappable.dart'; + +part 'ip_info_entity.mapper.dart'; + +@MappableClass() +class IpInfo with IpInfoMappable { + const IpInfo({ + required this.ip, + required this.countryCode, + required this.region, + required this.city, + this.timezone, + this.asn, + this.org, + }); + + final String ip; + final String countryCode; + final String region; + final String city; + final String? timezone; + final String? asn; + final String? org; + + static IpInfo fromIpInfoIoJson(Map json) { + return switch (json) { + { + "ip": final String ip, + "country": final String country, + "region": final String region, + "city": final String city, + "timezone": final String timezone, + "org": final String org, + } => + IpInfo( + ip: ip, + countryCode: country, + region: region, + city: city, + timezone: timezone, + org: org, + ), + _ => throw const FormatException("invalid json"), + }; + } + + static IpInfo fromIpApiCoJson(Map json) { + return switch (json) { + { + "ip": final String ip, + "country_code": final String countryCode, + "region": final String region, + "city": final String city, + "timezone": final String timezone, + "asn": final String asn, + "org": final String org, + } => + IpInfo( + ip: ip, + countryCode: countryCode, + region: region, + city: city, + timezone: timezone, + asn: asn, + org: org, + ), + _ => throw const FormatException("invalid json"), + }; + } +} diff --git a/pubspec.lock b/pubspec.lock index 36ca7197..c44e14a1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -289,6 +289,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.8" + dart_mappable: + dependency: "direct main" + description: + name: dart_mappable + sha256: "7b6d38ae95f1ae8ffa65df9a5464f14b56c2de94699a035202ca4cd3a0ba249e" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + dart_mappable_builder: + dependency: "direct dev" + description: + name: dart_mappable_builder + sha256: "98c058f7e80a98ea42d357d888ed1648d96bedac8b16872b58fc7024faefcdfe" + url: "https://pub.dev" + source: hosted + version: "4.2.0" dart_style: dependency: transitive description: @@ -1538,6 +1554,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.1" + type_plus: + dependency: transitive + description: + name: type_plus + sha256: "2e33cfac2e129297d5874567bdf7587502ec359881e9318551e014d91b02f84a" + url: "https://pub.dev" + source: hosted + version: "2.1.0" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 500a0f4e..fcaea7ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,6 +71,7 @@ dependencies: dio_smart_retry: ^6.0.0 cupertino_http: ^1.3.0 wolt_modal_sheet: ^0.4.0 + dart_mappable: ^4.2.0 dev_dependencies: flutter_test: @@ -86,6 +87,7 @@ dev_dependencies: flutter_gen_runner: ^5.4.0 go_router_builder: ^2.4.1 dependency_validator: ^3.2.3 + dart_mappable_builder: ^4.2.0 flutter: uses-material-design: true