package service import ( "errors" "evanpage-backend/internal/domain" "evanpage-backend/internal/repository" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) type UserService struct { repo *repository.UserRepository } func NewUserService(repo *repository.UserRepository) *UserService { return &UserService{repo: repo} } func (s *UserService) Register(username, email, password, role string) (*domain.User, error) { _, err := s.repo.FindByUsername(username) if err == nil { return nil, errors.New("username already exists") } _, err = s.repo.FindByEmail(email) if err == nil { return nil, errors.New("email already exists") } hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return nil, err } if role == "" { role = "user" } user := &domain.User{ Username: username, Email: email, PasswordHash: string(hash), Role: role, } if err := s.repo.Create(user); err != nil { return nil, err } return user, nil } func (s *UserService) ValidateLocalLogin(username, password string) (*domain.User, error) { user, err := s.repo.FindByUsername(username) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("invalid credentials") } return nil, err } if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil { return nil, errors.New("invalid credentials") } return user, nil } func (s *UserService) LookupKeycloakBinding(keycloakID string) (*domain.User, error) { return s.repo.FindByKeycloakID(keycloakID) } func (s *UserService) BindKeycloak(username, password, keycloakID, keycloakEmail string) (*domain.User, error) { user, err := s.ValidateLocalLogin(username, password) if err != nil { return nil, err } if user.KeycloakID != nil && *user.KeycloakID != "" && *user.KeycloakID != keycloakID { return nil, errors.New("user already bound to another keycloak account") } existing, err := s.repo.FindByKeycloakID(keycloakID) if err == nil && existing.ID != user.ID { return nil, errors.New("keycloak account already bound to another user") } user.KeycloakID = &keycloakID user.KeycloakEmail = keycloakEmail if err := s.repo.Update(user); err != nil { return nil, err } return user, nil } func (s *UserService) FindByRole(role string) (*domain.User, error) { return s.repo.FindByRole(role) } func (s *UserService) ListUsers() ([]domain.User, error) { return s.repo.ListAll() } func (s *UserService) DeleteUser(id uint) error { return s.repo.Delete(id) } func (s *UserService) CountUsers() (int64, error) { return s.repo.Count() }