namespace ScadaLink.Host; /// /// Validates required configuration before Akka.NET actor system creation. /// Runs early in startup to fail fast with clear error messages. /// public static class StartupValidator { public static void Validate(IConfiguration configuration) { var errors = new List(); var nodeSection = configuration.GetSection("ScadaLink:Node"); var role = nodeSection["Role"]; if (string.IsNullOrEmpty(role) || (role != "Central" && role != "Site")) errors.Add("ScadaLink:Node:Role must be 'Central' or 'Site'"); if (string.IsNullOrEmpty(nodeSection["NodeHostname"])) errors.Add("ScadaLink:Node:NodeHostname is required"); var portStr = nodeSection["RemotingPort"]; if (!int.TryParse(portStr, out var port) || port < 1 || port > 65535) errors.Add("ScadaLink:Node:RemotingPort must be 1-65535"); if (role == "Site" && string.IsNullOrEmpty(nodeSection["SiteId"])) errors.Add("ScadaLink:Node:SiteId is required for Site nodes"); if (role == "Central") { var dbSection = configuration.GetSection("ScadaLink:Database"); if (string.IsNullOrEmpty(dbSection["ConfigurationDb"])) errors.Add("ScadaLink:Database:ConfigurationDb connection string required for Central"); if (string.IsNullOrEmpty(dbSection["MachineDataDb"])) errors.Add("ScadaLink:Database:MachineDataDb connection string required for Central"); var secSection = configuration.GetSection("ScadaLink:Security"); if (string.IsNullOrEmpty(secSection["LdapServer"])) errors.Add("ScadaLink:Security:LdapServer required for Central"); if (string.IsNullOrEmpty(secSection["JwtSigningKey"])) errors.Add("ScadaLink:Security:JwtSigningKey required for Central"); } if (role == "Site") { var grpcPortStr = nodeSection["GrpcPort"]; if (grpcPortStr != null && (!int.TryParse(grpcPortStr, out var gp) || gp < 1 || gp > 65535)) errors.Add("ScadaLink:Node:GrpcPort must be 1-65535"); var dbSection = configuration.GetSection("ScadaLink:Database"); if (string.IsNullOrEmpty(dbSection["SiteDbPath"])) errors.Add("ScadaLink:Database:SiteDbPath required for Site nodes"); } var seedNodes = configuration.GetSection("ScadaLink:Cluster:SeedNodes").Get>(); if (seedNodes == null || seedNodes.Count < 2) errors.Add("ScadaLink:Cluster:SeedNodes must have at least 2 entries"); if (errors.Count > 0) throw new InvalidOperationException( $"Configuration validation failed:\n{string.Join("\n", errors.Select(e => $" - {e}"))}"); } }