---
type: data-requirements
work_item_id: ET-015
title: "Требования к данным — ET-015: Healthcheck enduro-trails-app через python urllib"
version: 1
status: approved
created_at: 2026-06-05
authors:
  - "agent:architect"
---

# Требования к данным — ET-015

## 1. Резюме

ET-015 — **pure container-config change**. Никаких изменений в данных:
ни в БД, ни в файлах на диске, ни в localStorage, ни в API-контрактах,
ни в конфигурациях приложения.

Меняется **команда, которую Docker запускает для проверки живости
контейнера** — она перестаёт зависеть от `curl` (отсутствующего в
образе) и переключается на python `urllib.request`. Запрос ходит на
**уже существующий** эндпоинт `/api/health` (`src/api/main.py:1224`),
который не меняется.

**Меняется:**

- Runtime-состояние `docker inspect enduro-trails-app-1 --format
  '{{.State.Health.Status}}'`: переключается с `unhealthy` (ложный)
  на `healthy` (честный).
- Содержимое `State.Health.Log`: теперь пишутся реальные `ExitCode: 0`
  результаты, а не `exec: "curl": executable file not found in $PATH`.

**Не меняется:**

- Содержимое и схема БД `centralfederal.sqlite`, `gps_tracks.sqlite`.
- Содержимое и формат PNG-тайлов в `data/terrain/*`.
- Файлы OSRM-графа (`data/osrm/*`), OSM-данные (`data/osm/*`).
- Контракты API (`/api/gps-tracks/*`, `/terrain/*`, `/api/route/*`,
  `/api/health`, прочие).
- Эндпоинт `/api/health` — формат ответа, поведение, путь
  (`src/api/main.py:1224`) (AC-08).
- Ключи `localStorage` фронтенда.
- `style.json`, `style-dark.json`.
- `config/*.yaml`.
- `src/web/*`, `src/api/*`, `Dockerfile`, миграции, скрипты деплоя.

## 2. Архитектурные границы данных

| Слой данных | Тип | Расположение | Изменения в ET-015 |
|-------------|-----|--------------|---------------------|
| OSM-vector (`trails`) | существующий | `/app/data/centralfederal.sqlite` | **нет** |
| Публичные GPS-треки (ET-008) | существующий | `/app/data/gps_tracks.sqlite` | **нет** |
| OSRM-граф | существующий | `/app/data/osrm/enduro.osrm.*` | **нет** |
| Terrain PNG-тайлы | существующий | `data/terrain/*` | **нет** |
| Личные GPX-треки (ET-006) | существующий | браузер (memory) | **нет** |
| User UI state | существующий | `localStorage` | **нет** |
| MapLibre client tile cache | существующий | браузер (LRU MapLibre) | **нет** |
| Серверный кэш | не предусмотрен | n/a | **нет** |
| Docker container state | runtime | Docker daemon на mva154 | **меняется**: `State.Health.Status: unhealthy → healthy`, `FailingStreak: 3762 → 0`, `Log[].ExitCode: -1 → 0` |
| `docker-compose.yml` | конфигурация | git, mva154 | **меняется**: секция `app.healthcheck` |
| `CHANGELOG.md` | документация | git | **меняется**: +1 строка в `Unreleased` |

## 3. Серверные данные

### 3.1 БД

**Без изменений vs ET-014/ET-013/ET-008/ET-012.**

- `centralfederal.sqlite` — read-only для ET-015 (даже не читается).
- `gps_tracks.sqlite` — read-only для ET-015 (даже не читается).
- Никаких ALTER/CREATE/INSERT/UPDATE/DELETE.
- Никаких миграций.

**Косвенная связь:** эндпоинт `/api/health` возвращает поле `db_exists`
(`os.path.exists(DATA_PATH)`). Это проверка **наличия файла**, не
открытия БД, не SELECT'а. ET-015 не делает БД «зависимостью
healthcheck'а» больше, чем она уже была.

### 3.2 Тайлы на диске

**Без изменений.** `data/terrain/*`, `data/osm/*`, `data/osrm/*` — не
трогаются. Healthcheck не обращается ни к одной плитке.

### 3.3 Статика `src/web/`

**Без изменений.** Healthcheck не задевает фронтенд.

| Файл | Изменение |
|------|-----------|
| `src/web/app.js` | **нет** |
| `src/web/app.css` | **нет** |
| `src/web/index.html` | **нет** |
| `src/web/gps_tracks.js` | **нет** |
| `src/web/gpx.js` | **нет** |
| `src/web/units.js` | **нет** |
| `src/web/style.json` | **нет** |
| `src/web/style-dark.json` | **нет** |

### 3.4 Backend `src/api/`

**Без изменений.** `/api/health` (`src/api/main.py:1224`) не правится
(AC-08, BRD §7).

| Файл | Изменение |
|------|-----------|
| `src/api/main.py` | **нет** |
| `src/api/requirements.txt` | **нет** (никаких новых python-зависимостей) |
| `src/api/gps_tracks/*` | **нет** |
| Прочие модули | **нет** |

### 3.5 Конфиги

| Файл | Изменение |
|------|-----------|
| `Dockerfile` | **нет** (см. ADR-020 Cons Варианта B) |
| `docker-compose.yml` | **да** — секция `app.healthcheck` |
| `config/gps_sources.yaml` | **нет** |
| `config/gps_regions.yaml` | **нет** |
| nginx-config на хосте | **нет** |
| systemd / cron на mva154 | **нет** |

### 3.6 Скрипты и миграции

| Каталог | Изменение |
|---------|-----------|
| `scripts/` | **нет** (никакого `scripts/healthcheck.py` — отклонено в Вариант E ADR-020) |
| `migrations/` | **нет** |
| `tests/` | **нет** *(новые тесты опциональны, см. test-plan; не блокируют merge)* |

## 4. Клиентские данные

### 4.1 localStorage

**Без изменений.** ET-015 фронтенд не задевает. Никаких новых ключей,
никакой миграции.

### 4.2 MapLibre LRU (browser-side)

Без изменений. Тайловый кэш не задействован.

### 4.3 DOM runtime state

Без изменений. UI не меняется.

### 4.4 In-memory constants

Без изменений.

## 5. Контракты API

### 5.1 Backend endpoints

**Без изменений.** ET-015 не добавляет, не модифицирует и не удаляет
ни один endpoint.

| Endpoint | До ET-015 | После ET-015 |
|----------|-----------|--------------|
| `GET /api/health` | HTTP 200, JSON `{"status": "ok", "db_path": ..., "db_exists": ...}` | **без изменений** (AC-08) |
| `GET /api/gps-tracks/health` | без изменений | без изменений |
| `GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt` | без изменений | без изменений |
| `GET /api/gps-tracks?bbox=…` | без изменений | без изменений |
| `GET /api/gps-tracks/{id}/download` | без изменений | без изменений |
| `GET /terrain/{layer}/{z}/{x}/{y}.png` | без изменений | без изменений |
| `GET /api/route/*` | без изменений | без изменений |
| `GET /api/trails/*` | без изменений | без изменений |

### 5.2 Внутренний контракт healthcheck-команды

| Контракт | До ET-015 | После ET-015 |
|----------|-----------|--------------|
| Команда | `curl -f http://localhost:5556/api/health` | `python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:5556/api/health', timeout=3).status == 200 else 1)"` |
| Тип команды (Docker) | `CMD` (массив) | `CMD` (массив) |
| Зависимость от пакетов | curl (отсутствует ⇒ exec error) | stdlib (присутствует ⇒ работает) |
| Exit code при HTTP 200 | 0 (если бы curl был) | 0 |
| Exit code при HTTP 4xx/5xx | ≠ 0 (`-f` фейлит на 4xx/5xx) | ≠ 0 (`HTTPError` ⇒ ненулевой код) |
| Exit code при connection refused | ≠ 0 (если бы curl был) | ≠ 0 (`URLError` ⇒ ненулевой код) |
| Exit code при отсутствии команды | -1 (exec error) | n/a (команда есть) |
| Внутренний timeout запроса | n/a (использовал default Docker) | 3 с (`urlopen(..., timeout=3)`) |
| Внешний timeout Docker | 5 с | 5 с (без изменений) |
| Interval | 30 с | 30 с (без изменений) |
| Retries | 3 | 3 (без изменений) |
| Start period | не задан | 20 с (новое) |

### 5.3 Что **не** становится зависимостью

- **БД** (`centralfederal.sqlite`, `gps_tracks.sqlite`): healthcheck не
  открывает их. `/api/health` только проверяет `os.path.exists()` —
  это файловая операция, БД-движок не задействован.
- **OSRM** (`http://172.22.0.1:5559`): healthcheck не дёргает routing.
- **Тайл-каталог**: healthcheck не запрашивает PNG-плитки.
- **Внешние тайл-провайдеры** (OSM, Esri): не задействованы.
- **nginx**: не на пути healthcheck-запроса.

## 6. Миграции

**Нет.** Никаких миграций БД, миграций localStorage, миграций
конфигов приложения.

При деплое в test:

- `data/*` — без изменений.
- БД — без изменений.
- localStorage — старые ключи интерпретируются как раньше.
- MapLibre LRU — без изменений.
- Контейнер `enduro-trails-app-1` пересоздаётся (старый удаляется,
  новый создаётся с тем же образом и тем же файловым состоянием).
  Все volume-mounts (`./data:/app/data`, `./src/web:/app/src/web`,
  `./config:/app/config:ro`) подхватываются как раньше → никаких
  потерь данных.

## 7. Тестовые данные

### 7.1 Для unit-тестов

См. `04-test-plan.yaml` UT-01..UT-03:

- **UT-01**: live uvicorn на `:5556` (через `make dev`) либо mock-сервер;
  запуск python one-liner с хоста; проверка exit code 0.
- **UT-02**: никто не слушает `:5556`; запуск python one-liner;
  проверка exit code ≠ 0 (URLError).
- **UT-03**: mock-сервер отдаёт 500; запуск python one-liner;
  проверка exit code ≠ 0.

Тестовые данные минимальны: либо реальный uvicorn (с реальной БД,
которая уже есть), либо python `http.server`-mock. Никаких fixtures,
seed-данных, моков БД.

### 7.2 Для integration-тестов

См. `04-test-plan.yaml` IT-01..IT-04:

- **IT-01..IT-04**: реальный `docker compose up -d app` на машине с
  доступом к `data/`. Данные реальные; ET-015 их не меняет.
- Никаких новых fixtures, никаких CSV/JSON seed-файлов.

### 7.3 Для UI-тестов (Playwright)

Не применимо. ET-015 не трогает UI.

### 7.4 Для E2E на mva154

См. `04-test-plan.yaml` E2E-01..E2E-02:

- **E2E-01**: `ssh mva154 'docker inspect ...'` — данные читаются
  напрямую из Docker daemon, никакие тестовые fixtures не нужны.
- **E2E-02**: `curl https://openclaw.mva154.duckdns.org/enduro/api/health`
  — проверка живого эндпоинта; ответ — реальный JSON с реальной БД на
  mva154.

## 8. Резервные копии и DR

**Без изменений.** ET-015 не пишет данных. RPO = 0.

Если деплой ET-015 сломается (например, новый healthcheck сам по себе
помечает контейнер `unhealthy` из-за неучтённой особенности):

- БД, тайлы, конфиги — не затронуты.
- Rollback = `git revert` + `docker compose up -d app` (см.
  `07-infra-requirements.md` §6.3).
- RTO ≤ 5 минут.

## 9. Privacy / Compliance

| Аспект | Требование |
|--------|------------|
| PII | **Нет.** ET-015 не собирает, не обрабатывает, не передаёт никаких пользовательских данных |
| Licensing | Не применимо |
| Attribution | MapLibre attribution control — без изменений |
| GDPR / 152-ФЗ | Не применимо (healthcheck — loopback внутри контейнера, не пересекает периметр) |
| Egress на внешние сервисы | **Нет** (healthcheck не делает egress) |
| Логирование PII | **Нет** (healthcheck-логи Docker содержат только exit code и stdout/stderr команды — пустые) |

## 10. Связанные документы

- `01-brd.md` §1 (контекст), §2 (корень проблемы), §3 (бизнес-проблема),
  §4 (цель), §6 (ограничения), §7 (out of scope)
- `02-trz.md` §1 (постановка), §2 (текущее состояние), §3 (целевое
  состояние), §4 (альтернативы), §5 (R-1..R-5), §6 (тестирование)
- `03-acceptance-criteria.md` AC-01..AC-10
- `04-test-plan.yaml` ST-01..ST-07, UT-01..UT-03, IT-01..IT-04,
  E2E-01..E2E-02
- `06-adr/ADR-020-healthcheck-via-python-urllib.md` §«Решение», §«Что
  НЕ меняется», §«Технический долг»
- `07-infra-requirements.md` §2 (контейнеры), §5 (конфигурация)
- `10-tech-risks.md`
- `docs/architecture/README.md` §«Компоненты», §«GPS Tracks Pipeline»
  (для контекста: ET-015 эту pipeline не трогает)
- `docs/work-items/ET-014/08-data-requirements.md` — образец «pure
  client UI change» документа (наследие)
- `docs/work-items/ET-013/08-data-requirements.md` — образец «read-only
  data» документа (наследие)
