using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services; namespace ZB.MOM.WW.ScadaBridge.InboundAPI; public static class ServiceCollectionExtensions { /// /// Registers all inbound API services (script executor, route helper, and endpoint filter). /// /// The service collection to register into. /// The same to allow chaining. public static IServiceCollection AddInboundAPI(this IServiceCollection services) { // Inbound authentication is handled by the shared // ZB.MOM.WW.Auth.ApiKeys verifier (Bearer sbk__), registered by // AddZbApiKeyAuth in the Host composition root. The legacy ApiKeyValidator + // peppered IApiKeyHasher and the SQL Server ApiKey store were retired, so this // extension no longer registers them. services.AddSingleton(); services.AddScoped(); // Short-TTL cache of resolved ApiMethod rows, sitting in front of the // per-request repository fetch in EndpointExtensions. Invalidated by name via // ScriptArtifactChangeSubscriber (below) and self-heals via TTL expiry for // changes the bus does not cover. services.AddSingleton(sp => new ApiMethodCache( sp.GetRequiredService>().Value.ApiMethodCacheTtl)); // Routed calls go through the IInstanceRouter seam; the // production implementation delegates to CommunicationService. services.AddScoped(); // Endpoint filter enforcing the request // body size cap and active-node gating for POST /api/{methodName}. services.AddSingleton(); // Standby-node forwarding (2026-08-28): a standby relays POST /api/{methodName} // to the active peer over HTTP instead of answering 503 STANDBY_NODE. The // named client has no HttpClient.Timeout of its own — the forwarder's per-attempt // CTS (InboundApiOptions.StandbyForwardTimeout) is the single deadline, so a // long-running method on the active node is not cut off by a second, hidden cap. services.AddHttpClient(HttpStandbyRequestForwarder.HttpClientName) .ConfigureHttpClient(c => c.Timeout = Timeout.InfiniteTimeSpan); services.AddSingleton(); // N1: the ApiMethod consumer of the script-artifact invalidation bus. // The bus parameter is optional — hosts without a registered // IScriptArtifactChangeBus (site roles, plain test hosts) start it as a no-op // and the executor's per-request revision check alone keeps handlers fresh. services.AddHostedService(); // ScadaBridge#103: a create / update / delete / recompile made through the other central // node drops this node's handler, known-bad record and cached row for the method. services.TryAddEnumerable(ServiceDescriptor.Singleton()); return services; } }