package service import ( "context" "eslogad-be/internal/contract" "eslogad-be/internal/entities" "eslogad-be/internal/repository" "eslogad-be/internal/transformer" "github.com/google/uuid" ) type RBACServiceImpl struct { repo *repository.RBACRepository } func NewRBACService(repo *repository.RBACRepository) *RBACServiceImpl { return &RBACServiceImpl{repo: repo} } // Permissions func (s *RBACServiceImpl) CreatePermission(ctx context.Context, req *contract.CreatePermissionRequest) (*contract.PermissionResponse, error) { p := &entities.Permission{Code: req.Code} if req.Description != nil { p.Description = *req.Description } if err := s.repo.CreatePermission(ctx, p); err != nil { return nil, err } return &contract.PermissionResponse{ ID: p.ID, Code: p.Code, Action: p.Action, Description: &p.Description, }, nil } func (s *RBACServiceImpl) UpdatePermission(ctx context.Context, id uuid.UUID, req *contract.UpdatePermissionRequest) (*contract.PermissionResponse, error) { p := &entities.Permission{ID: id} if req.Code != nil { p.Code = *req.Code } if req.Description != nil { p.Description = *req.Description } if err := s.repo.UpdatePermission(ctx, p); err != nil { return nil, err } // fetch full row perms, err := s.repo.ListPermissions(ctx) if err != nil { return nil, err } for _, x := range perms { if x.ID == id { return &contract.PermissionResponse{ ID: x.ID, Code: x.Code, Action: x.Action, Description: &x.Description, }, nil } } return nil, nil } func (s *RBACServiceImpl) DeletePermission(ctx context.Context, id uuid.UUID) error { return s.repo.DeletePermission(ctx, id) } func (s *RBACServiceImpl) ListPermissions(ctx context.Context) (*contract.ListPermissionsResponse, error) { perms, err := s.repo.ListPermissions(ctx) if err != nil { return nil, err } return &contract.ListPermissionsResponse{Permissions: transformer.PermissionsToContract(perms)}, nil } // Roles func (s *RBACServiceImpl) CreateRole(ctx context.Context, req *contract.CreateRoleRequest) (*contract.RoleWithPermissionsResponse, error) { role := &entities.Role{Name: req.Name, Code: req.Code} if req.Description != nil { role.Description = *req.Description } if err := s.repo.CreateRole(ctx, role); err != nil { return nil, err } if len(req.PermissionCodes) > 0 { _ = s.repo.SetRolePermissionsByCodes(ctx, role.ID, req.PermissionCodes) } perms, _ := s.repo.GetPermissionsByRoleID(ctx, role.ID) resp := transformer.RoleWithPermissionsToContract(*role, perms) return &resp, nil } func (s *RBACServiceImpl) UpdateRole(ctx context.Context, id uuid.UUID, req *contract.UpdateRoleRequest) (*contract.RoleWithPermissionsResponse, error) { role := &entities.Role{ID: id} if req.Name != nil { role.Name = *req.Name } if req.Code != nil { role.Code = *req.Code } if req.Description != nil { role.Description = *req.Description } if err := s.repo.UpdateRole(ctx, role); err != nil { return nil, err } if req.PermissionCodes != nil { _ = s.repo.SetRolePermissionsByCodes(ctx, id, *req.PermissionCodes) } perms, _ := s.repo.GetPermissionsByRoleID(ctx, id) // fetch updated role roles, err := s.repo.ListRoles(ctx) if err != nil { return nil, err } for _, r := range roles { if r.ID == id { resp := transformer.RoleWithPermissionsToContract(r, perms) return &resp, nil } } return nil, nil } func (s *RBACServiceImpl) DeleteRole(ctx context.Context, id uuid.UUID) error { return s.repo.DeleteRole(ctx, id) } func (s *RBACServiceImpl) ListRoles(ctx context.Context) (*contract.ListRolesResponse, error) { roles, err := s.repo.ListRoles(ctx) if err != nil { return nil, err } out := make([]contract.RoleWithPermissionsResponse, 0, len(roles)) for _, r := range roles { perms, _ := s.repo.GetPermissionsByRoleID(ctx, r.ID) out = append(out, transformer.RoleWithPermissionsToContract(r, perms)) } return &contract.ListRolesResponse{Roles: out}, nil } // New methods for the required API endpoints func (s *RBACServiceImpl) GetPermissionsGrouped(ctx context.Context) (*contract.PermissionsGroupedResponse, error) { modules, err := s.repo.ListModules(ctx) if err != nil { return nil, err } result := make([]contract.ModuleWithPermissionsResponse, 0, len(modules)) for _, module := range modules { perms, err := s.repo.ListPermissions(ctx) if err != nil { return nil, err } modulePerms := make([]contract.PermissionResponse, 0) for _, perm := range perms { if perm.ModuleID != nil && *perm.ModuleID == module.ID { modulePerms = append(modulePerms, contract.PermissionResponse{ ID: perm.ID, Code: perm.Code, Action: perm.Action, Description: &perm.Description, }) } } result = append(result, contract.ModuleWithPermissionsResponse{ Module: contract.ModuleResponse{ ID: module.ID, Name: module.Name, Code: module.Code, }, Permissions: modulePerms, }) } return &contract.PermissionsGroupedResponse{Data: result}, nil } func (s *RBACServiceImpl) CreateOrUpdateRole(ctx context.Context, req *contract.CreateOrUpdateRoleRequest) (*contract.RoleDetailResponse, error) { // Check if role exists existingRole, _ := s.repo.GetRoleByCode(ctx, req.Code) var role *entities.Role if existingRole != nil { // Update existing role role = existingRole role.Name = req.Name role.Description = req.Description if err := s.repo.UpdateRole(ctx, role); err != nil { return nil, err } } else { // Create new role role = &entities.Role{ Name: req.Name, Code: req.Code, Description: req.Description, } if err := s.repo.CreateRole(ctx, role); err != nil { return nil, err } } // Set permissions based on module and actions permissionIDs := make([]uuid.UUID, 0) for _, modPerm := range req.Permissions { _, err := s.repo.GetModuleByCode(ctx, modPerm.Module) if err != nil { continue // Skip if module not found } for _, action := range modPerm.Actions { permCode := modPerm.Module + "_" + action perm, err := s.repo.GetPermissionByCode(ctx, permCode) if err == nil && perm != nil { permissionIDs = append(permissionIDs, perm.ID) } } } if err := s.repo.SetRolePermissionsByIDs(ctx, role.ID, permissionIDs); err != nil { return nil, err } // Build response return s.GetRoleDetail(ctx, role.ID) } func (s *RBACServiceImpl) GetRoleDetail(ctx context.Context, roleID uuid.UUID) (*contract.RoleDetailResponse, error) { role, err := s.repo.GetRoleByID(ctx, roleID) if err != nil { return nil, err } permissions, err := s.repo.GetPermissionsByRoleID(ctx, roleID) if err != nil { return nil, err } // Group permissions by module moduleMap := make(map[uuid.UUID]*contract.RolePermissionModuleResponse) for _, perm := range permissions { if perm.ModuleID == nil { continue } if _, exists := moduleMap[*perm.ModuleID]; !exists { if perm.Module != nil { moduleMap[*perm.ModuleID] = &contract.RolePermissionModuleResponse{ Module: contract.ModuleResponse{ ID: perm.Module.ID, Name: perm.Module.Name, Code: perm.Module.Code, }, Actions: []contract.PermissionActionResponse{}, } } } if modResp, exists := moduleMap[*perm.ModuleID]; exists { modResp.Actions = append(modResp.Actions, contract.PermissionActionResponse{ ID: perm.ID, Action: perm.Action, Code: perm.Code, Description: perm.Description, }) } } // Convert map to slice permissionModules := make([]contract.RolePermissionModuleResponse, 0, len(moduleMap)) for _, modResp := range moduleMap { permissionModules = append(permissionModules, *modResp) } return &contract.RoleDetailResponse{ ID: role.ID, Name: role.Name, Code: role.Code, Description: role.Description, CreatedAt: role.CreatedAt, UpdatedAt: role.UpdatedAt, Permissions: permissionModules, }, nil }