Add ip info fetcher
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,6 +35,7 @@ migrate_working_dir/
|
|||||||
# generated files
|
# generated files
|
||||||
**/*.g.dart
|
**/*.g.dart
|
||||||
**/*.freezed.dart
|
**/*.freezed.dart
|
||||||
|
**/*.mapper.dart
|
||||||
**/*.gen.dart
|
**/*.gen.dart
|
||||||
**/libclash.so
|
**/libclash.so
|
||||||
**/libclash.h
|
**/libclash.h
|
||||||
|
|||||||
@@ -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/features/proxy/data/proxy_repository.dart';
|
||||||
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -8,5 +9,6 @@ part 'proxy_data_providers.g.dart';
|
|||||||
ProxyRepository proxyRepository(ProxyRepositoryRef ref) {
|
ProxyRepository proxyRepository(ProxyRepositoryRef ref) {
|
||||||
return ProxyRepositoryImpl(
|
return ProxyRepositoryImpl(
|
||||||
singbox: ref.watch(singboxServiceProvider),
|
singbox: ref.watch(singboxServiceProvider),
|
||||||
|
client: ref.watch(httpClientProvider),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import 'package:fpdart/fpdart.dart';
|
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/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_entity.dart';
|
||||||
import 'package:hiddify/features/proxy/model/proxy_failure.dart';
|
import 'package:hiddify/features/proxy/model/proxy_failure.dart';
|
||||||
import 'package:hiddify/singbox/service/singbox_service.dart';
|
import 'package:hiddify/singbox/service/singbox_service.dart';
|
||||||
@@ -7,6 +9,7 @@ import 'package:hiddify/utils/custom_loggers.dart';
|
|||||||
|
|
||||||
abstract interface class ProxyRepository {
|
abstract interface class ProxyRepository {
|
||||||
Stream<Either<ProxyFailure, List<ProxyGroupEntity>>> watchProxies();
|
Stream<Either<ProxyFailure, List<ProxyGroupEntity>>> watchProxies();
|
||||||
|
TaskEither<ProxyFailure, IpInfo> getCurrentIpInfo();
|
||||||
TaskEither<ProxyFailure, Unit> selectProxy(
|
TaskEither<ProxyFailure, Unit> selectProxy(
|
||||||
String groupTag,
|
String groupTag,
|
||||||
String outboundTag,
|
String outboundTag,
|
||||||
@@ -17,13 +20,18 @@ abstract interface class ProxyRepository {
|
|||||||
class ProxyRepositoryImpl
|
class ProxyRepositoryImpl
|
||||||
with ExceptionHandler, InfraLogger
|
with ExceptionHandler, InfraLogger
|
||||||
implements ProxyRepository {
|
implements ProxyRepository {
|
||||||
ProxyRepositoryImpl({required this.singbox});
|
ProxyRepositoryImpl({
|
||||||
|
required this.singbox,
|
||||||
|
required this.client,
|
||||||
|
});
|
||||||
|
|
||||||
final SingboxService singbox;
|
final SingboxService singbox;
|
||||||
|
final DioHttpClient client;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Either<ProxyFailure, List<ProxyGroupEntity>>> watchProxies() {
|
Stream<Either<ProxyFailure, List<ProxyGroupEntity>>> watchProxies() {
|
||||||
return singbox.watchOutbounds().map((event) {
|
return singbox.watchOutbounds().map((event) {
|
||||||
|
print("outbounds: $event");
|
||||||
final groupWithSelected = {
|
final groupWithSelected = {
|
||||||
for (final group in event) group.tag: group.selected,
|
for (final group in event) group.tag: group.selected,
|
||||||
};
|
};
|
||||||
@@ -76,4 +84,32 @@ class ProxyRepositoryImpl
|
|||||||
ProxyUnexpectedFailure.new,
|
ProxyUnexpectedFailure.new,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Map<String, IpInfo Function(Map<String, dynamic> response)>
|
||||||
|
_ipInfoSources = {
|
||||||
|
"https://ipapi.co/json/": IpInfo.fromIpApiCoJson,
|
||||||
|
"https://ipinfo.io/json/": IpInfo.fromIpInfoIoJson,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<ProxyFailure, IpInfo> 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<Map<String, dynamic>>(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,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
70
lib/features/proxy/model/ip_info_entity.dart
Normal file
70
lib/features/proxy/model/ip_info_entity.dart
Normal file
@@ -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<String, dynamic> 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<String, dynamic> 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"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
24
pubspec.lock
24
pubspec.lock
@@ -289,6 +289,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.8"
|
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:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1538,6 +1554,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1"
|
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:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ dependencies:
|
|||||||
dio_smart_retry: ^6.0.0
|
dio_smart_retry: ^6.0.0
|
||||||
cupertino_http: ^1.3.0
|
cupertino_http: ^1.3.0
|
||||||
wolt_modal_sheet: ^0.4.0
|
wolt_modal_sheet: ^0.4.0
|
||||||
|
dart_mappable: ^4.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -86,6 +87,7 @@ dev_dependencies:
|
|||||||
flutter_gen_runner: ^5.4.0
|
flutter_gen_runner: ^5.4.0
|
||||||
go_router_builder: ^2.4.1
|
go_router_builder: ^2.4.1
|
||||||
dependency_validator: ^3.2.3
|
dependency_validator: ^3.2.3
|
||||||
|
dart_mappable_builder: ^4.2.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|||||||
Reference in New Issue
Block a user