refactor: migrate from raw pgx to GORM, unify ErrNoRows, cleanup auth
ci / build (push) Successful in 2m58s
ci / build (pull_request) Successful in 2m47s

This commit is contained in:
Mephimeow
2026-06-14 16:41:33 +00:00
parent 9da532e9dc
commit 2da484d781
19 changed files with 321 additions and 348 deletions
+41 -23
View File
@@ -11,30 +11,33 @@ import (
"time"
"unicode"
"gitea.d3m0k1d.ru/HellreigN/Control-plane/internal/db"
"golang.org/x/crypto/bcrypt"
)
var (
ErrEmailExists = errors.New("email already registered")
ErrInvalidCreds = errors.New("invalid email or password")
ErrUserNotFound = errors.New("user not found")
ErrInvalidUserID = errors.New("invalid user ID")
ErrInvalidRefresh = errors.New("invalid refresh token")
ErrRefreshExpired = errors.New("refresh token expired")
ErrLogoutInvalid = errors.New("refresh token not found or already used")
ErrWrongPassword = errors.New("current password is incorrect")
ErrWeakPassword = errors.New("password must be at least 8 characters with uppercase, lowercase, and digit")
ErrSamePassword = errors.New("new password must differ from current password")
ErrEmailExists = errors.New("email already registered")
ErrInvalidCreds = errors.New("invalid email or password")
ErrUserNotFound = errors.New("user not found")
ErrInvalidUserID = errors.New("invalid user ID")
ErrInvalidRefresh = errors.New("invalid refresh token")
ErrRefreshExpired = errors.New("refresh token expired")
ErrLogoutInvalid = errors.New("refresh token not found or already used")
ErrWrongPassword = errors.New("current password is incorrect")
ErrWeakPassword = errors.New(
"password must be at least 8 characters with uppercase, lowercase, and digit",
)
ErrSamePassword = errors.New("new password must differ from current password")
)
type Service struct {
repo *Repository
repo UserRepository
jwtSecret []byte
jwtExp time.Duration
refreshExp time.Duration
}
func NewService(repo *Repository, jwtSecret string, jwtExp, refreshExp time.Duration) *Service {
func NewService(repo UserRepository, jwtSecret string, jwtExp, refreshExp time.Duration) *Service {
return &Service{
repo: repo,
jwtSecret: []byte(jwtSecret),
@@ -113,7 +116,7 @@ func (s *Service) Register(ctx context.Context, req RegisterRequest) (*AuthRespo
req.Email = strings.ToLower(req.Email)
existing, err := s.repo.FindByEmail(ctx, req.Email)
if err != nil && !errors.Is(err, ErrNoRows) {
if err != nil && !errors.Is(err, db.ErrNoRows) {
return nil, fmt.Errorf("failed to check existing user: %w", err)
}
if existing != nil {
@@ -145,13 +148,16 @@ func (s *Service) Login(ctx context.Context, req LoginRequest) (*AuthResponse, e
req.Email = strings.ToLower(req.Email)
user, err := s.repo.FindByEmail(ctx, req.Email)
if err != nil {
if errors.Is(err, ErrNoRows) {
if errors.Is(err, db.ErrNoRows) {
return nil, ErrInvalidCreds
}
return nil, fmt.Errorf("failed to find user: %w", err)
}
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
if err := bcrypt.CompareHashAndPassword(
[]byte(user.PasswordHash),
[]byte(req.Password),
); err != nil {
return nil, ErrInvalidCreds
}
@@ -163,7 +169,7 @@ func (s *Service) Refresh(ctx context.Context, rawRefresh string) (*AuthResponse
doc, err := s.repo.FindRefreshTokenByHash(ctx, hash)
if err != nil {
if errors.Is(err, ErrNoRows) {
if errors.Is(err, db.ErrNoRows) {
return nil, ErrInvalidRefresh
}
return nil, fmt.Errorf("failed to find refresh token: %w", err)
@@ -202,7 +208,7 @@ func (s *Service) GetUserByID(ctx context.Context, userID string) (*UserPublic,
user, err := s.repo.FindByID(ctx, userID)
if err != nil {
if errors.Is(err, ErrNoRows) {
if errors.Is(err, db.ErrNoRows) {
return nil, ErrUserNotFound
}
return nil, fmt.Errorf("failed to find user: %w", err)
@@ -212,20 +218,27 @@ func (s *Service) GetUserByID(ctx context.Context, userID string) (*UserPublic,
return &public, nil
}
func (s *Service) ChangePassword(ctx context.Context, userID string, req PasswordChangeRequest) error {
func (s *Service) ChangePassword(
ctx context.Context,
userID string,
req PasswordChangeRequest,
) error {
if userID == "" {
return ErrInvalidUserID
}
user, err := s.repo.FindByID(ctx, userID)
if err != nil {
if errors.Is(err, ErrNoRows) {
if errors.Is(err, db.ErrNoRows) {
return ErrUserNotFound
}
return fmt.Errorf("failed to find user: %w", err)
}
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.OldPassword)); err != nil {
if err := bcrypt.CompareHashAndPassword(
[]byte(user.PasswordHash),
[]byte(req.OldPassword),
); err != nil {
return ErrWrongPassword
}
@@ -249,14 +262,18 @@ func (s *Service) ChangePassword(ctx context.Context, userID string, req Passwor
return nil
}
func (s *Service) UpdateProfile(ctx context.Context, userID string, req UpdateProfileRequest) (*UserPublic, error) {
func (s *Service) UpdateProfile(
ctx context.Context,
userID string,
req UpdateProfileRequest,
) (*UserPublic, error) {
if userID == "" {
return nil, ErrInvalidUserID
}
user, err := s.repo.FindByID(ctx, userID)
if err != nil {
if errors.Is(err, ErrNoRows) {
if errors.Is(err, db.ErrNoRows) {
return nil, ErrUserNotFound
}
return nil, fmt.Errorf("failed to find user: %w", err)
@@ -272,5 +289,6 @@ func (s *Service) UpdateProfile(ctx context.Context, userID string, req UpdatePr
}
func isPGUniqueViolation(err error) bool {
return err != nil && (strings.Contains(err.Error(), "unique") || strings.Contains(err.Error(), "23505"))
return err != nil &&
(strings.Contains(err.Error(), "unique") || strings.Contains(err.Error(), "23505"))
}