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 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(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 GetLocationAsync(string? ip, CancellationToken cancellationToken) { var location = await GetLocationByIp(ip, cancellationToken:cancellationToken); return location!; } public async ValueTask 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; } } }