using System.Text.Json;
using System.Text.Json.Serialization;
namespace MeterVault.Infrastructure.Ingestion;
///
/// The parsed JSON for an MQTT broker. Credentials
/// are never held here as plaintext (SDD §6.4): /
/// name environment variables resolved at runtime, or /
/// hold them encrypted under the app's data-protection key ring.
///
public sealed record EndpointConfig
{
private static readonly JsonSerializerOptions Options = new()
{
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
public string Host { get; init; } = "localhost";
public int Port { get; init; } = 1883;
public bool Tls { get; init; }
/// Extra topic filters to subscribe (beyond the sources' own topics), e.g. tele/+/SENSOR.
public IReadOnlyList ExtraTopics { get; init; } = [];
public string? UsernameEnv { get; init; }
public string? PasswordEnv { get; init; }
///
/// Username entered directly in the admin UI. Held as-is: §6.4 covers tokens and passwords, and
/// encrypting a username would only blank the field on every edit for no security gain.
///
public string? Username { get; init; }
/// Password encrypted by (entered in the admin UI).
public string? PasswordEnc { get; init; }
public static EndpointConfig Parse(string? json)
{
if (string.IsNullOrWhiteSpace(json))
{
return new EndpointConfig();
}
try
{
return JsonSerializer.Deserialize(json, Options) ?? new EndpointConfig();
}
catch (JsonException)
{
return new EndpointConfig();
}
}
public string ToJson() => JsonSerializer.Serialize(this, Options);
public string? ResolveUsername(Security.SecretProtector? protector = null) =>
!string.IsNullOrWhiteSpace(Username)
? Username
: EndpointSecret.Resolve(null, UsernameEnv, protector);
public string? ResolvePassword(Security.SecretProtector? protector = null) =>
EndpointSecret.Resolve(PasswordEnc, PasswordEnv, protector);
}