feat(api): add PdfSignatureController and wire signing DI

This commit is contained in:
2026-10-22 02:26:21 +02:00
parent f452821f22
commit 3b21dbc8f7
3 changed files with 78 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
using DocumentOperator.Application.Common.Dto;
using DocumentOperator.Application.SignPdf;
using DocumentService.Domain.Common.Exceptions;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace DocumentService.API.Controllers;
/// <summary>
/// Controller for PDF digital signing (PKCS#11 / company certificate).
/// </summary>
[ApiController]
[Route("api/pdf/signature")]
public class PdfSignatureController(IMediator mediator) : ControllerBase
{
/// <summary>
/// Digitally signs a PDF using the configured PKCS#11 signing infrastructure.
/// </summary>
/// <param name="request">Base64-encoded PDF, optional signature image, and signing options</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Signed PDF</returns>
/// <response code="200">PDF signed successfully - returns signed PDF</response>
/// <response code="400">Invalid input (Base64 format error, invalid appearance)</response>
/// <response code="500">Internal server error during signing</response>
[HttpPost("sign")]
[Consumes("application/json")]
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> Sign([FromBody] SignPdfRequest request, CancellationToken cancellationToken)
{
byte[] signedPdf;
try
{
signedPdf = await mediator.Send(new SignPdfCommand
{
InputPdf = new MemoryStream(Convert.FromBase64String(request.Base64Pdf)),
Options = request.Options,
SignatureImage = string.IsNullOrWhiteSpace(request.Base64SignatureImage)
? null
: new MemoryStream(Convert.FromBase64String(request.Base64SignatureImage))
}, cancellationToken);
}
catch (FormatException ex)
{
throw new BadRequestException("Invalid Base64 format: " + ex.Message);
}
return File(signedPdf, "application/pdf", "signed.pdf");
}
}
/// <summary>
/// Request DTO for PDF signing via JSON payload.
/// </summary>
public sealed class SignPdfRequest
{
/// <summary>
/// Base64-encoded PDF to sign.
/// </summary>
public required string Base64Pdf { get; init; }
/// <summary>
/// Optional Base64-encoded signature image (PNG/JPEG).
/// </summary>
public string? Base64SignatureImage { get; init; }
/// <summary>
/// Signing options (metadata + appearance).
/// </summary>
public required PdfSignOptionsDto Options { get; init; }
}

View File

@@ -36,6 +36,8 @@
<ProjectReference Include="..\DocumentService.Client\DocumentService.Client.csproj" />
<ProjectReference Include="..\DocumentService.Domain\DocumentService.Domain.csproj" />
<ProjectReference Include="..\DocumentService.Infrastructure\DocumentService.Infrastructure.csproj" />
<ProjectReference Include="..\DocumentOperator.Application\DocumentOperator.Application.csproj" />
<ProjectReference Include="..\DocumentService.Infrastructure.Crypt\DocumentService.Infrastructure.Crypt.csproj" />
</ItemGroup>
</Project>

View File

@@ -7,8 +7,10 @@ using DocumentService.Infrastructure.Configuration;
using DocumentService.Application;
using DocumentService.Application.Common.Configuration;
using DocumentService.Infrastructure;
using DocumentService.Infrastructure.Crypt.DependencyInjection;
using DocumentService.API.Middleware;
using DocumentService.API.Configuration;
using DocumentOperator.Application;
var builder = WebApplication.CreateBuilder(args);
@@ -50,6 +52,8 @@ try
// ========================================
builder.Services.AddApplication(builder.Configuration); // Application Layer (MediatR, FluentValidation, Behaviors)
builder.Services.AddInfrastructure(); // Infrastructure Layer (DevExpress, Services)
builder.Services.AddDocumentOperatorApplication(builder.Configuration); // PDF signing Application (MediatR)
builder.Services.AddCryptInfrastructure(builder.Configuration); // PKCS#11 PDF signing (DevExpress)
builder.Services.AddControllers(); // Controllers (Controller-based API)
builder.Services.AddEndpointsApiExplorer();