using System.Text.Json.Serialization; using AspNetCore.Authentication.ApiKey; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.OpenApi.Models; using Npgsql; using StalwartSimpleLoginMiddleware.Contexts; using StalwartSimpleLoginMiddleware.Models; using StalwartSimpleLoginMiddleware.Repositories; using StalwartSimpleLoginMiddleware.Services; var logger = LoggerFactory.Create(logging => logging.AddConsole()).CreateLogger("Startup"); var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddDbContextPool((_, options) => ApiKeyContext.ConfigureOptions(options)); builder.Services.AddHealthChecks() .AddCheck("WebService", () => HealthCheckResult.Healthy("The web service is running.")) .AddDbContextCheck(); builder.Services.AddScoped() .AddScoped(); builder.Services.AddAuthentication(ApiKeyDefaults.AuthenticationScheme) .AddApiKeyInHeader(options => { options.Realm = "StalwartSimpleLoginMiddleware"; options.KeyName = "Authentication"; options.Events = new ApiKeyProvider(); }); builder.Services.AddAuthorization(options => { options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); }); builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; }); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { // Add the API Key Security Definition c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme { Description = "API Key needed to access endpoints. Add it to the request headers as 'Authentication: '", Type = SecuritySchemeType.ApiKey, Name = "Authentication", // Header name for the API Key In = ParameterLocation.Header }); // Add security requirements to ensure the header is required c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "ApiKey" } }, new string[] { } // No specific scopes } }); }); builder.Services.AddSingleton() .AddSingleton(); var app = builder.Build(); // Ensure the database is migrated at startup. using (var scope = app.Services.CreateScope()) { try { var services = scope.ServiceProvider; var context = services.GetRequiredService(); await context.EnsureDatabaseMigrated(logger); } catch (NpgsqlException ex) { if (ex.SqlState is PostgresErrorCodes.ConnectionFailure or PostgresErrorCodes.ConnectionException || ex.Message.StartsWith("Failed to connect")) logger.LogCritical($"Database connection failed: {ex.Message}"); else if (ex.SqlState is PostgresErrorCodes.InvalidPassword or PostgresErrorCodes.InvalidAuthorizationSpecification) logger.LogCritical("Failed to connect. Invalid password."); else logger.LogCritical($"An unknown error occurred:{Environment.NewLine}{ex.StackTrace}"); } } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapHealthChecks("/health"); app.Run();