diff --git a/Elecciones-Web/src/Elecciones.Infrastructure/Services/CurlElectoralApiService.cs b/Elecciones-Web/src/Elecciones.Infrastructure/Services/CurlElectoralApiService.cs new file mode 100644 index 0000000..ce33bb5 --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Infrastructure/Services/CurlElectoralApiService.cs @@ -0,0 +1,83 @@ +using Elecciones.Core.DTOs; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.Json; +using System.Threading.Tasks; +using static Elecciones.Core.DTOs.BancaDto; + +namespace Elecciones.Infrastructure.Services; + +// Implementación de emergencia que usa el comando 'curl' del sistema operativo +public class CurlElectoralApiService : IElectoralApiService +{ + private readonly ILogger _logger; + private readonly IConfiguration _configuration; + private readonly string _baseUrl; + + public CurlElectoralApiService(ILogger logger, IConfiguration configuration) + { + _logger = logger; + _configuration = configuration; + _baseUrl = _configuration["ElectoralApi:BaseUrl"] ?? ""; + } + + private async Task<(int, string)> ExecuteCurlCommand(string arguments) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "curl", + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; + + process.Start(); + string output = await process.StandardOutput.ReadToEndAsync(); + string error = await process.StandardError.ReadToEndAsync(); + await process.WaitForExitAsync(); + + if (process.ExitCode != 0) + { + _logger.LogError("Error al ejecutar curl. Exit Code: {ExitCode}. Error: {Error}", process.ExitCode, error); + return (-1, error); + } + + // Extraemos el código de estado HTTP del final del stderr si usamos -w + // (Simplificación: asumimos 200 si el ExitCode es 0) + return (200, output); + } + + public async Task GetAuthTokenAsync() + { + var username = _configuration["ElectoralApi:Username"]; + var password = _configuration["ElectoralApi:Password"]; + var arguments = $"-s -H \"username: {username}\" -H \"password: {password}\" \"{_baseUrl}/api/createtoken\""; + + var (status, output) = await ExecuteCurlCommand(arguments); + if (status != 200) return null; + + var tokenResponse = JsonSerializer.Deserialize(output); + return tokenResponse?.Data?.AccessToken; + } + + // Implementa los demás métodos de la interfaz usando el mismo patrón + // ... + // Por ahora, solo necesitamos GetAuthTokenAsync para validar la conexión. + public Task?> GetCategoriasAsync(string authToken) => Task.FromResult?>(null); + public Task?> GetCatalogoCompletoAsync(string authToken) => Task.FromResult?>(null); + public Task?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId) => Task.FromResult?>(null); + public Task GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId) => Task.FromResult(null); + public Task GetBancasAsync(string authToken, string distritoId, string seccionId) => Task.FromResult(null); + public Task?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId) => Task.FromResult?>(null); + public Task GetTelegramaFileAsync(string authToken, string mesaId) => Task.FromResult(null); + public Task GetResumenAsync(string authToken, string distritoId) => Task.FromResult(null); + public Task GetEstadoRecuentoGeneralAsync(string authToken, string distritoId) => Task.FromResult(null); +} \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs index 7785234..f4f4891 100644 --- a/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+75ff9d5593b957c5ae0d08223a689a95181172d5")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/Elecciones-Web/src/Elecciones.Worker/Dockerfile b/Elecciones-Web/src/Elecciones.Worker/Dockerfile index 2ceca7d..fca5bd2 100644 --- a/Elecciones-Web/src/Elecciones.Worker/Dockerfile +++ b/Elecciones-Web/src/Elecciones.Worker/Dockerfile @@ -19,6 +19,12 @@ RUN dotnet publish "Elecciones.Worker.csproj" -c Release -o /app/publish # --- Etapa 2: Final --- # Usamos la imagen de runtime genérica de .NET, ya que no necesita ASP.NET FROM mcr.microsoft.com/dotnet/runtime:9.0 AS final +# Instalamos curl y ca-certificates para las peticiones HTTP +USER root +RUN apt-get update && apt-get install -y curl ca-certificates +USER app +# --- FIN DE LA SECCIÓN --- + WORKDIR /app COPY --from=build /app/publish . ENTRYPOINT ["dotnet", "Elecciones.Worker.dll"] \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Worker/Program.cs b/Elecciones-Web/src/Elecciones.Worker/Program.cs index 117867d..9b5254a 100644 --- a/Elecciones-Web/src/Elecciones.Worker/Program.cs +++ b/Elecciones-Web/src/Elecciones.Worker/Program.cs @@ -32,38 +32,7 @@ builder.Services.AddDbContext(options => #if DEBUG builder.Services.AddSingleton(); #else -// --- SECCIÓN MODIFICADA (FINAL) --- -builder.Services.AddHttpClient("ElectoralApiClient", client => -{ - var baseUrl = builder.Configuration["ElectoralApi:BaseUrl"]; - if (!string.IsNullOrEmpty(baseUrl)) - { - client.BaseAddress = new Uri(baseUrl); - } -}) -.ConfigurePrimaryHttpMessageHandler(() => -{ - return new SocketsHttpHandler - { - SslOptions = new SslClientAuthenticationOptions - { - // Forzamos el protocolo TLS 1.3 - EnabledSslProtocols = SslProtocols.Tls13, - - // --- ¡¡¡LA LÍNEA CLAVE CORREGIDA!!! --- - // Forzamos explícitamente los únicos 3 cipher suites que el servidor acepta. - CipherSuitesPolicy = new CipherSuitesPolicy(new[] - { - TlsCipherSuite.TLS_AES_128_GCM_SHA256, - TlsCipherSuite.TLS_AES_256_GCM_SHA384, - TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256 - }) - } - }; -}); - -builder.Services.AddSingleton(); -// --- FIN DE LA SECCIÓN MODIFICADA --- +builder.Services.AddSingleton(); #endif builder.Services.AddHostedService(); diff --git a/Elecciones-Web/src/Elecciones.Worker/appsettings.json b/Elecciones-Web/src/Elecciones.Worker/appsettings.json index 07c8e4c..18fb995 100644 --- a/Elecciones-Web/src/Elecciones.Worker/appsettings.json +++ b/Elecciones-Web/src/Elecciones.Worker/appsettings.json @@ -4,5 +4,10 @@ "Default": "Information", "Microsoft.Hosting.Lifetime": "Information" } + }, + "ElectoralApi": { + "BaseUrl": "https://api.eleccionesbonaerenses.gba.gob.ar", + "Username": "30500094156@elecciones2025.onmicrosoft.com", + "Password": "PTP847elec" } } \ No newline at end of file