using line_gestao_api.Models; using line_gestao_api.Services; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace line_gestao_api.Data; public class SeedOptions { public bool Enabled { get; set; } = true; public string AdminMasterName { get; set; } = "System Admin"; public string? AdminMasterEmail { get; set; } = "sysadmin@linegestao.local"; public string? AdminMasterPassword { get; set; } = "DevSysAdmin123!"; public bool ReapplyAdminCredentialsOnStartup { get; set; } = false; } public static class SeedData { public static async Task EnsureSeedDataAsync(IServiceProvider services) { using var scope = services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var userManager = scope.ServiceProvider.GetRequiredService>(); var roleManager = scope.ServiceProvider.GetRequiredService>>(); var tenantProvider = scope.ServiceProvider.GetRequiredService(); var options = scope.ServiceProvider.GetRequiredService>().Value; if (db.Database.IsRelational()) { await db.Database.MigrateAsync(); } else { await db.Database.EnsureCreatedAsync(); } // Mantem o contrato de roles atualizado em todos os ambientes, inclusive // quando o seed de usuario master estiver desabilitado. var roles = AppRoles.All; foreach (var role in roles) { if (!await roleManager.RoleExistsAsync(role)) { var roleResult = await roleManager.CreateAsync(new IdentityRole(role)); EnsureIdentitySucceeded(roleResult, $"Falha ao criar role '{role}'."); } } await MigrateLegacyRolesAsync(db, roleManager); if (!options.Enabled) { return; } var systemTenantId = SystemTenantConstants.SystemTenantId; var systemTenant = await db.Tenants.FirstOrDefaultAsync(t => t.Id == systemTenantId); if (systemTenant == null) { systemTenant = new Tenant { Id = systemTenantId, NomeOficial = SystemTenantConstants.SystemTenantNomeOficial, IsSystem = true, Ativo = true, CreatedAt = DateTime.UtcNow }; db.Tenants.Add(systemTenant); } else { systemTenant.NomeOficial = SystemTenantConstants.SystemTenantNomeOficial; systemTenant.IsSystem = true; systemTenant.Ativo = true; } await db.SaveChangesAsync(); var emailFromEnv = Environment.GetEnvironmentVariable("SYSADMIN_EMAIL") ?? Environment.GetEnvironmentVariable("ADMIN_MASTER_EMAIL"); var passwordFromEnv = Environment.GetEnvironmentVariable("SYSADMIN_PASSWORD") ?? Environment.GetEnvironmentVariable("ADMIN_MASTER_PASSWORD"); var adminMasterEmail = (emailFromEnv ?? options.AdminMasterEmail ?? string.Empty).Trim().ToLowerInvariant(); var adminMasterPassword = passwordFromEnv ?? options.AdminMasterPassword ?? string.Empty; if (string.IsNullOrWhiteSpace(adminMasterEmail) || string.IsNullOrWhiteSpace(adminMasterPassword)) { throw new InvalidOperationException( "Credenciais do sysadmin ausentes. Defina SYSADMIN_EMAIL e SYSADMIN_PASSWORD (ou Seed:AdminMasterEmail/Seed:AdminMasterPassword)."); } var normalizedEmail = userManager.NormalizeEmail(adminMasterEmail); var previousTenant = tenantProvider.TenantId; tenantProvider.SetTenantId(systemTenantId); try { var existingAdminMaster = await userManager.Users .IgnoreQueryFilters() .FirstOrDefaultAsync(u => u.TenantId == systemTenantId && u.NormalizedEmail == normalizedEmail); if (existingAdminMaster == null) { var adminMaster = new ApplicationUser { Name = options.AdminMasterName, Email = adminMasterEmail, UserName = adminMasterEmail, TenantId = systemTenantId, EmailConfirmed = true, IsActive = true, LockoutEnabled = true }; var createResult = await userManager.CreateAsync(adminMaster, adminMasterPassword); EnsureIdentitySucceeded(createResult, "Falha ao criar usuário sysadmin."); var addRoleResult = await userManager.AddToRoleAsync(adminMaster, SystemTenantConstants.SystemRole); EnsureIdentitySucceeded(addRoleResult, "Falha ao associar role sysadmin ao usuário inicial."); } else { existingAdminMaster.Name = options.AdminMasterName; existingAdminMaster.Email = adminMasterEmail; existingAdminMaster.UserName = adminMasterEmail; existingAdminMaster.EmailConfirmed = true; existingAdminMaster.IsActive = true; existingAdminMaster.LockoutEnabled = true; var updateResult = await userManager.UpdateAsync(existingAdminMaster); EnsureIdentitySucceeded(updateResult, "Falha ao atualizar usuário sysadmin."); if (options.ReapplyAdminCredentialsOnStartup) { await userManager.SetLockoutEndDateAsync(existingAdminMaster, null); await userManager.ResetAccessFailedCountAsync(existingAdminMaster); var resetToken = await userManager.GeneratePasswordResetTokenAsync(existingAdminMaster); var resetPasswordResult = await userManager.ResetPasswordAsync(existingAdminMaster, resetToken, adminMasterPassword); if (!resetPasswordResult.Succeeded) { var removePasswordResult = await userManager.RemovePasswordAsync(existingAdminMaster); if (removePasswordResult.Succeeded) { var addPasswordResult = await userManager.AddPasswordAsync(existingAdminMaster, adminMasterPassword); EnsureIdentitySucceeded(addPasswordResult, "Falha ao definir senha do sysadmin."); } else { var addPasswordResult = await userManager.AddPasswordAsync(existingAdminMaster, adminMasterPassword); EnsureIdentitySucceeded(addPasswordResult, "Falha ao definir senha do sysadmin."); } } } if (!await userManager.IsInRoleAsync(existingAdminMaster, SystemTenantConstants.SystemRole)) { var addRoleResult = await userManager.AddToRoleAsync(existingAdminMaster, SystemTenantConstants.SystemRole); EnsureIdentitySucceeded(addRoleResult, "Falha ao associar role sysadmin ao usuário inicial."); } } } finally { tenantProvider.SetTenantId(previousTenant); } } private static void EnsureIdentitySucceeded(IdentityResult result, string message) { if (result.Succeeded) { return; } var details = string.Join("; ", result.Errors.Select(e => e.Description)); throw new InvalidOperationException($"{message} Detalhes: {details}"); } private static async Task MigrateLegacyRolesAsync(AppDbContext db, RoleManager> roleManager) { await MigrateLegacyRoleAsync(db, roleManager, "admin_master", AppRoles.SysAdmin); await MigrateLegacyRoleAsync(db, roleManager, "admin", AppRoles.SysAdmin); await MigrateLegacyRoleAsync(db, roleManager, "leitura", AppRoles.Cliente); await MigrateLegacyRoleAsync(db, roleManager, "operador", AppRoles.Cliente); } private static async Task MigrateLegacyRoleAsync( AppDbContext db, RoleManager> roleManager, string legacyRole, string newRole) { var legacyRoleId = await roleManager.Roles .Where(r => r.Name == legacyRole) .Select(r => (Guid?)r.Id) .FirstOrDefaultAsync(); if (!legacyRoleId.HasValue) { return; } var newRoleId = await roleManager.Roles .Where(r => r.Name == newRole) .Select(r => (Guid?)r.Id) .FirstOrDefaultAsync(); if (!newRoleId.HasValue) { return; } var legacyUserIds = await db.UserRoles .Where(ur => ur.RoleId == legacyRoleId.Value) .Select(ur => ur.UserId) .Distinct() .ToListAsync(); if (legacyUserIds.Count == 0) { return; } var alreadyInNewRole = await db.UserRoles .Where(ur => ur.RoleId == newRoleId.Value && legacyUserIds.Contains(ur.UserId)) .Select(ur => ur.UserId) .ToListAsync(); var existingSet = alreadyInNewRole.ToHashSet(); foreach (var userId in legacyUserIds) { if (!existingSet.Contains(userId)) { db.UserRoles.Add(new IdentityUserRole { UserId = userId, RoleId = newRoleId.Value }); } } var legacyAssignments = await db.UserRoles .Where(ur => ur.RoleId == legacyRoleId.Value) .ToListAsync(); if (legacyAssignments.Count > 0) { db.UserRoles.RemoveRange(legacyAssignments); } await db.SaveChangesAsync(); var legacyRoleStillUsed = await db.UserRoles.AnyAsync(ur => ur.RoleId == legacyRoleId.Value); if (!legacyRoleStillUsed) { var legacyRoleEntity = await roleManager.Roles.FirstOrDefaultAsync(r => r.Id == legacyRoleId.Value); if (legacyRoleEntity != null) { await roleManager.DeleteAsync(legacyRoleEntity); } } } }