using System; using System.Collections.Generic; using System.IO; using Serilog; namespace ZB.MOM.WW.LmxProxy.Host.Configuration { /// /// Validates the LmxProxy configuration at startup. /// Throws InvalidOperationException on any validation error. /// public static class ConfigurationValidator { private static readonly ILogger Log = Serilog.Log.ForContext(typeof(ConfigurationValidator)); /// /// Validates all configuration settings and logs the effective values. /// Throws on first validation error. /// public static void ValidateAndLog(LmxProxyConfiguration config) { var errors = new List(); // GrpcPort if (config.GrpcPort < 1 || config.GrpcPort > 65535) errors.Add($"GrpcPort must be 1-65535, got {config.GrpcPort}"); // Connection var conn = config.Connection; if (conn.MonitorIntervalSeconds <= 0) errors.Add($"Connection.MonitorIntervalSeconds must be > 0, got {conn.MonitorIntervalSeconds}"); if (conn.ConnectionTimeoutSeconds <= 0) errors.Add($"Connection.ConnectionTimeoutSeconds must be > 0, got {conn.ConnectionTimeoutSeconds}"); if (conn.ReadTimeoutSeconds <= 0) errors.Add($"Connection.ReadTimeoutSeconds must be > 0, got {conn.ReadTimeoutSeconds}"); if (conn.WriteTimeoutSeconds <= 0) errors.Add($"Connection.WriteTimeoutSeconds must be > 0, got {conn.WriteTimeoutSeconds}"); if (conn.MaxConcurrentOperations <= 0) errors.Add($"Connection.MaxConcurrentOperations must be > 0, got {conn.MaxConcurrentOperations}"); if (conn.NodeName != null && conn.NodeName.Length > 255) errors.Add("Connection.NodeName must be <= 255 characters"); if (conn.GalaxyName != null && conn.GalaxyName.Length > 255) errors.Add("Connection.GalaxyName must be <= 255 characters"); // Subscription var sub = config.Subscription; if (sub.ChannelCapacity < 0 || sub.ChannelCapacity > 100000) errors.Add($"Subscription.ChannelCapacity must be 0-100000, got {sub.ChannelCapacity}"); var validModes = new HashSet(StringComparer.OrdinalIgnoreCase) { "DropOldest", "DropNewest", "Wait" }; if (!validModes.Contains(sub.ChannelFullMode)) errors.Add($"Subscription.ChannelFullMode must be DropOldest, DropNewest, or Wait, got '{sub.ChannelFullMode}'"); // ServiceRecovery var sr = config.ServiceRecovery; if (sr.FirstFailureDelayMinutes < 0) errors.Add($"ServiceRecovery.FirstFailureDelayMinutes must be >= 0, got {sr.FirstFailureDelayMinutes}"); if (sr.SecondFailureDelayMinutes < 0) errors.Add($"ServiceRecovery.SecondFailureDelayMinutes must be >= 0, got {sr.SecondFailureDelayMinutes}"); if (sr.SubsequentFailureDelayMinutes < 0) errors.Add($"ServiceRecovery.SubsequentFailureDelayMinutes must be >= 0, got {sr.SubsequentFailureDelayMinutes}"); if (sr.ResetPeriodDays <= 0) errors.Add($"ServiceRecovery.ResetPeriodDays must be > 0, got {sr.ResetPeriodDays}"); // TLS if (config.Tls.Enabled) { if (!File.Exists(config.Tls.ServerCertificatePath)) Log.Warning("TLS enabled but server certificate not found at {Path} (will auto-generate)", config.Tls.ServerCertificatePath); if (!File.Exists(config.Tls.ServerKeyPath)) Log.Warning("TLS enabled but server key not found at {Path} (will auto-generate)", config.Tls.ServerKeyPath); } // WebServer if (config.WebServer.Enabled) { if (config.WebServer.Port < 1 || config.WebServer.Port > 65535) errors.Add($"WebServer.Port must be 1-65535, got {config.WebServer.Port}"); } if (errors.Count > 0) { foreach (var error in errors) Log.Error("Configuration error: {Error}", error); throw new InvalidOperationException( $"Configuration validation failed with {errors.Count} error(s): {string.Join("; ", errors)}"); } // Log effective configuration Log.Information("Configuration validated successfully"); Log.Information(" GrpcPort: {Port}", config.GrpcPort); Log.Information(" ApiKeyConfigFile: {File}", config.ApiKeyConfigFile); Log.Information(" Connection.AutoReconnect: {AutoReconnect}", conn.AutoReconnect); Log.Information(" Connection.MonitorIntervalSeconds: {Interval}", conn.MonitorIntervalSeconds); Log.Information(" Connection.MaxConcurrentOperations: {Max}", conn.MaxConcurrentOperations); Log.Information(" Subscription.ChannelCapacity: {Capacity}", sub.ChannelCapacity); Log.Information(" Subscription.ChannelFullMode: {Mode}", sub.ChannelFullMode); Log.Information(" Tls.Enabled: {Enabled}", config.Tls.Enabled); Log.Information(" WebServer.Enabled: {Enabled}, Port: {Port}", config.WebServer.Enabled, config.WebServer.Port); } } }