package services import ( "context" "fmt" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" "github.com/yourusername/victorialogs-manager/internal/models" "github.com/yourusername/victorialogs-manager/internal/repository" ) // UserService handles user management business logic type UserService struct { userRepo *repository.UserRepository } // NewUserService creates a new user service func NewUserService(userRepo *repository.UserRepository) *UserService { return &UserService{ userRepo: userRepo, } } // UpdateUserRequest represents a user update request type UpdateUserRequest struct { Username string `json:"username,omitempty"` Email string `json:"email,omitempty"` Role models.Role `json:"role,omitempty"` IsActive *bool `json:"is_active,omitempty"` } // ListUsersRequest represents a list users request type ListUsersRequest struct { Limit int `json:"limit" form:"limit"` Offset int `json:"offset" form:"offset"` } // ListUsersResponse represents a list users response type ListUsersResponse struct { Users []*models.User `json:"users"` Total int `json:"total"` Limit int `json:"limit"` Offset int `json:"offset"` } // ListUsers returns a paginated list of users func (s *UserService) ListUsers(ctx context.Context, req *ListUsersRequest) (*ListUsersResponse, error) { // Set defaults if req.Limit <= 0 { req.Limit = 20 } if req.Limit > 100 { req.Limit = 100 } if req.Offset < 0 { req.Offset = 0 } // Get users users, err := s.userRepo.List(ctx, req.Limit, req.Offset) if err != nil { return nil, fmt.Errorf("failed to list users: %w", err) } // Get total count total, err := s.userRepo.Count(ctx) if err != nil { return nil, fmt.Errorf("failed to count users: %w", err) } // Clear password hashes for _, user := range users { user.PasswordHash = "" } return &ListUsersResponse{ Users: users, Total: total, Limit: req.Limit, Offset: req.Offset, }, nil } // GetUser returns a user by ID func (s *UserService) GetUser(ctx context.Context, id string) (*models.User, error) { user, err := s.userRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("user not found") } // Clear password hash user.PasswordHash = "" return user, nil } // CreateUser creates a new user func (s *UserService) CreateUser(ctx context.Context, req *CreateUserRequest) (*models.User, error) { // Check if username exists exists, err := s.userRepo.UsernameExists(ctx, req.Username) if err != nil { return nil, fmt.Errorf("failed to check username: %w", err) } if exists { return nil, fmt.Errorf("username already exists") } // Check if email exists exists, err = s.userRepo.EmailExists(ctx, req.Email) if err != nil { return nil, fmt.Errorf("failed to check email: %w", err) } if exists { return nil, fmt.Errorf("email already exists") } // Hash password hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } // Create user user := &models.User{ ID: uuid.New().String(), Username: req.Username, Email: req.Email, PasswordHash: string(hashedPassword), Role: req.Role, IsActive: true, } err = s.userRepo.Create(ctx, user) if err != nil { return nil, fmt.Errorf("failed to create user: %w", err) } // Clear password hash user.PasswordHash = "" return user, nil } // UpdateUser updates a user func (s *UserService) UpdateUser(ctx context.Context, id string, req *UpdateUserRequest) (*models.User, error) { // Get existing user user, err := s.userRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("user not found") } // Update fields if provided if req.Username != "" && req.Username != user.Username { // Check if new username exists exists, err := s.userRepo.UsernameExists(ctx, req.Username) if err != nil { return nil, fmt.Errorf("failed to check username: %w", err) } if exists { return nil, fmt.Errorf("username already exists") } user.Username = req.Username } if req.Email != "" && req.Email != user.Email { // Check if new email exists exists, err := s.userRepo.EmailExists(ctx, req.Email) if err != nil { return nil, fmt.Errorf("failed to check email: %w", err) } if exists { return nil, fmt.Errorf("email already exists") } user.Email = req.Email } if req.Role != "" { user.Role = req.Role } if req.IsActive != nil { user.IsActive = *req.IsActive } // Update user err = s.userRepo.Update(ctx, user) if err != nil { return nil, fmt.Errorf("failed to update user: %w", err) } // Clear password hash user.PasswordHash = "" return user, nil } // DeleteUser deletes a user func (s *UserService) DeleteUser(ctx context.Context, id string) error { err := s.userRepo.Delete(ctx, id) if err != nil { return fmt.Errorf("failed to delete user: %w", err) } return nil } // ResetUserPassword resets a user's password (admin only) type ResetPasswordRequest struct { NewPassword string `json:"new_password" binding:"required,min=8"` } // ResetUserPassword resets a user's password func (s *UserService) ResetUserPassword(ctx context.Context, userID string, req *ResetPasswordRequest) error { // Hash new password hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) if err != nil { return fmt.Errorf("failed to hash password: %w", err) } // Update password err = s.userRepo.UpdatePassword(ctx, userID, string(hashedPassword)) if err != nil { return fmt.Errorf("failed to reset password: %w", err) } return nil }