using FinalFactory.Rendezvous.Contracts; using FinalFactory.Rendezvous.Server.Observability; using FinalFactory.Rendezvous.Server.Provisioning; using FinalFactory.Rendezvous.Server.State; namespace FinalFactory.Rendezvous.Server.Operations; internal sealed class OperatorService( InMemoryEphemeralRendezvousStore store, ProvisioningRuntime provisioning, RendezvousReadiness readiness, AuditTrail audit, IWallClock clock) { public OperatorStatusResponse GetStatus() { ReadinessSnapshot readinessSnapshot = readiness.GetSnapshot(); EphemeralStoreSnapshot storeSnapshot = store.GetSnapshot(); return new OperatorStatusResponse { Status = readinessSnapshot.IsReady ? "ready" : "not-ready", Compatibility = ReleaseCompatibility.CreateResponse(), Readiness = new OperatorReadinessResponse { HttpListener = readinessSnapshot.HttpListenerReady, UdpIpv4Listener = readinessSnapshot.UdpIpv4ListenerReady, UdpIpv6Listener = readinessSnapshot.UdpIpv6ListenerReady, Provisioning = readinessSnapshot.ProvisioningReady, Store = readinessSnapshot.StoreAvailable, Draining = readinessSnapshot.Draining, }, Store = new OperatorStoreResponse { ActiveListings = storeSnapshot.ActiveListings, FreshPresenceBindings = storeSnapshot.FreshPresenceBindings, ActiveJoinAttempts = storeSnapshot.ActiveJoinAttempts, RetainedOutcomeReports = storeSnapshot.RetainedOutcomeReports, ReplayMarkers = storeSnapshot.ReplayMarkers, PrincipalRevocations = storeSnapshot.PrincipalRevocations, IdempotencyEntries = storeSnapshot.IdempotencyEntries, MaintenanceSweeps = storeSnapshot.MaintenanceSweeps, ExpiryChurn = storeSnapshot.ExpiryChurn, }, Tenants = provisioning.Policies.EnabledPolicies .OrderBy(static policy => policy.GameId.Value, StringComparer.Ordinal) .ThenBy(static policy => policy.EnvironmentId.Value, StringComparer.Ordinal) .Select(static policy => new OperatorTenantResponse { GameId = policy.GameId.Value, EnvironmentId = policy.EnvironmentId.Value, Status = "enabled", }) .ToArray(), SigningKeys = provisioning.SigningKeys.GetStatuses(clock.UtcNow) .Select(static key => new OperatorSigningKeyResponse { KeyId = key.KeyId, Status = key.Status, SignUntil = key.SignUntil, VerifyUntil = key.VerifyUntil, GameId = key.GameId, EnvironmentId = key.EnvironmentId, CredentialKinds = key.CredentialKinds, }) .ToArray(), AuditCounts = audit.GetAggregateCounts(), }; } public StoreResult RevokeListing( SessionListingId listingId, CancellationToken cancellationToken) => store.RevokeListing(listingId, cancellationToken); public StoreResult RevokePrincipal( string subject, TimeSpan lifetime, CancellationToken cancellationToken) => store.RevokePrincipal(subject, lifetime, cancellationToken); public bool RevokeSigningKey(string keyId) => provisioning.SigningKeys.Revoke(keyId); public void BeginDrain(CancellationToken cancellationToken) => store.BeginDrain(cancellationToken); }