using Microsoft.EntityFrameworkCore; using mws.backend.dotnet.application.Auth; using mws.backend.dotnet.application.Permissions; using mws.backend.dotnet.domain.Documents; using mws.backend.dotnet.domain.MasterData; using mws.backend.dotnet.domain.Projects; using mws.backend.dotnet.domain.Roles; using mws.backend.dotnet.domain.Tasks; using mws.backend.dotnet.domain.Users; using TaskPriority = mws.backend.dotnet.domain.Tasks.TaskPriority; using TaskStatus = mws.backend.dotnet.domain.Tasks.TaskStatus; namespace mws.backend.dotnet.infrastructure.Persistence; public static class DbSeeder { public static async Task SeedAsync(AppDbContext db, IPasswordHasher passwordHasher) { await SeedMasterDataAsync(db); await EnsureRolesAsync(db); var admin = await EnsureUserAsync(db, passwordHasher, "admin", "Admin", "Admin"); var namdh = await EnsureUserAsync(db, passwordHasher, "namdh", "Nam DH", "Member"); if (await db.Projects.AnyAsync()) { return; } var now = DateTime.UtcNow; var project = new Project { Id = Guid.NewGuid(), Name = "MWS", Description = "My Workspace - internal tool for the team.", Status = ProjectStatus.Active, CreatedBy = admin.Id, CreatedAt = now, UpdatedBy = admin.Id, UpdatedAt = now, }; db.Projects.Add(project); db.ProjectMembers.AddRange( new ProjectMember { ProjectId = project.Id, UserId = admin.Id, Role = MemberRole.Owner }, new ProjectMember { ProjectId = project.Id, UserId = namdh.Id, Role = MemberRole.Member }); db.ProjectMemberPermissions.AddRange( new ProjectMemberPermission { ProjectId = project.Id, UserId = namdh.Id, Screen = ProjectPermissionScreens.Documents, CanView = true }); await db.SaveChangesAsync(); var requirements = new Document { Id = Guid.NewGuid(), ProjectId = project.Id, ParentId = null, Title = "Requirements", Type = DocumentType.Folder, CreatedBy = admin.Id, CreatedAt = now, UpdatedBy = admin.Id, UpdatedAt = now, }; var design = new Document { Id = Guid.NewGuid(), ProjectId = project.Id, ParentId = null, Title = "Technical Design", Type = DocumentType.Folder, CreatedBy = admin.Id, CreatedAt = now, UpdatedBy = admin.Id, UpdatedAt = now, }; var notes = new Document { Id = Guid.NewGuid(), ProjectId = project.Id, ParentId = null, Title = "Notes", Type = DocumentType.Folder, CreatedBy = admin.Id, CreatedAt = now, UpdatedBy = admin.Id, UpdatedAt = now, }; db.Documents.AddRange(requirements, design, notes); db.Documents.Add(new Document { Id = Guid.NewGuid(), ProjectId = project.Id, ParentId = requirements.Id, Title = "Authentication", Type = DocumentType.Document, CreatedBy = admin.Id, CreatedAt = now, UpdatedBy = admin.Id, UpdatedAt = now, }); db.Documents.Add(new Document { Id = Guid.NewGuid(), ProjectId = project.Id, ParentId = requirements.Id, Title = "User Management", Type = DocumentType.Document, CreatedBy = admin.Id, CreatedAt = now, UpdatedBy = admin.Id, UpdatedAt = now, }); db.Documents.Add(new Document { Id = Guid.NewGuid(), ProjectId = project.Id, ParentId = design.Id, Title = "API Design", Type = DocumentType.Document, CreatedBy = admin.Id, CreatedAt = now, UpdatedBy = admin.Id, UpdatedAt = now, }); await db.SaveChangesAsync(); db.Tasks.AddRange( new TaskItem { Id = Guid.NewGuid(), ProjectId = project.Id, Title = "Set up solution", Status = TaskStatus.Done, Priority = TaskPriority.High, AssigneeId = admin.Id, CreatedBy = admin.Id, CreatedAt = now, UpdatedAt = now, }, new TaskItem { Id = Guid.NewGuid(), ProjectId = project.Id, Title = "Implement authentication", Description = "Login endpoint with BCrypt and JWT.", Status = TaskStatus.InProgress, Priority = TaskPriority.High, AssigneeId = namdh.Id, DueDate = now.AddDays(3), CreatedBy = admin.Id, CreatedAt = now, UpdatedAt = now, }, new TaskItem { Id = Guid.NewGuid(), ProjectId = project.Id, Title = "Task board drag & drop", Status = TaskStatus.Todo, Priority = TaskPriority.Medium, AssigneeId = namdh.Id, DueDate = now.AddDays(7), CreatedBy = admin.Id, CreatedAt = now, UpdatedAt = now, }, new TaskItem { Id = Guid.NewGuid(), ProjectId = project.Id, Title = "Polish documents UI", Status = TaskStatus.Todo, Priority = TaskPriority.Low, CreatedBy = admin.Id, CreatedAt = now, UpdatedAt = now, }); await db.SaveChangesAsync(); } private static async Task EnsureRolesAsync(AppDbContext db) { var now = DateTime.UtcNow; await SyncSystemRoleAsync( db, "Admin", _ => (View: true, Create: true, Edit: true, Delete: true), now); await SyncSystemRoleAsync( db, "Member", screen => screen.Key switch { "users" or "permissions" or "masterdata" => (View: false, Create: false, Edit: false, Delete: false), "projects" => (View: true, Create: false, Edit: false, Delete: false), _ => (View: true, Create: true, Edit: true, Delete: false), }, now); } private static async Task SyncSystemRoleAsync( AppDbContext db, string name, Func rule, DateTime now) { var role = await db.Roles .Include(r => r.Permissions) .FirstOrDefaultAsync(r => r.Name == name); if (role is null) { role = new Role { Id = Guid.NewGuid(), Name = name, IsSystem = true, CreatedAt = now, UpdatedAt = now }; db.Roles.Add(role); } var byScreen = role.Permissions.ToDictionary(p => p.Screen); foreach (var screen in ScreenCatalog.Screens) { if (!byScreen.TryGetValue(screen.Key, out var permission)) { permission = new RolePermission { RoleId = role.Id, Screen = screen.Key }; role.Permissions.Add(permission); } var (view, create, edit, delete) = rule(screen); permission.CanView = view; permission.CanCreate = create; permission.CanEdit = edit; permission.CanDelete = delete; } role.UpdatedAt = now; await db.SaveChangesAsync(); } private static async Task EnsureUserAsync(AppDbContext db, IPasswordHasher passwordHasher, string username, string displayName, string roleName) { var existing = await db.Users.Include(u => u.UserRoles).FirstOrDefaultAsync(u => u.Username == username); if (existing != null) { return existing; } var role = await db.Roles.FirstAsync(r => r.Name == roleName); var now = DateTime.UtcNow; var user = new User { Id = Guid.NewGuid(), Username = username, PasswordHash = passwordHasher.Hash("password"), DisplayName = displayName, UserRoles = new List { new UserRole { RoleId = role.Id } }, CreatedAt = now, UpdatedAt = now, }; db.Users.Add(user); await db.SaveChangesAsync(); return user; } private static async Task SeedMasterDataAsync(AppDbContext db) { if (await db.MasterData.AnyAsync()) { return; } var now = DateTime.UtcNow; MasterDataEntry Entry(string group, string label, string value, int sortOrder) => new() { Id = Guid.NewGuid(), Group = group, Label = label, Value = value, SortOrder = sortOrder, IsActive = true, CreatedAt = now, UpdatedAt = now, }; db.MasterData.AddRange( Entry("task_status", "Todo", nameof(TaskStatus.Todo), 1), Entry("task_status", "In Progress", nameof(TaskStatus.InProgress), 2), Entry("task_status", "Done", nameof(TaskStatus.Done), 3), Entry("task_status", "Cancelled", nameof(TaskStatus.Cancelled), 4), Entry("task_priority", "Low", nameof(TaskPriority.Low), 1), Entry("task_priority", "Medium", nameof(TaskPriority.Medium), 2), Entry("task_priority", "High", nameof(TaskPriority.High), 3), Entry("project_status", "Active", nameof(ProjectStatus.Active), 1), Entry("project_status", "Archived", nameof(ProjectStatus.Archived), 2), Entry("member_role", "Owner", nameof(MemberRole.Owner), 1), Entry("member_role", "Member", nameof(MemberRole.Member), 2), Entry("document_type", "Folder", nameof(DocumentType.Folder), 1), Entry("document_type", "Document", nameof(DocumentType.Document), 2)); await db.SaveChangesAsync(); } }