using DXPBLL.Auth; using FastEndpoints; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.DTO.Framework.Enum; using Microsoft.Extensions.Logging; namespace DXPSL.Common; // Base class for every JWT-protected DXP endpoint. Deliberately does NOT inherit // GB5Shared.FastEndPoint.BaseEndpoint — that base class's // GetLoginDTOFromRequest() trusts a client-supplied "Login" property on the request body, // which is the exact anti-pattern DXP must avoid for an internet-facing, non-employee // audience (see Part 2 of the DXP plan). Instead, the caller identity comes ONLY from // HttpContext.User.Claims, populated by ASP.NET Core's JWT bearer middleware after it has // cryptographically verified the token — never from anything the client sends in the request. // // Endpoints do not call AllowAnonymous(), so FastEndpoints' default auth requirement applies — // a request without a valid Bearer token never reaches ExecuteAsync. public abstract class DXPBaseEndpoint : Endpoint where TRequest : notnull { protected new ILogger Logger => Resolve>>(); protected abstract Task> ExecuteAsync(TRequest req, DXPCallerContext caller, CancellationToken ct); protected DXPCallerContext GetCaller() => DXPCallerContext.FromClaims(HttpContext.User.Claims); public override async Task HandleAsync(TRequest req, CancellationToken ct) { try { var caller = GetCaller(); var response = await ExecuteAsync(req, caller, ct); await Send.ResponseAsync((TResponse)(object)response, cancellation: ct); } catch (ArgumentException ex) { await Send.ResponseAsync((TResponse)(object)new ResponseStandardDTO { Status = FrameworkEnumDTO.ResponseStatus.Failed, ErrorBody = ex.Message }, statusCode: 400, cancellation: ct); } catch (InvalidOperationException ex) { await Send.ResponseAsync((TResponse)(object)new ResponseStandardDTO { Status = FrameworkEnumDTO.ResponseStatus.Failed, ErrorBody = ex.Message }, statusCode: 400, cancellation: ct); } catch (Exception ex) { Logger.LogError(ex, "[{Endpoint}] {ExceptionType}: {Message}", GetType().Name, ex.GetType().Name, ex.Message); await Send.ResponseAsync((TResponse)(object)new ResponseStandardDTO { Status = FrameworkEnumDTO.ResponseStatus.Failed, ErrorBody = "An unexpected error occurred. Please try again." }, statusCode: 500, cancellation: ct); } } }