# Докер-сборка UMNI Ecosystem

Развёртывание в Docker: БД (PostgreSQL), бэкенд (FastAPI) и фронтенд (Vue → nginx).
Документ описывает локальную сборку и запуск. Публикация образов — в [DOCKER_PUBLISH.md](DOCKER_PUBLISH.md).

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

```
Браузер ──> nginx (frontend, порт 80)
                │  /api/*  → backend:8000/*   (с префикса срезается /api)
                └  /       → статика SPA
backend (8000) ──> postgres (5432)
```

- `backend` доступен наружу **только** через nginx (`expose: 8000`), отдельного хост-порта нет.
- REST-роуты живут без префикса `/api` (auth, cameras, storages, ...), фронтенд обращается по `/api/...`, nginx срезает префикс.

## Быстрый старт (сборка из исходников)

Требуется: Docker Desktop (или Docker Engine с Compose v2).

```powershell
cd C:\Develop\UMNI\Ecosystem

# 1. Базовые значения для docker compose (имя БД, порт, уровень логов и т.п.)
Copy-Item .env.example .env

# 2. Конфигурация backend — положите/поправьте env_config\.env
#    (уже присутствует; при желании смените пароли). ВАЖНО: значения DB_* должны
#    совпадать с .env в корне.

# 3. Сборка и запуск
docker compose build
docker compose up -d

# 4. Откройте http://localhost  и пройдите мастер установки
```

Проверка:

```powershell
docker compose ps                 # все три контейнера Healthy
Invoke-RestMethod http://localhost/api/health
# {"status":"healthy","shutting_down":false,"stream_state":"running"}
```

Остановка: `docker compose down` (данные БД сохраняются в томе `postgres_data`).
Полная очистка вместе с БД: `docker compose down -v`.

## Переменные окружения

Интерполяция compose берёт значения из корневого `.env` (файл создаётся из `.env.example`).
Этот файл **не передаётся в контейнеры** и не влияет на pydantic-парсинг backend.
Конфигурация самого backend — `env_config/.env` (монтируется в `/app/env_config`).

| Переменная (корневой .env) | Назначение                              | По умолчанию |
|----------------------------|-----------------------------------------|--------------|
| `DB_USER` / `DB_PASSWORD`  | Учётка Postgres                         | `postgres` / `postgres` |
| `DB_NAME`                  | Имя создаваемой БД                      | `umni` (в `.env` — `ecosystem`) |
| `DB_PORT`                  | Порт БД в сети compose                  | `5432` |
| `WEB_PORT`                 | Порт наружу для веб-интерфейса          | `80` |
| `LOG_LEVEL`                | Уровень логов backend (`INFO`/`DEBUG`)  | `INFO` |
| `STORAGE_HOST_BASE`        | Корень монтирования дисков хоста        | `/host` |

В `env_config/.env` также задаются `APP_MODE`, `DEBUG_MODE`, `ENCRYPTION_KEY` (генерируется
автоматически при первом старте, если пусто).

## Тома

| Монтирование | Назначение |
|--------------|------------|
| `./logs` → `/app/logs` | Логи backend |
| `./env_config` → `/app/env_config` | `.env` бэкенда (на запись: авто-сохранение ключа) |
| `./storage` → `/app/storage` | Внутреннее хранилище (схемы устройств, сессии telegram) |
| `./plugins/custom` → `/app/plugins/custom` | Пользовательские плагины |
| `./nginx.conf` → `/etc/nginx/nginx.conf` (ro) | Конфиг nginx |
| хост-хранилища (см. ниже) | Записи/скриншоты с камер |

## Хранилища: хостовые пути в Docker

Путь хранилища задаётся в UI **как на хосте** — одинаково для Windows, Linux и Docker:

- Windows: `D:\CameraData`
- Linux: `/mnt/rec`

Backend переводит путь, только если это нужно (одна и та же логика во всех окружениях):

1. Если путь вида `X:\...` / `X:/...`, и внутри контейнера доступен `<STORAGE_HOST_BASE>/<X>/<rest>`
   — используется он (`/host/D/CameraData`).
2. Если нет — путь используется как есть (нативный Windows: `D:\CameraData` валиден).
3. Любой другой путь (Linux-абсолютный или относительный) не меняется.

В БД и в UI всегда остаётся хостовый путь, введённый пользователем.

### Обязательное условие: диск хоста должен быть смонтирован в compose

Смонтируйте диск/папку **до** добавления хранилища. Пример для `docker-compose.yml`
(и `docker-compose.release.yml`):

```yaml
# Windows-хост (Docker Desktop): весь диск D: доступен как /host/D
volumes:
  - ./logs:/app/logs
  - ./env_config:/app/env_config
  - ./storage:/app/storage
  - ./plugins/custom:/app/plugins/custom
  - type: bind
    source: 'D:\'
    target: /host/D
```

```yaml
# Linux-хост: путь в storages задаётся контейнерный, совпадающий с маунтом
  - type: bind
    source: /mnt/rec
    target: /mnt/rec
```

Если диск не смонтирован, добавление хранилища вернёт 404 «Path not found» — это ожидаемое
поведение. Запись видео/скриншотов идёт по адресу `<диск>/<id камеры>/...` на хосте.

## Troubleshooting

- **`pip`/`apt` падает при сборке (UTF-16 requirements):** файл `requirements.txt` должен быть в UTF-8.
  Если он повреждён (BOM `FF FE`), пересохраните: в VS Code «Save with Encoding → UTF-8».
- **`apt-get: Unable to connect to deb.debian.org`:** сеть BuildKit/прокси. Проверьте доступность
  сети из контейнера: `docker run --rm python:3.11-slim apt-get update`.
- **Live-просмотр камер (WebRTC) не работает за bridge-сетью:** в мосту aiortc отдаёт ICE
  host-кандидаты с внутренним IP (172.x), недоступным с хоста. Варианты: `network_mode: host`
  для backend (тогда `DB_HOST` → `localhost`), либо TURN-сервер и `WEBRTC_STUN_URLS`.
- **Linux-хост: права на bind-каталоги:** контейнер работает от UID 1000 (`appuser`).
  На Linux хосте каталоги `./logs`, `./env_config`, `./storage` должны быть `chown 1000:1000`
  (в Docker Desktop/Windows проблем нет).
- **БД не найдена (database "ecosystem" does not exist):** см. раздел «Переменные окружения» —
  имя БД в корневом `.env` (`DB_NAME`) и в `env_config/.env` должны совпадать; после изменения
  пересоздайте: `docker compose down -v && docker compose up -d`.
- **Backend не стартует из-за CORS:** CORS-миддлварь отключён намеренно — всё ходит через
  один nginx-same-origin.