feat: migrate from useAuth to AuthContext for centralized auth state
• Полная миграция 64 компонентов с useAuth на AuthContext • Исправлена race condition в SMS регистрации • Улучшена SSR совместимость с таймаутами • Удалена дублирующая система регистрации • Обновлена документация архитектуры аутентификации Технические изменения: - AuthContext.tsx: централизованная система состояния - auth-flow.tsx: убрана агрессивная логика logout - confirmation-step.tsx: исправлена передача телефона - page.tsx: добавлена синхронизация состояния - 64 файла: миграция useAuth → useAuthContext 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
250
2025-09-18/SMS_REGISTRATION_DEBUG_PLAN.md
Normal file
250
2025-09-18/SMS_REGISTRATION_DEBUG_PLAN.md
Normal file
@ -0,0 +1,250 @@
|
||||
# 🔍 ПЛАН ИСПРАВЛЕНИЯ SMS РЕГИСТРАЦИИ - ДЕТАЛЬНЫЙ АНАЛИЗ ПРОБЛЕМЫ
|
||||
|
||||
> **Дата:** 2025-09-18
|
||||
> **Проблема:** После SMS верификации пользователь перебрасывается на `/` вместо продолжения регистрации
|
||||
|
||||
---
|
||||
|
||||
## 🚨 ОПИСАНИЕ ПРОБЛЕМЫ
|
||||
|
||||
### 📱 Что должно происходить (правильная логика SFERA):
|
||||
|
||||
**ЕДИНЫЙ FLOW авторизации/регистрации:**
|
||||
1. Пользователь вводит номер телефона → SMS отправляется
|
||||
2. Пользователь вводит SMS код → код верифицируется
|
||||
3. **Определяется сценарий:**
|
||||
- **СЦЕНАРИЙ A:** Пользователь существует + есть организация → **Dashboard**
|
||||
- **СЦЕНАРИЙ B:** Пользователь существует + НЕТ организации → **Cabinet-select** (выбор типа кабинета)
|
||||
- **СЦЕНАРИЙ C:** Новый пользователь → **Cabinet-select** (выбор типа кабинета)
|
||||
|
||||
### ❌ Что происходит сейчас (баг):
|
||||
|
||||
```
|
||||
1. Пользователь вводит телефон → ✅ SMS отправляется
|
||||
2. Пользователь вводит SMS код → ✅ код верифицируется успешно
|
||||
3. ❌ Пользователь перебрасывается на главную страницу `/`
|
||||
4. ❌ AuthFlow сбрасывается на step: "phone"
|
||||
5. ❌ Пользователь вынужден начинать регистрацию заново
|
||||
```
|
||||
|
||||
### 🔍 Логи ошибки:
|
||||
|
||||
```javascript
|
||||
// SMS verification прошла успешно:
|
||||
🌐 GraphQL REQUEST: { operationName: 'VerifySmsCode', variables: { phone: '76657584949', code: '1234' } }
|
||||
POST /api/graphql 200 in 1295ms
|
||||
|
||||
// Но после этого:
|
||||
AppShell state: { pathname: '/', isAuthenticated: false, hasUser: false }
|
||||
🎢 AuthFlow - useEffect triggered with: { isAuthenticated: false, hasUser: false, currentStep: "phone" }
|
||||
🎢 AuthFlow - User not authenticated, setting step to phone
|
||||
```
|
||||
|
||||
**КЛЮЧЕВАЯ ПРОБЛЕМА:** `isAuthenticated: false` после успешной SMS верификации!
|
||||
|
||||
---
|
||||
|
||||
## 🔍 КОРНЕВАЯ ПРИЧИНА
|
||||
|
||||
### 🎯 Диагноз:
|
||||
|
||||
**AuthContext не обновляется после SMS верификации**, поэтому:
|
||||
1. SMS верификация успешна → токен сохраняется в localStorage
|
||||
2. НО AuthContext остается `isAuthenticated: false, user: null`
|
||||
3. app/page.tsx видит неавторизованного пользователя → redirect на `/login`
|
||||
4. AuthFlow инициализируется заново → step: "phone"
|
||||
|
||||
### 🔧 Предполагаемые причины:
|
||||
|
||||
**1. Timing issue в AuthContext:**
|
||||
- SMS-step сохраняет токен через `verifySmsCode()`
|
||||
- AuthContext не успевает обновить состояние
|
||||
- useEffect в AuthFlow срабатывает с устаревшими данными
|
||||
|
||||
**2. Конфликт между экземплярами AuthFlow:**
|
||||
- AuthGuard на `/dashboard` может создавать второй AuthFlow
|
||||
- Два AuthFlow конфликтуют между собой
|
||||
|
||||
**3. Проблема в app/page.tsx:**
|
||||
- Слишком быстро перенаправляет до обновления AuthContext
|
||||
- Не учитывает `isLoading` состояние
|
||||
|
||||
---
|
||||
|
||||
## 📋 ПЛАН ИСПРАВЛЕНИЯ
|
||||
|
||||
### **PHASE 1: ДИАГНОСТИКА И ЛОГИРОВАНИЕ**
|
||||
|
||||
#### 1.1 Добавить детальное логирование в AuthContext
|
||||
```typescript
|
||||
// В verifySmsCode после успешной верификации:
|
||||
console.warn('🔑 AuthContext - BEFORE setState:', {
|
||||
isAuthenticated: this.isAuthenticated,
|
||||
hasUser: !!this.user
|
||||
})
|
||||
|
||||
setUser(result.user)
|
||||
setIsAuthenticated(true)
|
||||
|
||||
console.warn('🔑 AuthContext - AFTER setState:', {
|
||||
isAuthenticated: true,
|
||||
hasUser: !!result.user,
|
||||
userOrganization: result.user.organization
|
||||
})
|
||||
```
|
||||
|
||||
#### 1.2 Добавить логирование в AuthFlow useEffect
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
console.warn('🎢 AuthFlow - useEffect DETAILED:', {
|
||||
isAuthenticated,
|
||||
hasUser: !!user,
|
||||
hasOrganization: !!user?.organization,
|
||||
userFromContext: user,
|
||||
currentStep: step,
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
|
||||
// Существующая логика...
|
||||
}, [isAuthenticated, user])
|
||||
```
|
||||
|
||||
#### 1.3 Добавить логирование в app/page.tsx
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
console.warn('📍 app/page.tsx - routing decision:', {
|
||||
isLoading,
|
||||
hasUser: !!user,
|
||||
hasOrganization: !!user?.organization,
|
||||
currentPath: window.location.pathname
|
||||
})
|
||||
|
||||
// Существующая логика...
|
||||
}, [router, user, isLoading])
|
||||
```
|
||||
|
||||
### **PHASE 2: ИСПРАВЛЕНИЕ TIMING ISSUES**
|
||||
|
||||
#### 2.1 Исправить AuthContext - добавить промисы
|
||||
```typescript
|
||||
const verifySmsCode = async (phone: string, code: string) => {
|
||||
// ... существующий код ...
|
||||
|
||||
if (result.success && result.token && result.user) {
|
||||
setAuthToken(result.token)
|
||||
setUserData(result.user)
|
||||
|
||||
// Синхронно обновляем состояние
|
||||
setUser(result.user)
|
||||
setIsAuthenticated(true)
|
||||
|
||||
// ЖДЕМ обновления Apollo Client
|
||||
await refreshApolloClient()
|
||||
|
||||
// Возвращаем промис когда все готово
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve({ success: true, message: result.message, user: result.user })
|
||||
}, 100) // Небольшая задержка для обновления состояния
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 Добавить isLoading в app/page.tsx
|
||||
```typescript
|
||||
const { user, isLoading, isAuthenticated } = useAuthContext()
|
||||
|
||||
useEffect(() => {
|
||||
// КРИТИЧНО: ждем завершения всех проверок
|
||||
if (isLoading) {
|
||||
console.warn('📍 app/page.tsx - still loading, waiting...')
|
||||
return
|
||||
}
|
||||
|
||||
// Только после isLoading: false делаем redirect
|
||||
if (user?.organization) {
|
||||
router.replace('/dashboard')
|
||||
} else if (isAuthenticated) {
|
||||
router.replace('/login') // Продолжить на странице регистрации
|
||||
} else {
|
||||
router.replace('/login')
|
||||
}
|
||||
}, [router, user, isLoading, isAuthenticated])
|
||||
```
|
||||
|
||||
### **PHASE 3: УСТРАНЕНИЕ КОНФЛИКТОВ**
|
||||
|
||||
#### 3.1 Исправить AuthGuard
|
||||
```typescript
|
||||
// НЕ создавать новый AuthFlow на dashboard
|
||||
if (!isAuthenticated || (isAuthenticated && user && !user.organization)) {
|
||||
// Перенаправить вместо показа AuthFlow
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
router.replace('/login')
|
||||
}, [])
|
||||
return <div>Перенаправление...</div>
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 Убрать window.location.href из AuthFlow
|
||||
```typescript
|
||||
// ЗАМЕНИТЬ:
|
||||
window.location.href = '/dashboard'
|
||||
|
||||
// НА:
|
||||
router.push('/dashboard')
|
||||
```
|
||||
|
||||
### **PHASE 4: ТЕСТИРОВАНИЕ**
|
||||
|
||||
#### 4.1 Тест-сценарии:
|
||||
1. **Новый пользователь:** phone → sms → cabinet-select → inn/api → complete → dashboard
|
||||
2. **Существующий без организации:** phone → sms → cabinet-select → inn/api → complete → dashboard
|
||||
3. **Существующий с организацией:** phone → sms → dashboard
|
||||
4. **Партнерские коды:** все сценарии с параметрами
|
||||
|
||||
#### 4.2 Проверки:
|
||||
- ✅ Нет перебросов на `/`
|
||||
- ✅ AuthFlow не сбрасывается на phone
|
||||
- ✅ Состояние AuthContext корректное
|
||||
- ✅ Нет создания второго AuthFlow
|
||||
|
||||
---
|
||||
|
||||
## 🎯 ОЖИДАЕМЫЙ РЕЗУЛЬТАТ
|
||||
|
||||
### ✅ После исправления:
|
||||
|
||||
```
|
||||
1. Пользователь вводит телефон → SMS отправляется
|
||||
2. Пользователь вводит SMS код → код верифицируется
|
||||
3. AuthContext обновляется → isAuthenticated: true, user: {...}
|
||||
4. AuthFlow определяет сценарий:
|
||||
- СЦЕНАРИЙ A: user.organization есть → step: 'complete' → dashboard
|
||||
- СЦЕНАРИЙ B/C: user.organization нет → step: 'cabinet-select'
|
||||
5. Пользователь продолжает регистрацию БЕЗ сброса
|
||||
```
|
||||
|
||||
### 📊 Логи после исправления:
|
||||
|
||||
```javascript
|
||||
🔑 AuthContext - SMS verification successful
|
||||
🔑 AuthContext - State updated: isAuthenticated=true, user=...
|
||||
🎢 AuthFlow - useEffect: { isAuthenticated: true, hasUser: true, hasOrganization: false }
|
||||
🎢 AuthFlow - Setting step to cabinet-select
|
||||
✅ Пользователь видит страницу выбора типа кабинета
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 ГОТОВНОСТЬ К РЕАЛИЗАЦИИ
|
||||
|
||||
**✅ Проблема диагностирована:** Timing issue в AuthContext + конфликт AuthFlow экземпляров
|
||||
|
||||
**✅ План пошаговый:** 4 фазы с детальным логированием и тестированием
|
||||
|
||||
**✅ Минимальные изменения:** Фокус на исправлении, а не переписывании архитектуры
|
||||
|
||||
**🎯 Следующий шаг:** Начать PHASE 1 - добавить детальное логирование для подтверждения диагноза
|
Reference in New Issue
Block a user