diff --git a/docs/TZ.md b/docs/TZ.md index bbf38de..5260ce1 100644 --- a/docs/TZ.md +++ b/docs/TZ.md @@ -1,9 +1,1510 @@ -# Compton Technical Specification +# Техническое задание +## Платформа «Комптон» — React-приложение с модульной архитектурой +### Испольнителю следовать плану реализации с особым пристрастрием -The canonical technical specification is maintained in project planning artifacts and reflected in the implementation constraints in this repository. +**Версия документа:** 1.0 +**Дата:** 09.07.2026 +**Разработчик:** Huli -This project intentionally follows: -- Modular monolith backend -- FSD-like frontend layers -- Mandatory test gates (unit/integration/component/e2e) -- Security-first auth/token handling +--- + +## Содержание + +1. [Общие сведения](#1-общие-сведения) +2. [Цели и границы проекта](#2-цели-и-границы-проекта) +3. [Профиль нагрузки и масштабирование](#3-профиль-нагрузки-и-масштабирование) +4. [Технологический стек](#4-технологический-стек) +5. [Архитектура системы](#5-архитектура-системы) +6. [Модульная структура Frontend (React)](#6-модульная-структура-frontend-react) +7. [Модульная структура Backend (API)](#7-модульная-структура-backend-api) +8. [Модель данных](#8-модель-данных) +9. [API-контракты](#9-api-контракты) +10. [Аутентификация и авторизация](#10-аутентификация-и-авторизация) +11. [UI/UX и дизайн-система](#11-uiux-и-дизайн-система) +12. [Нефункциональные требования](#12-нефункциональные-требования) +13. [Инфраструктура и DevOps](#13-инфраструктура-и-devops) +14. [Безопасность](#14-безопасность) +15. [Тестирование и качество](#15-тестирование-и-качество) +16. [Этапы разработки](#16-этапы-разработки) +17. [Критерии приёмки](#17-критерии-приёмки) +18. [Риски и допущения](#18-риски-и-допущения) + +--- + +## 1. Общие сведения + +### 1.1. Наименование проекта + +**Комптон®** — веб-платформа бренда «Organic Tech» с публичной витриной, личным кабинетом пользователя и административной панелью. + +### 1.2. Текущее состояние + +- Чисто поржать на ошибках и нейронке +- Заглушка на Flask (`main.py`) с одностраничным лендингом. +- Визуальная идентичность задана не полностью: цвета, шрифты (Inter, JetBrains Mono), hero-блок, бегущая строка. +- Рефакторинг текщего состояния не возможен, задача переписать проект. +- Функциональность отсутствует: нет регистрации, контента, API, БД. + + +### 1.3. Заказчик / продукт + +Еблан который зовет себя Huli. Документ описывает целевую архитектуру для замены заглушки полноценным продуктом. + +### 1.4. Термины + +| Термин | Определение | +|--------|-------------| +| **Модуль (feature)** | Изолированный функциональный блок с собственными компонентами, API-слоем, типами и тестами | +| **Shared** | Переиспользуемый код без бизнес-логики конкретного модуля | +| **Core** | Ядро приложения: роутинг, провайдеры, конфигурация, HTTP-клиент | +| **MAU** | Monthly Active Users — уникальные пользователи за месяц | +| **CCU** | Concurrent Users — одновременно онлайн | + +--- + +## 2. Цели и границы проекта + +### 2.1. Бизнес-цели + +1. Заменить заглушку работающим сайтом с сохранением бренда. +2. Обеспечить регистрацию и работу **до 1 000 зарегистрированных пользователей** (~100–150 DAU) без деградации UX. +3. Заложить архитектуру, позволяющую масштабироваться до **10 000+ пользователей** без переписывания модулей. +4. Обеспечить независимую разработку модулей разными разработчиками. +5. **Каждый шаг реализации** (задача / PR / модуль) поставляется **только с полным набором тестов**: unit/integration логики + E2E пользовательских сценариев (§15). + +### 2.2. Функциональный scope (MVP → v1.0) + +#### MVP (фаза 1) + +| Модуль | Функции | +|--------|---------| +| **Landing** | Hero, бегущая строка, «О бренде», контакты, SEO-мета | +| **Auth** | Регистрация, вход, выход, восстановление пароля, подтверждение email *(требует SMTP в MVP — см. §16)* | +| **Profile** | Просмотр/редактирование профиля, аватар, смена пароля | +| **Content** | Статические страницы (О нас, Политика, Условия) из CMS или markdown | +| **Admin** | Управление пользователями, контентом, просмотр метрик | + +#### v1.0 (фаза 2, опционально) + +| Модуль | Функции | +|--------|---------| +| **Catalog** | Каталог продуктов/услуг, карточки, фильтры | +| **Orders** | Корзина, оформление заявки (без оплаты — см. §2.3), история | +| **Notifications** | In-app уведомления; email-рассылки через очередь (Celery) | +| **Analytics** | Дашборд событий, экспорт | + +### 2.3. Out of scope (не входит в v1.0) + +- Мобильное нативное приложение (только responsive web). +- Мультиязычность (заложить i18n-структуру, реализовать позже). +- Платёжные шлюзы (интеграция — отдельный этап). Модуль Orders в v1.0 работает как **заявка/бронирование без оплаты**. +- Real-time чат / WebRTC. + +--- + +## 3. Профиль нагрузки и масштабирование + +### 3.1. Целевые метрики (1000 пользователей) + +| Метрика | Значение | Комментарий | +|---------|----------|-------------| +| Зарегистрированных пользователей | 1 000 | Целевой объём на старте | +| DAU | 100–150 (10–15%) | Типичный коэффициент для B2C | +| CCU (пик) | 30–50 | Одновременные сессии | +| RPS API (пик) | 20–50 req/s | С запасом ×3 | +| Размер БД | < 5 GB | Профили, контент, логи | +| Медиа | < 50 GB | CDN для статики | + +### 3.2. Путь масштабирования + +```mermaid +flowchart LR + subgraph phase1 [Фаза 1: до 1K] + A1[Monolith API] + A2[PostgreSQL] + A3[Redis cache] + A4[CDN static] + end + + subgraph phase2 [Фаза 2: 1K–10K] + B1[2× API instances] + B2[Read replica PG] + B3[Redis cluster] + B4[Object storage S3] + end + + subgraph phase3 [Фаза 3: 10K+] + C1[Horizontal API scale] + C2[Extract heavy modules] + C3[Message queue] + C4[Separate admin] + end + + phase1 --> phase2 --> phase3 +``` + +**Принцип:** на 1000 пользователей достаточно **модульного монолита** (один backend-процесс, чёткие границы модулей). Микросервисы не нужны до 10K+ и только для узких «тяжёлых» модулей (уведомления, аналитика). + +### 3.3. SLA (целевые) + +| Параметр | MVP | v1.0 | +|----------|-----|------| +| Uptime | 99.5% | 99.9% | +| TTFB публичных страниц | < 500 ms | < 300 ms | +| API p95 latency | < 300 ms | < 200 ms | +| LCP (Lighthouse mobile) | < 2.5 s | < 2.0 s | + +--- + +## 4. Технологический стек + +### 4.1. Frontend + +| Слой | Технология | Обоснование | +|------|------------|-------------| +| Framework | **React 19** + **TypeScript 5** | Экосистема, типизация | +| Bundler | **Vite 6** | Быстрая сборка, HMR | +| Routing | **React Router 7** | Стандарт de facto | +| Server state | **TanStack Query 5** | Кеш, retry, invalidation | +| Client state | **Zustand** | Лёгкий, без boilerplate | +| Forms | **React Hook Form** + **Zod** | Валидация, DX | +| UI | **Tailwind CSS 4** + headless (Radix UI) | Соответствие дизайн-системе | +| HTTP | **Axios** или fetch-обёртка | Interceptors, типизация | +| i18n (заготовка) | **react-i18next** | Структура без реализации | +| Tests | **Vitest** + **Testing Library** + **Playwright** + **pytest** | Unit + integration + E2E на каждом PR (§15) | + +### 4.2. Backend + +| Слой | Технология | Обоснование | +|------|------------|-------------| +| Runtime | **Python 3.12** | Преемственность Flask-проекта | +| Framework | **FastAPI** | Async, OpenAPI, производительность | +| ORM | **SQLAlchemy 2** + **Alembic** | Миграции, типизация | +| Validation | **Pydantic v2** | Согласованность с OpenAPI | +| Auth | **JWT** (access + refresh rotation) + **passlib/bcrypt** | Stateless access, revocable refresh | +| Task queue (v1.0) | **Celery** + Redis | Email, фоновые задачи | +| Email | **SMTP** / SendGrid / Resend | MVP: синхронная отправка verify/reset; v1.0: Celery queue | + +### 4.3. Data & Infra + +| Компонент | Технология | +|-----------|------------| +| Primary DB | **PostgreSQL 16** | +| Cache / sessions | **Redis 7** | +| File storage | **S3-compatible** (MinIO dev / Yandex S3 / AWS prod) | +| Reverse proxy | **Nginx** | +| Containerization | **Docker** + **Docker Compose** (dev/staging) | +| CI/CD | **GitHub Actions** | +| Monitoring | **Prometheus** + **Grafana** (или managed: Datadog) | +| Logging | Structured JSON → **Loki** или cloud logs | +| Error tracking | **Sentry** | + +### 4.4. Архитектурный стиль Frontend + +**Feature-Sliced Design (FSD)** — адаптированная версия с явными модулями: + +``` +app → pages → modules → shared +``` + +- **app** — инициализация, провайдеры, глобальный роутер. +- **pages** — композиция модулей в маршруты (тонкий слой). +- **modules** — бизнес-фичи (auth, profile, catalog…). +- **shared** — UI-kit, utils, API client, types без доменной логики. + +--- + +## 5. Архитектура системы + +### 5.1. Общая схема + +```mermaid +flowchart TB + subgraph client [Клиент] + Browser[Browser / Mobile Web] + SPA[React SPA] + end + + subgraph edge [Edge] + CDN[CDN — static assets] + Nginx[Nginx — TLS, gzip, rate limit] + end + + subgraph backend [Backend] + API[FastAPI Monolith] + subgraph modules_api [Modules] + M1[auth] + M2[users] + M3[content] + M4[admin] + end + end + + subgraph data [Data Layer] + PG[(PostgreSQL)] + Redis[(Redis)] + S3[(Object Storage)] + end + + Browser --> CDN + Browser --> Nginx + Nginx -->|static| SPA + SPA -->|REST JSON /api| Nginx + Nginx --> API + API --> modules_api + modules_api --> PG + modules_api --> Redis + modules_api --> S3 +``` + +### 5.2. Принципы модульности + +1. **Вертикальные срезы** — каждый модуль владеет UI + API + схемой БД (таблицы через общий ORM, но namespace по модулю). +2. **Запрет cross-import между modules** — общение только через: + - публичный API модуля (`modules/auth/api/index.ts`); + - shared-слой; + - backend REST/events. +3. **Публичный контракт модуля** — `index.ts` экспортирует только то, что нужно снаружи. +4. **Приватная реализация** — `internal/` не импортируется из других модулей. + +### 5.3. Репозиторий (monorepo-lite) + +``` +compton/ +├── apps/ +│ ├── web/ # React SPA +│ └── api/ # FastAPI +├── packages/ +│ ├── shared-types/ # OpenAPI-generated TS types +│ └── eslint-config/ # Общие lint rules +├── infra/ +│ ├── docker/ +│ ├── nginx/ +│ └── terraform/ # опционально +├── docs/ +│ └── TZ.md +└── docker-compose.yml +``` + +На старте допустим **single repo** без Turborepo; `packages/shared-types` генерируется из OpenAPI при CI. + +--- + +## 6. Модульная структура Frontend (React) + +### 6.1. Дерево каталогов `apps/web/src` + +``` +src/ +├── app/ +│ ├── App.tsx +│ ├── providers/ +│ │ ├── QueryProvider.tsx +│ │ ├── AuthProvider.tsx +│ │ └── ThemeProvider.tsx +│ ├── router/ +│ │ ├── routes.tsx +│ │ └── guards/ +│ │ ├── AuthGuard.tsx +│ │ └── AdminGuard.tsx +│ └── styles/ +│ └── globals.css +│ +├── pages/ +│ ├── HomePage/ +│ ├── LoginPage/ +│ ├── RegisterPage/ +│ ├── ProfilePage/ +│ ├── ContentPage/ +│ └── AdminPage/ +│ +├── modules/ +│ ├── landing/ +│ │ ├── index.ts # public API +│ │ ├── components/ +│ │ │ ├── HeroSection.tsx +│ │ │ └── MarqueeSection.tsx +│ │ ├── hooks/ +│ │ └── types/ +│ │ +│ ├── auth/ +│ │ ├── index.ts +│ │ ├── api/ +│ │ │ └── authApi.ts +│ │ ├── components/ +│ │ │ ├── LoginForm.tsx +│ │ │ └── RegisterForm.tsx +│ │ ├── hooks/ +│ │ │ ├── useLogin.ts +│ │ │ └── useAuth.ts +│ │ ├── store/ +│ │ │ └── authStore.ts +│ │ └── types/ +│ │ +│ ├── profile/ +│ │ ├── index.ts +│ │ ├── api/ +│ │ ├── components/ +│ │ ├── hooks/ +│ │ └── types/ +│ │ +│ ├── content/ +│ │ ├── index.ts +│ │ ├── api/ +│ │ ├── components/ +│ │ └── hooks/ +│ │ +│ ├── admin/ +│ │ ├── index.ts +│ │ ├── api/ +│ │ ├── components/ +│ │ └── hooks/ +│ │ +│ └── catalog/ # v1.0 +│ └── ... +│ +├── __tests__/ # cross-module integration (frontend) +│ └── setup.ts +│ +└── shared/ + ├── api/ + │ ├── client.ts + │ ├── client.test.ts + │ ├── errors.ts + │ └── types.ts + ├── ui/ + │ ├── Button/ + │ │ ├── Button.tsx + │ │ └── Button.test.tsx + │ └── ... + └── ... + +# Каждый module обязан содержать: +modules// +├── __tests__/ # unit: hooks, utils, store +├── components/ +│ └── *.test.tsx # component tests (Testing Library) +└── api/ + └── *.test.ts # API client + mock handlers +``` + +### 6.2. Правила зависимостей (import rules) + +```mermaid +flowchart TD + app --> pages + pages --> modules + modules --> shared + modules -.->|FORBIDDEN| modules + shared -.->|FORBIDDEN| modules + shared -.->|FORBIDDEN| pages +``` + +Enforcement через **ESLint** (`eslint-plugin-boundaries` или custom rules): + +| From → To | app | pages | modules | shared | +|-----------|-----|-------|---------|--------| +| app | ✓ | ✓ | ✓ | ✓ | +| pages | — | ✓ | ✓ | ✓ | +| modules | — | — | ✓ (own) | ✓ | +| shared | — | — | ✗ | ✓ | + +### 6.3. Описание модулей Frontend + +#### 6.3.1. `landing` + +**Ответственность:** + +- Буду ебать за каждый нейрослоп + +**Зависимости:** только `shared/ui`, `shared/hooks`. + +#### 6.3.2. `auth` + +**Ответственность:** аутентификация, сессия, guards. + +| Export (public API) | Описание | +|---------------------|----------| +| `LoginForm`, `RegisterForm` | Формы | +| `useAuth()` | `{ user, isAuthenticated, login, logout, refreshSession }` | + +**Guards** (`AuthGuard`, `GuestGuard`, `AdminGuard`) живут в `app/router/guards/` — они **импортируют** `useAuth` из модуля `auth`, но не экспортируются из него (слой `app` композирует модули). + +**Store:** `accessToken` только in-memory (Zustand). `refreshToken` **только** httpOnly Secure cookie (`SameSite=Lax`). Хранение refresh в `localStorage` / `sessionStorage` **запрещено**. + +#### 6.3.3. `profile` + +**Ответственность:** CRUD профиля пользователя. + +| Функции | API endpoints | +|---------|---------------| +| Просмотр профиля | `GET /api/v1/users/me` | +| Редактирование | `PATCH /api/v1/users/me` | +| Смена пароля (авторизован) | `POST /api/v1/users/me/password` | +| Загрузка аватара | `POST /api/v1/users/me/avatar` | + +#### 6.3.4. `content` + +**Ответственность:** рендер CMS-страниц по slug. Запись — только для `admin` (через те же роуты с RBAC-проверкой; отдельный `admin`-модуль не дублирует CRUD контента). + +| Функции | Описание | +|---------|----------| +| `ContentPage` | `/about`, `/privacy`, `/terms` | +| SEO | meta title, description, og:image | + +#### 6.3.5. `admin` + +**Ответственность:** панель администратора (role: `admin`). + +| Раздел | Функции | +|--------|---------| +| Users | список, блокировка, смена роли *(с ограничениями — §14.10)* | +| Content | — *(CRUD контента — модуль `content`, §9.4)* | +| Dashboard | базовые метрики (users count, registrations/day) | + +### 6.4. Роутинг + +| Маршрут | Page | Guard | Модуль(и) | +|---------|------|-------|-----------| +| `/` | HomePage | — | landing | +| `/login` | LoginPage | guest only | auth | +| `/register` | RegisterPage | guest only | auth | +| `/profile` | ProfilePage | AuthGuard | profile | +| `/pages/:slug` | ContentPage | — | content | +| `/admin/*` | AdminPage | AdminGuard | admin | + +### 6.5. Code splitting + +- Lazy load: `admin`, `profile`, `catalog`. +- Prefetch on hover для `/login`, `/register`. +- Landing — в initial bundle (LCP-critical). + +--- + +## 7. Модульная структура Backend (API) + +### 7.1. Дерево каталогов `apps/api` + +``` +app/ +├── main.py # FastAPI app factory +├── core/ +│ ├── config.py +│ ├── database.py +│ ├── redis.py +│ ├── security.py # JWT, password hashing +│ ├── dependencies.py # get_db, get_current_user +│ └── exceptions.py +│ +├── modules/ +│ ├── auth/ +│ │ ├── router.py +│ │ ├── service.py +│ │ ├── schemas.py +│ │ └── repository.py +│ │ +│ ├── users/ +│ │ ├── router.py +│ │ ├── service.py +│ │ ├── models.py +│ │ ├── schemas.py +│ │ └── repository.py +│ │ +│ ├── content/ +│ │ ├── router.py +│ │ ├── service.py +│ │ ├── models.py +│ │ └── schemas.py +│ │ +│ ├── media/ +│ │ ├── router.py +│ │ ├── service.py # S3 upload +│ │ └── schemas.py +│ │ +│ └── admin/ +│ ├── router.py +│ ├── service.py +│ └── schemas.py +│ +├── migrations/ # Alembic +└── tests/ + ├── conftest.py # fixtures: db, client, test users + ├── factories.py # user/content factories + ├── modules/ + │ ├── auth/ + │ │ ├── test_service.py # unit: бизнес-логика + │ │ ├── test_router.py # integration: HTTP + DB + │ │ └── test_security.py # rotation, lockout, enumeration + │ ├── users/ + │ ├── content/ + │ └── admin/ + └── e2e/ # pytest API smoke (optional layer) + └── test_health.py +``` + +### 7.2. Слои внутри модуля (Clean Architecture lite) + +``` +Router → Service → Repository → Model + ↓ + Schemas (Pydantic) +``` + +| Слой | Ответственность | +|------|-----------------| +| **Router** | HTTP, status codes, dependency injection | +| **Service** | Бизнес-логика, orchestration | +| **Repository** | SQL-запросы, абстракция БД | +| **Schemas** | Request/Response DTO | +| **Models** | SQLAlchemy ORM | + +**Правило:** модуль не импортирует `repository` другого модуля. Межмодульные вызовы — только через **публичный `service`-фасад** (например, `users.public.get_by_id()`) или domain events. + +### 7.3. Версионирование API + +- Префикс: `/api/v1/` +- Breaking changes → `/api/v2/` +- OpenAPI: `/api/v1/openapi.json` +- Swagger UI: `/api/v1/docs` (только dev/staging) + +--- + +## 8. Модель данных + +### 8.1. ER-диаграмма (MVP) + +```mermaid +erDiagram + users ||--o| user_profiles : has + users ||--o{ refresh_tokens : has + users ||--o{ password_reset_tokens : has + users ||--o{ content_pages : creates + + users { + uuid id PK + string email UK + string password_hash + enum role "user|admin" + enum status "active|blocked|pending" + timestamp email_verified_at + int failed_login_attempts + timestamp locked_until + timestamp created_at + timestamp updated_at + } + + user_profiles { + uuid user_id PK,FK + string display_name + string avatar_url + json metadata + } + + refresh_tokens { + uuid id PK + uuid user_id FK + string token_hash UK + uuid family_id + timestamp expires_at + timestamp revoked_at + timestamp created_at + } + + password_reset_tokens { + uuid id PK + uuid user_id FK + string token_hash UK + timestamp expires_at + timestamp used_at + } + + content_pages { + uuid id PK + string slug UK + string title + text body + enum status "draft|published" + uuid author_id FK + timestamp published_at + timestamp updated_at + } +``` + +### 8.2. Индексы + +```sql +CREATE UNIQUE INDEX idx_users_email ON users(email); +CREATE INDEX idx_users_status ON users(status); +CREATE INDEX idx_content_slug ON content_pages(slug); +CREATE INDEX idx_content_status ON content_pages(status); +CREATE INDEX idx_refresh_tokens_user ON refresh_tokens(user_id); +CREATE INDEX idx_refresh_tokens_family ON refresh_tokens(family_id); +CREATE INDEX idx_password_reset_user ON password_reset_tokens(user_id); +``` + +### 8.3. Миграции + +- Alembic, одна миграция = одна логическая задача. +- Rollback обязателен для каждой миграции. +- Seed: admin user, demo content pages. + +--- + +## 9. API-контракты + +### 9.1. Общие соглашения + +| Аспект | Стандарт | +|--------|----------| +| Format | JSON, UTF-8 | +| Dates | ISO 8601 UTC (`2026-07-09T12:00:00Z`) | +| IDs | UUID v4 | +| Pagination | `?page=1&limit=20`, response: `{ data, meta: { total, page, limit } }` | +| Errors | `{ "error": { "code": "VALIDATION_ERROR", "message": "...", "details": [] } }` | + +### 9.2. Auth endpoints + +| Method | Path | Auth | Описание | +|--------|------|------|----------| +| POST | `/api/v1/auth/register` | — | Регистрация → `status: pending`, письмо с подтверждением | +| POST | `/api/v1/auth/login` | — | Вход → access в body, refresh в `Set-Cookie` | +| POST | `/api/v1/auth/refresh` | refresh cookie | Новая пара access + refresh (rotation) | +| POST | `/api/v1/auth/logout` | refresh cookie | Revoke token family, очистка cookie | +| POST | `/api/v1/auth/verify-email` | — | Body: `{ "token": "..." }` → `status: active` | +| POST | `/api/v1/auth/resend-verification` | — | Повторная отправка (rate limited) | +| POST | `/api/v1/auth/forgot-password` | — | Отправка reset-link (без раскрытия наличия email) | +| POST | `/api/v1/auth/reset-password` | — | Смена пароля по одноразовому token | + +**Правила ответов auth (anti-enumeration):** +- `register` с существующим email → **200** с нейтральным телом *или* **409** без указания причины в prod (на выбор; зафиксировать в реализации). +- `forgot-password` → всегда **200** «Если email зарегистрирован, письмо отправлено». +- `login` при неверных данных → **401** с общим сообщением «Неверный email или пароль». +- `login` при `status: pending` → **403** `EMAIL_NOT_VERIFIED`. +- `login` при `status: blocked` → **403** `ACCOUNT_BLOCKED`. +- `login` при блокировке brute-force → **429** `ACCOUNT_TEMPORARILY_LOCKED`. + +**Response login (200):** +```json +{ + "access_token": "eyJ...", + "token_type": "bearer", + "expires_in": 900, + "user": { + "id": "uuid", + "email": "user@example.com", + "role": "user", + "status": "active" + } +} +``` + +**Set-Cookie (login / refresh — обязательно):** +``` +Set-Cookie: refresh_token=; HttpOnly; Secure; SameSite=Lax; Path=/api/v1/auth; Max-Age=2592000 +``` + +> Refresh token **никогда** не возвращается в JSON-body. + +### 9.3. Users endpoints + +| Method | Path | Auth | Описание | +|--------|------|------|----------| +| GET | `/api/v1/users/me` | user | Текущий пользователь + profile | +| PATCH | `/api/v1/users/me` | user | Обновление профиля (whitelist полей) | +| POST | `/api/v1/users/me/password` | user | `{ current_password, new_password }` | +| POST | `/api/v1/users/me/avatar` | user | multipart upload | +| DELETE | `/api/v1/users/me` | user | Удаление аккаунта (v1.0, soft-delete + anonymize) | + +### 9.4. Content endpoints + +| Method | Path | Auth | Описание | +|--------|------|------|----------| +| GET | `/api/v1/content/pages` | — | Список published pages | +| GET | `/api/v1/content/pages/{slug}` | — | Страница по slug | +| POST | `/api/v1/content/pages` | admin | Создание | +| PATCH | `/api/v1/content/pages/{id}` | admin | Обновление | +| DELETE | `/api/v1/content/pages/{id}` | admin | Удаление | + +### 9.5. Admin endpoints + +| Method | Path | Auth | Описание | +|--------|------|------|----------| +| GET | `/api/v1/admin/users` | admin | Список пользователей (pagination, без password_hash) | +| PATCH | `/api/v1/admin/users/{id}` | admin | Блокировка, смена роли *(§14.10)* | +| GET | `/api/v1/admin/stats` | admin | Базовые метрики | +| GET | `/api/v1/admin/audit-log` | admin | Журнал действий админов (v1.0) | + +### 9.6. Rate limiting + +| Endpoint group | Limit | Key | +|----------------|-------|-----| +| Auth: login | 5 req/min | IP + email (composite) | +| Auth: register | 3 req/hour | IP | +| Auth: forgot-password, resend-verification | 3 req/hour | IP + email | +| Auth: refresh | 30 req/min | user_id (из token) | +| Public API | 100 req/min | IP | +| Authenticated API | 300 req/min | user_id (fallback: IP) | +| Upload (avatar) | 10 req/hour | user_id | + +При превышении → **429** + заголовок `Retry-After`. + +**Brute-force (login):** после 5 неудачных попыток за 15 мин — `locked_until = now + 15 min` (поле в `users`). + +--- + +## 10. Аутентификация и авторизация + +### 10.1. JWT Strategy + +| Token | Формат | TTL | Storage | Передача | +|-------|--------|-----|---------|----------| +| Access | JWT (HS256 или RS256) | 15 min | In-memory (JS) | `Authorization: Bearer` | +| Refresh | Opaque random (256 bit) | 30 days | httpOnly Secure cookie | Cookie `refresh_token` | + +**Claims access JWT:** `sub` (user_id), `role`, `iat`, `exp`, `jti` (уникальный ID для optional denylist). + +**Refresh token:** +- В БД хранится **только SHA-256 hash**, не plaintext. +- **Rotation:** каждый `/auth/refresh` выдаёт новый refresh, старый revoke. +- **Reuse detection:** повторное использование revoked token → revoke всей `family_id`, принудительный logout на всех устройствах. +- Cookie `Path=/api/v1/auth` — не отправляется на остальные API-роуты (минимизация поверхности CSRF). + +### 10.2. RBAC + +| Role | Permissions | +|------|-------------| +| `guest` | Чтение опубликованного контента | +| `user` (`status: active`) | Управление своим профилем | +| `admin` | Управление пользователями, контентом, stats; **не может** изменить собственную роль (§14.10) | + +**Проверка на каждом protected endpoint:** +1. Валидный access JWT (подпись, exp, jti). +2. Пользователь существует и `status != blocked`. +3. Для admin-роутов — `role == admin`. +4. Для user-роутов — `sub == resource owner` (защита от IDOR). + +`pending`-пользователь может вызвать только: verify-email, resend-verification, logout. + +### 10.3. Password policy + +- Минимум 8 символов, 1 uppercase, 1 lowercase, 1 digit. +- Denylist топ-10k паролей (Have I Been Pwned или локальный список). +- bcrypt, cost factor 12. +- Reset/verify token: 256 bit random, **hash в БД**, TTL 1 hour, одноразовый. +- При смене/сбросе пароля — revoke все `refresh_tokens` пользователя. + +### 10.4. CORS и credentials + +```python +# FastAPI CORSMiddleware +allow_origins = CORS_ORIGINS # whitelist, без wildcard в prod +allow_credentials = True # обязательно для refresh cookie +allow_methods = ["GET", "POST", "PATCH", "DELETE", "OPTIONS"] +allow_headers = ["Authorization", "Content-Type", "X-Request-ID"] +``` + +Frontend HTTP-клиент: `withCredentials: true` только для auth-запросов (login, refresh, logout). + +### 10.5. CSRF (cookie-based refresh) + +| Механизм | Применение | +|----------|------------| +| `SameSite=Lax` на refresh cookie | Блокирует cross-site POST в большинстве браузеров | +| `Path=/api/v1/auth` | Cookie не уходит на PATCH/POST других ресурсов | +| Проверка `Origin` / `Referer` на auth-роутах | Backend отклоняет запросы с чужого origin | +| Access JWT в header (не cookie) | State-changing API через Bearer не уязвим к CSRF | + +Дополнительный CSRF-token **не требуется** при соблюдении схемы выше. Если refresh cookie когда-либо расширят на весь `/api` — добавить double-submit CSRF token. + +--- + +## 11. UI/UX и дизайн-система + +### 11.1. Design tokens (из текущей заглушки) + +```css +:root { + --bg: #F6F6F4; + --foreground: #1A1E1C; + --primary: #48816D; + --muted: #4A5A52; + --marquee-bg: #EBEBE5; +} +``` + +### 11.2. Типографика + +| Назначение | Шрифт | +|------------|-------| +| UI / body | Inter (400, 500, 600) | +| Mono / акценты | JetBrains Mono (400, 500) | +| Декоративный (опционально) | Oktyabrina Script | + +### 11.3. Breakpoints + +| Token | Width | +|-------|-------| +| `sm` | 380px | +| `md` | 480px | +| `lg` | 768px | +| `xl` | 1024px | +| `2xl` | 1280px | + +### 11.4. Компоненты shared/ui (MVP) + +- Button (primary, secondary, ghost) +- Input, Textarea +- Label, ErrorMessage +- Card, Modal, Spinner +- Header, Footer, Container +- Avatar, Badge + +### 11.5. Accessibility + +- WCAG 2.1 AA для MVP. +- Keyboard navigation, focus visible. +- `alt` для всех изображений. +- `prefers-reduced-motion` — отключение marquee-анимации. + +--- + +## 12. Нефункциональные требования + +### 12.1. Performance + +| Метрика | Target | +|---------|--------| +| Initial JS bundle (landing) | < 150 KB gzip | +| Total JS (app loaded) | < 350 KB gzip | +| API cache (public content) | Redis TTL 5 min | +| Static assets | CDN, cache-control 1 year (hash in filename) | + +### 12.2. SEO + +- SSR/SSG **не обязателен** для MVP (SPA + prerender landing через Vite SSG plugin). +- Meta tags, sitemap.xml, robots.txt. +- Semantic HTML (`header`, `main`, `section`). + +### 12.3. Observability + +| Signal | Tool | +|--------|------| +| Logs | JSON structured, correlation-id | +| Metrics | request duration, error rate, DB pool | +| Traces | OpenTelemetry (optional v1.0) | +| Frontend errors | Sentry browser SDK | + +### 12.4. Backup & Recovery + +- PostgreSQL: daily backup, retention 30 days. +- RPO: 24 hours, RTO: 4 hours (на 1000 users достаточно). + +--- + +## 13. Инфраструктура и DevOps + +### 13.1. Окружения + +| Env | Назначение | URL | +|-----|------------|-----| +| local | разработка | localhost:5173 (web), :8000 (api) | +| staging | QA, demo | staging.compton.example | +| production | prod | compton.example | + +### 13.2. Docker Compose (local) + +```yaml +services: + web: + build: ./apps/web + ports: ["5173:5173"] + api: + build: ./apps/api + ports: ["8000:8000"] + depends_on: [postgres, redis] + postgres: + image: postgres:16 + redis: + image: redis:7-alpine + minio: + image: minio/minio # S3-compatible dev + +# apps/web/e2e/ — Playwright specs (§15.3) +# docker-compose.test.yml — postgres + redis + api + web для CI/E2E +``` + +### 13.3. CI/CD Pipeline + +```mermaid +flowchart LR + Push[Git Push / PR] --> Lint[Lint + Typecheck] + Lint --> Unit[Unit + Integration] + Unit --> Cov{Coverage ≥ threshold?} + Cov -->|No| Block1[❌ Block merge] + Cov -->|Yes| Build[Build Docker] + Build --> DeployStaging[Deploy Staging] + DeployStaging --> E2E[Playwright E2E] + E2E --> E2EPass{All E2E pass?} + E2EPass -->|No| Block2[❌ Block merge] + E2EPass -->|Yes| Review[Code Review] + Review --> ManualApprove[Manual Approve] + ManualApprove --> DeployProd[Deploy Production] +``` + +**Checks на каждый PR (обязательны, merge заблокирован при падении):** + +| Gate | Команда | Условие pass | +|------|---------|--------------| +| Lint | `eslint`, `ruff`, `prettier --check` | 0 errors | +| Types | `tsc --noEmit`, `mypy` | 0 errors | +| Unit (FE) | `vitest run --coverage` | ≥ порогов §15.2 | +| Unit + Integration (BE) | `pytest --cov=app --cov-fail-under=...` | ≥ порогов §15.2 | +| OpenAPI | schema diff | no breaking без bump version | +| E2E | `playwright test` | 100% pass, 0 flaky retries exhausted | +| Security | `pip-audit`, `npm audit`, gitleaks | no unwaived critical | + +> **Правило:** PR без тестов на изменённую логику **не ревьюится** и **не мержится**. + +### 13.4. Production topology (1000 users) + +``` +1× VPS (4 vCPU, 8 GB RAM) или managed PaaS +├── Nginx +├── API container (uvicorn, 2 workers) +├── Web static (built SPA) +├── PostgreSQL (managed или co-located) +└── Redis (managed или co-located) +``` + +**Запас на рост:** второй API instance за load balancer при CPU > 70% sustained. + +--- + +## 14. Безопасность + +### 14.1. Threat model (STRIDE, MVP) + +| Угроза | Вектор | Митигация | +|--------|--------|-----------| +| **Spoofing** | Подделка JWT / session | Подпись JWT, короткий TTL, rotation refresh | +| **Tampering** | Изменение чужих данных | RBAC + owner check (`sub == user_id`) | +| **Repudiation** | Отрицание admin-действий | Audit log (v1.0), correlation-id в логах | +| **Information disclosure** | Enumeration email, утечка PII | Anti-enumeration (§9.2), маскировка логов | +| **Denial of service** | Flood login/register | Rate limit + account lockout (§9.6) | +| **Elevation of privilege** | user → admin | RBAC middleware, admin self-edit запрещён | + +**Trust boundaries:** +1. Browser (недоверенный) ↔ Nginx (TLS termination) +2. Nginx ↔ API (internal network / docker network) +3. API ↔ PostgreSQL / Redis / S3 (credentials via env) + +### 14.2. Transport и security headers + +**Обязательно в production (Nginx / middleware):** + +| Header | Значение | +|--------|----------| +| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | +| `X-Content-Type-Options` | `nosniff` | +| `X-Frame-Options` | `DENY` | +| `Referrer-Policy` | `strict-origin-when-cross-origin` | +| `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | +| `Content-Security-Policy` | см. §14.3 | + +- TLS 1.2+ only, сильные cipher suites. +- Swagger UI (`/docs`) и OpenAPI JSON — **отключены в production** (env flag `ENABLE_DOCS=false`). + +### 14.3. Content Security Policy + +``` +default-src 'self'; +script-src 'self'; +style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; +font-src 'self' https://fonts.gstatic.com; +img-src 'self' data: https://; +connect-src 'self' https://; +frame-ancestors 'none'; +base-uri 'self'; +form-action 'self'; +``` + +- Inline scripts в SPA — через nonce или только bundled (Vite). +- Google Fonts — допустимое исключение; при ужесточении CSP — self-host шрифтов. + +### 14.4. XSS и инъекции + +| Поверхность | Защита | +|-------------|--------| +| React UI | Auto-escaping JSX | +| CMS `content_pages.body` (HTML) | Server-side sanitize (**bleach** / **nh3**) + client DOMPurify allowlist перед `dangerouslySetInnerHTML` | +| User input (profile) | Pydantic validation, max length, strip control chars | +| SQL | SQLAlchemy ORM, parameterized queries; raw SQL запрещён без review | +| Log injection | Structured JSON logging, sanitize `\n\r` в user input | + +**Allowlist HTML-тегов CMS:** `p, h1-h4, ul, ol, li, a, strong, em, br, img` — без `script`, `iframe`, `on*` attributes. + +### 14.5. IDOR и авторизация + +| Endpoint | Правило | +|----------|---------| +| `GET/PATCH /users/me` | `current_user.id` из JWT, не из body | +| `POST /users/me/avatar` | только свой user_id | +| `PATCH /admin/users/{id}` | admin + business rules (§14.10) | +| `GET /content/pages/{slug}` | только `status=published` для non-admin | +| `PATCH /content/pages/{id}` | admin only; проверка существования id | + +**Запрещено:** принимать `user_id` / `role` из request body для повышения привилегий. + +### 14.6. Загрузка файлов (avatar) + +| Проверка | Значение | +|----------|----------| +| Max size | 2 MB | +| MIME (whitelist) | `image/jpeg`, `image/png`, `image/webp` | +| Magic bytes | python-magic / file signature check | +| Filename | UUID + ext, без user-supplied path | +| Storage | S3 private bucket; URL — signed / через CDN proxy | +| Processing | Re-encode (Pillow) для удаления EXIF и embedded payload | + +**Запрещено:** SVG upload (XSS vector), исполнение файлов на сервере. + +### 14.7. Secrets и криптография + +| Secret | Хранение | Rotation | +|--------|----------|----------| +| `JWT_ACCESS_SECRET` | env / vault | при компрометации, quarterly | +| `JWT_REFRESH_PEPPER` | env / vault | отдельный от access secret | +| DB credentials | env / managed DB | managed rotation | +| S3 keys | IAM role (preferred) или env | quarterly | + +- `.env` в `.gitignore`; `.env.example` без реальных значений. +- Pre-commit hook / CI: **gitleaks** или **trufflehog**. +- JWT secret ≥ 256 bit entropy. + +### 14.8. Сессии и logout + +- **Logout:** revoke refresh token family + clear cookie (`Max-Age=0`). +- **Password change / reset:** revoke **все** refresh tokens пользователя. +- **Block user (admin):** revoke tokens немедленно. +- Optional: Redis denylist для access JWT by `jti` до exp (для instant revoke admin-сессий). + +### 14.9. Логирование и PII + +**Логировать:** +- `request_id`, method, path, status, duration, user_id (if auth), IP (hashed в prod опционально). + +**Не логировать:** +- Passwords, tokens, reset links, full email в debug (mask: `u***@example.com`). + +**Retention:** application logs 30 days; audit log admin-действий 1 year. + +### 14.10. Admin security rules + +| Правило | Описание | +|---------|----------| +| Last admin | Нельзя удалить/понизить последнего admin | +| Self-demotion | Admin не может снять с себя роль admin | +| Self-block | Admin не может заблокировать себя | +| Role escalation | Только admin может назначать admin; 2FA для admin — v1.0 (recommended) | +| Seed admin | Создаётся миграцией; пароль из env при первом deploy, затем смена | + +### 14.11. Supply chain + +- Dependabot / Renovate — auto PR на CVE. +- CI: `pip-audit` (backend), `npm audit` (frontend), **Trivy** scan Docker images. +- Pin dependencies (`poetry.lock`, `package-lock.json`). +- Block merge при critical/high без explicit waiver. + +### 14.12. GDPR / 152-ФЗ + +| Требование | Реализация | +|------------|------------| +| Согласие | Checkbox при регистрации + ссылка на политику | +| Политика конфиденциальности | CMS-страница `/privacy` | +| Право на удаление | `DELETE /users/me` (v1.0): soft-delete, anonymize PII, retain audit/legal minimum | +| Data minimization | `metadata` json — только необходимые поля | +| Хранение | RU/EU region hosting (допущение §18.1) | +| Breach notification | Runbook в docs (v1.0) | + +### 14.13. Security checklist (pre-production) + +- [ ] HTTPS + HSTS +- [ ] CSP, security headers (§14.2) +- [ ] CORS whitelist, `allow_credentials` только для trusted origins +- [ ] Refresh: httpOnly, Secure, SameSite, Path, rotation + reuse detection +- [ ] Access JWT in memory only +- [ ] Anti-enumeration на auth endpoints +- [ ] Rate limiting + brute-force lockout +- [ ] IDOR tests на все `/me` и admin routes +- [ ] File upload validation + private S3 +- [ ] CMS HTML sanitization +- [ ] Secrets not in git; gitleaks in CI +- [ ] Swagger disabled in prod +- [ ] Dependency scan clean (no unwaived critical) +- [ ] OWASP ASVS Level 1 review +- [ ] Sentry: scrub PII from breadcrumbs + +### 14.14. Security testing (дополнение к §15) + +| Тест | Tool / метод | +|------|--------------| +| SAST | Bandit (Python), ESLint security plugins | +| DAST | OWASP ZAP baseline scan на staging | +| Auth flows | pytest: token rotation, reuse detection, lockout | +| IDOR | pytest: user A cannot access user B | +| Fuzzing upload | Invalid MIME, oversized, polyglot files | + +--- + +## 15. Тестирование и качество + +> **Базовое правило проекта:** функция считается реализованной только когда поставлены **тесты логики** (unit + integration) **и E2E-сценарии** для затронутых пользовательских потоков. Исключений нет. + +### 15.1. Пирамида и обязательные уровни + +| Уровень | Что покрывает | Tools | Обязательность | +|---------|---------------|-------|----------------| +| **Unit** | service, repository, hooks, store, utils, pure components | Vitest, pytest | **Обязательно** на каждый PR с логикой | +| **Integration** | API routers + DB + Redis; form → API client | pytest + TestClient, MSW | **Обязательно** для backend и API-слоя frontend | +| **Component** | UI: render, a11y, user events | Testing Library | **Обязательно** для новых/изменённых компонентов | +| **E2E** | Сквозные user journeys в браузере | Playwright | **Обязательно** на каждый модуль / фазу (§15.5) | +| **Visual** | Landing regression | Percy/Chromatic | Опционально | +| **Load** | API под CCU | k6 | Перед релизом MVP/v1.0 | +| **Security** | auth, IDOR, upload | pytest + ZAP | Перед релизом (§14.14) | + +### 15.2. Пороги покрытия (coverage gates) + +Измерение: **line coverage** (Vitest v8 / pytest-cov). CI падает при падении ниже порога. + +| Область | Минимум | Примечание | +|---------|---------|------------| +| `apps/api/app/modules/*/service.py` | **≥ 95%** | Бизнес-логика backend | +| `apps/api/app/modules/*/repository.py` | **≥ 90%** | SQL, edge cases | +| `apps/api/app/core/security.py` | **100%** | JWT, hash, lockout | +| `apps/web/src/modules/*/hooks`, `store`, `api` | **≥ 90%** | Логика frontend | +| `apps/web/src/shared/lib`, `shared/api` | **≥ 90%** | Общие утилиты | +| `apps/web/src/modules/*/components` | **≥ 80%** | UI; допускается исключать pure layout | +| **Overall backend** `app/` | **≥ 90%** | `--cov-fail-under=90` | +| **Overall frontend** `src/` | **≥ 85%** | `--coverage.thresholds.lines=85` | + +**Diff coverage (рекомендуется):** новые/changed строки в PR — **≥ 95%** (Codecov / diff-cover). + +**Критические модули** (`auth`, `admin`, `core/security`) — **100% branch coverage** для функций авторизации и token rotation. + +### 15.3. Структура E2E (Playwright) + +``` +apps/web/e2e/ +├── fixtures/ +│ ├── auth.fixture.ts # login helpers, test users +│ └── api.fixture.ts # seed via API +├── landing/ +│ └── landing.spec.ts +├── auth/ +│ ├── register.spec.ts +│ ├── login.spec.ts +│ └── password-reset.spec.ts +├── profile/ +│ └── profile.spec.ts +├── content/ +│ └── content-pages.spec.ts +├── admin/ +│ └── admin-users.spec.ts +└── playwright.config.ts +``` + +**Конфигурация:** +- Бrowsers: Chromium (CI), + Firefox/WebKit локально. +- Retry: 1 в CI только для infra flakes; повторный flake → bug. +- Trace/video: on-first-retry. +- Параллельность: по файлам, isolated test users. +- E2E против **staging** в CI post-deploy; PR — против docker-compose stack. + +### 15.4. Матрица тестов по модулям (MVP) + +Каждая строка — **минимальный обязательный набор** перед merge задачи. + +| Модуль | Unit / Integration (логика) | E2E (Playwright) | +|--------|----------------------------|------------------| +| **landing** | Hero/Marquee render, breakpoints, reduced-motion | `/` загрузка, marquee visible, a11y snapshot | +| **auth** | register, login, refresh rotation, reuse detection, lockout, anti-enumeration, verify-email | register → verify → login; wrong password; pending/blocked redirect | +| **profile** | PATCH whitelist, avatar validation (MIME, size), IDOR | edit name, upload avatar, change password | +| **content** | slug unique, draft vs published, HTML sanitize | public page by slug; admin publish → visible | +| **admin** | last-admin rule, self-block forbidden, RBAC | admin list users, block user, blocked cannot login | +| **shared/ui** | Button, Input, Form validation states | используются в module E2E | + +### 15.5. Тесты на каждом шаге реализации (workflow) + +```mermaid +flowchart TD + Task[Задача из §16] --> Impl[Реализация] + Impl --> Unit[Unit tests] + Unit --> Int[Integration tests] + Int --> Comp[Component tests если UI] + Comp --> E2E[E2E spec для flow] + E2E --> CI[CI green] + CI --> DoD[Definition of Done §15.6] + DoD --> Merge[Merge allowed] +``` + +**Порядок TDD (рекомендуется, не optional для auth/security):** +1. Написать failing test (unit или e2e). +2. Реализовать минимальный код. +3. Довести coverage до порога. +4. Открыть PR — CI должен быть green. + +**Запрещено:** +- Откладывать тесты «на потом» / отдельным PR. +- Merge с `@pytest.mark.skip` / `test.todo` / `it.skip` без linked issue и срока. +- Mock всего подряд в integration — DB/Redis должны быть real (testcontainers или docker-compose). + +### 15.6. Definition of Done (модуль / задача) + +- [ ] TypeScript strict / mypy — 0 errors +- [ ] **Unit tests** для всей новой/изменённой бизнес-логики +- [ ] **Integration tests** для новых/изменённых API endpoints +- [ ] **Component tests** для новых/изменённых React-компонентов +- [ ] **E2E spec** для затронутых user flows (§15.4) +- [ ] Coverage ≥ порогов §15.2 (CI enforced) +- [ ] API documented in OpenAPI +- [ ] ESLint boundaries pass +- [ ] Security tests пройдены (если модуль auth/admin/media) +- [ ] Code review approved +- [ ] 0 flaky E2E за 3 прогона CI + +### 15.7. Critical E2E scenarios (регрессия релиза) + +Полный прогон перед каждым релизом MVP/v1.0: + +1. Landing: LCP < 2.5s, hero + marquee, `prefers-reduced-motion`. +2. Register → verify email → login → profile edit → change password → logout. +3. Forgot password → reset → login with new password. +4. Admin: create content → publish → visible on `/pages/:slug`. +5. Admin: block user → blocked user gets 403 on login. +6. Pending user cannot access `/profile`. +7. Refresh rotation: old refresh token rejected; reuse revokes family. +8. IDOR: user A cannot GET/PATCH user B profile. +9. Admin cannot demote/block self; last admin protected. +10. Avatar: reject .svg, oversize, wrong MIME. + +### 15.8. Test data и изоляция + +| Аспект | Правило | +|--------|---------| +| Test DB | Отдельная `compton_test`; транзакционный rollback per test | +| Seed users | factories.py: `user`, `admin`, `pending_user`, `blocked_user` | +| E2E users | Создаются через API fixture перед spec, cleanup after | +| Secrets in tests | Только `test-*` keys из `.env.test` | +| Parallel | Изolated data via UUID suffix emails | + +### 15.9. Команды (локально) + +```bash +# Frontend +pnpm --filter web test # vitest watch +pnpm --filter web test:ci # vitest run --coverage +pnpm --filter web e2e # playwright test +pnpm --filter web e2e:ui # playwright --ui + +# Backend +cd apps/api && pytest # all +pytest tests/modules/auth -v # module +pytest --cov=app --cov-report=term-missing --cov-fail-under=90 + +# Full stack (pre-push) +docker compose -f docker-compose.test.yml up -d +pnpm test:ci && pytest && playwright test +``` + +--- + +## 16. Этапы разработки + +### Фаза 0 — Подготовка (1–2 недели) + +| # | Задача | Результат | Тесты (обязательно) | +|---|--------|-----------|---------------------| +| 0.1 | Monorepo: Vite, FastAPI skeleton | `apps/web`, `apps/api` | smoke: `vitest` 1 test, `pytest` health 1 test | +| 0.2 | Docker Compose + `docker-compose.test.yml` | dev + test stack | CI job: lint + smoke green | +| 0.3 | Shared UI kit + design tokens | Button, Input, Layout | component tests ≥80%; Storybook optional | +| 0.4 | ESLint boundaries, OpenAPI codegen, coverage gates | Quality gates в CI | CI блокирует merge без coverage config | +| 0.5 | Playwright + pytest fixtures | `e2e/`, `conftest.py`, factories | E2E smoke: landing page loads | + +### Фаза 1 — MVP (4–6 недель) + +> Каждая задача завершается **своим** набором unit + integration + E2E. Задача 1.6 — финальный регресс, не «первые E2E». + +| # | Задача | Модули | Тесты (обязательно в том же PR) | +|---|--------|--------|----------------------------------| +| 1.1 | Landing (перенос заглушки) | landing | unit: breakpoints; E2E: `landing.spec.ts` | +| 1.2 | Auth + SMTP | auth | unit: service/security; integration: all auth routes; E2E: register/login/verify/reset | +| 1.2b | Security baseline | core, auth | 100% coverage `security.py`; tests: lockout, rotation, enumeration | +| 1.3 | Profile CRUD | profile | unit: validation; integration: `/users/me`; E2E: edit + avatar + password | +| 1.4 | Content pages | content | unit: sanitize; integration: CRUD; E2E: publish → public view | +| 1.5 | Admin panel | admin | unit: business rules; integration: admin routes; E2E: block user flow | +| 1.6 | Staging deploy + **полный регресс** | — | Playwright §15.7 (all 10 scenarios); coverage ≥ §15.2 | + +### Фаза 2 — v1.0 (4–6 недель) + +| # | Задача | Модули | Тесты (обязательно в том же PR) | +|---|--------|--------|----------------------------------| +| 2.1 | Celery + email queue | notifications | unit: task handlers; integration: queue + mock SMTP; E2E: notification received | +| 2.2 | Catalog + Orders | catalog, orders | unit + integration per module; E2E: browse → cart → submit order | +| 2.3 | Analytics dashboard | admin | integration: stats API; E2E: dashboard renders metrics | +| 2.4 | Performance + monitoring | infra | k6 load test §17.2; no coverage regression | + +### Фаза 3 — Scale prep (по необходимости) + +- Read replica PostgreSQL +- Celery workers +- CDN + object storage migration +- Horizontal API scaling + +--- + +## 17. Критерии приёмки + +### 17.1. MVP + +**Функциональность:** +- [ ] Landing визуально соответствует текущей заглушке (hero, marquee, цвета). +- [ ] Регистрация и вход работают; load test **50 CCU** на API. +- [ ] Профиль редактируется, аватар загружается. +- [ ] Admin создаёт/редактирует контент-страницы. + +**Производительность и безопасность:** +- [ ] API p95 < 300 ms при 50 CCU (k6). +- [ ] Lighthouse Performance ≥ 85 на mobile. +- [ ] Security checklist §14.13 — все пункты закрыты. +- [ ] OWASP ZAP baseline — 0 high/critical на staging. + +**Тестирование (обязательно):** +- [ ] Backend coverage ≥ **90%** (`app/`), auth/security ≥ **95–100%** (§15.2). +- [ ] Frontend coverage ≥ **85%** (`src/`), hooks/api ≥ **90%**. +- [ ] Все E2E сценарии §15.7 — **10/10 pass** на staging. +- [ ] 0 skipped tests без waiver. +- [ ] CI pipeline §13.3 — все gates green на `main`. +- [ ] Каждый модуль MVP имеет строки в матрице §15.4 (закрыты). + +**Документация:** +- [ ] README: как запускать `test`, `test:ci`, `e2e`. +- [ ] OpenAPI, `.env.example`. + +### 17.2. Load test сценарий (k6, API-only) + +```javascript +// 50 VU, ramp 5 min, target: api:8000 через Nginx +// 35% GET /api/v1/content/pages +// 25% POST /api/v1/auth/login (test credentials pool) +// 20% GET /api/v1/users/me (authenticated) +// 10% POST /api/v1/auth/refresh (cookie) +// 10% GET /api/v1/content/pages/{slug} +``` + +**Pass:** error rate < 1%, p95 < 300ms, 0 auth bypass. + +> Frontend (`GET /`) — отдельный Lighthouse-тест, не смешивать с API load test. + +--- + +## 18. Риски и допущения + +### 18.1. Допущения + +1. 1000 пользователей — registered, не 1000 RPS. +2. Контент преимущественно текст + изображения, без video streaming. +3. Один регион хостинга (RU/EU), latency < 100ms для целевой аудитории. +4. Команда: 1–3 fullstack-разработчика. + +### 18.2. Риски + +| Риск | Вероятность | Митигация | +|------|-------------|-----------| +| Scope creep (catalog/orders раньше MVP) | Высокая | Жёсткое следование фазам | +| SPA SEO проблемы | Средняя | Prerender landing, meta tags | +| Cross-module imports ломают архитектуру | Средняя | ESLint boundaries в CI | +| Auth flow complexity (rotation, verify) | Средняя | TDD + integration tests §15.4–15.7 | +| Flaky E2E блокируют CI | Средняя | fixtures, isolated data §15.8, trace-on-retry | +| Перегрузка monolith при росте | Низкая (до 10K) | Redis cache, horizontal scale | + +--- + +## Приложение A — Карта модулей (summary) + +```mindmap + root((Комpton)) + Frontend + app + pages + modules + landing + auth + profile + content + admin + catalog + shared + Backend + core + modules + auth + users + content + media + admin + Infra + PostgreSQL + Redis + S3 + Nginx + "CI/CD" +``` + +--- + +## Приложение B — Переменные окружения + +### Frontend (`apps/web/.env`) + +```env +VITE_API_URL=http://localhost:8000 +VITE_APP_NAME=Комpton +VITE_SENTRY_DSN= +``` + +### Backend (`apps/api/.env`) + +```env +DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/compton +REDIS_URL=redis://localhost:6379/0 +JWT_ACCESS_SECRET= # ≥ 32 bytes random, NOT commit +JWT_REFRESH_PEPPER= # отдельный секрет для hash refresh tokens +JWT_ACCESS_TTL_MIN=15 +JWT_REFRESH_TTL_DAYS=30 +ENABLE_DOCS=true # false в production +CORS_ORIGINS=http://localhost:5173 +S3_ENDPOINT=http://localhost:9000 +S3_BUCKET=compton-media +S3_ACCESS_KEY= +S3_SECRET_KEY= +SMTP_HOST= +SMTP_PORT=587 +SMTP_USER= +SMTP_PASSWORD= +SMTP_FROM=noreply@compton.example +ADMIN_INITIAL_PASSWORD= # только first deploy, затем сменить +``` + +--- + +## Приложение C — Согласование + +| Роль | ФИО | Подпись | Дата | +|------|-----|---------|------| +| Product Owner | | | | +| Tech Lead | | | | +| Developer | | | | + +--- + +*Документ является живым артеfactом. Изменения фиксируются через PR в `docs/TZ.md` с bump версии.*