using StellaOps.Auth.Abstractions; using StellaOps.Localization; using StellaOps.Auth.ServerIntegration; using StellaOps.Auth.ServerIntegration.Tenancy; using StellaOps.Eventing; using StellaOps.Router.AspNet; using StellaOps.Timeline.Core; using StellaOps.Timeline.WebService.Audit; using StellaOps.Timeline.WebService.Endpoints; using StellaOps.Timeline.WebService.Security; var builder = WebApplication.CreateBuilder(args); // Add services builder.Services.AddStellaOpsEventing(builder.Configuration); builder.Services.AddTimelineServices(builder.Configuration); builder.Services.AddSingleton(TimeProvider.System); builder.Services.Configure(options => { options.JobEngineBaseUrl = builder.Configuration["UnifiedAudit:Sources:JobEngine"] ?? builder.Configuration["STELLAOPS_JOBENGINE_URL"] ?? options.JobEngineBaseUrl; options.PolicyBaseUrl = builder.Configuration["UnifiedAudit:Sources:Policy"] ?? builder.Configuration["STELLAOPS_POLICY_GATEWAY_URL"] ?? options.PolicyBaseUrl; options.EvidenceLockerBaseUrl = builder.Configuration["UnifiedAudit:Sources:EvidenceLocker"] ?? builder.Configuration["STELLAOPS_EVIDENCELOCKER_URL"] ?? options.EvidenceLockerBaseUrl; options.NotifyBaseUrl = builder.Configuration["UnifiedAudit:Sources:Notify"] ?? builder.Configuration["STELLAOPS_NOTIFY_URL"] ?? options.NotifyBaseUrl; if (int.TryParse(builder.Configuration["UnifiedAudit:FetchLimitPerModule"], out var fetchLimit)) { options.FetchLimitPerModule = fetchLimit; } if (int.TryParse(builder.Configuration["UnifiedAudit:RequestTimeoutSeconds"], out var timeoutSeconds)) { options.RequestTimeoutSeconds = timeoutSeconds; } }); builder.Services.AddHttpClient(HttpUnifiedAuditEventProvider.ClientName, (provider, client) => { var options = provider.GetRequiredService>().Value; client.Timeout = TimeSpan.FromSeconds(Math.Max(1, options.RequestTimeoutSeconds)); }); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new() { Title = "StellaOps Timeline API", Version = "v1", Description = "Unified event timeline API for querying, replaying, and exporting HLC-ordered events" }); }); builder.Services.AddHealthChecks() .AddCheck("timeline"); // Authentication and authorization builder.Services.AddStellaOpsResourceServerAuthentication(builder.Configuration); builder.Services.AddStellaOpsTenantServices(); builder.Services.AddAuthorization(options => { options.AddStellaOpsScopePolicy(TimelinePolicies.Read, StellaOpsScopes.TimelineRead); options.AddStellaOpsScopePolicy(TimelinePolicies.Write, StellaOpsScopes.TimelineWrite); }); builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration); builder.Services.AddStellaOpsLocalization(builder.Configuration); builder.Services.AddTranslationBundle(System.Reflection.Assembly.GetExecutingAssembly()); // Stella Router integration var routerEnabled = builder.Services.AddRouterMicroservice( builder.Configuration, serviceName: "timeline", version: System.Reflection.CustomAttributeExtensions.GetCustomAttribute(System.Reflection.Assembly.GetExecutingAssembly())?.InformationalVersion ?? "1.0.0", routerOptionsSection: "Router"); builder.TryAddStellaOpsLocalBinding("timeline"); var app = builder.Build(); app.LogStellaOpsLocalHostname("timeline"); // Configure the HTTP request pipeline if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseStellaOpsCors(); app.UseStellaOpsLocalization(); app.UseAuthentication(); app.UseAuthorization(); app.UseStellaOpsTenantMiddleware(); app.TryUseStellaRouter(routerEnabled); // Map endpoints await app.LoadTranslationsAsync(); app.MapTimelineEndpoints(); app.MapReplayEndpoints(); app.MapExportEndpoints(); app.MapUnifiedAuditEndpoints(); app.MapHealthEndpoints(); app.TryRefreshStellaRouterEndpoints(routerEnabled); await app.RunAsync().ConfigureAwait(false); namespace StellaOps.Timeline.WebService { public partial class Program { } }