using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Options; using StellaOps.AirGap.Time.Models; using StellaOps.AirGap.Time.Services; namespace StellaOps.AirGap.Time.Health; public sealed class TimeAnchorHealthCheck : IHealthCheck { private readonly TimeStatusService _statusService; private readonly IOptions _options; private readonly TimeProvider _timeProvider; public TimeAnchorHealthCheck(TimeStatusService statusService, IOptions options, TimeProvider timeProvider) { _statusService = statusService; _options = options; _timeProvider = timeProvider; } public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { var opts = _options.Value; var status = await _statusService.GetStatusAsync(opts.TenantId, _timeProvider.GetUtcNow(), cancellationToken).ConfigureAwait(false); if (status.Anchor == TimeAnchor.Unknown) { return HealthCheckResult.Unhealthy("time-anchor-missing"); } if (status.Staleness.IsBreach) { return HealthCheckResult.Unhealthy("time-anchor-stale"); } IReadOnlyDictionary data = new Dictionary { ["anchorDigest"] = status.Anchor.TokenDigest, ["ageSeconds"] = status.Staleness.AgeSeconds, ["warningSeconds"] = status.Staleness.WarningSeconds, ["breachSeconds"] = status.Staleness.BreachSeconds }; if (status.Staleness.IsWarning) { return HealthCheckResult.Degraded("time-anchor-warning", data: data); } return HealthCheckResult.Healthy("time-anchor-healthy", data); } }