using Microsoft.EntityFrameworkCore; using Serilog; using Sufi.Demo.PeopleDirectory.Application.Extensions; using Sufi.Demo.PeopleDirectory.Infrastructure; using Sufi.Demo.PeopleDirectory.Persistence.Contexts; using Sufi.Demo.PeopleDirectory.Persistence.Extensions; using Sufi.Demo.PeopleDirectory.UI.Server.Extensions; using Sufi.Demo.PeopleDirectory.UI.Server.Options; using System.Threading.RateLimiting; #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member namespace Sufi.Demo.PeopleDirectory.UI.Server { public static class StartupExtensions { public static WebApplication ConfigureServices(this WebApplicationBuilder builder) { var configuration = builder.Configuration; Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .CreateLogger(); builder.Host.UseSerilog(); var rateLimitOptions = configuration.GetSection("RateLimit").Get()!; var services = builder.Services; var licenseKey = configuration.GetValue("LuckyPennyLicenseKey") ?? string.Empty; // Add services to the container. services.AddHttpContextAccessor(); services.AddApplicationLayer(licenseKey) .AddInfrastructureServices() .AddPersistenceServices(configuration); services.AddControllersWithViews(); services.AddRazorPages(); services.AddHealthChecks(); services.AddRateLimiter(options => { // Apply for all requests. options.GlobalLimiter = PartitionedRateLimiter.Create(httpContext => RateLimitPartition.GetFixedWindowLimiter( partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown", factory: partition => new FixedWindowRateLimiterOptions { AutoReplenishment = true, PermitLimit = rateLimitOptions.PermitLimit, Window = TimeSpan.FromSeconds(rateLimitOptions.Window) })); options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; }); // Register Swagger services. services.AddEndpointsApiExplorer(); services.RegisterSwagger(); return builder.Build(); } public static WebApplication ConfigurePipeline(this WebApplication app) { app.UseSerilogRequestLogging(options => { options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => { diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value); diagnosticContext.Set("UserAgent", httpContext.Request.Headers.UserAgent); diagnosticContext.Set("ClientIP", httpContext.Connection.RemoteIpAddress?.ToString() ?? ""); }; }); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); app.ConfigureSwagger(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); app.UseRouting(); app.UseRateLimiter(); app.MapHealthChecks("/health"); app.MapRazorPages(); app.MapControllers(); app.MapFallbackToFile("index.html"); return app; } public static void ConfigureSwagger(this IApplicationBuilder app) { app.UseSwagger(); app.UseSwaggerUI(options => { options.RoutePrefix = "swagger"; options.DisplayRequestDuration(); foreach (var desc in ((IEndpointRouteBuilder)app).DescribeApiVersions()) { options.SwaggerEndpoint($"{desc.GroupName}/swagger.json", desc.GroupName.ToUpperInvariant()); } }); } public static async Task EnsureDatabaseMigrationAsync(this IApplicationBuilder app) { using (var scope = app.ApplicationServices.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); await dbContext.Database.MigrateAsync(); } return app; } } }