142 lines
4.4 KiB
Go
142 lines
4.4 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/repository"
|
|
applogger "github.com/gochat/gochat/pkg/logger"
|
|
)
|
|
|
|
// CustomRoleService implements business logic for CustomRole operations.
|
|
// Reference: Chatwoot enterprise/app/models/custom_role.rb
|
|
type CustomRoleService struct {
|
|
repo *repository.CustomRoleRepo
|
|
}
|
|
|
|
// NewCustomRoleService creates a new CustomRole service.
|
|
func NewCustomRoleService(repo *repository.CustomRoleRepo) *CustomRoleService {
|
|
return &CustomRoleService{repo: repo}
|
|
}
|
|
|
|
// CreateCustomRoleRequest is the DTO for creating a custom role.
|
|
type CreateCustomRoleRequest struct {
|
|
Name string `json:"name" validate:"required,min=2"`
|
|
Permissions []string `json:"permissions"`
|
|
Description string `json:"description,omitempty"`
|
|
}
|
|
|
|
// UpdateCustomRoleRequest is the DTO for updating a custom role.
|
|
type UpdateCustomRoleRequest struct {
|
|
Name string `json:"name,omitempty" validate:"omitempty,min=2"`
|
|
Permissions []string `json:"permissions,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
}
|
|
|
|
// List retrieves all custom roles for an account with pagination.
|
|
func (s *CustomRoleService) List(ctx context.Context, accountID uint, page, pageSize int) ([]model.CustomRole, int64, error) {
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if pageSize < 1 {
|
|
pageSize = 25
|
|
}
|
|
offset := (page - 1) * pageSize
|
|
|
|
roles, total, err := s.repo.FindByAccount(ctx, accountID, offset, pageSize)
|
|
if err != nil {
|
|
applogger.L().Errorf("List custom roles for account %d: %v", accountID, err)
|
|
return nil, 0, fmt.Errorf("failed to list custom roles: %w", err)
|
|
}
|
|
return roles, total, nil
|
|
}
|
|
|
|
// Create creates a new custom role within an account.
|
|
func (s *CustomRoleService) Create(ctx context.Context, accountID uint, req CreateCustomRoleRequest) (*model.CustomRole, error) {
|
|
role := &model.CustomRole{
|
|
AccountID: accountID,
|
|
Name: req.Name,
|
|
Description: req.Description,
|
|
}
|
|
|
|
if req.Permissions != nil {
|
|
if err := role.SetPermissionKeys(permissionStringsToDimensions(req.Permissions)); err != nil {
|
|
return nil, fmt.Errorf("invalid permissions: %w", err)
|
|
}
|
|
} else {
|
|
role.Permissions = "[]"
|
|
}
|
|
|
|
// Validate the role before persisting
|
|
if err := role.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := s.repo.Create(ctx, role); err != nil {
|
|
applogger.L().Errorf("failed to create custom role: %v", err)
|
|
return nil, fmt.Errorf("failed to create custom role: %w", err)
|
|
}
|
|
return role, nil
|
|
}
|
|
|
|
// Update updates an existing custom role scoped to an account.
|
|
func (s *CustomRoleService) Update(ctx context.Context, id, accountID uint, req UpdateCustomRoleRequest) (*model.CustomRole, error) {
|
|
role, err := s.repo.FindByIDAndAccount(ctx, id, accountID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("custom role not found: %w", err)
|
|
}
|
|
|
|
// Merge fields: only update non-empty values
|
|
if req.Name != "" {
|
|
role.Name = req.Name
|
|
}
|
|
if req.Description != "" {
|
|
role.Description = req.Description
|
|
}
|
|
|
|
// Merge permissions: if provided, replace entirely
|
|
if req.Permissions != nil {
|
|
if err := role.SetPermissionKeys(permissionStringsToDimensions(req.Permissions)); err != nil {
|
|
return nil, fmt.Errorf("invalid permissions: %w", err)
|
|
}
|
|
}
|
|
|
|
// Validate the updated role before persisting
|
|
if err := role.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := s.repo.Update(ctx, role); err != nil {
|
|
applogger.L().Errorf("failed to update custom role: %v", err)
|
|
return nil, fmt.Errorf("failed to update custom role: %w", err)
|
|
}
|
|
return role, nil
|
|
}
|
|
|
|
func permissionStringsToDimensions(keys []string) []model.PermissionDimension {
|
|
dims := make([]model.PermissionDimension, 0, len(keys))
|
|
for _, key := range keys {
|
|
dims = append(dims, model.PermissionDimension(key))
|
|
}
|
|
return dims
|
|
}
|
|
|
|
// Delete soft-deletes a custom role scoped to an account.
|
|
func (s *CustomRoleService) Delete(ctx context.Context, id, accountID uint) error {
|
|
if err := s.repo.Delete(ctx, id, accountID); err != nil {
|
|
applogger.L().Errorf("failed to delete custom role %d for account %d: %v", id, accountID, err)
|
|
return fmt.Errorf("failed to delete custom role: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetByID retrieves a single custom role scoped to an account.
|
|
func (s *CustomRoleService) GetByID(ctx context.Context, id, accountID uint) (*model.CustomRole, error) {
|
|
role, err := s.repo.FindByIDAndAccount(ctx, id, accountID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("custom role not found: %w", err)
|
|
}
|
|
return role, nil
|
|
}
|