Upgraded projects to .NET 9.0 and added new projects `KBR.Cache`, `KBR.Shared`, and `KBR.Shared.Lite` to the solution. Introduced JWT authentication and OTP handling with new models, services, and configuration options. Updated database schema with new entities `Users` and `RefreshTokens`, and added migrations for schema changes. Implemented caching strategies using `AppDistributedCache` with support for in-memory, SQL Server, and Redis. Enhanced email handling with `MailHelpers` for domain replacement. Updated controllers, repositories, and configuration files to support new features.
271 lines
12 KiB
C#
271 lines
12 KiB
C#
using Asp.Versioning;
|
|
using AutoMapper;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using PortBlog.API.Models;
|
|
using PortBlog.API.Repositories.Contracts;
|
|
using PortBlog.API.Services.Contracts;
|
|
|
|
namespace PortBlog.API.Controllers
|
|
{
|
|
[Route("api/v{versions:apiVersion}/cv")]
|
|
[ApiController]
|
|
[ApiVersion(1)]
|
|
public class CvController : ControllerBase
|
|
{
|
|
private readonly ILogger<CvController> _logger;
|
|
private readonly ICandidateRepository _candidateRepository;
|
|
private readonly IResumeRepository _resumeRepository;
|
|
private readonly IMailRepository _mailRepository;
|
|
private readonly IMailService _mailService;
|
|
private readonly IMapper _mapper;
|
|
|
|
public CvController(ILogger<CvController> logger, ICandidateRepository candidateRepository, IResumeRepository resumeRepository, IMailService mailService, IMapper mapper, IMailRepository mailRepository)
|
|
{
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
_candidateRepository = candidateRepository ?? throw new ArgumentNullException(nameof(candidateRepository));
|
|
_resumeRepository = resumeRepository ?? throw new ArgumentNullException(nameof(resumeRepository));
|
|
_mailRepository = mailRepository;
|
|
_mailService = mailService;
|
|
_mapper = mapper;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get CV details of the candidate by candidateid.
|
|
/// </summary>
|
|
/// <param name="candidateId">The id of the candidate whose cv to get</param>
|
|
/// <returns>CV details of the candidate</returns>
|
|
/// <response code="200">Returns the requested cv of the candidate</response>
|
|
[HttpGet("{candidateId}")]
|
|
[Obsolete]
|
|
[ApiVersion(0.1, Deprecated = true)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<ActionResult<CvDto>> Get(int candidateId)
|
|
{
|
|
try
|
|
{
|
|
if (!await _candidateRepository.CandidateExistAsync(candidateId))
|
|
{
|
|
_logger.LogInformation($"Candidate with id {candidateId} wasn't found when accessing cv details.");
|
|
return NotFound();
|
|
}
|
|
|
|
var latestResumeForCandidate = await _resumeRepository.GetLatestResumeForCandidateAsync(candidateId, true);
|
|
|
|
return Ok(_mapper.Map<CvDto>(latestResumeForCandidate));
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical($"Exception while getting Cv details for the candidate with id {candidateId}.", ex);
|
|
return StatusCode(500, "A problem happened while handling your request.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get hobbies of the candidate by candidateid
|
|
/// </summary>
|
|
/// <param name="candidateId">The id of the candidate whose hobbies to get</param>
|
|
/// <returns>Hobbies of the candidate</returns>
|
|
/// <response code="200">Returns the requested hobbies of the candidate</response>
|
|
[HttpGet("GetHobbies/{candidateId}")]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<ActionResult<AboutDto>> GetHobbies(int candidateId)
|
|
{
|
|
try
|
|
{
|
|
if (!await _candidateRepository.CandidateExistAsync(candidateId))
|
|
{
|
|
_logger.LogInformation($"Candidate with id {candidateId} wasn't found when fetching about details.");
|
|
return NotFound();
|
|
}
|
|
|
|
var aboutDetails = await _resumeRepository.GetHobbiesAsync(candidateId);
|
|
|
|
return Ok(_mapper.Map<AboutDto>(aboutDetails));
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical($"Exception while getting about details for the candidate with id {candidateId}.", ex);
|
|
return StatusCode(500, "A problem happened while handling your request.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get Candidate details with social links by candidateid
|
|
/// </summary>
|
|
/// <param name="candidateId">The id of the candidate whose detials to get with social links</param>
|
|
/// <returns>Candidate details with sociallinks</returns>
|
|
/// <response code="200">Returns the requested candidate details with social links</response>
|
|
[HttpGet("GetCandidateWithSocialLinks/{candidateId}")]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<ActionResult<CandidateSocialLinksDto>> GetContact(int candidateId)
|
|
{
|
|
try
|
|
{
|
|
if (!await _candidateRepository.CandidateExistAsync(candidateId))
|
|
{
|
|
_logger.LogInformation($"Candidate with id {candidateId} wasn't found when fetching candidate with social links.");
|
|
return NotFound();
|
|
}
|
|
|
|
var contact = await _resumeRepository.GetCandidateWithSocialLinksAsync(candidateId);
|
|
|
|
return Ok(_mapper.Map<CandidateSocialLinksDto>(contact));
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical($"Exception while getting contact for the candidate with social links with id {candidateId}.", ex);
|
|
return StatusCode(500, "A problem happened while handling your request.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get Candidate resume by candidateid
|
|
/// </summary>
|
|
/// <param name="candidateId">The id of the candidate whose resume to get</param>
|
|
/// <returns>Candidate resume</returns>
|
|
/// <response code="200">Returns the requested candidate resume</response>
|
|
[HttpGet("GetResume/{candidateId}")]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<ActionResult<ResumeDto>> GetResume(int candidateId)
|
|
{
|
|
try
|
|
{
|
|
if (!await _candidateRepository.CandidateExistAsync(candidateId))
|
|
{
|
|
_logger.LogInformation($"Candidate with id {candidateId} wasn't found when fetching resume.");
|
|
return NotFound();
|
|
}
|
|
|
|
var resume = await _resumeRepository.GetResumeAsync(candidateId);
|
|
|
|
return Ok(_mapper.Map<ResumeDto>(resume));
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical($"Exception while getting resume for the candidate with id {candidateId}.", ex);
|
|
return StatusCode(500, "A problem happened while handling your request.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get Candidate projects by candidateid
|
|
/// </summary>
|
|
/// <param name="candidateId">The id of the candidate whose projects to get</param>
|
|
/// <returns>Candidate projects</returns>
|
|
/// <response code="200">Returns the requested candidate projects</response>
|
|
[HttpGet("GetProjects/{candidateId}")]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<ActionResult<ProjectsDto>> GetProjects(int candidateId)
|
|
{
|
|
try
|
|
{
|
|
if (!await _candidateRepository.CandidateExistAsync(candidateId))
|
|
{
|
|
_logger.LogInformation($"Candidate with id {candidateId} wasn't found when fetching projects.");
|
|
return NotFound();
|
|
}
|
|
|
|
var projects = await _resumeRepository.GetProjectsAsync(candidateId);
|
|
|
|
return Ok(_mapper.Map<ProjectsDto>(projects));
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical($"Exception while getting projects for the candidate with id {candidateId}.", ex);
|
|
return StatusCode(500, "A problem happened while handling your request.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get Candidate blog with posts by candidateid
|
|
/// </summary>
|
|
/// <param name="candidateId">The id of the candidate whose blog with posts to get</param>
|
|
/// <returns>Candidate blog with posts</returns>
|
|
/// <response code="200">Returns the requested candidate blog with posts</response>
|
|
[HttpGet("GetBlog/{candidateId}")]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<ActionResult<BlogDto>> GetBlog(int candidateId)
|
|
{
|
|
try
|
|
{
|
|
if (!await _candidateRepository.CandidateExistAsync(candidateId))
|
|
{
|
|
_logger.LogInformation($"Candidate with id {candidateId} wasn't found when fetching projects.");
|
|
return NotFound();
|
|
}
|
|
|
|
var blog = await _resumeRepository.GetBlogAsync(candidateId);
|
|
|
|
return Ok(_mapper.Map<BlogDto>(blog));
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical($"Exception while getting projects for the candidate with id {candidateId}.", ex);
|
|
return StatusCode(500, "A problem happened while handling your request.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send Message through email
|
|
/// </summary>
|
|
/// <param name="candidateId">The id of the candidate to whom the message should be sent</param>
|
|
/// <param name="message">Details of the Message to send to the candidate</param>
|
|
/// <returns>Returns the status code</returns>
|
|
/// <response code="204">Returns nothing</response>
|
|
[HttpPost("SendMessage/{candidateId}")]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
public async Task<ActionResult<bool>> SendMessage(int candidateId, [FromBody] MessageDto message)
|
|
{
|
|
try
|
|
{
|
|
var candidate = await _candidateRepository.GetCandidateAsync(candidateId);
|
|
if (candidate == null)
|
|
{
|
|
_logger.LogInformation($"Candidate with id {candidateId} wasn't found when fetching projects.");
|
|
return NotFound();
|
|
}
|
|
|
|
var candidateDto = _mapper.Map<CandidateDto>(candidate);
|
|
var messageSendDto = _mapper.Map<MessageSendDto>(message);
|
|
messageSendDto.ToEmail = candidateDto.Email;
|
|
messageSendDto.Name = candidateDto.DisplayName;
|
|
messageSendDto.CandidateId = candidateDto.CandidateId;
|
|
await _mailService.SendAsync(messageSendDto);
|
|
return Ok(true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical($"Exception while sending message from {message.Name}.", ex);
|
|
return StatusCode(500, "A problem happened while handling your request.");
|
|
}
|
|
}
|
|
}
|
|
}
|