feature/core-service-auth #1

Merged
aep merged 4 commits from feature/core-service-auth into master 2025-08-27 14:09:33 +03:00
12 changed files with 661 additions and 37 deletions
Showing only changes of commit cce7622ae1 - Show all commits

View File

@@ -74,11 +74,25 @@ type RegisterRequest struct {
OrganizationType string `json:"organization_type"` OrganizationType string `json:"organization_type"`
} }
// UserResponse ответ с информацией о пользователе
type UserResponse struct {
ID uuid.UUID `json:"id"`
Email string `json:"email"`
Role string `json:"role"`
}
// OrganizationResponse ответ с информацией об организации
type OrganizationResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
}
// LoginResponse ответ на аутентификацию // LoginResponse ответ на аутентификацию
type LoginResponse struct { type LoginResponse struct {
Token string `json:"token"` Token string `json:"token"`
User User `json:"user"` User UserResponse `json:"user"`
ExpiresAt time.Time `json:"expires_at"` Organization OrganizationResponse `json:"organization"`
} }
// CreateLocationRequest запрос на создание места хранения // CreateLocationRequest запрос на создание места хранения

View File

@@ -3,6 +3,7 @@ package repository
import ( import (
"context" "context"
"database/sql" "database/sql"
"encoding/json"
"fmt" "fmt"
"erp-mvp/core-service/internal/models" "erp-mvp/core-service/internal/models"
@@ -29,7 +30,13 @@ func (r *organizationRepository) Create(ctx context.Context, org *models.Organiz
VALUES ($1, $2, $3, $4, $5) VALUES ($1, $2, $3, $4, $5)
` `
_, err := r.db.ExecContext(ctx, query, org.ID, org.Name, org.Type, org.Settings, org.CreatedAt) // Конвертируем JSON в строку
settingsJSON, err := json.Marshal(org.Settings)
if err != nil {
return fmt.Errorf("failed to marshal settings: %w", err)
}
_, err = r.db.ExecContext(ctx, query, org.ID, org.Name, org.Type, string(settingsJSON), org.CreatedAt)
if err != nil { if err != nil {
return fmt.Errorf("failed to create organization: %w", err) return fmt.Errorf("failed to create organization: %w", err)
} }
@@ -44,12 +51,13 @@ func (r *organizationRepository) GetByID(ctx context.Context, id uuid.UUID) (*mo
WHERE id = $1 WHERE id = $1
` `
var settingsJSON []byte
org := &models.Organization{} org := &models.Organization{}
err := r.db.QueryRowContext(ctx, query, id).Scan( err := r.db.QueryRowContext(ctx, query, id).Scan(
&org.ID, &org.ID,
&org.Name, &org.Name,
&org.Type, &org.Type,
&org.Settings, &settingsJSON,
&org.CreatedAt, &org.CreatedAt,
) )
@@ -60,6 +68,16 @@ func (r *organizationRepository) GetByID(ctx context.Context, id uuid.UUID) (*mo
return nil, fmt.Errorf("failed to get organization: %w", err) return nil, fmt.Errorf("failed to get organization: %w", err)
} }
// Конвертируем JSON строку в map
if len(settingsJSON) > 0 {
err = json.Unmarshal(settingsJSON, &org.Settings)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal settings: %w", err)
}
} else {
org.Settings = make(models.JSON)
}
return org, nil return org, nil
} }
@@ -70,7 +88,13 @@ func (r *organizationRepository) Update(ctx context.Context, org *models.Organiz
WHERE id = $1 WHERE id = $1
` `
result, err := r.db.ExecContext(ctx, query, org.ID, org.Name, org.Type, org.Settings) // Конвертируем JSON в строку
settingsJSON, err := json.Marshal(org.Settings)
if err != nil {
return fmt.Errorf("failed to marshal settings: %w", err)
}
result, err := r.db.ExecContext(ctx, query, org.ID, org.Name, org.Type, string(settingsJSON))
if err != nil { if err != nil {
return fmt.Errorf("failed to update organization: %w", err) return fmt.Errorf("failed to update organization: %w", err)
} }

View File

@@ -7,7 +7,9 @@ import (
"erp-mvp/core-service/internal/auth" "erp-mvp/core-service/internal/auth"
"erp-mvp/core-service/internal/models" "erp-mvp/core-service/internal/models"
"erp-mvp/core-service/internal/repository" "erp-mvp/core-service/internal/repository"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sirupsen/logrus"
) )
type AuthService interface { type AuthService interface {
@@ -19,6 +21,7 @@ type authService struct {
orgRepo repository.OrganizationRepository orgRepo repository.OrganizationRepository
userRepo repository.UserRepository userRepo repository.UserRepository
jwtService *auth.JWTService jwtService *auth.JWTService
logger *logrus.Logger
} }
func NewAuthService(orgRepo repository.OrganizationRepository, userRepo repository.UserRepository, jwtService *auth.JWTService) AuthService { func NewAuthService(orgRepo repository.OrganizationRepository, userRepo repository.UserRepository, jwtService *auth.JWTService) AuthService {
@@ -26,37 +29,46 @@ func NewAuthService(orgRepo repository.OrganizationRepository, userRepo reposito
orgRepo: orgRepo, orgRepo: orgRepo,
userRepo: userRepo, userRepo: userRepo,
jwtService: jwtService, jwtService: jwtService,
logger: logrus.New(),
} }
} }
func (s *authService) Register(ctx context.Context, req *models.RegisterRequest) (*models.LoginResponse, error) { func (s *authService) Register(ctx context.Context, req *models.RegisterRequest) (*models.LoginResponse, error) {
s.logger.Info("Starting registration process")
// Проверяем, что пользователь с таким email не существует // Проверяем, что пользователь с таким email не существует
existingUser, err := s.userRepo.GetByEmail(ctx, req.UserEmail) existingUser, err := s.userRepo.GetByEmail(ctx, req.UserEmail)
if err == nil && existingUser != nil { if err == nil && existingUser != nil {
s.logger.Error("User with this email already exists")
return nil, &ValidationError{Message: "User with this email already exists"} return nil, &ValidationError{Message: "User with this email already exists"}
} }
// Создаём организацию // Создаем организацию
orgID := uuid.New() orgID := uuid.New()
org := &models.Organization{ org := &models.Organization{
ID: orgID, ID: orgID,
Name: req.OrganizationName, Name: req.OrganizationName,
Type: req.OrganizationType, Type: req.OrganizationType,
Settings: models.JSON{}, Settings: models.JSON{"created_at": time.Now().Unix()},
CreatedAt: time.Now(), CreatedAt: time.Now(),
} }
s.logger.Info("Creating organization with ID: ", orgID)
if err := s.orgRepo.Create(ctx, org); err != nil { if err := s.orgRepo.Create(ctx, org); err != nil {
s.logger.Error("Failed to create organization: ", err)
return nil, err return nil, err
} }
s.logger.Info("Organization created successfully")
// Хешируем пароль // Хешируем пароль
passwordHash, err := auth.HashPassword(req.UserPassword) passwordHash, err := auth.HashPassword(req.UserPassword)
if err != nil { if err != nil {
s.logger.Error("Failed to hash password: ", err)
return nil, err return nil, err
} }
s.logger.Info("Password hashed successfully")
// Создаём пользователя // Создаем пользователя
userID := uuid.New() userID := uuid.New()
user := &models.User{ user := &models.User{
ID: userID, ID: userID,
@@ -66,20 +78,33 @@ func (s *authService) Register(ctx context.Context, req *models.RegisterRequest)
CreatedAt: time.Now(), CreatedAt: time.Now(),
} }
s.logger.Info("Creating user with ID: ", userID)
if err := s.userRepo.Create(ctx, user, passwordHash); err != nil { if err := s.userRepo.Create(ctx, user, passwordHash); err != nil {
s.logger.Error("Failed to create user: ", err)
return nil, err return nil, err
} }
s.logger.Info("User created successfully")
// Генерируем JWT токен // Генерируем JWT токен
token, err := s.jwtService.GenerateToken(user.ID, org.ID, user.Email, user.Role) token, err := s.jwtService.GenerateToken(user.ID, org.ID, user.Email, user.Role)
if err != nil { if err != nil {
s.logger.Error("Failed to generate token: ", err)
return nil, err return nil, err
} }
s.logger.Info("JWT token generated successfully")
return &models.LoginResponse{ return &models.LoginResponse{
Token: token, Token: token,
User: *user, User: models.UserResponse{
ExpiresAt: time.Now().Add(24 * time.Hour), // TTL из конфигурации ID: user.ID,
Email: user.Email,
Role: user.Role,
},
Organization: models.OrganizationResponse{
ID: org.ID,
Name: org.Name,
Type: org.Type,
},
}, nil }, nil
} }
@@ -101,10 +126,24 @@ func (s *authService) Login(ctx context.Context, req *models.LoginRequest) (*mod
return nil, err return nil, err
} }
// Получаем организацию для ответа
org, err := s.orgRepo.GetByID(ctx, user.OrganizationID)
if err != nil {
return nil, err
}
return &models.LoginResponse{ return &models.LoginResponse{
Token: token, Token: token,
User: *user, User: models.UserResponse{
ExpiresAt: time.Now().Add(24 * time.Hour), // TTL из конфигурации ID: user.ID,
Email: user.Email,
Role: user.Role,
},
Organization: models.OrganizationResponse{
ID: org.ID,
Name: org.Name,
Type: org.Type,
},
}, nil }, nil
} }