using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; using NexusReader.Domain.Entities; using System; using System.Linq; using System.Threading.Tasks; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; namespace NexusReader.Data.Persistence; public static class DbInitializer { public static async Task SeedAsync(IServiceProvider serviceProvider) { using var scope = serviceProvider.CreateScope(); var passwordHasher = scope.ServiceProvider.GetRequiredService>(); var dbContextFactory = scope.ServiceProvider.GetRequiredService>(); var configuration = scope.ServiceProvider.GetService(); using var dbContext = await dbContextFactory.CreateDbContextAsync(); try { Console.WriteLine("[Seeder] Starting database seeding..."); // Seed Subscription Plans if (!dbContext.SubscriptionPlans.Any()) { dbContext.SubscriptionPlans.AddRange(new List { new SubscriptionPlan { Id = SubscriptionPlan.FreeId, PlanName = SubscriptionPlan.FreeName, AITokenLimit = 5000, IsUnlimitedTokens = false, MonthlyPrice = 0, StripeProductId = "prod_Free789" }, new SubscriptionPlan { Id = SubscriptionPlan.ProId, PlanName = SubscriptionPlan.ProName, AITokenLimit = 50000, IsUnlimitedTokens = false, MonthlyPrice = 19, StripeProductId = "prod_Pro123" }, new SubscriptionPlan { Id = SubscriptionPlan.EnterpriseId, PlanName = SubscriptionPlan.EnterpriseName, AITokenLimit = 1000000000, IsUnlimitedTokens = true, MonthlyPrice = 99, StripeProductId = "prod_Enterprise456" } }); await dbContext.SaveChangesAsync(); Console.WriteLine("[Seeder] Subscription plans seeded."); } // Seed Roles string[] roleNames = { "Admin", "User" }; foreach (var roleName in roleNames) { var roleExist = dbContext.Roles.Any(r => r.Name == roleName); if (!roleExist) { dbContext.Roles.Add(new IdentityRole { Name = roleName, NormalizedName = roleName.ToUpper() }); Console.WriteLine($"[Seeder] Created role: {roleName}"); } } await dbContext.SaveChangesAsync(); // Seed Admin User var adminEmail = "admin@nexus.com"; var normalizedEmail = adminEmail.ToUpper(); var adminUser = await dbContext.Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail); if (adminUser == null) { adminUser = new NexusUser { UserName = adminEmail, NormalizedUserName = normalizedEmail, Email = adminEmail, NormalizedEmail = normalizedEmail, EmailConfirmed = true, SubscriptionPlanId = SubscriptionPlan.EnterpriseId, AITokenLimit = 1000000, TenantId = Guid.NewGuid().ToString(), SecurityStamp = Guid.NewGuid().ToString() }; var adminPassword = configuration?["Nexus:AdminPassword"] ?? configuration?["NEXUS_ADMIN_PASSWORD"] ?? Environment.GetEnvironmentVariable("NEXUS_ADMIN_PASSWORD"); var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Development"; var isDevelopment = string.Equals(env, "Development", StringComparison.OrdinalIgnoreCase); if (string.IsNullOrEmpty(adminPassword)) { if (!isDevelopment) { throw new InvalidOperationException( "CRITICAL SECURITY ERROR: Admin password is NOT configured! " + "In non-Development environments (e.g. Test/Production), the admin password must be explicitly set " + "via configuration ('Nexus:AdminPassword' or 'NEXUS_ADMIN_PASSWORD') or environment variables. " + "Seeding aborted to prevent insecure credentials fallback."); } Console.WriteLine("[Seeder] WARNING: Admin password is not set. Falling back to default weak password 'Admin123!' in Development environment."); adminPassword = "Admin123!"; } adminUser.PasswordHash = passwordHasher.HashPassword(adminUser, adminPassword); dbContext.Users.Add(adminUser); await dbContext.SaveChangesAsync(); var adminRole = await dbContext.Roles.FirstAsync(r => r.Name == "Admin"); dbContext.UserRoles.Add(new IdentityUserRole { UserId = adminUser.Id, RoleId = adminRole.Id }); await dbContext.SaveChangesAsync(); Console.WriteLine($"[Seeder] Admin user created successfully: {adminEmail}"); // Seed Sample Author var author = await dbContext.Authors.FirstOrDefaultAsync(a => a.Name == "Giorgio Vasari"); if (author == null) { author = new Author { Name = "Giorgio Vasari" }; dbContext.Authors.Add(author); await dbContext.SaveChangesAsync(); } // Seed Sample Ebook if (!dbContext.Ebooks.Any(e => e.UserId == adminUser.Id)) { dbContext.Ebooks.Add(new Ebook { Title = "Lives of the Most Excellent Painters, Sculptors, and Architects", AuthorId = author.Id, UserId = adminUser.Id, FilePath = "wwwroot/assets/book.epub", AddedDate = DateTime.UtcNow, LastReadDate = DateTime.UtcNow, Progress = 0, LastChapter = "Introduction" }); await dbContext.SaveChangesAsync(); Console.WriteLine("[Seeder] Sample book seeded for admin."); } } else { Console.WriteLine("[Seeder] Admin user already exists."); } } catch (Exception ex) { Console.WriteLine($"[Seeder] Critical error during seeding: {ex.Message}"); } } }