V13-FE-011: finalize search list layout slice
This commit is contained in:
+70
@@ -0,0 +1,70 @@
|
||||
using Kbx.Shared.Configuration.Generated;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Kbx.Shared.Configuration;
|
||||
|
||||
public sealed class KbxConfigurationStartupValidator(IConfiguration configuration)
|
||||
{
|
||||
private static readonly string[] PlaceholderSecrets = ["changeme", "change-me", "example", "secret", "password", "todo"];
|
||||
|
||||
public KbxConfigurationValidationResult Validate()
|
||||
{
|
||||
var environment = configuration["Kbx:Runtime:Environment"] ?? "Development";
|
||||
var issues = new List<KbxConfigurationIssue>();
|
||||
|
||||
if (!KbxConfigurationCatalog.Environments.TryGetValue(environment, out _))
|
||||
issues.Add(new("CONFIG_ENVIRONMENT_UNKNOWN", "Kbx:Runtime:Environment", $"Unknown KBX environment '{environment}'.", true));
|
||||
|
||||
foreach (var setting in KbxConfigurationCatalog.Settings.Values)
|
||||
{
|
||||
var value = configuration[setting.Key] ?? setting.DefaultValue;
|
||||
if (setting.RequiredIn.Contains(environment, StringComparer.Ordinal) && string.IsNullOrWhiteSpace(value))
|
||||
issues.Add(new("CONFIG_REQUIRED", setting.Key, "Required configuration is missing.", true));
|
||||
|
||||
if (setting.RequiredWhenKey is not null &&
|
||||
string.Equals(configuration[setting.RequiredWhenKey], setting.RequiredWhenEquals, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.IsNullOrWhiteSpace(value))
|
||||
issues.Add(new("CONFIG_DEPENDENCY_REQUIRED", setting.Key, $"{setting.Key} is required when {setting.RequiredWhenKey}={setting.RequiredWhenEquals}.", true));
|
||||
|
||||
if (setting.Secret && !string.IsNullOrWhiteSpace(value) && PlaceholderSecrets.Any(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)))
|
||||
issues.Add(new("CONFIG_SECRET_PLACEHOLDER", setting.Key, "Secret configuration still contains a placeholder value.", true));
|
||||
|
||||
if (setting.AllowedValues.Length > 0 && !string.IsNullOrWhiteSpace(value) && !setting.AllowedValues.Contains(value, StringComparer.OrdinalIgnoreCase))
|
||||
issues.Add(new("CONFIG_ENUM", setting.Key, $"Unsupported configured value for {setting.Key}.", true));
|
||||
|
||||
if (setting.Type == "integer" && !string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
if (!int.TryParse(value, out var number)) issues.Add(new("CONFIG_INTEGER", setting.Key, "Configuration must be an integer.", true));
|
||||
else
|
||||
{
|
||||
if (setting.Minimum is not null && number < setting.Minimum) issues.Add(new("CONFIG_MINIMUM", setting.Key, "Configuration is below the allowed minimum.", true));
|
||||
if (setting.Maximum is not null && number > setting.Maximum) issues.Add(new("CONFIG_MAXIMUM", setting.Key, "Configuration exceeds the allowed maximum.", true));
|
||||
}
|
||||
}
|
||||
if (setting.Type == "uri" && !string.IsNullOrWhiteSpace(value) && !Uri.TryCreate(value, UriKind.Absolute, out _))
|
||||
issues.Add(new("CONFIG_URI", setting.Key, "Configuration must be an absolute URI.", true));
|
||||
}
|
||||
|
||||
if (string.Equals(environment, "Production", StringComparison.Ordinal))
|
||||
{
|
||||
if (!string.Equals(configuration["Kbx:Database:MigrationsMode"] ?? "validate", "predeploy", StringComparison.OrdinalIgnoreCase))
|
||||
issues.Add(new("CONFIG_PRODUCTION_MIGRATION_MODE", "Kbx:Database:MigrationsMode", "Production requires predeploy migrations; startup schema mutation is forbidden.", true));
|
||||
|
||||
if (!bool.TryParse(configuration["Kbx:Security:RequireHttps"], out var https) || !https)
|
||||
issues.Add(new("CONFIG_PRODUCTION_HTTPS", "Kbx:Security:RequireHttps", "Production requires HTTPS.", true));
|
||||
|
||||
if (string.Equals(configuration["Kbx:Logging:MinimumLevel"], "Debug", StringComparison.OrdinalIgnoreCase))
|
||||
issues.Add(new("CONFIG_PRODUCTION_DEBUG_LOGGING", "Kbx:Logging:MinimumLevel", "Production Debug logging is forbidden by the KBX reference policy.", true));
|
||||
}
|
||||
|
||||
return new(environment, KbxConfigurationFingerprint.Compute(configuration), issues);
|
||||
}
|
||||
|
||||
public KbxConfigurationValidationResult ValidateOrThrow()
|
||||
{
|
||||
var result = Validate();
|
||||
if (!result.IsValid)
|
||||
throw new InvalidOperationException("KBX configuration validation failed: " + string.Join(" | ", result.Issues.Where(x => x.Fatal).Select(x => $"{x.Code}:{x.Key}")));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user