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);
}
}