Files
sfera-new/2025-09-18/AUTH_CONTEXT_MIGRATION_PLAN.md

505 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔐 ПЛАН БЕЗОПАСНОЙ МИГРАЦИИ НА AuthContext
**Дата создания:** 2025-09-18
**Автор:** Claude AI + Вероника Смирнова
**Статус:** Готов к реализации
## 📊 Результаты глубокой диагностики
### Анализ текущей реализации useAuth
**Размер и сложность:**
- 657 строк кода
- 65 файлов используют useAuth()
- Сложная логика с rollback механизмами
- Интеграция с Apollo Client через localStorage
**Ключевые компоненты:**
1. **State Management**
- `user: User | null` - данные пользователя
- `isAuthenticated: boolean` - статус авторизации
- `isLoading: boolean` - индикатор загрузки
- `isCheckingAuth: boolean` - защита от дублирования
2. **Методы авторизации**
- `sendSmsCode` - отправка SMS
- `verifySmsCode` - проверка кода
- `checkAuth` - проверка текущей сессии
- `logout` - выход
3. **Методы регистрации**
- `registerFulfillmentOrganization`
- `registerSellerOrganization`
- `registerOrganization` (универсальный)
4. **Интеграции**
- Apollo Client для GraphQL
- localStorage для токенов
- refreshApolloClient для синхронизации
### Выявленные проблемы
1. **Множественные экземпляры состояния**
```
AppShell → useAuth() → useState (копия 1)
Sidebar → useAuth() → useState (копия 2)
Component → useAuth() → useState (копия 3)
```
2. **Race conditions**
- checkAuth вызывается параллельно из разных компонентов
- isCheckingAuth защищает только локальный экземпляр
3. **Отсутствие синхронизации**
- Обновления в одном компоненте не видны в других
- GET_ME выполняется многократно
4. **Проблемы с SSR**
- Прямое обращение к localStorage
- window checks разбросаны по коду
## 🎯 Архитектура решения
### Новая структура с AuthContext
```
AuthProvider (глобальное состояние)
├── Apollo Provider
│ └── Auth Link (токены из контекста)
├── State Management
│ ├── user
│ ├── isAuthenticated
│ └── isLoading
└── Methods
├── Authentication
├── Registration
└── Session Management
```
### Преимущества
1. **Единое состояние** - все компоненты видят одни данные
2. **Оптимизация запросов** - GET_ME выполняется 1 раз
3. **Синхронизация** - изменения видны везде мгновенно
4. **SSR совместимость** - централизованные проверки
5. **Типобезопасность** - строгая типизация контекста
## 📋 Поэтапный план миграции
### ЭТАП 0: Подготовка [30 мин]
**Цель:** Создать безопасную среду для миграции
1. **Создать backup текущего состояния**
```bash
git add .
git commit -m "backup: перед миграцией на AuthContext"
git branch backup-before-auth-context
```
2. **Создать feature branch**
```bash
git checkout -b feature/auth-context-migration
```
3. **Подготовить структуру папок**
```
src/
├── contexts/
│ └── auth/
│ ├── AuthContext.tsx # Основной контекст
│ ├── AuthProvider.tsx # Provider компонент
│ ├── types.ts # TypeScript типы
│ └── utils.ts # Вспомогательные функции
```
### ЭТАП 1: Создание AuthContext с минимальной функциональностью [1 час]
**Цель:** Создать работающий контекст без нарушения существующего функционала
1. **Создать типы** (`src/contexts/auth/types.ts`)
```typescript
export interface User {
id: string
phone: string
avatar?: string
managerName?: string
organization?: Organization
}
export interface Organization {
id: string
inn: string
type: 'FULFILLMENT' | 'SELLER' | 'LOGIST' | 'WHOLESALE'
// ... остальные поля
}
export interface AuthState {
user: User | null
isAuthenticated: boolean
isLoading: boolean
}
export interface AuthContextType extends AuthState {
// Методы будем добавлять постепенно
checkAuth: () => Promise<void>
logout: () => void
}
```
2. **Создать контекст** (`src/contexts/auth/AuthContext.tsx`)
```typescript
import { createContext } from 'react'
import type { AuthContextType } from './types'
export const AuthContext = createContext<AuthContextType | null>(null)
```
3. **Создать базовый Provider** (`src/contexts/auth/AuthProvider.tsx`)
```typescript
import { useState, useCallback, useEffect } from 'react'
import { AuthContext } from './AuthContext'
import { getAuthToken, removeAuthToken } from '@/lib/apollo-client'
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [isAuthenticated, setIsAuthenticated] = useState(false)
const [isLoading, setIsLoading] = useState(true)
// Минимальная реализация checkAuth
const checkAuth = useCallback(async () => {
const token = getAuthToken()
if (!token) {
setIsAuthenticated(false)
setUser(null)
setIsLoading(false)
return
}
// TODO: Добавить GET_ME запрос
setIsLoading(false)
}, [])
// Минимальная реализация logout
const logout = useCallback(() => {
removeAuthToken()
setUser(null)
setIsAuthenticated(false)
window.location.href = '/'
}, [])
useEffect(() => {
checkAuth()
}, [checkAuth])
const value = {
user,
isAuthenticated,
isLoading,
checkAuth,
logout
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
```
4. **Создать временный хук-обертку** (`src/hooks/useAuth.ts`)
```typescript
// В начале файла добавляем
import { useContext } from 'react'
import { AuthContext } from '@/contexts/auth/AuthContext'
// Временный флаг для постепенной миграции
const USE_AUTH_CONTEXT = false
export const useAuth = (): UseAuthReturn => {
if (USE_AUTH_CONTEXT) {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within AuthProvider')
}
// Адаптер для совместимости API
return {
...context,
// Методы-заглушки для совместимости
sendSmsCode: async () => ({ success: false, message: 'Not implemented' }),
verifySmsCode: async () => ({ success: false, message: 'Not implemented' }),
registerFulfillmentOrganization: async () => ({ success: false, message: 'Not implemented' }),
registerSellerOrganization: async () => ({ success: false, message: 'Not implemented' }),
registerOrganization: async () => ({ success: false, message: 'Not implemented' }),
updateUser: () => {}
}
}
// Существующая реализация остается без изменений
// ... весь текущий код
}
```
### ЭТАП 2: Тестирование базовой интеграции [30 мин]
**Цель:** Убедиться что ничего не сломалось
1. **Добавить AuthProvider в providers.tsx**
```typescript
import { AuthProvider } from '@/contexts/auth/AuthProvider'
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ApolloProvider client={apolloClient}>
<AuthProvider>
<SidebarProvider>
{children}
</SidebarProvider>
</AuthProvider>
</ApolloProvider>
)
}
```
2. **Включить USE_AUTH_CONTEXT для одного компонента**
- Начать с простого компонента (например, UserProfile в sidebar)
- Проверить что компонент рендерится
- Проверить что нет ошибок в консоли
3. **Rollback план**
- Если есть ошибки - установить USE_AUTH_CONTEXT = false
- Исправить проблемы
- Повторить тестирование
### ЭТАП 3: Миграция основного функционала [2 часа]
**Цель:** Перенести всю логику в AuthContext
1. **Перенести checkAuth с GET_ME**
```typescript
const checkAuth = useCallback(async () => {
if (isCheckingAuth.current) return
const token = getAuthToken()
if (!token) {
setIsAuthenticated(false)
setUser(null)
setIsLoading(false)
return
}
isCheckingAuth.current = true
setIsLoading(true)
try {
const { data } = await apolloClient.query({
query: GET_ME,
errorPolicy: 'all',
fetchPolicy: 'network-only'
})
if (data?.me) {
setUser(data.me)
setIsAuthenticated(true)
setUserData(data.me)
}
} catch (error) {
// Обработка ошибок
} finally {
isCheckingAuth.current = false
setIsLoading(false)
}
}, [])
```
2. **Перенести SMS методы**
- sendSmsCode
- verifySmsCode
- Сохранить всю логику с логированием
3. **Перенести методы регистрации**
- registerFulfillmentOrganization
- registerSellerOrganization
- registerOrganization
- Сохранить rollback механизмы
4. **Добавить updateUser**
```typescript
const updateUser = useCallback((updatedUser: Partial<User>) => {
setUser(current => {
if (!current) return current
const updated = { ...current, ...updatedUser }
setUserData(updated) // Синхронизация с localStorage
return updated
})
}, [])
```
### ЭТАП 4: Постепенная миграция компонентов [1 день]
**Цель:** Безопасно перевести все компоненты на новую систему
1. **Приоритетные компоненты** (первая очередь)
- AppShell
- Sidebar и все его варианты
- AuthGuard
2. **Критические компоненты** (вторая очередь)
- Страницы авторизации (login, register)
- DashboardHome
- useRoleGuard
3. **Остальные компоненты** (третья очередь)
- Разбить на группы по 10-15 файлов
- Мигрировать группами
- Тестировать после каждой группы
**Процесс для каждого компонента:**
1. Включить USE_AUTH_CONTEXT локально
2. Проверить функциональность
3. Если работает - коммит
4. Если нет - откат и исправление
### ЭТАП 5: Оптимизация и очистка [1 час]
**Цель:** Удалить старый код и оптимизировать
1. **Удалить старую реализацию из useAuth**
- Оставить только обертку для контекста
- Удалить локальные useState
- Удалить дублированную логику
2. **Оптимизировать рендеринг**
```typescript
// Мемоизация значения контекста
const value = useMemo(() => ({
user,
isAuthenticated,
isLoading,
checkAuth,
logout,
// ... другие методы
}), [user, isAuthenticated, isLoading])
```
3. **Добавить DevTools**
```typescript
if (process.env.NODE_ENV === 'development') {
(window as any).__AUTH_STATE__ = { user, isAuthenticated }
}
```
### ЭТАП 6: Финальное тестирование [1 час]
**Цель:** Убедиться что все работает
1. **Функциональные тесты**
- [ ] Авторизация по SMS
- [ ] Регистрация всех типов организаций
- [ ] Отображение sidebar
- [ ] Переходы между страницами
- [ ] Выход из системы
- [ ] Обновление страницы (F5)
2. **Тесты производительности**
- [ ] Нет множественных GET_ME запросов
- [ ] Нет лишних ре-рендеров
- [ ] Быстрая загрузка после F5
3. **Регрессионные тесты**
- [ ] Все 65 компонентов работают
- [ ] API ключи сохраняются
- [ ] Роутинг по типам организаций
## 🚨 Риски и митигация
### Риск 1: Поломка авторизации
**Митигация:**
- Постепенная миграция через флаг USE_AUTH_CONTEXT
- Возможность быстрого отката
- Тестирование на каждом этапе
### Риск 2: Потеря состояния
**Митигация:**
- Сохранение в localStorage остается
- Rollback механизмы сохраняются
- Логирование всех изменений
### Риск 3: Проблемы с SSR
**Митигация:**
- Все проверки window в одном месте
- useEffect для клиентских операций
- Правильная инициализация состояния
### Риск 4: Race conditions
**Митигация:**
- useRef для флагов загрузки
- Отмена дублированных запросов
- Правильная очередность операций
## 📊 Метрики успеха
1. **Функциональность**
- ✅ Все 65 компонентов работают
- ✅ Sidebar отображается корректно
- ✅ Авторизация стабильна
2. **Производительность**
- ✅ GET_ME вызывается 1 раз
- ✅ Нет задержек при навигации
- ✅ Быстрая загрузка после F5
3. **Качество кода**
- ✅ Единое место управления состоянием
- ✅ Типобезопасность
- ✅ Отсутствие дублирования
## 🔄 Rollback план
Если что-то пойдет не так на любом этапе:
1. **Быстрый откат**
```bash
git checkout backup-before-auth-context
```
2. **Частичный откат**
- Установить USE_AUTH_CONTEXT = false
- Вернуть проблемные компоненты на старую версию
- Исправить проблемы в изолированной ветке
3. **Восстановление данных**
- localStorage сохраняется
- Токены остаются валидными
- Пользователи не заметят проблем
## 📝 Чек-лист готовности
Перед началом миграции убедитесь:
- [ ] Создан backup текущего состояния
- [ ] Команда предупреждена о работах
- [ ] Подготовлен план коммуникации при проблемах
- [ ] Есть доступ к логам и мониторингу
- [ ] Определено время для миграции (лучше в период низкой активности)
## 🎯 Ожидаемый результат
После завершения миграции:
1. **Немедленные улучшения**
- Sidebar работает стабильно
- Состояние синхронизировано между компонентами
- Уменьшено количество запросов к API
2. **Долгосрочные преимущества**
- Готовность к добавлению новых функций
- Упрощенная отладка
- Лучшая производительность
- Возможность добавления продвинутых функций (персистентность, refresh tokens)
---
*Документ будет обновляться по ходу выполнения миграции*