Init Eslogad
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/entities"
|
||||
"eslogad-be/internal/transformer"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UserProcessorImpl struct {
|
||||
userRepo UserRepository
|
||||
profileRepo UserProfileRepository
|
||||
}
|
||||
|
||||
type UserProfileRepository interface {
|
||||
GetByUserID(ctx context.Context, userID uuid.UUID) (*entities.UserProfile, error)
|
||||
Create(ctx context.Context, profile *entities.UserProfile) error
|
||||
Upsert(ctx context.Context, profile *entities.UserProfile) error
|
||||
Update(ctx context.Context, profile *entities.UserProfile) error
|
||||
}
|
||||
|
||||
func NewUserProcessor(
|
||||
userRepo UserRepository,
|
||||
profileRepo UserProfileRepository,
|
||||
) *UserProcessorImpl {
|
||||
return &UserProcessorImpl{
|
||||
userRepo: userRepo,
|
||||
profileRepo: profileRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) CreateUser(ctx context.Context, req *contract.CreateUserRequest) (*contract.UserResponse, error) {
|
||||
existingUser, err := p.userRepo.GetByEmail(ctx, req.Email)
|
||||
if err == nil && existingUser != nil {
|
||||
return nil, fmt.Errorf("user with email %s already exists", req.Email)
|
||||
}
|
||||
|
||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
userEntity := transformer.CreateUserRequestToEntity(req, string(passwordHash))
|
||||
|
||||
err = p.userRepo.Create(ctx, userEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
||||
// create default user profile
|
||||
defaultFullName := userEntity.Name
|
||||
profile := &entities.UserProfile{
|
||||
UserID: userEntity.ID,
|
||||
FullName: defaultFullName,
|
||||
Timezone: "Asia/Jakarta",
|
||||
Locale: "id-ID",
|
||||
Preferences: entities.JSONB{},
|
||||
NotificationPrefs: entities.JSONB{},
|
||||
}
|
||||
_ = p.profileRepo.Create(ctx, profile)
|
||||
|
||||
return transformer.EntityToContract(userEntity), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) UpdateUser(ctx context.Context, id uuid.UUID, req *contract.UpdateUserRequest) (*contract.UserResponse, error) {
|
||||
existingUser, err := p.userRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
if req.Email != nil && *req.Email != existingUser.Email {
|
||||
existingUserByEmail, err := p.userRepo.GetByEmail(ctx, *req.Email)
|
||||
if err == nil && existingUserByEmail != nil && existingUserByEmail.ID != id {
|
||||
return nil, fmt.Errorf("user with email %s already exists", *req.Email)
|
||||
}
|
||||
}
|
||||
|
||||
updated := transformer.UpdateUserEntity(existingUser, req)
|
||||
|
||||
err = p.userRepo.Update(ctx, updated)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update user: %w", err)
|
||||
}
|
||||
|
||||
return transformer.EntityToContract(updated), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) DeleteUser(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := p.userRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.userRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserByID(ctx context.Context, id uuid.UUID) (*contract.UserResponse, error) {
|
||||
user, err := p.userRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
return transformer.EntityToContract(user), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserByEmail(ctx context.Context, email string) (*contract.UserResponse, error) {
|
||||
user, err := p.userRepo.GetByEmail(ctx, email)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
return transformer.EntityToContract(user), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) ListUsers(ctx context.Context, page, limit int) ([]contract.UserResponse, int, error) {
|
||||
offset := (page - 1) * limit
|
||||
|
||||
filters := map[string]interface{}{}
|
||||
|
||||
users, totalCount, err := p.userRepo.List(ctx, filters, limit, offset)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to get users: %w", err)
|
||||
}
|
||||
|
||||
responses := transformer.EntitiesToContracts(users)
|
||||
return responses, int(totalCount), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserEntityByEmail(ctx context.Context, email string) (*entities.User, error) {
|
||||
user, err := p.userRepo.GetByEmail(ctx, email)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) ChangePassword(ctx context.Context, userID uuid.UUID, req *contract.ChangePasswordRequest) error {
|
||||
user, err := p.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.CurrentPassword))
|
||||
if err != nil {
|
||||
return fmt.Errorf("current password is incorrect")
|
||||
}
|
||||
|
||||
newPasswordHash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash new password: %w", err)
|
||||
}
|
||||
|
||||
err = p.userRepo.UpdatePassword(ctx, userID, string(newPasswordHash))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update password: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) ActivateUser(ctx context.Context, userID uuid.UUID) error {
|
||||
_, err := p.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.userRepo.UpdateActiveStatus(ctx, userID, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to activate user: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) DeactivateUser(ctx context.Context, userID uuid.UUID) error {
|
||||
_, err := p.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.userRepo.UpdateActiveStatus(ctx, userID, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deactivate user: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RBAC implementations
|
||||
func (p *UserProcessorImpl) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]contract.RoleResponse, error) {
|
||||
roles, err := p.userRepo.GetRolesByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transformer.RolesToContract(roles), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserPermissionCodes(ctx context.Context, userID uuid.UUID) ([]string, error) {
|
||||
perms, err := p.userRepo.GetPermissionsByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
codes := make([]string, 0, len(perms))
|
||||
for _, p := range perms {
|
||||
codes = append(codes, p.Code)
|
||||
}
|
||||
return codes, nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserPositions(ctx context.Context, userID uuid.UUID) ([]contract.PositionResponse, error) {
|
||||
positions, err := p.userRepo.GetPositionsByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transformer.PositionsToContract(positions), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserProfile(ctx context.Context, userID uuid.UUID) (*contract.UserProfileResponse, error) {
|
||||
prof, err := p.profileRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transformer.ProfileEntityToContract(prof), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) UpdateUserProfile(ctx context.Context, userID uuid.UUID, req *contract.UpdateUserProfileRequest) (*contract.UserProfileResponse, error) {
|
||||
existing, _ := p.profileRepo.GetByUserID(ctx, userID)
|
||||
entity := transformer.ProfileUpdateToEntity(userID, req, existing)
|
||||
if existing == nil {
|
||||
if err := p.profileRepo.Create(ctx, entity); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := p.profileRepo.Update(ctx, entity); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return transformer.ProfileEntityToContract(entity), nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/entities"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UserRepository interface {
|
||||
Create(ctx context.Context, user *entities.User) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.User, error)
|
||||
GetByEmail(ctx context.Context, email string) (*entities.User, error)
|
||||
GetByRole(ctx context.Context, role entities.UserRole) ([]*entities.User, error)
|
||||
GetActiveUsers(ctx context.Context, organizationID uuid.UUID) ([]*entities.User, error)
|
||||
Update(ctx context.Context, user *entities.User) error
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
UpdatePassword(ctx context.Context, id uuid.UUID, passwordHash string) error
|
||||
UpdateActiveStatus(ctx context.Context, id uuid.UUID, isActive bool) error
|
||||
List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.User, int64, error)
|
||||
Count(ctx context.Context, filters map[string]interface{}) (int64, error)
|
||||
|
||||
GetRolesByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Role, error)
|
||||
GetPermissionsByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Permission, error)
|
||||
GetPositionsByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Position, error)
|
||||
}
|
||||
Reference in New Issue
Block a user