/
afanasevn
/
PdfEncoder
Обзор
Документация
Войти
/
afanasevn
/
PdfEncoder
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/PdfEncoder.Api/Controllers/DocumentsController.cs
161 строка
5 KB
IBS\NAfanasev
Full async product flow
01 июл 2026, 17:49
01 июл 2026, 17:49
d25833e
Код
Авторство
О чём код?
using Microsoft.AspNetCore.Mvc; using PdfEncoder.Application.Abstractions; using PdfEncoder.Application.Models; using PdfEncoder.Domain.Errors; namespace PdfEncoder.Api.Controllers; /// <summary> /// REST API для upload, polling и history документов. /// </summary> [ApiController] [Route("api/documents")] public sealed class DocumentsController( IDocumentJobService documentJobService, IWebHostEnvironment environment) : ControllerBase { /// <summary> /// Загружает PDF и запускает async/sync parse pipeline. /// </summary> [HttpPost("parse")] [RequestSizeLimit(21 * 1024 * 1024)] [ProducesResponseType(typeof(DocumentJobResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(DocumentJobResponse), StatusCodes.Status202Accepted)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status413PayloadTooLarge)] public async Task<IActionResult> Parse( IFormFile? file, [FromForm] string documentType = "auto", [FromForm] string language = "ru", [FromForm] bool async = true, CancellationToken ct = default) { if (file is null || file.Length == 0) { return Problem( detail: "File is empty.", statusCode: StatusCodes.Status400BadRequest, title: "Invalid file", type: "https://pdfencoder.local/errors/invalid-file"); } await using var stream = file.OpenReadStream(); using var memory = new MemoryStream(); await stream.CopyToAsync(memory, ct); try { var submitResult = await documentJobService.SubmitAsync(new SubmitDocumentJobRequest { Content = memory.ToArray(), FileName = file.FileName, Options = new ParseOptions { DocumentType = documentType, Language = language, Async = async } }, ct); return StatusCode(submitResult.StatusCode, submitResult.Response); } catch (PdfValidationException ex) { return Problem( detail: ex.Message, statusCode: ex.StatusCode, title: "Invalid file", type: $"https://pdfencoder.local/errors/{ex.ErrorCode}"); } catch (LlmException ex) { return Problem( detail: ex.Message, statusCode: ex.StatusCode, title: "LLM error", type: $"https://pdfencoder.local/errors/{ex.ErrorCode}"); } } /// <summary> /// Возвращает статус job и результат (polling endpoint). /// </summary> [HttpGet("{id:guid}")] [ProducesResponseType(typeof(DocumentJobResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] public async Task<IActionResult> Get( Guid id, CancellationToken ct) { var response = await documentJobService.GetAsync(id, ct); if (response is null) { return NotFoundProblem("document-not-found", "Document not found."); } return Ok(response); } /// <summary> /// Возвращает history с pagination. /// </summary> [HttpGet] [ProducesResponseType(typeof(DocumentListResponse), StatusCodes.Status200OK)] public async Task<IActionResult> List( [FromQuery] int limit = 20, [FromQuery] int offset = 0, [FromQuery] string? status = null, CancellationToken ct = default) { Domain.Enums.DocumentJobStatus? statusFilter = null; if (!string.IsNullOrWhiteSpace(status) && Enum.TryParse<Domain.Enums.DocumentJobStatus>(status, ignoreCase: true, out var parsed)) { statusFilter = parsed; } var response = await documentJobService.ListAsync(new ListDocumentsQuery { Limit = limit, Offset = offset, Status = statusFilter }, ct); return Ok(response); } /// <summary> /// Возвращает raw DocumentContext (Development only). /// </summary> [HttpGet("{id:guid}/raw-context")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] public async Task<IActionResult> GetRawContext( Guid id, CancellationToken ct) { if (!environment.IsDevelopment()) { return NotFoundProblem("not-found", "Raw context is available in Development only."); } var context = await documentJobService.GetRawContextAsync(id, ct); if (context is null) { return NotFoundProblem("document-not-found", "Document not found."); } return Ok(context); } private ObjectResult NotFoundProblem( string errorCode, string detail) { return Problem( detail: detail, statusCode: StatusCodes.Status404NotFound, title: "Not found", type: $"https://pdfencoder.local/errors/{errorCode}"); } }