using FastEndpoints; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.FastEndPoint; using RecruitmentBLL.CandidatePortal; using static GB5Shared.GB5Constant.Constant; namespace RecruitmentSL.EndPoints.CandidatePortal { // Anonymous — this is the credential check itself (same posture as DXPSL.Endpoints.Auth. // Login). Validates against DXP, resolves/creates the candidate's CandidateId, and returns a // CandidatePortal access token — see ICandidatePortalBLL.LoginAsync's doc comment for why // this token is minted by Recruitment itself rather than relaying DXP's own login token. public class Login : BaseEndpoint> { private readonly ICandidatePortalBLL _BLL; public Login(ICandidatePortalBLL bll) => _BLL = bll; public record LoginBody(string Email, string Password); public record LoginParameters( [property: FromHeader] string Login, [property: FromBody] LoginBody Body ); public override void Configure() { Post("/CandidatePortal/Login"); AllowAnonymous(); } protected override string? GetCacheKey(LoginParameters req, LoginDTO login) => null; protected override async Task> ExecuteAsync( LoginParameters req, LoginDTO login, CancellationToken ct) { var result = await _BLL.LoginAsync(req.Body.Email, req.Body.Password, login, ct) .ConfigureAwait(false); if (!result.Success) { // Deliberately generic — never reveal whether the email exists (same posture as // DXP's own Login.cs). return await GB5Shared.ResponseStandard.Response.CreateErrorResponse( result.Error ?? "Invalid email or password.", CacheKeyLevel.NOT_REQUIRED, login, statusCode: 401); } return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse( result, CacheKeyLevel.NOT_REQUIRED, login); } } }