docs: добавить планы улучшения архитектуры SFERA

This commit is contained in:
Veronika Smirnova
2025-09-18 21:28:07 +03:00
parent 733ccadeb7
commit 3efc387308
18 changed files with 3130 additions and 74 deletions

View File

@ -0,0 +1,505 @@
# 🔐 ПЛАН БЕЗОПАСНОЙ МИГРАЦИИ НА 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)
---
*Документ будет обновляться по ходу выполнения миграции*