# Структура и архитектура

Monorepo-lite: статический лендинг + React SPA + FastAPI + PostgreSQL + Redis + MinIO.  
Если вы искали микросервисы на Kubernetes — это другой коридор.

## Стек

| Слой | Технологии |
|------|------------|
| Frontend | React 19, TS, Vite, React Router, TanStack Query, Zustand, RHF+Zod, Ant Design |
| Backend | FastAPI, SQLAlchemy 2, Alembic, Pydantic v2 |
| Данные | PostgreSQL 16, Redis 7, MinIO |
| Инфра | Docker Compose, Nginx, GitHub Actions |
| Качество | Vitest, Playwright, pytest (≥90% / ≥85% cov) |

## Дерево репозитория

```
site/
├── apps/
│   ├── api/                         # Backend
│   │   ├── app/
│   │   │   ├── core/                # crypto, jwt_denylist, redis, install_secrets…
│   │   │   ├── db/                  # models, seed, migrations helpers
│   │   │   └── modules/             # auth, users, content, admin, media, test
│   │   ├── migrations/              # Alembic
│   │   ├── scripts/                 # bootstrap_install.py, docker_entrypoint.py
│   │   ├── tests/
│   │   └── data/
│   │       ├── secrets/             # install.env (gitignore!)
│   │       └── logs/                # server.log, admin-audit.jsonl (gitignore)
│   └── web/
│       ├── index.html               # лендинг /
│       ├── app.html                 # SPA entry
│       ├── main/                    # статика лендинга (CSS/JS/video)
│       ├── src/
│       │   ├── app/                 # router, guards
│       │   ├── modules/             # auth, profile, admin, content, landing
│       │   ├── pages/
│       │   └── shared/              # api client, ui
│       └── e2e/                     # Playwright
├── packages/                        # eslint-config, shared-types (target)
├── infra/
│   ├── docker/                      # staging/prod compose, deploy.sh
│   ├── nginx/                       # default.conf, default.tls.conf
│   ├── k6/                          # load test §17.2
│   └── scripts/                     # backup, health, smoke
├── docs/                            # вы здесь
├── docker-compose.yml               # dev
└── docker-compose.test.yml          # CI / E2E
```

## Runtime

```mermaid
flowchart TB
  subgraph browser [Браузер]
    L[index.html /]
    S[app.html SPA]
  end
  subgraph edge [Nginx :80/:443]
    N[TLS + headers]
  end
  subgraph internal [Docker internal]
    W[web]
    A[api]
    PG[(PostgreSQL)]
    R[(Redis)]
    M[(MinIO)]
  end
  L --> N
  S --> N
  N --> W
  N --> A
  A --> PG
  A --> R
  A --> M
```

**Prod/staging:** наружу только nginx. Postgres, Redis, MinIO — без host-портов.

## Frontend

### Два входа (dual-entry)

| Entry | URL | Содержимое |
|-------|-----|------------|
| `index.html` | `/` | Маркетинговый лендинг (`main/`) |
| `app.html` | `/login`, `/admin`, … | React SPA |

Vite переписывает SPA-пути на `app.html` (`vite.main-static.ts`).

### Маршруты

| Путь | Guard | Кто |
|------|-------|-----|
| `/` | — | все |
| `/login`, `/register`, `/forgot-password`, `/reset-password` | GuestGuard | гости |
| `/verify`, `/pages/:slug` | — | публично |
| `/profile` | AuthGuard | user |
| `/admin` | AdminGuard | admin |

**Auth UX:**
- Access JWT — только в памяти (Zustand), не localStorage
- Refresh — HttpOnly cookie, `Path=/api/v1/auth`
- После login: admin → `/admin`, user → `/profile`
- Кнопка «На сайт» в админке — **полный** переход на `/` (не React Router)

### Админка (WESP-style)

| Раздел | Кому | Что |
|--------|------|-----|
| Users | admin | CRUD пользователей |
| Content | admin | CMS |
| Security | superuser | runtime settings, Install Secrets |
| Diagnostics | superuser | health checks |
| Activity | admin | audit feed, server log |

Тема Light/Dark — `localStorage.wespAdminTheme`. Auth-страницы — zootech-карточки (`#48816d`).

## Backend API

База: `/api/v1`

| Модуль | Эндпоинты (основное) |
|--------|----------------------|
| health | `GET /health` |
| auth | register, login, logout, refresh, verify, forgot/reset password |
| users | `GET/PATCH /me`, password, avatar |
| content | публичные pages + admin CRUD |
| admin | users, stats, settings, diagnostics, secrets, activity |
| media | подписанные URL файлов |
| test | `/test/emails/latest-token` — **только** E2E |

### Core (`apps/api/app/core/`)

| Модуль | Зачем |
|--------|-------|
| `crypto.py` | bcrypt, JWT, HMAC, token hash — одна точка |
| `jwt_denylist.py` | мгновенный revoke access JWT |
| `install_secrets.py` | bootstrap + lock |
| `dependencies.py` | `get_current_user` |
| `redis.py` | rate limit + JWT revoke |
| `storage.py` | MinIO / memory |

## База данных

```mermaid
erDiagram
  users ||--o| user_profiles : has
  users ||--o{ refresh_tokens : owns
  users ||--o{ password_reset_tokens : owns
  users ||--o{ email_verification_tokens : owns
```

Таблицы: `users`, `user_profiles`, `refresh_tokens`, `password_reset_tokens`, `email_verification_tokens`, `content_pages`.

```bash
cd apps/api && alembic upgrade head
```

CHECK constraints на `role`, `status`; superuser только при `role=admin`. Cleanup expired tokens при старте API.

### Seed: стандартные логины (dev)

| Email | Пароль | Env |
|-------|--------|-----|
| admin@compton.example | Admin1234 | `ADMIN_INITIAL_PASSWORD` |
| ops@compton.example | OpsAdmin1234 | `DEMO_OPS_PASSWORD` |
| user@compton.example | User1234 | `DEMO_USER_PASSWORD` |

CMS: `about`, `privacy`, `terms`. Подробнее — [deploy.md § логины](./deploy.md#стандартные-логины-dev).

## Переменные окружения (ключевые)

| Переменная | Где | Назначение |
|------------|-----|------------|
| `DATABASE_URL` | api | PostgreSQL |
| `APP_ENV` | api | development / staging / production |
| `JWT_ACCESS_SECRET`, `JWT_REFRESH_PEPPER` | api | токены |
| `REDIS_URL` | api | rate limit + JWT revoke (prod обязателен) |
| `SEED_DEMO_USERS` | api | `false` на staging/prod |
| `ADMIN_INITIAL_PASSWORD` | api | пароль admin при seed (default `Admin1234`) |
| `DEMO_USER_PASSWORD`, `DEMO_OPS_PASSWORD` | api | demo user/ops (только dev) |
| `ENABLE_DOCS` | api | `false` на prod |
| `ENABLE_TEST_ROUTES` | api | `true` только E2E |
| `EMAIL_DELIVERY_MODE` | api | `memory` (dev) / `smtp` (prod) |
| `STORAGE_MODE` | api | `s3` / `memory` |
| `VITE_API_URL` | web | `http://api:8000` в Docker |
| `VITE_USE_API_PROXY` | web | `true` в dev |

Полные примеры: `apps/api/.env.example`, `apps/api/.env.production.example`.

### Runtime settings

`apps/api/data/compton_settings.json` — toggles без секретов. Superuser: `GET/PATCH /admin/settings`. Env с тем же ключом = **lock** (нельзя менять из UI).

## Docker Compose

| Файл | Когда |
|------|-------|
| `docker-compose.yml` | локальная разработка |
| `docker-compose.dev-ports.yml` | PG/Redis/MinIO на хост (DBeaver) |
| `docker-compose.test.yml` | CI, Playwright |
| `infra/docker/docker-compose.staging.yml` | staging VPS |
| `infra/docker/docker-compose.prod.yml` | production VPS |

## CI

`.github/workflows/ci.yml`: lint → types → mypy → tests → audit → Bandit → gitleaks → E2E.

## Тесты

```bash
pnpm --filter web test:ci
cd apps/api && python -m pytest --cov=app --cov-fail-under=90
pnpm --filter web e2e
```

E2E поднимает API `:8001` + Vite `:5175`. Против staging: `E2E_BASE_URL=… E2E_START_API=false`.
