77 lines
2.7 KiB
C#
77 lines
2.7 KiB
C#
using System.Net.Http.Json;
|
|
using Common.Security.Infrastructure;
|
|
using Modules.User.Application.Interfaces;
|
|
using Modules.User.Application.Interfaces.Services;
|
|
using Modules.User.Application.Models;
|
|
using Modules.User.Application.Models.User.Session;
|
|
|
|
namespace Modules.User.Application.Services;
|
|
|
|
public class LocationService(UserContext userContext, IHttpClientFactory httpClientFactory) : ILocationService
|
|
{
|
|
private const string _externalServiceUrl = "http://ip-api.com/json/";
|
|
private async Task<Location?> GetLocationByIp(string? ip, bool throwException = true,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(ip))
|
|
{
|
|
var userContextIp = userContext.GetClientIp();
|
|
if (string.IsNullOrWhiteSpace(userContextIp))
|
|
{
|
|
if (throwException) throw new Exception("Ip is null");
|
|
return null;
|
|
}
|
|
ip = userContextIp;
|
|
}
|
|
|
|
using var client = httpClientFactory.CreateClient();
|
|
try
|
|
{
|
|
var url = string.Concat(_externalServiceUrl, ip);
|
|
var response = await client.GetFromJsonAsync<IpApiResponse>(url, cancellationToken);
|
|
return response is not { lat: not null } || !response.lon.HasValue
|
|
? null
|
|
: new Location
|
|
{
|
|
Latitude = response.lat.Value,
|
|
Longitude = response.lon.Value,
|
|
Country = response.country,
|
|
Region = response.regionName,
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (throwException) throw new Exception("Error while retrieving location", ex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
public async ValueTask<Location> GetLocationAsync(string? ip, CancellationToken cancellationToken)
|
|
{
|
|
var location = await GetLocationByIp(ip, cancellationToken:cancellationToken);
|
|
return location!;
|
|
|
|
}
|
|
|
|
public async ValueTask<Location?> TryGetLocationAsync(string? ip, CancellationToken cancellationToken)
|
|
{
|
|
var location = await GetLocationByIp(ip, throwException:false, cancellationToken:cancellationToken);
|
|
return location;
|
|
}
|
|
|
|
internal sealed class IpApiResponse
|
|
{
|
|
public string? status { get; set; }
|
|
public string? continent { get; set; }
|
|
public string? country { get; set; }
|
|
public string? regionName { get; set; }
|
|
public string? city { get; set; }
|
|
public string? district { get; set; }
|
|
public string? zip { get; set; }
|
|
public double? lat { get; set; }
|
|
public double? lon { get; set; }
|
|
public string? isp { get; set; }
|
|
public string? query { get; set; }
|
|
}
|
|
} |