using PostFundManagement.Domain; using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Api; using PostFundManagement.Domain.Extensions; using PostFundManagement.Infrastructure.Database; namespace PostFundManagement.Api.Endpoints.Evidence; [ApiVersionTarget(1)] public class UploadEvidenceFilesEndpoint : IEndpoint { public void Map(IEndpointRouteBuilder builder) { builder.MapPost("api/evidence/files", async (HttpRequest request, ClaimsPrincipal principal, IDbContextFactory contextFactory, [FromKeyedServices(Constants.EvidenceS3SettingsSection)] IS3Service s3Service, CancellationToken cancellationToken = default) => { var sidClaim = principal.FindFirst("sid")?.Value ?? principal.FindFirst(ClaimTypes.Sid)?.Value; if (!Guid.TryParse(sidClaim, out var userId)) return Results.BadRequest("Missing or invalid 'sid' claim."); if (!request.HasFormContentType) return Results.BadRequest("Request must be a multipart form."); var form = await request.ReadFormAsync(cancellationToken); var file = form.Files.GetFile("file"); if (file == null || file.Length == 0) return Results.BadRequest("No file uploaded or file is empty."); _ = long.TryParse(form["projectId"], out var projectId); _ = long.TryParse(form["milestoneId"], out var milestoneId); _ = long.TryParse(form["indicatorId"], out var indicatorId); _ = long.TryParse(form["activityId"], out var activityId); _ = Enum.TryParse(form["type"], out var type); using var stream = file.OpenReadStream(); var uploadResult = await s3Service.UploadFileAsync(file.FileName, stream, file.ContentType, cancellationToken); if (uploadResult.IsFailed) return Results.BadRequest(uploadResult.Errors.FirstOrDefault()?.Message ?? "File upload failed."); var documentUrl = uploadResult.Value; using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var evidence = new Domain.Entities.Evidence { ProjectId = projectId, MilestoneId = milestoneId, IndicatorId = indicatorId, ActivityId = activityId, CreatedBy = userId, CreatedAt = DateTime.UtcNow, Version = 1, Type = type, DocumentUrl = documentUrl, Status = ApprovalStatus.Pending }; context.Evidences.Add(evidence); return await context.SaveChangesAsync(cancellationToken) > 0 ? Results.Created($"api/evidence/files/{evidence.Id}", evidence) : Results.BadRequest("Evidence upload failed"); }) .RequireAuthorization() .DisableAntiforgery() .WithDescription("Upload supporting evidence file to S3 and register metadata (M14)") .WithName(typeof(UploadEvidenceFilesEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status201Created) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status401Unauthorized) .WithTags(EndpointTags.Evidence); } }