MyBookmark/Modules.User.Infrastructure/Services/LocationService.cs

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; }
}
}