From 92e2767f76d82d33f33b6071f42ea51fe649d68b Mon Sep 17 00:00:00 2001 From: administrator Date: Sun, 19 Apr 2026 21:42:40 +0200 Subject: [PATCH] =?UTF-8?q?Add=20FolderWatcherService=20-=20PDF-Druck=20pe?= =?UTF-8?q?r=20Ordner=C3=BCberwachung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MailPrint/MailPrintOptions.cs | 15 +++ MailPrint/MailPrintWorker.cs | 15 ++- MailPrint/Program.cs | 1 + MailPrint/Services/FolderWatcherService.cs | 141 +++++++++++++++++++++ 4 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 MailPrint/Services/FolderWatcherService.cs diff --git a/MailPrint/MailPrintOptions.cs b/MailPrint/MailPrintOptions.cs index 28de2fd..543b094 100644 --- a/MailPrint/MailPrintOptions.cs +++ b/MailPrint/MailPrintOptions.cs @@ -21,6 +21,9 @@ public class MailPrintOptions public List BlockedSenders { get; set; } = new(); public WebApiOptions WebApi { get; set; } = new(); + + /// Ordner-Überwachung: PDFs in diesen Ordnern werden automatisch gedruckt. + public List FolderWatchers { get; set; } = new(); } public class PrinterProfile @@ -54,3 +57,15 @@ public class WebApiOptions public bool BindAllInterfaces { get; set; } = false; public string ApiKey { get; set; } = ""; } + +public class FolderWatcher +{ + public string Name { get; set; } = ""; + /// Zu überwachender Ordner (vollständiger Pfad) + public string Path { get; set; } = ""; + /// Auch Unterordner überwachen + public bool IncludeSubfolders { get; set; } = false; + /// Datei nach erfolgreichem Druck löschen + public bool DeleteAfterPrint { get; set; } = true; + public string PrinterProfileName { get; set; } = ""; +} diff --git a/MailPrint/MailPrintWorker.cs b/MailPrint/MailPrintWorker.cs index 90aa01c..bfe5374 100644 --- a/MailPrint/MailPrintWorker.cs +++ b/MailPrint/MailPrintWorker.cs @@ -8,29 +8,38 @@ public class MailPrintWorker : BackgroundService private readonly ILogger _logger; private readonly MailFetchService _mailService; private readonly PrintService _printService; + private readonly FolderWatcherService _folderService; private readonly MailPrintOptions _options; - public MailPrintWorker(ILogger logger, MailFetchService mailService, - PrintService printService, IOptions options) + public MailPrintWorker( + ILogger logger, + MailFetchService mailService, + PrintService printService, + FolderWatcherService folderService, + IOptions options) { _logger = logger; _mailService = mailService; _printService = printService; + _folderService = folderService; _options = options.Value; } protected override async Task ExecuteAsync(CancellationToken ct) { + // Ordner-Überwachung starten (event-basiert, läuft dauerhaft) + _folderService.Start(); + if (_options.Accounts.Count == 0) { _logger.LogWarning("Keine Postfächer konfiguriert – Mail-Polling deaktiviert."); + await Task.Delay(Timeout.Infinite, ct).ConfigureAwait(false); return; } _logger.LogInformation("{Count} Postfach/Postfächer konfiguriert, Intervall: {Interval}s", _options.Accounts.Count, _options.PollIntervalSeconds); - // Pro Account einen eigenen Poll-Loop starten var tasks = _options.Accounts.Select(account => PollAccountAsync(account, ct)); await Task.WhenAll(tasks); } diff --git a/MailPrint/Program.cs b/MailPrint/Program.cs index 6a299d1..3e3c5a8 100644 --- a/MailPrint/Program.cs +++ b/MailPrint/Program.cs @@ -46,6 +46,7 @@ try builder.Services.Configure(builder.Configuration.GetSection("MailPrint")); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddHostedService(); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); diff --git a/MailPrint/Services/FolderWatcherService.cs b/MailPrint/Services/FolderWatcherService.cs new file mode 100644 index 0000000..7a8c6ea --- /dev/null +++ b/MailPrint/Services/FolderWatcherService.cs @@ -0,0 +1,141 @@ +using Microsoft.Extensions.Options; + +namespace MailPrint.Services; + +public class FolderWatcherService : IDisposable +{ + private readonly ILogger _logger; + private readonly PrintService _printService; + private readonly MailPrintOptions _options; + private readonly List _watchers = new(); + + public FolderWatcherService( + ILogger logger, + PrintService printService, + IOptions options) + { + _logger = logger; + _printService = printService; + _options = options.Value; + } + + public void Start() + { + if (_options.FolderWatchers.Count == 0) + { + _logger.LogInformation("Keine Ordner-Überwachung konfiguriert."); + return; + } + + foreach (var config in _options.FolderWatchers) + { + if (string.IsNullOrEmpty(config.Path) || !Directory.Exists(config.Path)) + { + _logger.LogWarning("[{Name}] Ordner nicht gefunden: {Path}", config.Name, config.Path); + continue; + } + + // Beim Start bereits vorhandene PDFs drucken + PrintExistingFiles(config); + + var watcher = new FileSystemWatcher(config.Path) + { + Filter = "*.pdf", + IncludeSubdirectories = config.IncludeSubfolders, + NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite, + EnableRaisingEvents = true + }; + + watcher.Created += (_, e) => OnFileDetected(e.FullPath, config); + watcher.Renamed += (_, e) => + { + if (e.Name?.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase) == true) + OnFileDetected(e.FullPath, config); + }; + + _watchers.Add(watcher); + _logger.LogInformation("[{Name}] Überwache: {Path} (Unterordner: {Sub})", + config.Name, config.Path, config.IncludeSubfolders); + } + } + + private void PrintExistingFiles(FolderWatcher config) + { + var files = Directory.GetFiles(config.Path, "*.pdf", + config.IncludeSubfolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); + + if (files.Length == 0) return; + + _logger.LogInformation("[{Name}] {Count} vorhandene PDF(s) gefunden – werden gedruckt.", config.Name, files.Length); + foreach (var file in files) + OnFileDetected(file, config); + } + + private void OnFileDetected(string filePath, FolderWatcher config) + { + // Kurz warten bis Datei vollständig geschrieben ist + if (!WaitForFile(filePath)) + { + _logger.LogWarning("[{Name}] Datei nicht lesbar (gesperrt?): {File}", config.Name, filePath); + return; + } + + _logger.LogInformation("[{Name}] Neue Datei: {File}", config.Name, filePath); + + try + { + _printService.PrintPdf(new PrintJob + { + FilePath = filePath, + MailSubject = Path.GetFileName(filePath), + MailFrom = $"Ordner:{config.Name}", + PrinterProfileName = config.PrinterProfileName + }); + + if (config.DeleteAfterPrint) + { + try + { + File.Delete(filePath); + _logger.LogInformation("[{Name}] Gelöscht: {File}", config.Name, filePath); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "[{Name}] Löschen fehlgeschlagen: {File}", config.Name, filePath); + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, "[{Name}] Druckfehler: {File}", config.Name, filePath); + } + } + + private static bool WaitForFile(string path, int timeoutMs = 5000) + { + var sw = System.Diagnostics.Stopwatch.StartNew(); + while (sw.ElapsedMilliseconds < timeoutMs) + { + try + { + using var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None); + return true; + } + catch (IOException) + { + Thread.Sleep(200); + } + } + return false; + } + + public void Dispose() + { + foreach (var w in _watchers) + { + w.EnableRaisingEvents = false; + w.Dispose(); + } + _watchers.Clear(); + } +}