diff --git a/JOBot.Backend/DAL/Models/User.cs b/JOBot.Backend/DAL/Models/User.cs index 0009df7..c8ea9aa 100644 --- a/JOBot.Backend/DAL/Models/User.cs +++ b/JOBot.Backend/DAL/Models/User.cs @@ -15,11 +15,13 @@ public class User public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - [MaxLength(255)] public string? AccessToken { get; set; } = null; - [MaxLength(255)] public string? RefreshToken { get; set; } = null; + [MaxLength(255)] public string? AccessToken { get; set; } + [MaxLength(255)] public string? RefreshToken { get; set; } - public bool Eula { get; set; } = false; - [MaxLength(255)] public string? CvUrl { get; set; } = null; + public long? ExpiresIn { get; set; } = null; + + public bool Eula { get; set; } + [MaxLength(255)] public string? CvUrl { get; set; } } //TODO: Негоже это маппинги в DAL ложить diff --git a/JOBot.Backend/Services/HeadHunterService.cs b/JOBot.Backend/Services/HeadHunterService.cs index 8df11f0..429113f 100644 --- a/JOBot.Backend/Services/HeadHunterService.cs +++ b/JOBot.Backend/Services/HeadHunterService.cs @@ -116,6 +116,7 @@ public class HeadHunterService( user.AccessToken = responseDto.AccessToken; user.RefreshToken = responseDto.RefreshToken; + user.ExpiresIn = responseDto.ExpiresIn; await dbContext.SaveChangesAsync(); @@ -133,9 +134,9 @@ public class HeadHunterService( public async Task UpdateUserAccessToken(User user) { - if(user.RefreshToken == null) + if (user.RefreshToken == null) return Status.UserAuthRejectedError; - + using var client = new HttpClient(); //TODO: Написать wrapper для работы с HH API var form = new Dictionary { @@ -156,24 +157,25 @@ public class HeadHunterService( { Content = new FormUrlEncodedContent(form) }); - + var responseDto = JsonSerializer.Deserialize(await res.Content.ReadAsStringAsync()); if (responseDto == null) { - logger.LogWarning($"User {user.UserId} access token accept completed with error " + - $"{nameof(Status.HeadHunterResponseDeserializationFailedError)}"); + logger.LogWarning( + $"User {user.Username ?? user.UserId.ToString()} access token accept completed with error " + + $"{nameof(Status.HeadHunterResponseDeserializationFailedError)}"); return Status.HeadHunterResponseDeserializationFailedError; } - + user.RefreshToken = responseDto.RefreshToken; user.AccessToken = responseDto.AccessToken; await dbContext.SaveChangesAsync(); - + return Status.Success; } - + private string GetRedirectUrl(long userId) { return new UriBuilder(_config.Links.HookDomain) diff --git a/JOBot.Backend/Services/ResumeService.cs b/JOBot.Backend/Services/ResumeService.cs index 4d970f8..6913599 100644 --- a/JOBot.Backend/Services/ResumeService.cs +++ b/JOBot.Backend/Services/ResumeService.cs @@ -1,3 +1,4 @@ +using System.Net.Http.Headers; using System.Text.Json; using JOBot.Backend.DAL.Context; using JOBot.Backend.DTOs.HeadHunterApi; @@ -9,7 +10,9 @@ namespace JOBot.Backend.Services; public class ResumeService( AppDbContext dbContext, - IOptions config) + ILogger logger, + IOptions config, + HeadHunterService headHunterService) { public async Task<(Status Status, List? Resume)> GetAvailableResumes(long userId) { @@ -19,6 +22,16 @@ public class ResumeService( return new(Status.UserNotFound, null); } + if (!user.ExpiresIn.HasValue || new DateTime().AddSeconds(user.ExpiresIn.Value) > DateTime.Now) + { + var status = await headHunterService.UpdateUserAccessToken(user); + if (status != HeadHunterService.Status.Success) + { + logger.LogError($"User {userId} has expired access and update of it was unsuccessful: {status}"); + return (Status.TokenExpired, null); + } + } + using var client = new HttpClient(); //TODO: Написать wrapper для работы с HH API client.BaseAddress = new UriBuilder(config.Value.Links.HeadHunterApiDomain) { @@ -26,13 +39,21 @@ public class ResumeService( Scheme = "https" }.Uri; client.DefaultRequestHeaders.UserAgent.ParseAdd("Jobot BackEnd Service"); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.AccessToken); using var res = await client.GetAsync("/resumes/mine"); + if (!res.IsSuccessStatusCode) + { + logger.LogWarning( + $"User {user.Username ?? user.UserId.ToString()} resume list is unavailable by unsuccessful status code: {res.StatusCode}"); + return new(Status.RequestError, null); + } + var responseDto = JsonSerializer.Deserialize(await res.Content.ReadAsStringAsync()); if (responseDto == null) return new(Status.Error, null); - + return new(Status.Success, responseDto.Items); } @@ -40,6 +61,8 @@ public class ResumeService( { Success, UserNotFound, - Error + Error, + RequestError, + TokenExpired } } \ No newline at end of file