using BirthList.Infrastructure.Persistence; using BirthList.Web.Authorization; using BirthList.Web.Configuration; using BirthList.Web.Features.Registries; using BirthList.Web.Services; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using BirthList.Web.Components; using BirthList.Web.Components.Account; using BirthList.Web.Data; using System.Data; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); builder.Services.AddHttpClient("RegistryMetadata", client => { client.Timeout = TimeSpan.FromSeconds(10); }); builder.Services.Configure(builder.Configuration.GetSection("Smtp")); builder.Services.AddCascadingAuthenticationState(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); var googleClientId = builder.Configuration["Authentication:Google:ClientId"]; var googleClientSecret = builder.Configuration["Authentication:Google:ClientSecret"]; var microsoftClientId = builder.Configuration["Authentication:Microsoft:ClientId"]; var microsoftClientSecret = builder.Configuration["Authentication:Microsoft:ClientSecret"]; var authBuilder = builder.Services.AddAuthentication(options => { options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme; }); authBuilder.AddIdentityCookies(); if (!string.IsNullOrWhiteSpace(googleClientId) && !string.IsNullOrWhiteSpace(googleClientSecret)) { authBuilder.AddGoogle(options => { options.ClientId = googleClientId; options.ClientSecret = googleClientSecret; }); } if (!string.IsNullOrWhiteSpace(microsoftClientId) && !string.IsNullOrWhiteSpace(microsoftClientSecret)) { authBuilder.AddMicrosoftAccount(options => { options.ClientId = microsoftClientId; options.ClientSecret = microsoftClientSecret; }); } var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); var dataProvider = builder.Configuration["Data:Provider"] ?? "SqlServer"; builder.Services.AddDbContext(options => { if (string.Equals(dataProvider, "SqlServer", StringComparison.OrdinalIgnoreCase)) { options.UseSqlServer(connectionString); return; } options.UseSqlite(connectionString); }); builder.Services.AddDbContext(options => { if (string.Equals(dataProvider, "SqlServer", StringComparison.OrdinalIgnoreCase)) { options.UseSqlServer(connectionString); return; } options.UseSqlite(connectionString); }); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddIdentityCore(options => { options.SignIn.RequireConfirmedAccount = false; }) .AddEntityFrameworkStores() .AddSignInManager() .AddDefaultTokenProviders(); builder.Services.AddScoped(); builder.Services.AddScoped>(serviceProvider => serviceProvider.GetRequiredService()); var app = builder.Build(); using (var scope = app.Services.CreateScope()) { var applicationDbContext = scope.ServiceProvider.GetRequiredService(); await applicationDbContext.Database.MigrateAsync(); var registryDbContext = scope.ServiceProvider.GetRequiredService(); var registryMigrations = registryDbContext.Database.GetMigrations(); if (registryMigrations.Any()) { await registryDbContext.Database.MigrateAsync(); } else { var databaseCreator = registryDbContext.Database.GetService(); if (!await databaseCreator.ExistsAsync()) { await registryDbContext.Database.EnsureCreatedAsync(); } else { var platformOwnersTableExists = await TableExistsAsync(registryDbContext, "PlatformOwners"); if (!platformOwnersTableExists) { await databaseCreator.CreateTablesAsync(); } } } } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error", createScopeForErrors: true); // 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.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAntiforgery(); app.MapRazorComponents() .AddInteractiveServerRenderMode(); // Add additional endpoints required by the Identity /Account Razor components. app.MapAdditionalIdentityEndpoints(); app.Run(); static async Task TableExistsAsync(DbContext context, string tableName) { ArgumentNullException.ThrowIfNull(context); ArgumentException.ThrowIfNullOrWhiteSpace(tableName); var connection = context.Database.GetDbConnection(); var closeConnection = connection.State != ConnectionState.Open; if (closeConnection) { await connection.OpenAsync(); } try { await using var command = connection.CreateCommand(); if (context.Database.IsSqlServer()) { command.CommandText = "SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @tableName"; } else if (context.Database.IsSqlite()) { command.CommandText = "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = @tableName"; } else { throw new NotSupportedException("Unsupported database provider for table existence check."); } var parameter = command.CreateParameter(); parameter.ParameterName = "@tableName"; parameter.Value = tableName; command.Parameters.Add(parameter); var result = await command.ExecuteScalarAsync(); return result is not null && result != DBNull.Value; } finally { if (closeConnection) { await connection.CloseAsync(); } } }