using ZA.CoreService.ESBCertificateManager.Data; using ZA.CoreService.ESBCertificateManager.Models; namespace ZA.CoreService.ESBCertificateManager.Services; public sealed class DeploymentOrchestrator { private readonly CertificateDeployer _deployer = new(); private readonly RestartExecutor _restartExecutor; private readonly TlsCertificateProbe _tlsProbe; private readonly PreflightValidator _preflightValidator = new(); private readonly SqlRunLogger _sqlRunLogger; private readonly AppSettings _settings; public DeploymentOrchestrator(AppSettings settings) { _settings = settings; _tlsProbe = new TlsCertificateProbe(settings.TlsTimeoutSeconds, settings.TlsRetryCount); _restartExecutor = new RestartExecutor(settings.SonicConnections); _sqlRunLogger = new SqlRunLogger(settings.ConnectionString); } public PreflightValidationResult ValidatePreflight( string? certificatePath, CertificateInfo? certificateInfo, IReadOnlyList selectedTargets) => _preflightValidator.Validate(certificatePath, certificateInfo, selectedTargets); public async Task RunAsync( string certificatePath, CertificateInfo certificateInfo, IReadOnlyList selectedTargets, IProgress? progress, CancellationToken cancellationToken = default) { DateTimeOffset startedAt = DateTimeOffset.Now; List results = []; using RunLogger logger = new(_settings.LogDirectory); logger.Write($"Deployment gestartet für {selectedTargets.Count} Ziel(e). Zertifikat={Path.GetFileName(certificatePath)}"); foreach (DeploymentTarget target in selectedTargets) { cancellationToken.ThrowIfCancellationRequested(); results.Add(await RunTargetAsync(target, certificatePath, certificateInfo, logger, progress, cancellationToken)); } DateTimeOffset finishedAt = DateTimeOffset.Now; DeploymentRunResult runResult = new() { TargetResults = results, StartedAt = startedAt, FinishedAt = finishedAt, CertificateFilePath = certificatePath, CertificateFingerprint = certificateInfo.FingerprintSha256 }; logger.Write( $"Deployment beendet. Erfolg={runResult.OverallSuccess}; Dauer={(finishedAt - startedAt).TotalSeconds:F1}s; Log={logger.LogFilePath}"); // Ergebnis in SQL-Datenbank protokollieren (wenn ConnectionString konfiguriert) try { await _sqlRunLogger.PersistRunResultAsync(runResult, cancellationToken); } catch (Exception ex) { logger.Write($"SQL-Protokollierung fehlgeschlagen (nicht kritisch): {ex.Message}"); } return runResult; } private async Task RunTargetAsync( DeploymentTarget target, string certificatePath, CertificateInfo certificateInfo, RunLogger logger, IProgress? progress, CancellationToken cancellationToken) { List steps = []; DateTimeOffset targetStart = DateTimeOffset.Now; progress?.Report(new TargetProgressUpdate(target.Id, "Läuft…", false)); (bool copyOk, string copyStatus, string? copyDetail, _) = await _deployer.DeployAsync(certificatePath, target, cancellationToken); RecordStep(steps, logger, target.Name, "Deploy", copyStatus, copyDetail); if (!copyOk) { return Fail(target, copyStatus, copyDetail, steps, targetStart, progress); } progress?.Report(new TargetProgressUpdate(target.Id, copyStatus, false)); (bool restartOk, string restartStatus, string? restartDetail) = await _restartExecutor.ExecuteAsync(target, cancellationToken); RecordStep(steps, logger, target.Name, "Restart", restartStatus, restartDetail); if (!restartOk) { return Fail(target, restartStatus, restartDetail, steps, targetStart, progress, copySucceeded: copyOk); } progress?.Report(new TargetProgressUpdate(target.Id, restartStatus, false)); (bool tlsOk, string tlsStatus, string? tlsDetail, string? observedFingerprint) = await _tlsProbe.ProbeAsync( target, certificateInfo.FingerprintSha256, cancellationToken); RecordStep(steps, logger, target.Name, "TLS", tlsStatus, tlsDetail); bool success = tlsOk; string finalStatus = success ? (string.IsNullOrWhiteSpace(target.TlsHost) ? "Erfolg" : tlsStatus) : tlsStatus; TargetStepResult targetResult = new() { TargetId = target.Id, TargetName = target.Name, Success = success, StatusText = finalStatus, Detail = tlsDetail, Steps = steps, StartedAt = targetStart, FinishedAt = DateTimeOffset.Now, CopySucceeded = copyOk, RestartSucceeded = restartOk, TlsSucceeded = tlsOk, ObservedFingerprint = observedFingerprint }; progress?.Report(new TargetProgressUpdate(target.Id, finalStatus, success)); return targetResult; } private static void RecordStep( List steps, RunLogger logger, string targetName, string phase, string status, string? detail) { steps.Add($"{status}: {detail}"); logger.Write($"[{targetName}] {phase}: {status} | {detail}"); } private static TargetStepResult Fail( DeploymentTarget target, string status, string? detail, List steps, DateTimeOffset startedAt, IProgress? progress, bool copySucceeded = false, bool restartSucceeded = false) { progress?.Report(new TargetProgressUpdate(target.Id, status, false)); return new TargetStepResult { TargetId = target.Id, TargetName = target.Name, Success = false, StatusText = status, Detail = detail, Steps = steps, StartedAt = startedAt, FinishedAt = DateTimeOffset.Now, CopySucceeded = copySucceeded, RestartSucceeded = restartSucceeded, TlsSucceeded = false }; } } public readonly record struct TargetProgressUpdate(int TargetId, string StatusText, bool? SuccessHint);