package repositories import ( "context" "fmt" "time" "veza-backend-api/internal/models" "github.com/google/uuid" "gorm.io/gorm" ) // UserRepository définit les méthodes pour interagir avec le modèle User // (Cette interface est celle utilisée par les autres packages qui dépendent de ce repository) // MIGRATION UUID: Toutes les méthodes utilisent maintenant uuid.UUID au lieu de int64 type UserRepository interface { CreateUser(ctx context.Context, user *models.User) error GetUserByID(ctx context.Context, id uuid.UUID) (*models.User, error) GetUserByEmail(ctx context.Context, email string) (*models.User, error) GetUserByUsername(ctx context.Context, username string) (*models.User, error) UpdateUser(ctx context.Context, user *models.User) error DeleteUser(ctx context.Context, id uuid.UUID) error UpdateLastLoginAt(ctx context.Context, userID uuid.UUID) error IncrementTokenVersion(ctx context.Context, userID uuid.UUID) error } // GormUserRepository est une implémentation de UserRepository utilisant GORM type GormUserRepository struct { db *gorm.DB } // NewGormUserRepository crée une nouvelle instance de GormUserRepository func NewGormUserRepository(db *gorm.DB) *GormUserRepository { return &GormUserRepository{db: db} } // CreateUser crée un nouvel utilisateur dans la base de données func (r *GormUserRepository) CreateUser(ctx context.Context, user *models.User) error { return r.db.WithContext(ctx).Create(user).Error } // GetUserByID récupère un utilisateur par son ID // MIGRATION UUID: Accepte maintenant uuid.UUID au lieu de int64 func (r *GormUserRepository) GetUserByID(ctx context.Context, id uuid.UUID) (*models.User, error) { var user models.User if err := r.db.WithContext(ctx).First(&user, "id = ?", id).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, nil // Utilisateur non trouvé } return nil, fmt.Errorf("failed to get user by ID: %w", err) } return &user, nil } // GetUserByEmail récupère un utilisateur par son email func (r *GormUserRepository) GetUserByEmail(ctx context.Context, email string) (*models.User, error) { var user models.User if err := r.db.WithContext(ctx).Where("email = ?", email).First(&user).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, nil // Utilisateur non trouvé } return nil, fmt.Errorf("failed to get user by email: %w", err) } return &user, nil } // GetUserByUsername récupère un utilisateur par son nom d'utilisateur func (r *GormUserRepository) GetUserByUsername(ctx context.Context, username string) (*models.User, error) { var user models.User if err := r.db.WithContext(ctx).Where("username = ?", username).First(&user).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, nil // Utilisateur non trouvé } return nil, fmt.Errorf("failed to get user by username: %w", err) } return &user, nil } // UpdateUser met à jour un utilisateur existant func (r *GormUserRepository) UpdateUser(ctx context.Context, user *models.User) error { return r.db.WithContext(ctx).Save(user).Error } // DeleteUser supprime un utilisateur (soft delete si GORM est configuré pour ça) // BE-DB-003: Add soft delete support to all models // MIGRATION UUID: Accepte maintenant uuid.UUID au lieu de int64 func (r *GormUserRepository) DeleteUser(ctx context.Context, id uuid.UUID) error { return r.db.WithContext(ctx).Delete(&models.User{}, "id = ?", id).Error } // RestoreUser restaure un utilisateur supprimé (soft delete) // BE-DB-003: Add soft delete support to all models func (r *GormUserRepository) RestoreUser(ctx context.Context, id uuid.UUID) error { return r.db.WithContext(ctx).Unscoped().Model(&models.User{}).Where("id = ?", id).Update("deleted_at", nil).Error } // HardDeleteUser supprime définitivement un utilisateur (hard delete) // BE-DB-003: Add soft delete support to all models func (r *GormUserRepository) HardDeleteUser(ctx context.Context, id uuid.UUID) error { return r.db.WithContext(ctx).Unscoped().Delete(&models.User{}, "id = ?", id).Error } // UpdateLastLoginAt met à jour le champ last_login_at pour un utilisateur // MIGRATION UUID: Accepte maintenant uuid.UUID au lieu de int64 func (r *GormUserRepository) UpdateLastLoginAt(ctx context.Context, userID uuid.UUID) error { return r.db.WithContext(ctx).Model(&models.User{}).Where("id = ?", userID).Update("last_login_at", time.Now()).Error } // IncrementTokenVersion incrémente la version du token d'un utilisateur // MIGRATION UUID: Accepte maintenant uuid.UUID au lieu de int64 func (r *GormUserRepository) IncrementTokenVersion(ctx context.Context, userID uuid.UUID) error { return r.db.WithContext(ctx).Model(&models.User{}).Where("id = ?", userID).Update("token_version", gorm.Expr("token_version + ?", 1)).Error } // --- Compatibility methods for services.UserRepository interface --- // MIGRATION UUID: Parse UUID string directement au lieu de int64 // SECURITY(HIGH-003): Legacy convenience methods now require context propagation. // context.Background() bypass removed to prevent request timeout/cancellation bypass. // Callers must pass the HTTP request context to enable proper timeout and cancellation. func (r *GormUserRepository) GetByID(ctx context.Context, id string) (*models.User, error) { userID, err := uuid.Parse(id) if err != nil { return nil, fmt.Errorf("invalid user ID format (expected UUID): %w", err) } return r.GetUserByID(ctx, userID) } func (r *GormUserRepository) GetByEmail(ctx context.Context, email string) (*models.User, error) { return r.GetUserByEmail(ctx, email) } func (r *GormUserRepository) GetByUsername(ctx context.Context, username string) (*models.User, error) { return r.GetUserByUsername(ctx, username) } func (r *GormUserRepository) Create(ctx context.Context, user *models.User) error { return r.CreateUser(ctx, user) } func (r *GormUserRepository) Update(ctx context.Context, user *models.User) error { return r.UpdateUser(ctx, user) } func (r *GormUserRepository) Delete(ctx context.Context, id string) error { userID, err := uuid.Parse(id) if err != nil { return fmt.Errorf("invalid user ID format (expected UUID): %w", err) } return r.DeleteUser(ctx, userID) }