Enhance geolocation data retrieval by implementing multiple API sources with error handling

This commit is contained in:
ImBenji
2025-08-19 21:22:24 +01:00
parent cbbe64abd4
commit bd5f303448

View File

@@ -127,18 +127,217 @@ class GeolocationData {
}
}
// Get the geolocation data of the device using its IP address.
Future<GeolocationData> getGeolocationData() async {
// Get the IP address of the server. https://ipapi.co/json/
var response = await http.get(Uri.parse('https://ipapi.co/json/'));
// List of API configurations with their respective mappers
final apiConfigs = [
_ApiConfig(
url: 'https://ipapi.co/json/',
mapper: _mapIpapiCoResponse,
name: 'ipapi.co',
),
_ApiConfig(
url: 'http://ip-api.com/json/',
mapper: _mapIpApiResponse,
name: 'ip-api.com',
),
_ApiConfig(
url: 'https://freeipapi.com/api/json/',
mapper: _mapFreeIpApiResponse,
name: 'freeipapi.com',
),
_ApiConfig(
url: 'http://ipwhois.app/json/',
mapper: _mapIpWhoisResponse,
name: 'ipwhois.app',
),
];
Exception? lastException;
for (final config in apiConfigs) {
try {
print('Trying ${config.name}...');
final response = await http.get(
Uri.parse(config.url),
headers: {'User-Agent': 'Dart/3.0'},
).timeout(const Duration(seconds: 10));
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
return GeolocationData.fromJson(data);
} else {
throw Exception('Failed to get geolocation data');
final data = jsonDecode(response.body);
// Check if the response indicates success (for APIs that include status)
if (data is Map<String, dynamic>) {
final status = data['status']?.toString().toLowerCase();
if (status != null && status != 'success' && status != 'ok') {
throw Exception('API returned error status: $status');
}
}
final geolocationData = config.mapper(data);
print('Successfully got data from ${config.name}');
return geolocationData;
} else {
throw Exception('HTTP ${response.statusCode}: ${response.reasonPhrase}');
}
} catch (e) {
print('Failed to get data from ${config.name}: $e');
lastException = Exception('${config.name}: $e');
// Add delay before trying next API to avoid hitting rate limits
if (config != apiConfigs.last) {
await Future.delayed(const Duration(milliseconds: 500));
}
}
}
throw lastException ?? Exception('All geolocation APIs failed');
}
class _ApiConfig {
final String url;
final GeolocationData Function(Map<String, dynamic>) mapper;
final String name;
_ApiConfig({
required this.url,
required this.mapper,
required this.name,
});
}
// Mapper for ipapi.co (primary API - has most complete data)
GeolocationData _mapIpapiCoResponse(Map<String, dynamic> json) {
return GeolocationData(
ip: json['ip'] ?? '',
network: json['network'] ?? '',
version: json['version'] ?? 'IPv4',
city: json['city'] ?? '',
region: json['region'] ?? '',
regionCode: json['region_code'] ?? '',
country: json['country'] ?? '',
countryName: json['country_name'] ?? '',
countryCode: json['country_code'] ?? '',
countryCodeIso3: json['country_code_iso3'] ?? '',
countryCapital: json['country_capital'] ?? '',
countryTld: json['country_tld'] ?? '',
continentCode: json['continent_code'] ?? '',
inEu: json['in_eu'] ?? false,
postal: json['postal'] ?? '',
latitude: (json['latitude'] ?? 0.0).toDouble(),
longitude: (json['longitude'] ?? 0.0).toDouble(),
timezone: json['timezone'] ?? '',
utcOffset: json['utc_offset'] ?? '',
countryCallingCode: json['country_calling_code'] ?? '',
currency: json['currency'] ?? '',
currencyName: json['currency_name'] ?? '',
languages: json['languages'] ?? '',
countryArea: (json['country_area'] ?? 0.0).toDouble(),
countryPopulation: json['country_population'] ?? 0,
asn: json['asn'] ?? '',
org: json['org'] ?? '',
);
}
// Mapper for ip-api.com
GeolocationData _mapIpApiResponse(Map<String, dynamic> json) {
return GeolocationData(
ip: json['query'] ?? '',
network: '', // Not provided by ip-api.com
version: json['query']?.toString().contains(':') == true ? 'IPv6' : 'IPv4',
city: json['city'] ?? '',
region: json['regionName'] ?? '',
regionCode: json['region'] ?? '',
country: json['country'] ?? '',
countryName: json['country'] ?? '',
countryCode: json['countryCode'] ?? '',
countryCodeIso3: '', // Not provided by ip-api.com
countryCapital: '', // Not provided by ip-api.com
countryTld: '', // Not provided by ip-api.com
continentCode: '', // Not provided by ip-api.com
inEu: false, // Not provided by ip-api.com
postal: json['zip'] ?? '',
latitude: (json['lat'] ?? 0.0).toDouble(),
longitude: (json['lon'] ?? 0.0).toDouble(),
timezone: json['timezone'] ?? '',
utcOffset: '', // Not provided by ip-api.com
countryCallingCode: '', // Not provided by ip-api.com
currency: '', // Not provided by ip-api.com
currencyName: '', // Not provided by ip-api.com
languages: '', // Not provided by ip-api.com
countryArea: 0.0, // Not provided by ip-api.com
countryPopulation: 0, // Not provided by ip-api.com
asn: json['as'] ?? '',
org: json['org'] ?? json['isp'] ?? '',
);
}
// Mapper for freeipapi.com
GeolocationData _mapFreeIpApiResponse(Map<String, dynamic> json) {
return GeolocationData(
ip: json['ipAddress'] ?? '',
network: '', // Not provided by freeipapi.com
version: json['ipVersion'] ?? 'IPv4',
city: json['cityName'] ?? '',
region: json['regionName'] ?? '',
regionCode: '', // Not provided by freeipapi.com
country: json['countryName'] ?? '',
countryName: json['countryName'] ?? '',
countryCode: json['countryCode'] ?? '',
countryCodeIso3: '', // Not provided by freeipapi.com
countryCapital: '', // Not provided by freeipapi.com
countryTld: '', // Not provided by freeipapi.com
continentCode: '', // Not provided by freeipapi.com
inEu: false, // Not provided by freeipapi.com
postal: json['zipCode'] ?? '',
latitude: (json['latitude'] ?? 0.0).toDouble(),
longitude: (json['longitude'] ?? 0.0).toDouble(),
timezone: json['timeZone'] ?? '',
utcOffset: '', // Not provided by freeipapi.com
countryCallingCode: '', // Not provided by freeipapi.com
currency: '', // Not provided by freeipapi.com
currencyName: '', // Not provided by freeipapi.com
languages: '', // Not provided by freeipapi.com
countryArea: 0.0, // Not provided by freeipapi.com
countryPopulation: 0, // Not provided by freeipapi.com
asn: '', // Not provided by freeipapi.com
org: json['isProxy'] == true ? 'Proxy Service' : '',
);
}
// Mapper for ipwhois.app
GeolocationData _mapIpWhoisResponse(Map<String, dynamic> json) {
return GeolocationData(
ip: json['ip'] ?? '',
network: '', // Not provided by ipwhois.app
version: json['type'] ?? 'IPv4',
city: json['city'] ?? '',
region: json['region'] ?? '',
regionCode: json['region_code'] ?? '',
country: json['country'] ?? '',
countryName: json['country'] ?? '',
countryCode: json['country_code'] ?? '',
countryCodeIso3: '', // Not provided by ipwhois.app
countryCapital: json['country_capital'] ?? '',
countryTld: '', // Not provided by ipwhois.app
continentCode: json['continent_code'] ?? '',
inEu: json['country_eu'] ?? false,
postal: '', // Not provided by ipwhois.app
latitude: (json['latitude'] ?? 0.0).toDouble(),
longitude: (json['longitude'] ?? 0.0).toDouble(),
timezone: json['timezone_name'] ?? '',
utcOffset: json['timezone_gmt'] ?? '',
countryCallingCode: json['country_phone'] ?? '',
currency: json['currency'] ?? '',
currencyName: json['currency_name'] ?? '',
languages: '', // Not provided by ipwhois.app
countryArea: 0.0, // Not provided by ipwhois.app
countryPopulation: 0, // Not provided by ipwhois.app
asn: json['asn'] ?? '',
org: json['org'] ?? json['isp'] ?? '',
);
}
/// Check if Wireguard is installed on the system.
Future<bool> isWireguardInstalled() async {
try {