Improved the data seeder function
This commit is contained in:
parent
cf01fd1138
commit
0b1d2669ca
@ -230,8 +230,8 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
: image.Base64Data;
|
: image.Base64Data;
|
||||||
|
|
||||||
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||||
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_report");
|
var fileName = _s3Service.GenerateFileName(fileType, taskAllocation.Id, "task_report");
|
||||||
var objectKey = $"tenant-{tenantId}/project-{projectId}/Actitvity/{fileName}";
|
var objectKey = $"tenant-{tenantId}/project-{projectId}/Activity/{fileName}";
|
||||||
|
|
||||||
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
: image.Base64Data;
|
: image.Base64Data;
|
||||||
|
|
||||||
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||||
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_comment");
|
var fileName = _s3Service.GenerateFileName(fileType, comment.Id, "task_comment");
|
||||||
var objectKey = $"tenant-{tenantId}/project-{projectId}/Activity/{fileName}";
|
var objectKey = $"tenant-{tenantId}/project-{projectId}/Activity/{fileName}";
|
||||||
|
|
||||||
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
||||||
|
|||||||
@ -4,134 +4,211 @@ using Marco.Pms.Model.Entitlements;
|
|||||||
using Marco.Pms.Model.Roles;
|
using Marco.Pms.Model.Roles;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Options; // For configuration
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace Marco.Pms.Services.Service
|
namespace Marco.Pms.Services.Service
|
||||||
{
|
{
|
||||||
|
// A configuration class to hold settings from appsettings.json
|
||||||
|
// This avoids hardcoding sensitive information.
|
||||||
|
public class SuperAdminSettings
|
||||||
|
{
|
||||||
|
public const string CONFIG_SECTION_NAME = "SuperAdminAccount";
|
||||||
|
public string Email { get; set; } = "admin@marcoaiot.com";
|
||||||
|
public string Password { get; set; } = "User@123";
|
||||||
|
public string TenantId { get; set; } = "b3466e83-7e11-464c-b93a-daf047838b26";
|
||||||
|
}
|
||||||
|
|
||||||
public class StartupUserSeeder : IHostedService
|
public class StartupUserSeeder : IHostedService
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly ILogger<StartupUserSeeder> _logger;
|
||||||
|
|
||||||
public StartupUserSeeder(IServiceProvider serviceProvider)
|
// Constants to avoid "magic strings"
|
||||||
|
private const string AdminJobRoleName = "Admin";
|
||||||
|
private const string SuperUserRoleName = "Super User";
|
||||||
|
|
||||||
|
public StartupUserSeeder(IServiceProvider serviceProvider, ILogger<StartupUserSeeder> logger)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("Starting database seeding process...");
|
||||||
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
var serviceProvider = scope.ServiceProvider;
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
||||||
|
|
||||||
var userEmail = "admin@marcoaiot.com";
|
// Get services from the scoped provider
|
||||||
|
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||||
|
var dbContext = serviceProvider.GetRequiredService<ApplicationDbContext>();
|
||||||
|
var adminSettings = serviceProvider.GetRequiredService<IOptions<SuperAdminSettings>>().Value;
|
||||||
|
var tenantId = Guid.Parse(adminSettings.TenantId);
|
||||||
|
|
||||||
var user = await userManager.FindByEmailAsync(userEmail);
|
// Use a database transaction to ensure all operations succeed or none do.
|
||||||
var newUser = new ApplicationUser
|
await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
UserName = userEmail,
|
// 1. Seed the Application User (Super Admin)
|
||||||
Email = userEmail,
|
var user = await SeedSuperAdminUserAsync(userManager, adminSettings, tenantId);
|
||||||
EmailConfirmed = true,
|
|
||||||
IsRootUser = true,
|
|
||||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
|
||||||
};
|
|
||||||
var result = new IdentityResult();
|
|
||||||
|
|
||||||
|
// 2. Seed the Job Role
|
||||||
|
var jobRole = await GetOrCreateAsync(
|
||||||
|
dbContext.JobRoles,
|
||||||
|
j => j.Name == AdminJobRoleName && j.TenantId == tenantId,
|
||||||
|
() => new JobRole
|
||||||
|
{
|
||||||
|
Name = AdminJobRoleName,
|
||||||
|
Description = "Administrator with full system access.",
|
||||||
|
TenantId = tenantId
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Seed the Application Role
|
||||||
|
var appRole = await GetOrCreateAsync(
|
||||||
|
dbContext.ApplicationRoles,
|
||||||
|
a => a.Role == SuperUserRoleName && a.TenantId == tenantId,
|
||||||
|
() => new ApplicationRole
|
||||||
|
{
|
||||||
|
Role = SuperUserRoleName,
|
||||||
|
Description = "System role with all permissions.",
|
||||||
|
IsSystem = true,
|
||||||
|
TenantId = tenantId
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Seed the Employee record linked to the user and job role
|
||||||
|
var employee = await GetOrCreateAsync(
|
||||||
|
dbContext.Employees,
|
||||||
|
e => e.Email == adminSettings.Email && e.TenantId == tenantId,
|
||||||
|
() => new Employee
|
||||||
|
{
|
||||||
|
ApplicationUserId = user.Id,
|
||||||
|
FirstName = "Admin",
|
||||||
|
LastName = "User",
|
||||||
|
Email = adminSettings.Email,
|
||||||
|
TenantId = tenantId,
|
||||||
|
PhoneNumber = "9876543210",
|
||||||
|
JobRoleId = jobRole.Id,
|
||||||
|
IsSystem = true,
|
||||||
|
JoiningDate = DateTime.UtcNow,
|
||||||
|
BirthDate = new DateTime(1970, 1, 1)
|
||||||
|
// Set other non-nullable fields to sensible defaults
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. Seed the Employee-Role Mapping
|
||||||
|
await GetOrCreateAsync(
|
||||||
|
dbContext.EmployeeRoleMappings,
|
||||||
|
erm => erm.EmployeeId == employee.Id && erm.RoleId == appRole.Id,
|
||||||
|
() => new EmployeeRoleMapping
|
||||||
|
{
|
||||||
|
EmployeeId = employee.Id,
|
||||||
|
RoleId = appRole.Id,
|
||||||
|
TenantId = tenantId,
|
||||||
|
IsEnabled = true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. Seed Role Permissions (Efficiently)
|
||||||
|
await SeedRolePermissionsAsync(dbContext, appRole.Id);
|
||||||
|
|
||||||
|
// All entities are now tracked by the DbContext.
|
||||||
|
// A single SaveChanges call is more efficient.
|
||||||
|
await dbContext.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
// If all operations were successful, commit the transaction.
|
||||||
|
await transaction.CommitAsync(cancellationToken);
|
||||||
|
_logger.LogInformation("Database seeding process completed successfully.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "An error occurred during database seeding. Rolling back changes.");
|
||||||
|
await transaction.RollbackAsync(cancellationToken);
|
||||||
|
// Optionally re-throw or handle the exception as needed
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ApplicationUser> SeedSuperAdminUserAsync(UserManager<ApplicationUser> userManager, SuperAdminSettings settings, Guid tenantId)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Seeding Super Admin user: {Email}", settings.Email);
|
||||||
|
var user = await userManager.FindByEmailAsync(settings.Email);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
result = await userManager.CreateAsync(newUser, "User@123");
|
user = new ApplicationUser
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newUser = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
var jobRole = new JobRole
|
|
||||||
{
|
|
||||||
Id = Guid.Empty,
|
|
||||||
Name = "Admin",
|
|
||||||
Description = "Admin",
|
|
||||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!await dbContext.JobRoles.Where(j => j.Name == "Admin").AnyAsync())
|
|
||||||
{
|
|
||||||
await dbContext.JobRoles.AddAsync(jobRole);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jobRole = await dbContext.JobRoles.Where(j => j.Name == "Admin").FirstOrDefaultAsync();
|
|
||||||
}
|
|
||||||
var role = new ApplicationRole
|
|
||||||
{
|
|
||||||
Role = "Super User",
|
|
||||||
Description = "Super User",
|
|
||||||
IsSystem = true,
|
|
||||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
|
||||||
};
|
|
||||||
if (!await dbContext.ApplicationRoles.Where(a => a.Role == "Super User").AnyAsync())
|
|
||||||
{
|
|
||||||
await dbContext.ApplicationRoles.AddAsync(role);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
role = await dbContext.ApplicationRoles.Where(a => a.Role == "Super User").FirstOrDefaultAsync();
|
|
||||||
}
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
var employee = new Employee
|
|
||||||
{
|
|
||||||
ApplicationUserId = newUser.Id,
|
|
||||||
FirstName = "Admin",
|
|
||||||
LastName = "",
|
|
||||||
Email = userEmail,
|
|
||||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26"),
|
|
||||||
CurrentAddress = "",
|
|
||||||
BirthDate = Convert.ToDateTime("1965-04-20 10:11:17.588000"),
|
|
||||||
EmergencyPhoneNumber = "",
|
|
||||||
EmergencyContactPerson = "",
|
|
||||||
AadharNumber = "",
|
|
||||||
Gender = "",
|
|
||||||
MiddleName = "",
|
|
||||||
PanNumber = "",
|
|
||||||
PermanentAddress = "",
|
|
||||||
PhoneNumber = "9876543210",
|
|
||||||
Photo = null, // GetFileDetails(model.Photo).Result.FileData,
|
|
||||||
JobRoleId = jobRole != null ? jobRole.Id : Guid.Empty,
|
|
||||||
IsSystem = true,
|
|
||||||
JoiningDate = Convert.ToDateTime("2000-04-20 10:11:17.588000"),
|
|
||||||
};
|
|
||||||
if ((!await dbContext.Employees.Where(e => e.Email == "admin@marcoaiot.com").AnyAsync()) && jobRole?.Id != Guid.Empty)
|
|
||||||
{
|
|
||||||
await dbContext.Employees.AddAsync(employee);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
employee = await dbContext.Employees.Where(e => e.Email == "admin@marcoaiot.com").FirstOrDefaultAsync();
|
|
||||||
}
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
if (!await dbContext.EmployeeRoleMappings.AnyAsync())
|
|
||||||
{
|
|
||||||
await dbContext.EmployeeRoleMappings.AddAsync(new EmployeeRoleMapping
|
|
||||||
{
|
{
|
||||||
EmployeeId = employee?.Id ?? Guid.Empty,
|
UserName = settings.Email,
|
||||||
IsEnabled = true,
|
Email = settings.Email,
|
||||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26"),
|
EmailConfirmed = true,
|
||||||
RoleId = role?.Id ?? Guid.Empty
|
IsRootUser = true,
|
||||||
});
|
TenantId = tenantId
|
||||||
}
|
};
|
||||||
if (!await dbContext.RolePermissionMappings.AnyAsync())
|
var result = await userManager.CreateAsync(user, settings.Password);
|
||||||
{
|
|
||||||
List<FeaturePermission> permissions = await dbContext.FeaturePermissions.ToListAsync();
|
if (!result.Succeeded)
|
||||||
List<RolePermissionMappings> rolesMapping = new List<RolePermissionMappings>();
|
|
||||||
foreach (var permission in permissions)
|
|
||||||
{
|
{
|
||||||
rolesMapping.Add(new RolePermissionMappings
|
// If user creation fails, it's a critical error.
|
||||||
{
|
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||||
ApplicationRoleId = role != null ? role.Id : Guid.Empty,
|
_logger.LogError("Failed to create super admin user. Errors: {Errors}", errors);
|
||||||
FeaturePermissionId = permission.Id
|
throw new InvalidOperationException($"Failed to create super admin user: {errors}");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
await dbContext.RolePermissionMappings.AddRangeAsync(rolesMapping);
|
_logger.LogInformation("Super Admin user created successfully.");
|
||||||
}
|
}
|
||||||
await dbContext.SaveChangesAsync();
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Super Admin user already exists.");
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SeedRolePermissionsAsync(ApplicationDbContext dbContext, Guid superUserRoleId)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Seeding permissions for Super User role (ID: {RoleId})", superUserRoleId);
|
||||||
|
|
||||||
|
var allPermissionIds = await dbContext.FeaturePermissions
|
||||||
|
.Select(p => p.Id)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var permissionIdsFromDb = await dbContext.RolePermissionMappings
|
||||||
|
.Where(pm => pm.ApplicationRoleId == superUserRoleId)
|
||||||
|
.Select(pm => pm.FeaturePermissionId)
|
||||||
|
.ToListAsync(); // 1. Fetch data from DB into a List
|
||||||
|
|
||||||
|
var existingPermissionIds = new HashSet<Guid>(permissionIdsFromDb); // 2. Convert the List to a HashSet in memory
|
||||||
|
|
||||||
|
var missingPermissionIds = allPermissionIds.Except(existingPermissionIds).ToList();
|
||||||
|
|
||||||
|
if (missingPermissionIds.Any())
|
||||||
|
{
|
||||||
|
var newMappings = missingPermissionIds.Select(permissionId => new RolePermissionMappings
|
||||||
|
{
|
||||||
|
ApplicationRoleId = superUserRoleId,
|
||||||
|
FeaturePermissionId = permissionId
|
||||||
|
});
|
||||||
|
|
||||||
|
await dbContext.RolePermissionMappings.AddRangeAsync(newMappings);
|
||||||
|
_logger.LogInformation("Added {Count} new permission mappings to the Super User role.", missingPermissionIds.Count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Super User role already has all available permissions.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A generic helper to find an entity by a predicate or create, add, and return it if not found.
|
||||||
|
/// This promotes code reuse and makes the main logic cleaner.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<T> GetOrCreateAsync<T>(DbSet<T> dbSet, Expression<Func<T, bool>> predicate, Func<T> factory) where T : class
|
||||||
|
{
|
||||||
|
var entity = await dbSet.FirstOrDefaultAsync(predicate);
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
entity = factory();
|
||||||
|
await dbSet.AddAsync(entity);
|
||||||
|
_logger.LogInformation("Creating new entity of type {EntityType}.", typeof(T).Name);
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user