diff --git a/ZA.CoreService.ESBCertificateManager/Database/05_AddCertificateCheckUrl.sql b/ZA.CoreService.ESBCertificateManager/Database/05_AddCertificateCheckUrl.sql
index 96adcab..0edd640 100644
--- a/ZA.CoreService.ESBCertificateManager/Database/05_AddCertificateCheckUrl.sql
+++ b/ZA.CoreService.ESBCertificateManager/Database/05_AddCertificateCheckUrl.sql
@@ -12,7 +12,38 @@ GO
IF COL_LENGTH(N'dbo.SonicContainer', N'CertificateCheckUrl') IS NULL
BEGIN
- ALTER TABLE [dbo].[SonicContainer]
- ADD [CertificateCheckUrl] [nvarchar](500) NULL;
+ IF COL_LENGTH(N'dbo.SonicContainer', N'vertifiaktechecurl') IS NOT NULL
+ BEGIN
+ EXEC sp_rename
+ N'dbo.SonicContainer.vertifiaktechecurl',
+ N'CertificateCheckUrl',
+ N'COLUMN';
+ END
+ ELSE IF COL_LENGTH(N'dbo.SonicContainer', N'VerifyCertificateCheckCurl') IS NOT NULL
+ BEGIN
+ EXEC sp_rename
+ N'dbo.SonicContainer.VerifyCertificateCheckCurl',
+ N'CertificateCheckUrl',
+ N'COLUMN';
+ END
+ ELSE IF COL_LENGTH(N'dbo.SonicContainer', N'CertificateCheckCurl') IS NOT NULL
+ BEGIN
+ EXEC sp_rename
+ N'dbo.SonicContainer.CertificateCheckCurl',
+ N'CertificateCheckUrl',
+ N'COLUMN';
+ END
+ ELSE IF COL_LENGTH(N'dbo.SonicContainer', N'ZertifikatCheckCurl') IS NOT NULL
+ BEGIN
+ EXEC sp_rename
+ N'dbo.SonicContainer.ZertifikatCheckCurl',
+ N'CertificateCheckUrl',
+ N'COLUMN';
+ END
+ ELSE
+ BEGIN
+ ALTER TABLE [dbo].[SonicContainer]
+ ADD [CertificateCheckUrl] [nvarchar](500) NULL;
+ END
END;
GO
diff --git a/ZA.CoreService.ESBCertificateManager/Program.cs b/ZA.CoreService.ESBCertificateManager/Program.cs
index dfef8e0..137486d 100644
--- a/ZA.CoreService.ESBCertificateManager/Program.cs
+++ b/ZA.CoreService.ESBCertificateManager/Program.cs
@@ -67,6 +67,19 @@ internal static class Program
}
}
+ try
+ {
+ DatabaseSchemaPatcher
+ .EnsureCertificateCheckUrlColumnAsync(
+ settings.Database.ConnectionString)
+ .GetAwaiter()
+ .GetResult();
+ }
+ catch
+ {
+ // Ohne ALTER-Rechte weiter starten; Repositories nutzen Fallback-SQL.
+ }
+
Application.Run(
new Form1());
}
diff --git a/ZA.CoreService.ESBCertificateManager/Services/DatabaseSchemaPatcher.cs b/ZA.CoreService.ESBCertificateManager/Services/DatabaseSchemaPatcher.cs
new file mode 100644
index 0000000..8a6666c
--- /dev/null
+++ b/ZA.CoreService.ESBCertificateManager/Services/DatabaseSchemaPatcher.cs
@@ -0,0 +1,126 @@
+using System.Data;
+using Microsoft.Data.SqlClient;
+
+namespace ZA.CoreService.ESBCertificateManager.Services;
+
+///
+/// Stellt sicher, dass optionale Schema-Patches auf bestehenden Datenbanken
+/// angewendet wurden (z. B. SonicContainer.CertificateCheckUrl).
+///
+public static class DatabaseSchemaPatcher
+{
+ private static readonly string[] LegacyCertificateCheckUrlColumns =
+ [
+ "vertifiaktechecurl",
+ "VerifyCertificateCheckCurl",
+ "CertificateCheckCurl",
+ "ZertifikatCheckCurl"
+ ];
+
+ private static bool? _hasCertificateCheckUrlColumn;
+
+ public static async Task EnsureCertificateCheckUrlColumnAsync(
+ string connectionString,
+ CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrWhiteSpace(connectionString))
+ {
+ return;
+ }
+
+ await using SqlConnection connection = new(connectionString);
+ await connection.OpenAsync(cancellationToken);
+
+ if (await ColumnExistsAsync(
+ connection,
+ "CertificateCheckUrl",
+ cancellationToken))
+ {
+ _hasCertificateCheckUrlColumn = true;
+ return;
+ }
+
+ foreach (string legacyColumn in LegacyCertificateCheckUrlColumns)
+ {
+ if (!await ColumnExistsAsync(
+ connection,
+ legacyColumn,
+ cancellationToken))
+ {
+ continue;
+ }
+
+ await using SqlCommand renameCommand = new(
+ """
+ EXEC sp_rename
+ N'dbo.SonicContainer.LEGACY_COLUMN',
+ N'CertificateCheckUrl',
+ N'COLUMN';
+ """
+ .Replace(
+ "LEGACY_COLUMN",
+ legacyColumn,
+ StringComparison.Ordinal),
+ connection);
+
+ await renameCommand.ExecuteNonQueryAsync(cancellationToken);
+ _hasCertificateCheckUrlColumn = true;
+ return;
+ }
+
+ const string addColumnSql = """
+ ALTER TABLE [dbo].[SonicContainer]
+ ADD [CertificateCheckUrl] [nvarchar](500) NULL;
+ """;
+
+ await using SqlCommand addCommand = new(addColumnSql, connection);
+ await addCommand.ExecuteNonQueryAsync(cancellationToken);
+ _hasCertificateCheckUrlColumn = true;
+ }
+
+ public static async Task HasCertificateCheckUrlColumnAsync(
+ string connectionString,
+ CancellationToken cancellationToken = default)
+ {
+ if (_hasCertificateCheckUrlColumn is bool cached)
+ {
+ return cached;
+ }
+
+ if (string.IsNullOrWhiteSpace(connectionString))
+ {
+ return false;
+ }
+
+ await using SqlConnection connection = new(connectionString);
+ await connection.OpenAsync(cancellationToken);
+
+ _hasCertificateCheckUrlColumn = await ColumnExistsAsync(
+ connection,
+ "CertificateCheckUrl",
+ cancellationToken);
+
+ return _hasCertificateCheckUrlColumn.Value;
+ }
+
+ private static async Task ColumnExistsAsync(
+ SqlConnection connection,
+ string columnName,
+ CancellationToken cancellationToken)
+ {
+ await using SqlCommand command = new(
+ "SELECT COL_LENGTH(N'dbo.SonicContainer', @ColumnName);",
+ connection);
+
+ command.Parameters.Add(
+ new SqlParameter("@ColumnName", SqlDbType.NVarChar, 128)
+ {
+ Value = columnName
+ });
+
+ object? result =
+ await command.ExecuteScalarAsync(cancellationToken);
+
+ return result is not null and not DBNull;
+ }
+}
diff --git a/ZA.CoreService.ESBCertificateManager/Services/DeployTargetRepository.cs b/ZA.CoreService.ESBCertificateManager/Services/DeployTargetRepository.cs
index 6b0cfdf..aff4962 100644
--- a/ZA.CoreService.ESBCertificateManager/Services/DeployTargetRepository.cs
+++ b/ZA.CoreService.ESBCertificateManager/Services/DeployTargetRepository.cs
@@ -31,7 +31,7 @@ public sealed class DeploymentTargetRepository
nameof(environmentCode));
}
- const string sql = """
+ const string sqlWithCheckUrl = """
SELECT
target.[CertificateTargetId],
target.[TargetDirectory],
@@ -75,11 +75,63 @@ public sealed class DeploymentTargetRepository
target.[TargetFileName];
""";
+ const string sqlWithoutCheckUrl = """
+ SELECT
+ target.[CertificateTargetId],
+ target.[TargetDirectory],
+ target.[TargetFileName],
+ target.[BackupEnabled],
+ target.[BackupDirectoryName],
+ target.[IsActive] AS [TargetIsActive],
+
+ container.[SonicContainerId],
+ container.[ContainerName],
+ container.[ContainerDisplayName],
+ container.[RestartTimeoutSeconds],
+
+ connection.[ConnectionName],
+
+ environment.[EnvironmentCode],
+ environment.[SortOrder] AS [EnvironmentSortOrder],
+
+ company.[CompanyCode]
+ FROM [dbo].[CertificateTarget] AS target
+ INNER JOIN [dbo].[SonicContainer] AS container
+ ON container.[SonicContainerId] = target.[SonicContainerId]
+ INNER JOIN [dbo].[SonicConnection] AS connection
+ ON connection.[SonicConnectionId] = container.[SonicConnectionId]
+ INNER JOIN [dbo].[Environment] AS environment
+ ON environment.[EnvironmentId] = connection.[EnvironmentId]
+ INNER JOIN [dbo].[Company] AS company
+ ON company.[CompanyId] = container.[CompanyId]
+ WHERE target.[IsActive] = 1
+ AND container.[IsActive] = 1
+ AND connection.[IsActive] = 1
+ AND company.[IsActive] = 1
+ AND environment.[IsActive] = 1
+ AND environment.[EnvironmentCode] = @EnvironmentCode
+ ORDER BY
+ company.[CompanyCode],
+ environment.[SortOrder],
+ container.[ContainerName],
+ target.[TargetDirectory],
+ target.[TargetFileName];
+ """;
+
List targets = [];
await using SqlConnection connection = new(_connectionString);
await connection.OpenAsync(cancellationToken);
+ bool hasCertificateCheckUrl =
+ await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
+ _connectionString,
+ cancellationToken);
+
+ string sql = hasCertificateCheckUrl
+ ? sqlWithCheckUrl
+ : sqlWithoutCheckUrl;
+
await using SqlCommand command = new(sql, connection);
command.Parameters.Add(
@@ -120,8 +172,9 @@ public sealed class DeploymentTargetRepository
int containerDisplayNameOrdinal =
reader.GetOrdinal("ContainerDisplayName");
- int certificateCheckUrlOrdinal =
- reader.GetOrdinal("CertificateCheckUrl");
+ int? certificateCheckUrlOrdinal = hasCertificateCheckUrl
+ ? reader.GetOrdinal("CertificateCheckUrl")
+ : null;
int restartTimeoutOrdinal =
reader.GetOrdinal("RestartTimeoutSeconds");
@@ -149,9 +202,11 @@ public sealed class DeploymentTargetRepository
containerDisplayNameOrdinal);
string? certificateCheckUrl =
- ReadNullableString(
- reader,
- certificateCheckUrlOrdinal);
+ certificateCheckUrlOrdinal is int checkUrlOrdinal
+ ? ReadNullableString(
+ reader,
+ checkUrlOrdinal)
+ : null;
string companyCode =
reader.GetString(companyCodeOrdinal);
diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs
index 94e5f76..76f5855 100644
--- a/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs
+++ b/ZA.CoreService.ESBCertificateManager/Services/SonicSetupRepository.cs
@@ -878,7 +878,7 @@ public sealed class SonicSetupRepository
nameof(environmentCode));
}
- const string sql = """
+ const string sqlWithCheckUrl = """
SELECT
container.[SonicContainerId],
container.[SonicConnectionId],
@@ -913,6 +913,40 @@ public sealed class SonicSetupRepository
container.[ContainerName];
""";
+ const string sqlWithoutCheckUrl = """
+ SELECT
+ container.[SonicContainerId],
+ container.[SonicConnectionId],
+ container.[CompanyId],
+ container.[ContainerName],
+ container.[ContainerDisplayName],
+ container.[RestartTimeoutSeconds],
+ company.[CompanyCode],
+ connection.[ConnectionName]
+ FROM [dbo].[SonicContainer] AS container
+ INNER JOIN [dbo].[Company] AS company
+ ON company.[CompanyId] = container.[CompanyId]
+ INNER JOIN [dbo].[SonicConnection] AS connection
+ ON connection.[SonicConnectionId] =
+ container.[SonicConnectionId]
+ INNER JOIN [dbo].[Environment] AS environment
+ ON environment.[EnvironmentId] =
+ connection.[EnvironmentId]
+ WHERE container.[IsActive] = 1
+ AND company.[IsActive] = 1
+ AND connection.[IsActive] = 1
+ AND environment.[IsActive] = 1
+ AND environment.[EnvironmentCode] = @EnvironmentCode
+ AND
+ (
+ @SonicConnectionId IS NULL
+ OR container.[SonicConnectionId] = @SonicConnectionId
+ )
+ ORDER BY
+ company.[CompanyCode],
+ container.[ContainerName];
+ """;
+
List containers = [];
await using SqlConnection connection =
@@ -920,6 +954,15 @@ public sealed class SonicSetupRepository
await connection.OpenAsync(cancellationToken);
+ bool hasCertificateCheckUrl =
+ await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
+ _connectionString,
+ cancellationToken);
+
+ string sql = hasCertificateCheckUrl
+ ? sqlWithCheckUrl
+ : sqlWithoutCheckUrl;
+
await using SqlCommand command =
new(sql, connection);
@@ -952,8 +995,9 @@ public sealed class SonicSetupRepository
int displayOrdinal =
reader.GetOrdinal("ContainerDisplayName");
- int checkUrlOrdinal =
- reader.GetOrdinal("CertificateCheckUrl");
+ int? checkUrlOrdinal = hasCertificateCheckUrl
+ ? reader.GetOrdinal("CertificateCheckUrl")
+ : null;
containers.Add(
new ContainerOption
@@ -980,9 +1024,10 @@ public sealed class SonicSetupRepository
: reader.GetString(displayOrdinal),
CertificateCheckUrl =
- reader.IsDBNull(checkUrlOrdinal)
- ? null
- : reader.GetString(checkUrlOrdinal),
+ checkUrlOrdinal is int urlOrdinal
+ && !reader.IsDBNull(urlOrdinal)
+ ? reader.GetString(urlOrdinal)
+ : null,
RestartTimeoutSeconds =
reader.GetInt32(
@@ -1328,7 +1373,13 @@ public sealed class SonicSetupRepository
string? checkUrl =
TlsEndpointProbeService.NormalizeCheckUrl(certificateCheckUrl);
- const string sql = """
+ bool hasCertificateCheckUrl =
+ await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
+ _connectionString,
+ cancellationToken);
+
+ string sql = hasCertificateCheckUrl
+ ? """
INSERT INTO [dbo].[SonicContainer]
(
[CompanyId],
@@ -1354,6 +1405,31 @@ public sealed class SonicSetupRepository
SYSUTCDATETIME(),
SUSER_SNAME()
);
+ """
+ : """
+ INSERT INTO [dbo].[SonicContainer]
+ (
+ [CompanyId],
+ [SonicConnectionId],
+ [ContainerName],
+ [ContainerDisplayName],
+ [RestartTimeoutSeconds],
+ [IsActive],
+ [CreationDateTime],
+ [CreatedBy]
+ )
+ OUTPUT INSERTED.[SonicContainerId]
+ VALUES
+ (
+ @CompanyId,
+ @SonicConnectionId,
+ @ContainerName,
+ @ContainerDisplayName,
+ @RestartTimeoutSeconds,
+ 1,
+ SYSUTCDATETIME(),
+ SUSER_SNAME()
+ );
""";
await using SqlConnection connection =
@@ -1396,16 +1472,19 @@ public sealed class SonicSetupRepository
: display
});
- command.Parameters.Add(
- new SqlParameter(
- "@CertificateCheckUrl",
- SqlDbType.NVarChar,
- 500)
- {
- Value = checkUrl is null
- ? DBNull.Value
- : checkUrl
- });
+ if (hasCertificateCheckUrl)
+ {
+ command.Parameters.Add(
+ new SqlParameter(
+ "@CertificateCheckUrl",
+ SqlDbType.NVarChar,
+ 500)
+ {
+ Value = checkUrl is null
+ ? DBNull.Value
+ : checkUrl
+ });
+ }
command.Parameters.Add(
new SqlParameter(
@@ -1751,7 +1830,13 @@ public sealed class SonicSetupRepository
string? checkUrl =
TlsEndpointProbeService.NormalizeCheckUrl(certificateCheckUrl);
- const string sql = """
+ bool hasCertificateCheckUrl =
+ await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
+ _connectionString,
+ cancellationToken);
+
+ string sql = hasCertificateCheckUrl
+ ? """
UPDATE [dbo].[SonicContainer]
SET
[CompanyId] = @CompanyId,
@@ -1764,6 +1849,19 @@ public sealed class SonicSetupRepository
[ModifiedBy] = SUSER_SNAME()
WHERE [SonicContainerId] = @SonicContainerId
AND [IsActive] = 1;
+ """
+ : """
+ UPDATE [dbo].[SonicContainer]
+ SET
+ [CompanyId] = @CompanyId,
+ [SonicConnectionId] = @SonicConnectionId,
+ [ContainerName] = @ContainerName,
+ [ContainerDisplayName] = @ContainerDisplayName,
+ [RestartTimeoutSeconds] = @RestartTimeoutSeconds,
+ [ModifiedDateTime] = SYSUTCDATETIME(),
+ [ModifiedBy] = SUSER_SNAME()
+ WHERE [SonicContainerId] = @SonicContainerId
+ AND [IsActive] = 1;
""";
await using SqlConnection connection =
@@ -1812,16 +1910,19 @@ public sealed class SonicSetupRepository
: display
});
- command.Parameters.Add(
- new SqlParameter(
- "@CertificateCheckUrl",
- SqlDbType.NVarChar,
- 500)
- {
- Value = checkUrl is null
- ? DBNull.Value
- : checkUrl
- });
+ if (hasCertificateCheckUrl)
+ {
+ command.Parameters.Add(
+ new SqlParameter(
+ "@CertificateCheckUrl",
+ SqlDbType.NVarChar,
+ 500)
+ {
+ Value = checkUrl is null
+ ? DBNull.Value
+ : checkUrl
+ });
+ }
command.Parameters.Add(
new SqlParameter(