using Microsoft.Extensions.Configuration; using ZB.MOM.WW.Secrets.MasterKey; using ZB.MOM.WW.Secrets.Replicator.SqlServer; namespace ZB.MOM.WW.Secrets.Cli.Interactive; /// /// Composes a deployment target's Secrets configuration the same way the target app would at /// startup — base appsettings.json, optional per-environment overlay, and environment /// variables — so the interactive console operates on exactly the store the app reads. /// public sealed class TargetConfigReader { /// /// Loads the target app's config exactly the way the app would (base json + optional environment /// overlay + environment variables) and binds the Secrets options out of it. /// /// Absolute or relative path to the target app's base appsettings.json. /// /// Optional ASP.NET environment name; when non-empty an appsettings.{environment}.json /// overlay is layered on top of the base file (optional — absent overlays are ignored). /// /// The configuration section the Secrets options bind from. Defaults to Secrets. /// The resolved , with the SQLite path made absolute against the app directory. /// The base does not exist. public StoreTarget Read(string appSettingsPath, string? environment = null, string sectionPath = "Secrets") { string fullPath = Path.GetFullPath(appSettingsPath); if (!File.Exists(fullPath)) { throw new FileNotFoundException( $"Target appsettings file not found: {fullPath}", fullPath); } string dir = Path.GetDirectoryName(fullPath)!; string fileName = Path.GetFileName(fullPath); ConfigurationBuilder builder = new(); builder.SetBasePath(dir) .AddJsonFile(fileName, optional: false); if (!string.IsNullOrEmpty(environment)) { builder.AddJsonFile($"appsettings.{environment}.json", optional: true); } builder.AddEnvironmentVariables(); IConfigurationRoot config = builder.Build(); SecretsOptions secrets = config.GetSection(sectionPath).Get() ?? new SecretsOptions(); secrets.SqlitePath = Path.GetFullPath(secrets.SqlitePath, dir); SqlServerSecretsOptions? sqlServer = null; IConfigurationSection sqlSection = config.GetSection(sectionPath + ":SqlServer"); if (sqlSection.GetChildren().Any()) { sqlServer = sqlSection.Get(); } string name = new DirectoryInfo(dir).Name; return new StoreTarget(name, fullPath, secrets, sqlServer, config); } /// /// Builds a manual target from an explicit db path + master-key options, with no backing /// appsettings file (for ad-hoc stores the console operates on directly). /// /// Path to the SQLite store; made absolute against the current directory. /// The master-key resolution options for this manual store. /// A named manual with an empty composed configuration and no SQL-Server hub. public StoreTarget Manual(string sqlitePath, MasterKeyOptions masterKey) { SecretsOptions secrets = new() { SqlitePath = Path.GetFullPath(sqlitePath), MasterKey = masterKey, }; IConfigurationRoot config = new ConfigurationBuilder().Build(); return new StoreTarget("manual", AppSettingsPath: null, secrets, SqlServer: null, config); } }