136 lines
4.4 KiB
Go
136 lines
4.4 KiB
Go
package service
|
|
|
|
// PlatformUserService handles business logic for Platform API user endpoints.
|
|
// Reference: Chatwoot Platform::Api::V1::UsersController — AccessToken authenticated
|
|
//
|
|
// These endpoints are distinct from the SuperAdmin-platform routes because they use
|
|
// AccessToken authentication (api_access_token header) instead of JWT+SuperAdmin.
|
|
// The Permissible system governs which resources each PlatformApp can access.
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/repository"
|
|
)
|
|
|
|
// PlatformUserService provides user CRUD for Platform API (AccessToken auth).
|
|
type PlatformUserService struct {
|
|
userRepo *repository.UserRepo
|
|
permissibleRepo *repository.PermissibleRepo
|
|
}
|
|
|
|
// NewPlatformUserService creates a new PlatformUserService.
|
|
func NewPlatformUserService(
|
|
userRepo *repository.UserRepo,
|
|
permissibleRepo *repository.PermissibleRepo,
|
|
) *PlatformUserService {
|
|
return &PlatformUserService{
|
|
userRepo: userRepo,
|
|
permissibleRepo: permissibleRepo,
|
|
}
|
|
}
|
|
|
|
// ValidatePermissible checks that the PlatformApp has permission to access the target user.
|
|
func (s *PlatformUserService) ValidatePermissible(ctx context.Context, platformAppID uint, userID uint) error {
|
|
perm, err := s.permissibleRepo.FindByPlatformAppAndResource(ctx, platformAppID, model.PermissibleTypeUser, userID)
|
|
if err != nil {
|
|
// GORM returns "record not found" when no matching row — treat as non-permissible
|
|
return errors.New("non permissible resource")
|
|
}
|
|
if perm == nil {
|
|
return errors.New("non permissible resource")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetUser retrieves a user by ID, after verifying permissible access.
|
|
func (s *PlatformUserService) GetUser(ctx context.Context, platformAppID uint, userID uint) (*model.User, error) {
|
|
if err := s.ValidatePermissible(ctx, platformAppID, userID); err != nil {
|
|
return nil, err
|
|
}
|
|
return s.userRepo.FindByID(ctx, userID)
|
|
}
|
|
|
|
// CreateUser creates a new user and auto-creates a Permissible record.
|
|
// Reference: Chatwoot UsersController#create — skips confirmation, auto-permissible
|
|
func (s *PlatformUserService) CreateUser(ctx context.Context, platformAppID uint, name, email, password string) (*model.User, error) {
|
|
user := &model.User{
|
|
Name: name,
|
|
Email: email,
|
|
Provider: "email",
|
|
}
|
|
if password != "" {
|
|
user.Password = password
|
|
}
|
|
|
|
if err := s.userRepo.Create(ctx, user); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Auto-create Permissible record (PlatformApp can access this user)
|
|
perm := &model.Permissible{
|
|
PlatformAppID: platformAppID,
|
|
PermissibleType: model.PermissibleTypeUser,
|
|
PermissibleID: user.ID,
|
|
}
|
|
if err := s.permissibleRepo.Create(ctx, perm); err != nil {
|
|
// Non-critical — permissible creation failure shouldn't block user creation
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
// UpdateUser updates a user, after verifying permissible access.
|
|
// Reference: Chatwoot UsersController#update — merges custom_attributes, skips reconfirmation
|
|
func (s *PlatformUserService) UpdateUser(ctx context.Context, platformAppID uint, userID uint, name, email string) (*model.User, error) {
|
|
if err := s.ValidatePermissible(ctx, platformAppID, userID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
user, err := s.userRepo.FindByID(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if name != "" {
|
|
user.Name = name
|
|
}
|
|
if email != "" {
|
|
user.Email = email
|
|
}
|
|
|
|
if err := s.userRepo.Update(ctx, user); err != nil {
|
|
return nil, err
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
// DeleteUser deletes a user, after verifying permissible access.
|
|
// Reference: Chatwoot UsersController#destroy — uses DeleteObjectJob (async)
|
|
func (s *PlatformUserService) DeleteUser(ctx context.Context, platformAppID uint, userID uint) error {
|
|
if err := s.ValidatePermissible(ctx, platformAppID, userID); err != nil {
|
|
return err
|
|
}
|
|
return s.userRepo.Delete(ctx, userID)
|
|
}
|
|
|
|
// ListPermissibleUsers returns all users that the PlatformApp has permissible access to.
|
|
func (s *PlatformUserService) ListPermissibleUsers(ctx context.Context, platformAppID uint) ([]model.User, error) {
|
|
permissibles, err := s.permissibleRepo.FindByPlatformAppID(ctx, platformAppID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var users []model.User
|
|
for _, perm := range permissibles {
|
|
if perm.PermissibleType == model.PermissibleTypeUser {
|
|
user, err := s.userRepo.FindByID(ctx, perm.PermissibleID)
|
|
if err != nil {
|
|
continue // skip missing users
|
|
}
|
|
users = append(users, *user)
|
|
}
|
|
}
|
|
return users, nil
|
|
} |