Files
myAi/api/Controllers/CvMatcherController.cs
T
claude 91cdb536b1
Build and Push Docker Images / build (push) Successful in 32s
Changes
2026-05-06 12:48:44 +03:00

147 lines
5.9 KiB
C#

using Api.Models.Requests;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace Api.Controllers;
[ApiController]
[Route("api/cv-matcher")]
[EnableRateLimiting("cv-matcher")]
public sealed class RagController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
private readonly ILogger<RagController> _logger;
public RagController(
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
ILogger<RagController> logger)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
_logger = logger;
}
[HttpPost("upload")]
[RequestSizeLimit(8 * 1024 * 1024)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status502BadGateway)]
public async Task<IActionResult> UploadCv(
[FromForm(Name = "cv")] IFormFile? cv,
[FromForm] bool gdprConsent,
CancellationToken ct)
{
if (cv is null)
{
return BadRequest(new { error = "Missing CV PDF." });
}
var baseUrl = GetCvMatcherBaseUrl();
if (string.IsNullOrWhiteSpace(baseUrl))
{
_logger.LogError("CvMatcherApi:BaseUrl is not configured. The public API cannot proxy CV upload requests.");
return StatusCode(StatusCodes.Status502BadGateway, new { error = "CV matcher API is not configured." });
}
try
{
_logger.LogInformation("Proxying CV upload to cv-matcher-api. FileName={FileName}, Size={SizeBytes}, GdprConsent={GdprConsent}",
cv.FileName, cv.Length, gdprConsent);
using var client = CreateCvMatcherClient(baseUrl);
using var form = new MultipartFormDataContent();
await using var stream = cv.OpenReadStream();
using var fileContent = new StreamContent(stream);
fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
form.Add(fileContent, "cv", cv.FileName);
form.Add(new StringContent(gdprConsent.ToString().ToLowerInvariant()), "gdprConsent");
using var response = await client.PostAsync("api/cv/upload", form, ct);
return await ProxyResponseAsync(response, ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
_logger.LogWarning("CV upload proxy request was cancelled by the client.");
return StatusCode(499, new { error = "Request cancelled." });
}
catch (Exception ex)
{
_logger.LogError(ex, "CV upload proxy request failed.");
return StatusCode(StatusCodes.Status502BadGateway, new { error = "CV matcher API request failed." });
}
}
[HttpPost("match-job")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status502BadGateway)]
public async Task<IActionResult> MatchJob([FromBody] JobMatchRequest request, CancellationToken ct)
{
var baseUrl = GetCvMatcherBaseUrl();
if (string.IsNullOrWhiteSpace(baseUrl))
{
_logger.LogError("CvMatcherApi:BaseUrl is not configured. The public API cannot proxy job matching requests.");
return StatusCode(StatusCodes.Status502BadGateway, new { error = "CV matcher API is not configured." });
}
try
{
_logger.LogInformation("Proxying job match request to cv-matcher-api. CvDocumentId={CvDocumentId}, HasJobUrl={HasJobUrl}, HasJobDescription={HasJobDescription}",
request.CvDocumentId,
!string.IsNullOrWhiteSpace(request.JobUrl),
!string.IsNullOrWhiteSpace(request.JobDescription));
using var client = CreateCvMatcherClient(baseUrl);
var json = JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.Web));
using var response = await client.PostAsync(
"api/cv/match-job",
new StringContent(json, Encoding.UTF8, "application/json"),
ct);
return await ProxyResponseAsync(response, ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
_logger.LogWarning("Job match proxy request was cancelled by the client.");
return StatusCode(499, new { error = "Request cancelled." });
}
catch (Exception ex)
{
_logger.LogError(ex, "Job match proxy request failed.");
return StatusCode(StatusCodes.Status502BadGateway, new { error = "CV matcher API request failed." });
}
}
private string GetCvMatcherBaseUrl() => _configuration["CvMatcherApi:BaseUrl"] ?? string.Empty;
private HttpClient CreateCvMatcherClient(string baseUrl)
{
var client = _httpClientFactory.CreateClient("CvMatcherApi");
client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/");
var key = _configuration["CvMatcherApi:InternalApiKey"];
if (!string.IsNullOrWhiteSpace(key) && !client.DefaultRequestHeaders.Contains("X-Internal-Api-Key"))
{
client.DefaultRequestHeaders.Add("X-Internal-Api-Key", key);
}
return client;
}
private static async Task<ContentResult> ProxyResponseAsync(HttpResponseMessage response, CancellationToken ct)
{
var body = await response.Content.ReadAsStringAsync(ct);
return new ContentResult
{
StatusCode = (int)response.StatusCode,
Content = body,
ContentType = response.Content.Headers.ContentType?.ToString() ?? "application/json"
};
}
}