using System.Text.Json.Serialization; using OpenTelemetry.Trace; using Serilog; using TeamUp.Bootstrap; using TeamUp.Infrastructure.Observability; using TeamUp.Infrastructure.Persistence; var builder = WebApplication.CreateBuilder(args); builder.Host.UseSerilog((context, services, configuration) => configuration .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services)); builder.Services.AddOpenApi(); // Bind/serialize enums as strings across the API (e.g. ScopeType "Organization", RoleType "Member"). builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter())); builder.Services.AddTeamUpObservability( builder.Configuration, serviceName: "teamup-web", configureTracing: tracing => tracing.AddAspNetCoreInstrumentation()); builder.Services.AddTeamUpPersistence(builder.Configuration); builder.Services.AddTeamUpModules(builder.Configuration); var app = builder.Build(); // Apply migrations on startup when configured (default: in Development). EF Core takes a // DB-wide lock, so the web and worker applying concurrently is safe. if (app.Configuration.GetValue("Database:ApplyMigrationsOnStartup", app.Environment.IsDevelopment())) { await MigrationRunner.MigrateAllAsync(app.Services); } if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } app.UseSerilogRequestLogging(); // Serve the built SPA from wwwroot (single deployable). UseStaticFiles (not MapStaticAssets) // because the SPA is copied into wwwroot at publish/Docker time, after the build-time asset // manifest is computed. app.UseDefaultFiles(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapHealthChecks("/health"); app.MapTeamUpModules(); // SPA deep links (client-side routing) fall back to index.html. app.MapFallbackToFile("index.html"); app.Run(); /// Exposed so the integration tests can drive the host via WebApplicationFactory. public partial class Program { }