using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using Azaion.Common.DTO; using Newtonsoft.Json; using Serilog; namespace Azaion.Common.Services; public interface IAzaionApi { ApiCredentials Credentials { get; } Task GetCurrentUserAsync(); Task UpdateOffsetsAsync(UserQueueOffsets offsets); } public class AzaionApi(ILogger logger, HttpClient client, ICache cache, ApiCredentials credentials) : IAzaionApi { private readonly SemaphoreSlim _authLock = new(1, 1); private string? _jwtToken; const string APP_JSON = "application/json"; public ApiCredentials Credentials => credentials; public async Task GetCurrentUserAsync() { var user = await cache.GetFromCacheAsync(Constants.CURRENT_USER_CACHE_KEY, async () => await GetAsync("users/current")); return user ?? throw new Exception("Can't get current user"); } public async Task UpdateOffsetsAsync(UserQueueOffsets offsets) { var user = await GetCurrentUserAsync(); await PutAsync("/users/queue-offsets/set", new { Email = user.Email, Offsets = offsets }); } private async Task GetAsync(string url) { var response = await SendWithAuthAsync(() => client.GetAsync(url)); return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); } private async Task PutAsync(string url, T obj) { var content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, APP_JSON); await SendWithAuthAsync(() => client.PutAsync(url, content)); } private async Task SendWithAuthAsync(Func> sendAction) { if (string.IsNullOrEmpty(_jwtToken)) await AuthorizeAsync(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken); var response = await sendAction(); if (response.StatusCode == HttpStatusCode.Unauthorized) { await AuthorizeAsync(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken); response = await sendAction(); } if (!response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); if (response.StatusCode == HttpStatusCode.Conflict) { var error = JsonConvert.DeserializeObject(content); throw new Exception($"Failed: {response.StatusCode}! Error Code: {error?.ErrorCode}. Message: {error?.Message}"); } throw new Exception($"Failed: {response.StatusCode}! Result: {content}"); } return response; } private async Task AuthorizeAsync() { await _authLock.WaitAsync(); try { if (string.IsNullOrEmpty(credentials.Email) || credentials.Password.Length == 0) throw new Exception("Email or password is empty!"); var content = new StringContent( JsonConvert.SerializeObject(new { email = credentials.Email, password = credentials.Password }), Encoding.UTF8, APP_JSON); client.DefaultRequestHeaders.Authorization = null; var response = await client.PostAsync("login", content); if (!response.IsSuccessStatusCode) throw new Exception($"Authorization failed: {response.StatusCode}"); var result = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()) ?? throw new Exception("JWT Token not found in response"); _jwtToken = result.Token ?? throw new Exception("JWT Token not found in response"); } catch (Exception e) { logger.Error(e, e.Message); throw; } finally { _authLock.Release(); } } }