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

304 lines
11 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.

# 📋 ПЛАН УЛУЧШЕНИЯ АРХИТЕКТУРЫ АВТОРИЗАЦИИ И БЕЗОПАСНОСТИ SFERA
**Дата создания:** 2025-09-18
**Автор:** Claude AI + Вероника Смирнова
**Статус:** В разработке
## 📊 Анализ текущего состояния
### ✅ Что уже реализовано и работает:
1. **Безопасные ID (CUID)**
- Все модели используют `@default(cuid())`
- Невозможно угадать ID других организаций
- Пример: `cmfpe46iv0001y51d87f4vy2n`
2. **Защита типов кабинетов**
- `useRoleGuard` на 49 страницах
- Автоматический редирект при попытке доступа к чужому типу кабинета
- Селлер не может зайти в кабинет Поставщика
3. **Изоляция данных на уровне API**
- Все резолверы фильтруют по `organizationId`
- Каждая организация видит только свои данные
- Проверки в каждом GraphQL резолвере
4. **Структура роутинга**
- Четкое разделение: `/seller/*`, `/fulfillment/*`, `/logistics/*`, `/wholesale/*`
- DashboardHome автоматически направляет в нужный кабинет
### 🚨 Выявленные проблемы:
1. **Отсутствие глобального состояния (AuthContext)**
- Каждый компонент создает свой экземпляр useAuth
- Состояние не синхронизируется между компонентами
- Sidebar не видит данные пользователя после авторизации
2. **Дублирование кода безопасности**
- Одинаковые проверки в 50+ резолверах
- Риск забыть добавить проверку в новый резолвер
- Сложность поддержки
3. **Отсутствие персистентности**
- При обновлении страницы состояние теряется
- Повторные запросы GET_ME
- Плохой UX при F5
4. **Нет серверной проверки роутов**
- Проверки только на уровне компонентов
- Страница начинает загружаться до проверки прав
## 🎯 План улучшений
### ФАЗА 1: Критические исправления
#### 1. Реализация AuthContext [🔴 КРИТИЧНО]
**Срок:** 2-3 часа
**Приоритет:** Максимальный
**Влияние:** Решает проблему с sidebar и синхронизацией состояния
**План реализации:**
1. Создать `/src/contexts/AuthContext.tsx`:
```typescript
import { createContext, useContext, useState, useEffect } from 'react'
import { useApolloClient } from '@apollo/client'
interface AuthContextType {
user: User | null
isAuthenticated: boolean
isLoading: boolean
checkAuth: () => Promise<void>
login: (phone: string, code: string) => Promise<void>
logout: () => void
}
const AuthContext = createContext<AuthContextType | null>(null)
export function AuthProvider({ children }) {
// Перенести всю логику из текущего useAuth
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState(true)
// ... вся логика авторизации
return (
<AuthContext.Provider value={{ user, ... }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within AuthProvider')
}
return context
}
```
2. Обновить `/src/app/providers.tsx`:
```typescript
export function Providers({ children }) {
return (
<ApolloProvider client={apolloClient}>
<AuthProvider>
<SidebarProvider>
{children}
</SidebarProvider>
</AuthProvider>
</ApolloProvider>
)
}
```
3. Убрать временные решения:
- Удалить `useQuery(GET_ME)` из AppShell
- Убрать передачу user через props
- Вернуть оригинальную логику компонентов
#### 2. GraphQL Middleware для безопасности [🟡 ВАЖНО]
**Срок:** 1 день
**Приоритет:** Высокий
**Влияние:** Централизованная безопасность, чистый код
**План реализации:**
1. Создать `/src/graphql/middleware/organizationAccess.ts`:
```typescript
export const organizationAccessMiddleware = async (
resolve,
parent,
args,
context,
info
) => {
// Пропускаем публичные операции
const PUBLIC_OPERATIONS = ['login', 'sendSmsCode', 'verifySmsCode']
if (PUBLIC_OPERATIONS.includes(info.fieldName)) {
return resolve(parent, args, context, info)
}
// Проверяем авторизацию
if (!context.user?.organizationId) {
throw new GraphQLError('Unauthorized', {
extensions: { code: 'UNAUTHENTICATED' }
})
}
// Автоматически добавляем organizationId к запросам
if (args.where && typeof args.where === 'object') {
args.where.organizationId = context.user.organizationId
}
// Логирование для безопасности
console.log(`[${context.user.organizationId}] ${info.fieldName}`, {
userId: context.user.id,
operation: info.fieldName,
timestamp: new Date().toISOString()
})
return resolve(parent, args, context, info)
}
```
2. Применить middleware ко всем резолверам
3. Удалить дублированные проверки из резолверов
### ФАЗА 2: Важные улучшения
#### 3. Персистентность состояния [🟡 ВАЖНО]
**Срок:** 0.5 дня
**Приоритет:** Средний
**Влияние:** Улучшение UX при обновлении страницы
**План реализации:**
1. Добавить в AuthContext сохранение состояния:
```typescript
// При успешной авторизации
const encryptedUser = encrypt(JSON.stringify(user))
localStorage.setItem('auth:user', encryptedUser)
// При инициализации
const savedUser = localStorage.getItem('auth:user')
if (savedUser) {
const user = JSON.parse(decrypt(savedUser))
// Проверить валидность токена
}
```
2. Реализовать refresh token механизм
3. Очистка при logout
#### 4. Next.js Middleware [🟢 ЖЕЛАТЕЛЬНО]
**Срок:** 1 день
**Приоритет:** Средний
**Влияние:** Производительность, серверная защита
**План реализации:**
1. Создать `/src/middleware.ts`:
```typescript
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { jwtVerify } from 'jose'
export async function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value
const pathname = request.nextUrl.pathname
// Публичные маршруты
const publicPaths = ['/', '/login', '/register']
if (publicPaths.includes(pathname)) return
// Проверка токена
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
try {
const { payload } = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET!))
const orgType = payload.organization?.type
// Проверка доступа к кабинету
if (pathname.startsWith('/seller') && orgType !== 'SELLER') {
return NextResponse.redirect(new URL('/dashboard', request.url))
}
// ... остальные проверки
} catch (error) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
}
```
### ФАЗА 3: Оптимизации
#### 5. Улучшение загрузки [🔵 ОПЦИОНАЛЬНО]
**Срок:** 1 неделя
**Приоритет:** Низкий
**Влияние:** Улучшение UX
- Skeleton screens для всех страниц
- Prefetch критических данных
- Оптимистичные обновления UI
- PWA функциональность
#### 6. Расширенная система прав [🔵 ОПЦИОНАЛЬНО]
**Срок:** 2 недели
**Приоритет:** Низкий
**Влияние:** Enterprise функциональность
- Роли: Admin, Manager, Viewer
- Permissions: canEdit, canDelete, canApprove
- Аудит всех действий
- Делегирование доступа
## 📈 Ожидаемые результаты
### После ФАЗЫ 1:
- ✅ Sidebar работает стабильно
- ✅ Состояние синхронизировано между компонентами
- ✅ Единая точка контроля безопасности
- ✅ Чистый, поддерживаемый код
### После ФАЗЫ 2:
- ✅ Мгновенная загрузка после F5
- ✅ Защита на уровне сервера
- ✅ Автоматическое управление сессией
### После ФАЗЫ 3:
- ✅ Премиум UX
- ✅ Enterprise-ready система
- ✅ Готовность к масштабированию
## 🚀 Рекомендуемый порядок выполнения
1. **Неделя 1:** AuthContext (решает критическую проблему)
2. **Неделя 2:** GraphQL Middleware + Персистентность
3. **Месяц 2:** Next.js Middleware + Оптимизации по необходимости
## 📝 Критерии успеха
- [ ] Sidebar отображается сразу после авторизации
- [ ] Нет дублирования состояния между компонентами
- [ ] Единая проверка безопасности для всех API запросов
- [ ] Состояние сохраняется при обновлении страницы
- [ ] Производительность не ухудшилась
- [ ] Код стал чище и проще в поддержке
---
*Документ будет обновляться по мере реализации плана*