feat: Phase 1 - Implementation of Data Security Infrastructure

Implemented comprehensive data security infrastructure for SFERA platform:

## Security Classes Created:
- `SupplyDataFilter`: Role-based data filtering for supply orders
- `ParticipantIsolation`: Data isolation between competing organizations
- `RecipeAccessControl`: Protection of production recipes and trade secrets
- `CommercialDataAudit`: Audit logging and suspicious activity detection
- `SecurityLogger`: Centralized security event logging system

## Infrastructure Components:
- Feature flags system for gradual security rollout
- Database migrations for audit logging (AuditLog, SecurityAlert models)
- Secure resolver wrapper for automatic GraphQL security
- TypeScript interfaces and type safety throughout

## Security Features:
- Role-based access control (SELLER, WHOLESALE, FULFILLMENT, LOGIST)
- Commercial data protection between competitors
- Production recipe confidentiality
- Audit trail for all data access
- Real-time security monitoring and alerts
- Rate limiting and suspicious activity detection

## Implementation Notes:
- All console logging replaced with centralized security logger
- Comprehensive TypeScript typing with no explicit 'any' types
- Modular architecture following SFERA coding standards
- Feature flag controlled rollout for safe deployment

This completes Phase 1 of the security implementation plan.
Next phases will integrate these classes into existing GraphQL resolvers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-22 17:51:02 +03:00
parent e7e4889102
commit 6e3201f491
20 changed files with 5671 additions and 66 deletions

View File

@ -755,6 +755,265 @@ const componentVariants = cva(
{loading ? null : <ProductCard data={product} />}
```
## 🏪 КОМПОНЕНТЫ КАБИНЕТА ПОСТАВЩИКА (WHOLESALE)
### АРХИТЕКТУРА КОМПОНЕНТОВ ПОСТАВЩИКА:
```typescript
src/components/
├── warehouse/ # Компоненты склада поставщика
├── warehouse-dashboard.tsx # Главный dashboard склада
├── product-card.tsx # Карточка товара
├── product-form.tsx # Форма создания/редактирования товара
└── warehouse-statistics.tsx # Статистика склада
├── supplier-orders/ # Компоненты обработки заказов
├── supplier-orders-dashboard.tsx # Главный dashboard заказов
├── supplier-order-card.tsx # Карточка заказа
├── supplier-orders-tabs.tsx # Табы по статусам заказов
├── supplier-orders-search.tsx # Поиск и фильтры
└── supplier-order-stats.tsx # Статистика заказов
└── economics/ # Экономическая аналитика
└── wholesale-economics-page.tsx # Финансовая отчетность
```
### 🏢 КАРТОЧКА ПОСТАВЩИКА В ИНТЕРФЕЙСЕ:
**Структура карточки:**
```jsx
<div className="supplier-card glass-card">
<div className="flex items-start gap-2">
{/* Аватар организации */}
<OrganizationAvatar organization={supplier} size="sm" />
<div className="flex-1 min-w-0">
{/* Название поставщика */}
<h4 className="text-white font-medium text-sm truncate">{supplier.name || supplier.fullName}</h4>
{/* ИНН и рынок */}
<div className="flex items-center gap-2 mt-1">
<p className="text-white/60 text-xs font-mono">ИНН: {supplier.inn}</p>
{supplier.market && <Badge className="market-badge">{getMarketLabel(supplier.market)}</Badge>}
</div>
</div>
</div>
</div>
```
**Визуальные правила карточки поставщика:**
- **Аватар**: Размер `sm`, позиционирование слева от текста
- **Название**: Приоритет `name` над `fullName`, с усечением `truncate`
- **ИНН**: Моноширинный шрифт `font-mono`, цвет `text-white/60`
- **Рынок**: Badge компонент с индивидуальными цветовыми схемами
- **Glass эффект**: `glass-card` класс с полупрозрачным фоном
### 🔍 ПОИСКОВЫЙ ИНТЕРФЕЙС ПОСТАВЩИКОВ:
```jsx
<Input
placeholder="Поиск поставщиков..."
className="bg-white/5 border-white/10 text-white placeholder:text-white/50 pl-10 h-9"
onChange={(e) => handleSupplierSearch(e.target.value)}
/>
```
**Особенности поиска:**
- Glass эффект: `bg-white/5 border-white/10`
- Плейсхолдер: `placeholder:text-white/50`
- Левый отступ для иконки: `pl-10`
- Высота: `h-9` (36px)
### 🎨 ЦВЕТОВЫЕ СХЕМЫ РЫНКОВ ПОСТАВЩИКОВ:
```typescript
// Примеры цветовых схем для физических рынков
const marketColors = {
sadovod: 'bg-green-500/20 text-green-300 border-green-500/30',
'tyak-moscow': 'bg-blue-500/20 text-blue-300 border-blue-500/30',
default: 'bg-gray-500/20 text-gray-300 border-gray-500/30',
}
// Функция получения метки рынка
function getMarketLabel(market: string): string {
const labels = {
sadovod: 'Садовод',
'tyak-moscow': 'ТЯК Москва',
default: 'Рынок',
}
return labels[market] || labels.default
}
```
### 📦 БЛОКИ ПОСТАВЩИКОВ В СЕЛЛЕР ИНТЕРФЕЙСЕ:
**Правила горизонтальной прокрутки:**
```jsx
{
/* Контейнер с горизонтальной прокруткой */
}
;<div className="flex gap-3 overflow-x-auto scrollbar-hide pb-2">
{suppliers.map((supplier) => (
<div
key={supplier.id}
className="flex-none w-64" // Фиксированная ширина 256px
>
<SupplierCard supplier={supplier} />
</div>
))}
</div>
```
**Требования к горизонтальным блокам:**
- Фиксированная ширина карточек: `w-64` (256px)
- Отсутствие сжатия: `flex-none`
- Скрытие скроллбара: `scrollbar-hide`
- Отступ от низа: `pb-2` для визуального комфорта
### 🚨 CRITICAL UI RULES ДЛЯ ПОСТАВЩИКОВ:
#### **1. СТАТУСЫ vs КНОПКИ ДЕЙСТВИЙ:**
```jsx
{
/* ❌ НЕПРАВИЛЬНО: Показывать статус поставщику */
}
{
user.organization.type === 'WHOLESALE' && <StatusBadge status={order.status}>Ожидает подтверждения</StatusBadge>
}
{
/* ✅ ПРАВИЛЬНО: Только кнопки действий для поставщика */
}
{
user.organization.type === 'WHOLESALE' && order.status === 'PENDING' && (
<div className="flex gap-2">
<Button variant="glass" size="sm" onClick={() => approveOrder(order.id)}>
Одобрить
</Button>
<Button variant="outline" size="sm" onClick={() => rejectOrder(order.id)}>
Отклонить
</Button>
</div>
)
}
```
#### **2. ОПЦИОНАЛЬНЫЕ ПОЛЯ УПАКОВКИ ПРИ ОДОБРЕНИИ:**
```jsx
{
/* ОПЦИОНАЛЬНЫЕ поля для поставщика - отображаются сразу при одобрении заказа */
}
;<div className="grid grid-cols-2 gap-4">
<div>
<Label htmlFor="packagesCount">Количество грузовых мест</Label>
<Input
id="packagesCount"
type="number"
placeholder="Введите количество (опционально)"
aria-describedby="packages-help"
/>
<p id="packages-help" className="text-xs text-white/60 mt-1">
Используется логистикой для расчета тарифов
</p>
</div>
<div>
<Label htmlFor="volume">Объем груза (м³)</Label>
<Input id="volume" type="number" step="0.01" placeholder="0.00 (опционально)" aria-describedby="volume-help" />
<p id="volume-help" className="text-xs text-white/60 mt-1">
Помогает логистике в планировании маршрутов
</p>
</div>
<div className="col-span-2">
<Label htmlFor="readyDate">Дата готовности к отгрузке</Label>
<GlassDatePicker
id="readyDate"
value={readyDate}
onChange={setReadyDate}
placeholder="Выберите дату (опционально)"
aria-describedby="ready-date-help"
/>
<p id="ready-date-help" className="text-xs text-white/60 mt-1">
Когда товары будут готовы к передаче логистике
</p>
</div>
<div className="col-span-2">
<Label htmlFor="notes">Комментарии для логистики</Label>
<Textarea id="notes" placeholder="Дополнительная информация (опционально)" aria-describedby="notes-help" />
<p id="notes-help" className="text-xs text-white/60 mt-1">
Особые требования к транспортировке или упаковке
</p>
</div>
</div>
{
/* ВАЖНО: Поля показываются на 1-м уровне визуализации поставки */
}
;<div className="mt-4">
<p className="text-sm text-white/80"> Все поля опциональны, но рекомендуются для точного планирования логистики</p>
</div>
```
#### **3. ARIA LABELS ДЛЯ КОМПОНЕНТОВ ПОСТАВЩИКА:**
```jsx
// Кнопки действий с описательными ARIA-атрибутами
<Button
variant="glass"
aria-label={`Одобрить заказ №${order.number} от ${order.organization.name}`}
onClick={() => approveOrder(order.id)}
>
Одобрить
</Button>
// Поля ввода с полными описаниями
<Input
aria-label="Количество грузовых мест для логистического расчета"
aria-required="true"
aria-describedby="packages-error packages-help"
/>
```
#### **4. СПЕЦИАЛЬНЫЕ РАЗМЕРЫ ДЛЯ КАБИНЕТА ПОСТАВЩИКА:**
```typescript
// Размеры карточек в кабинете поставщика
const wholesaleSizes = {
supplierCard: 'h-[164px] w-64', // 164px высота, 256px ширина
orderCard: 'min-h-[120px]', // Минимум 120px для заказов
productCard: 'h-[180px]', // 180px для товарных карточек
containerWithPadding: 'h-[196px]', // 164 + 32px отступы сверху/снизу
}
```
### 📐 ФОРМУЛА РАСЧЕТА РАЗМЕРОВ КОНТЕЙНЕРОВ:
```typescript
// ОБЯЗАТЕЛЬНАЯ формула для всех контейнеров поставщика
const containerHeight = {
formula: 'Высота контента + padding-top + padding-bottom',
example: {
content: '164px', // Высота карточки поставщика
paddingTop: '16px',
paddingBottom: '16px',
totalContainer: '196px' // 164 + 16 + 16 = 196px
}
}
// ❌ ЗАПРЕЩЕНО: Произвольные размеры без расчета
<div className="h-200"> {/* Откуда 200px? */}
// ✅ ПРАВИЛЬНО: С математическим обоснованием
<div className="h-[196px]"> {/* 164px + 32px отступы */}
```
## 📱 АДАПТИВНОСТЬ
### Responsive Breakpoints: