using System.Net.Http.Headers; using LiquidCode.Tester.Common.Models; namespace LiquidCode.Tester.Gateway.Services; public class WorkerClientService : IWorkerClientService { private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly IConfiguration _configuration; public WorkerClientService( IHttpClientFactory httpClientFactory, ILogger logger, IConfiguration configuration) { _httpClientFactory = httpClientFactory; _logger = logger; _configuration = configuration; } public async Task SendToWorkerAsync(SubmitForTesterModel submit, string packagePath, bool deletePackageAfterSend) { var workerUrl = GetWorkerUrlForLanguage(submit.Language); _logger.LogInformation("Sending submit {SubmitId} to worker at {WorkerUrl}", submit.Id, workerUrl); try { var httpClient = _httpClientFactory.CreateClient(); using var form = new MultipartFormDataContent(); // Add submit metadata form.Add(new StringContent(submit.Id.ToString()), "Id"); form.Add(new StringContent(submit.MissionId.ToString()), "MissionId"); form.Add(new StringContent(submit.Language), "Language"); form.Add(new StringContent(submit.LanguageVersion), "LanguageVersion"); form.Add(new StringContent(submit.SourceCode), "SourceCode"); form.Add(new StringContent(submit.CallbackUrl), "CallbackUrl"); // Add package file var fileStream = File.OpenRead(packagePath); var fileContent = new StreamContent(fileStream); fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip"); form.Add(fileContent, "Package", Path.GetFileName(packagePath)); var response = await httpClient.PostAsync($"{workerUrl}/api/test", form); response.EnsureSuccessStatusCode(); _logger.LogInformation("Submit {SubmitId} sent successfully to worker", submit.Id); } catch (Exception ex) { _logger.LogError(ex, "Failed to send submit {SubmitId} to worker", submit.Id); throw; } finally { if (deletePackageAfterSend) { // Clean up package file when it is not needed anymore try { if (File.Exists(packagePath)) { File.Delete(packagePath); } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to delete package file {Path}", packagePath); } } } } private string GetWorkerUrlForLanguage(string language) { var workerUrl = language.ToLowerInvariant() switch { "c++" or "cpp" => _configuration["Workers:Cpp"], "java" => _configuration["Workers:Java"], "kotlin" => _configuration["Workers:Kotlin"], "c#" or "csharp" => _configuration["Workers:CSharp"], "python" => _configuration["Workers:Python"], _ => throw new NotSupportedException($"Language {language} is not supported") }; if (string.IsNullOrEmpty(workerUrl)) { throw new InvalidOperationException($"Worker URL for language {language} is not configured"); } return workerUrl; } }