diff --git a/BMA.EHR.Retirement.Service/Services/RetirementReportService.cs b/BMA.EHR.Retirement.Service/Services/RetirementReportService.cs index d3253afd..82bf2b27 100644 --- a/BMA.EHR.Retirement.Service/Services/RetirementReportService.cs +++ b/BMA.EHR.Retirement.Service/Services/RetirementReportService.cs @@ -170,7 +170,7 @@ namespace BMA.EHR.Retirement.Service.Services } else { - // PROD: Disabled local LibreOffice conversion + // // PROD: Disabled local LibreOffice conversion // await ConvertToPdfLocallyAsync(docxPath, pdfPath, timeout); throw new NotSupportedException("LibreOffice conversion is disabled."); } @@ -184,89 +184,66 @@ namespace BMA.EHR.Retirement.Service.Services private async Task ConvertToPdfViaDockerAsync(string docxPath, string pdfPath, int timeout) { - var container = _configuration["LibreOffice:DockerContainer"] ?? "libreoffice"; - var workingDir = _configuration["LibreOffice:WorkingDirectory"] ?? "/app/libreoffice/files"; - var dockerFilesPath = _configuration["LibreOffice:DockerFilesPath"] ?? "/files"; - var arguments = _configuration["LibreOffice:Arguments"] ?? "--headless --convert-to pdf --nologo --norestore"; - - // Ensure working directory exists - if (!Directory.Exists(workingDir)) - { - Directory.CreateDirectory(workingDir); - } - - // Copy file to shared directory + var inputDir = _configuration["LibreOffice:InputDirectory"] ?? "/app/libreoffice/input"; + var outputDir = _configuration["LibreOffice:OutputDirectory"] ?? "/app/libreoffice/output"; var fileName = Path.GetFileName(docxPath); - var sharedDocxPath = Path.Combine(workingDir, fileName); - var sharedPdfName = Path.ChangeExtension(fileName, ".pdf"); - var sharedPdfPath = Path.Combine(workingDir, sharedPdfName); + var pdfName = Path.ChangeExtension(fileName, ".pdf"); - await File.WriteAllBytesAsync(sharedDocxPath, await File.ReadAllBytesAsync(docxPath)); + // Ensure directories exist + Directory.CreateDirectory(inputDir); + Directory.CreateDirectory(outputDir); - // Run LibreOffice inside Docker container - var dockerCmd = $"docker exec {container} libreoffice {arguments} --outdir {dockerFilesPath} {dockerFilesPath}/{fileName}"; - - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - var shell = isWindows ? "cmd.exe" : "/bin/sh"; - var shellArg = isWindows ? "/c" : "-c"; + // Copy file to input folder (LibreOffice watcher will pick it up) + var inputPath = Path.Combine(inputDir, fileName).Replace('\\', '/'); + var outputPath = Path.Combine(outputDir, pdfName).Replace('\\', '/'); - var psi = new ProcessStartInfo + _logger.LogInformation("📤 Sending file to LibreOffice: {FileName}", fileName); + await File.WriteAllBytesAsync(inputPath, await File.ReadAllBytesAsync(docxPath)); + + // Wait for LibreOffice to convert (file watcher handles it) + var stopwatch = System.Diagnostics.Stopwatch.StartNew(); + var pollInterval = TimeSpan.FromMilliseconds(500); + + while (stopwatch.ElapsedMilliseconds < timeout) { - FileName = shell, - Arguments = $"{shellArg} \"{dockerCmd}\"", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; + if (File.Exists(outputPath)) + { + _logger.LogInformation("✅ PDF received: {PdfName} (took {ElapsedMs}ms)", pdfName, stopwatch.ElapsedMilliseconds); - using var process = Process.Start(psi); - var exited = process.WaitForExit(timeout); + await File.WriteAllBytesAsync(pdfPath, await File.ReadAllBytesAsync(outputPath)); - if (!exited) - { - process.Kill(entireProcessTree: true); - throw new TimeoutException($"LibreOffice Docker conversion timed out after {timeout}ms"); + // Cleanup + try + { + if (File.Exists(outputPath)) File.Delete(outputPath); + _logger.LogDebug("🗑️ Cleaned up output file: {PdfName}", pdfName); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to cleanup output file"); + } + + return; + } + + await Task.Delay(pollInterval); } - if (process.ExitCode != 0) - { - var error = await process.StandardError.ReadToEndAsync(); - throw new Exception($"LibreOffice Docker conversion failed: {error}"); - } - - // Copy result back - if (!File.Exists(sharedPdfPath)) - { - throw new FileNotFoundException($"PDF not generated in shared directory: {sharedPdfPath}"); - } - - await File.WriteAllBytesAsync(pdfPath, await File.ReadAllBytesAsync(sharedPdfPath)); - - // Cleanup shared files - try - { - if (File.Exists(sharedDocxPath)) File.Delete(sharedDocxPath); - if (File.Exists(sharedPdfPath)) File.Delete(sharedPdfPath); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Failed to cleanup shared files"); - } + throw new TimeoutException($"LibreOffice conversion timed out after {timeout}ms. File not found: {outputPath}"); } - // PROD: Disabled local LibreOffice conversion + // // PROD: Disabled local LibreOffice conversion // private async Task ConvertToPdfLocallyAsync(string docxPath, string pdfPath, int timeout) // { // var libreOfficePath = _configuration["LibreOffice:Path"] ?? GetDefaultLibreOfficePath(); // var arguments = _configuration["LibreOffice:Arguments"] ?? "--headless --convert-to pdf --nologo --norestore"; // var outputDir = Path.GetDirectoryName(pdfPath); - // + // if (string.IsNullOrEmpty(outputDir)) // { // throw new DirectoryNotFoundException("Output directory cannot be determined"); // } - // + // var psi = new ProcessStartInfo // { // FileName = libreOfficePath, @@ -276,16 +253,16 @@ namespace BMA.EHR.Retirement.Service.Services // RedirectStandardError = true, // CreateNoWindow = true // }; - // + // using var process = Process.Start(psi); // var exited = process.WaitForExit(timeout); - // + // if (!exited) // { // process.Kill(entireProcessTree: true); // throw new TimeoutException($"LibreOffice conversion timed out after {timeout}ms"); // } - // + // if (process.ExitCode != 0) // { // var error = await process.StandardError.ReadToEndAsync(); @@ -293,7 +270,7 @@ namespace BMA.EHR.Retirement.Service.Services // } // } - // PROD: Disabled local LibreOffice path detection + // // PROD: Disabled local LibreOffice path detection // private static string GetDefaultLibreOfficePath() // { // if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -304,11 +281,11 @@ namespace BMA.EHR.Retirement.Service.Services // @"C:\Program Files (x86)\LibreOffice\program\soffice.exe", // @"C:\Program Files\LibreOffice\program\soffice.com" // }; - // + // return possiblePaths.FirstOrDefault(File.Exists) // ?? throw new FileNotFoundException("LibreOffice not found. Please install LibreOffice or configure the path in appsettings.json"); // } - // + // // Linux/Docker: use default path // return "libreoffice"; // }