Безопасность довёл до ума — Cursor-генерацию переписал руками. IDOR закрыл, CSRF задушил, refresh rotation теперь как надо. HSTS на staging, ENABLE_DOCS=false, install.env recovery протестил. Backend: - jwt_denylist + auth_epoch: мгновенный revoke access JWT (logout/block/reset) - auth/admin/users: bump epoch, logout с Bearer, forgot_password skip для blocked - install_secrets: путь всегда apps/api/data/secrets/ (bootstrap из корня не ломает Docker) - seed: SEED_DEMO_USERS=false на prod/staging - тесты: jwt revoke, integration, coverage gate 90% Frontend: - logout шлёт Bearer, обработка TOKEN_REVOKED - guards TypeScript fix - E2E: blocked user → 401 сразу после block Infra: - staging/prod compose, TLS nginx, deploy-скрипты - k6 §17.2, backup/health/smoke scripts Docs: - docs/ на русском: project, security, deploy, release (старые md слили) - README короткий + план ТЗ + стандартные логины dev Код готов к плаванию. Капитан может идти писать фронт.
197 lines
7.1 KiB
Markdown
197 lines
7.1 KiB
Markdown
# Деплой и эксплуатация
|
|
|
|
От «запустил на ноуте» до «живёт на VPS и не стыдно показать security.md».
|
|
|
|
## Среды
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Dev[docker-compose.yml] --> St[staging]
|
|
St --> QA[k6 + ZAP + E2E]
|
|
QA --> Prod[production]
|
|
```
|
|
|
|
| Среда | Compose | APP_ENV | Docs | Demo users |
|
|
|-------|---------|---------|------|------------|
|
|
| Dev | `docker-compose.yml` | development | ✅ | ✅ |
|
|
| CI/E2E | `docker-compose.test.yml` | test | ✅ | ✅ |
|
|
| Staging | `infra/docker/docker-compose.staging.yml` | staging | ❌ | ❌ |
|
|
| Production | `infra/docker/docker-compose.prod.yml` | production | ❌ | ❌ |
|
|
|
|
## Локальная разработка
|
|
|
|
```bash
|
|
# 1. venv + зависимости backend (один раз)
|
|
python3 -m venv .venv
|
|
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
pip install -r apps/api/requirements-dev.txt
|
|
|
|
# 2. Секреты установки (один раз, до первого docker compose up)
|
|
python apps/api/scripts/bootstrap_install.py
|
|
|
|
# 3. Полный стек в Docker (API + БД + web)
|
|
docker compose --profile docker-web up -d --build
|
|
|
|
# 4. Проверка
|
|
curl http://localhost:8000/api/v1/health
|
|
```
|
|
|
|
| Сервис | URL |
|
|
|--------|-----|
|
|
| Web | http://localhost:5173 |
|
|
| API | http://localhost:8000 |
|
|
| PG/Redis/MinIO на хост | `docker-compose.dev-ports.yml` → 5432, 6379, 9000/9001 |
|
|
|
|
> Bootstrap и локальные тесты (`pytest`, `mypy`) — через активированный `.venv`. Docker API использует свой образ; venv нужен для скриптов и разработки на хосте.
|
|
|
|
**Гибрид** (инфра в Docker, frontend локально):
|
|
|
|
```bash
|
|
source .venv/bin/activate
|
|
docker compose up -d
|
|
pnpm install
|
|
pnpm --filter web dev
|
|
```
|
|
|
|
**Install secrets на хост** (DBeaver): Admin → Security → Install Secrets → Reveal DB password.
|
|
|
|
### Стандартные логины (dev)
|
|
|
|
Создаются при seed на старте API. Пароли по умолчанию — из `apps/api/.env.example` (или дефолты в `config.py`).
|
|
|
|
| Email | Пароль | Env | Роль | Superuser | Куда заходит |
|
|
|-------|--------|-----|------|:---------:|--------------|
|
|
| `admin@compton.example` | `Admin1234` | `ADMIN_INITIAL_PASSWORD` | admin | да | `/admin` — Users, Content, Security, Diagnostics, secrets |
|
|
| `ops@compton.example` | `OpsAdmin1234` | `DEMO_OPS_PASSWORD` | admin | нет | `/admin` — Users, Content, Activity (без Security) |
|
|
| `user@compton.example` | `User1234` | `DEMO_USER_PASSWORD` | user | — | `/profile` |
|
|
|
|
**CMS-страницы (seed):** `about`, `privacy`, `terms` → `/pages/about` и т.д.
|
|
|
|
> **Staging/production:** `SEED_DEMO_USERS=false` — demo `user@` и `ops@` **не создаются**, только admin + CMS. Пароль admin задаётся через `ADMIN_INITIAL_PASSWORD` **до первого seed**, потом — сменить в UI.
|
|
|
|
## Staging
|
|
|
|
**Нужно:** VPS, DNS, TLS certs в `infra/docker/certs/`, SMTP.
|
|
|
|
```bash
|
|
git clone https://git.groupkomton.ru/Matvey/site.git && cd site
|
|
python3 apps/api/scripts/bootstrap_install.py
|
|
cp infra/docker/.env.staging.example infra/docker/.env.staging
|
|
# правим: домен, CORS, SMTP, ADMIN_INITIAL_PASSWORD
|
|
./infra/docker/deploy-staging.sh
|
|
```
|
|
|
|
Проверка:
|
|
|
|
```bash
|
|
curl -fsS https://STAGING/api/v1/health
|
|
curl -fsS -o /dev/null -w "%{http_code}" https://STAGING/api/v1/docs # 404
|
|
./infra/scripts/health-check.sh https://STAGING
|
|
```
|
|
|
|
Nginx: `default.tls.conf` — 80→443, HSTS.
|
|
|
|
## Production
|
|
|
|
```bash
|
|
python3 apps/api/scripts/bootstrap_install.py
|
|
cp infra/docker/.env.production.example infra/docker/.env.production
|
|
./infra/docker/deploy-prod.sh infra/docker/.env.production
|
|
```
|
|
|
|
**Сразу после bootstrap:**
|
|
1. Бэкап `install.env` off-server (зашифровать)
|
|
2. Сменить пароль admin
|
|
|
|
| Сервис | Host ports |
|
|
|--------|------------|
|
|
| nginx | 80, 443 |
|
|
| api, web, pg, redis, minio | internal only |
|
|
|
|
```bash
|
|
./infra/scripts/smoke-prod.sh https://YOUR_DOMAIN
|
|
```
|
|
|
|
### Rollback
|
|
|
|
```bash
|
|
docker compose -f infra/docker/docker-compose.prod.yml down
|
|
git checkout PREVIOUS_TAG
|
|
./infra/docker/deploy-prod.sh infra/docker/.env.production
|
|
```
|
|
|
|
## Бэкапы и мониторинг
|
|
|
|
| Что | Команда / как |
|
|
|-----|---------------|
|
|
| PostgreSQL | `./infra/scripts/backup-postgres.sh` → `backups/postgres-*.sql.gz` |
|
|
| install.env | `cp …/install.env backups/install.env.$(date +%F).enc` + gpg |
|
|
| Uptime | `./infra/scripts/health-check.sh URL` или UptimeRobot на `/api/v1/health` + `/` |
|
|
| Логи | logrotate для `server.log`, `admin-audit.jsonl` |
|
|
|
|
## Восстановление секретов
|
|
|
|
`install.env` — единственный источник runtime-секретов. Потеряли — не генерируйте новый вслепую.
|
|
|
|
### Симптомы
|
|
|
|
- `password authentication failed for user "compton_app"`
|
|
- bootstrap после того, как Postgres volume уже создан
|
|
|
|
### Fix (есть бэкап)
|
|
|
|
```bash
|
|
docker compose down
|
|
# восстановить apps/api/data/secrets/install.env
|
|
docker compose up -d --build
|
|
```
|
|
|
|
### Fix (нет бэкапа)
|
|
|
|
| Вариант | Данные |
|
|
|---------|--------|
|
|
| Reveal из другой среды | сохраняются |
|
|
| `down -v` + bootstrap (**только dev**) | **удаляются все** |
|
|
|
|
```bash
|
|
docker compose --profile docker-web down -v
|
|
python apps/api/scripts/bootstrap_install.py
|
|
docker compose --profile docker-web up -d --build
|
|
```
|
|
|
|
## SMTP
|
|
|
|
Staging/prod: `EMAIL_DELIVERY_MODE=smtp`. Dev: `memory` (письма в RAM, SMTP не нужен).
|
|
|
|
| Env | Назначение |
|
|
|-----|------------|
|
|
| `SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM` | сервер |
|
|
| `FRONTEND_URL` | ссылки в письмах |
|
|
|
|
## Troubleshooting
|
|
|
|
| Проблема | Решение |
|
|
|----------|---------|
|
|
| `install.env missing` | `python apps/api/scripts/bootstrap_install.py` |
|
|
| `password authentication failed` | [восстановление секретов](#восстановление-секретов) |
|
|
| Login failed | `curl …/health`, проверить `.env`, restart web |
|
|
| 401 refresh в консоли (гость) | норма на публичных страницах |
|
|
| Logout после F5 на `/admin` | rebuild web, перелогиниться |
|
|
| «На сайт» ведёт на login | должно быть `href="/"`, rebuild |
|
|
| Port 5173 busy | stop Docker web **или** local Vite |
|
|
| CORS | `VITE_USE_API_PROXY=true`, не бить напрямую :8000 |
|
|
| Нет Security/Diagnostics | логин `admin@`, не `ops@` |
|
|
| Docker web: missing modules | `docker compose … up -d --build web` |
|
|
|
|
## Compose-справочник
|
|
|
|
| Файл | Назначение |
|
|
|------|------------|
|
|
| `docker-compose.yml` | dev |
|
|
| `docker-compose.dev-ports.yml` | порты на хост |
|
|
| `docker-compose.test.yml` | CI/E2E |
|
|
| `infra/docker/docker-compose.staging.yml` | staging |
|
|
| `infra/docker/docker-compose.prod.yml` | production |
|
|
|
|
Deploy: `infra/docker/deploy-staging.sh`, `deploy-prod.sh`.
|