using System.Text; using System.Text.Json.Serialization; using FluentValidation; using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Prometheus; using Serilog; using StackExchange.Redis; try { var builder = WebApplication.CreateBuilder(args); if (!builder.Environment.IsEnvironment("Testing")) builder.Host.UseSerilog((ctx, _, cfg) => cfg.ReadFrom.Configuration(ctx.Configuration)); builder.Services.AddDbContext(opts => opts.UseNpgsql(builder.Configuration.GetConnectionString("GatewayDb"))); builder.Services.AddSingleton(sp => ConnectionMultiplexer.Connect(builder.Configuration["Redis:ConnectionString"]!)); builder.Services.Configure(builder.Configuration.GetSection(GatewayOptions.Section)); builder.Services.Configure(builder.Configuration.GetSection(CentralApiOptions.Section)); builder.Services.Configure(builder.Configuration.GetSection(RabbitMqOptions.Section)); builder.Services.Configure(builder.Configuration.GetSection(SuppressionOptions.SectionName)); var jwt = builder.Configuration.GetSection("Jwt"); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => { o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwt["Issuer"], ValidAudience = jwt["Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwt["SigningKey"]!)) }; }); builder.Services.AddAuthorization(); builder.Services.AddFluentValidationAutoValidation(); builder.Services.AddValidatorsFromAssemblyContaining(); builder.Services.AddHttpClient("central"); builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); builder.Services.AddSingleton(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // Add services to the container. builder.Services.AddHealthChecks() .AddDbContextCheck("postgresql", tags: ["ready"]) .AddCheck("redis", tags: ["ready"]) .AddCheck("rabbitmq", tags: ["ready"]) .AddCheck("encounter_replica", tags: ["ready"]); builder.Services.AddControllers() .AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); builder.Services.AddCors(o => o.AddPolicy("Dashboard", p => p.WithOrigins("http://localhost:5173").AllowAnyHeader().AllowAnyMethod())); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); app.UseMiddleware(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } if (!app.Environment.IsEnvironment("Testing")) { app.UseSerilogRequestLogging(); } app.UseCors("Dashboard"); app.UseAuthentication(); app.UseAuthorization(); app.UseHttpMetrics(); app.MapControllers(); app.MapMetrics("/metrics"); app.MapHealthChecks("/health/live", new() { Predicate = _ => false, ResponseWriter = HealthCheckResponseWriter.WriteAsync }); app.MapHealthChecks("/health/ready", new() { Predicate = c => c.Tags.Contains("ready"), ResponseWriter = HealthCheckResponseWriter.WriteAsync }); // Startup migration is the proven path for this single-instance edge host. // Gate with Gateway:AutoMigrate so production can switch to an out-of-band // EF migration bundle later without a code change (Phase 36 Step 6). if (!app.Environment.IsEnvironment("Testing") && builder.Configuration.GetValue("Gateway:AutoMigrate", true)) { using var scope = app.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await db.Database.MigrateAsync(); } app.Run(); } catch (System.Exception) { throw; } public partial class Program { }