---
type: infra-requirements
work_item_id: ET-009
title: "Инфраструктурные требования — ET-009: Активация EnduroRussia + Wikiloc"
version: 1
status: approved
created_at: 2026-06-01
authors:
  - "agent:architect"
---

# Инфраструктурные требования — ET-009

## 1. Резюме

ET-009 — **конфиг-only активация** двух дополнительных источников
GPS-треков в pipeline ET-008. Инфраструктура **не меняется**:

- Никаких новых docker-сервисов;
- Никаких новых файлов БД;
- Никаких новых cron-записей (cron автоматизация — отдельный DevOps-task);
- Никаких новых env-переменных, секретов, ключей;
- Никаких новых портов и nginx-правил.

Все изменения — текстовые правки конфигов и тестовых артефактов плюс
один ручной первый прогон pipeline на mva154.

Эскалация: **minor change** (см. ADR-013 §«Классификация»).

## 2. Контейнеры и сервисы

| Аспект | Требование |
|---|---|
| Новый сервис `gps-collector` | Уже существует (ET-008). **Без изменений.** |
| Изменения `Dockerfile` | Нет |
| Изменения `docker-compose.yml` | Нет |
| Перезапуск API после деплоя | Нужен — `docker compose up -d --no-deps app` (≈ 5 сек простоя). Перезапуск нужен только потому, что `src/web/*.json` подаётся API-контейнером; обновлённые `style.json` / `style-dark.json` подхватываются после рестарта |
| Перезапуск `gps-collector` | Не applicable (не daemon). Следующий запуск через `docker compose --profile batch run --rm gps-collector ...` уже использует новый конфиг через примонтированный `config/` volume |

### 2.1 Зависимости между сервисами

Без изменений vs ET-008. `gps-collector` ↔ `app` коммуницируют через
docker-internal HTTP при cache-clear; этот контракт уже существует и
ET-009 его не трогает.

## 3. Сеть

| Аспект | Требование |
|---|---|
| Новые входящие порты | Нет |
| Изменения nginx | Нет |
| Новые исходящие HTTPS-соединения с mva154 | **Да** — две новых dest: `endurorussia.ru` (443) и `www.wikiloc.com` (443) |
| Firewall mva154 | Исходящие HTTPS уже разрешены (ET-008 §3, BRD §7). Дополнительных правил не нужно |
| DNS-резолвинг | Стандартный (системный resolver Docker). Никаких записей в `/etc/hosts` |

### 3.1 Изменение dest IP

**Перед ET-009** контейнер `gps-collector` обращался только к:
- `api.openstreetmap.org` (ADR-009);

**После ET-009** добавляются:
- `endurorussia.ru` (ADR-010 accepted);
- `www.wikiloc.com` (ADR-012 accepted).

Все три — стандартный HTTPS, без проксей и кастомных сертификатов.

### 3.2 Ограничение rate

| Источник | Rate-limit | Trafic за прогон | Пик |
|---|---|---|---|
| OSM | 1 req/sec | ≈ 100 МБ | без изменений |
| EnduroRussia | 5 sec / req | ≈ 30 МБ (≤ 305 треков × ~50 КБ + json list) | 1 req / 5 сек |
| Wikiloc | 10 sec / req | ≈ 5 МБ (≤ 50 треков × 3 req × ~30 КБ) | 1 req / 10 сек |
| **Итого пиковый egress mva154** | ≈ 0.1 req/sec суммарно | ≤ 150 МБ / прогон | пренебрежимо |

Влияния на пропускную способность mva154 нет.

## 4. Хранилища данных

| Аспект | Требование |
|---|---|
| Новые БД | Нет |
| Изменения схемы | Нет |
| Миграции | Нет |
| Изменения объёма `data/gps_tracks.sqlite` | +20–50 МБ ожидаемо (≤ 200 треков EnduroRussia × ~50 КБ + ≤ 50 треков Wikiloc × ~50 КБ + метаданные) |
| Лимит REQ-NF-03 (`08-data-requirements.md` ET-008) | 2 ГБ — далеко не достигнут |
| Backup `.sqlite` | Без изменений (тот же `cron`-скрипт, см. ET-008 §4.4) |

### 4.1 Опциональный one-shot fix старого URL

Если в БД test-сервера остались записи с старым `external_url` (с
дефисом `enduro-russia.ru`) — оператор может выполнить **один раз**
после первого прогона ET-009:

```sql
UPDATE tracks
SET external_urls_json = REPLACE(external_urls_json,
                                  'enduro-russia.ru',
                                  'endurorussia.ru')
WHERE external_urls_json LIKE '%enduro-russia.ru%';
```

На практике, поскольку до ET-009 `enduro_russia` был `enabled: false`,
**таких записей нет**. Скрипт — defensive, не обязательный (BRD R-9).

### 4.2 Backup retention

Без изменений. Ежедневный snapshot, 14 дней retention.

## 5. Конфигурация и секреты

| Аспект | Требование |
|---|---|
| Новые env-переменные | **Нет** |
| Новые секреты / API-ключи | **Нет** (EnduroRussia и Wikiloc — без авторизации) |
| Новые конфиг-файлы | Нет; меняется только содержимое существующих `config/gps_sources.yaml` и `config/gps_regions.yaml` |

### 5.1 Изменения `config/gps_sources.yaml`

См. ADR-013 §«Решение 2» — финальное содержимое. Изменения:
- `enduro_russia.base_url`: `https://enduro-russia.ru` → `https://endurorussia.ru` (без дефиса);
- `enduro_russia.enabled`: `false` → `true`;
- `enduro_russia.source_priority`: добавлено `80` (раньше отсутствовало, default fall-back в коде);
- новая запись `wikiloc` (15 строк).

### 5.2 Изменения `config/gps_regions.yaml`

См. ADR-013 §«Решение 3». В `tsfo_plus_chuvashia.sources` добавляется
`wikiloc`.

## 6. Зависимости

| Аспект | Требование |
|---|---|
| Новые Python-пакеты | **Нет** (defusedxml, httpx, shapely, pyyaml — все есть из ET-008) |
| Системные библиотеки в Dockerfile | Нет |
| Версия Python | 3.12, без изменений |
| Внешние runtime-зависимости (источники) | `endurorussia.ru` + `www.wikiloc.com` (см. §3.1) |
| Pinned-версии библиотек | Без изменений |

## 7. Сборка и деплой

### 7.1 Pipeline CI

Существующий Gitea Actions:
- `make lint` (ruff + eslint) — должен пройти без замечаний;
- `make test` — должен включать новые тесты UT-ER-*, UT-WL-*, IT-*;
- `make build` — пересобирает образ (никаких изменений в Dockerfile,
  но новые тестовые фикстуры и конфиги попадают в образ).

### 7.2 Деплой шаг-за-шагом

1. `git pull origin main` на mva154.
2. `docker compose build` (опционально; никаких изменений
   в Dockerfile/requirements не было, но сборка идемпотентна и
   быстрая).
3. `docker compose up -d --no-deps app` — рестарт API (≈ 5 сек простоя)
   для подхвата обновлённых `style.json` / `style-dark.json` и client-side
   JS (если изменился `gps_tracks.js`).
4. **Первый ручной прогон EnduroRussia:**
   ```bash
   docker compose --profile batch run --rm gps-collector \
       python -m scripts.gps_collect \
       --region tsfo_plus_chuvashia --source enduro_russia
   ```
   Ожидаемая длительность: 20–30 минут. Ожидаемый результат:
   `tracks_new ≥ 200`, `status: ok`.
5. **Первый ручной прогон Wikiloc:**
   ```bash
   docker compose --profile batch run --rm gps-collector \
       python -m scripts.gps_collect \
       --region tsfo_plus_chuvashia --source wikiloc
   ```
   Ожидаемая длительность: 10–25 минут (cap `max_tracks_per_run=50`).
   Ожидаемый результат: `tracks_new ≥ 1`, `status: ok | partial`.
6. Проверить `/api/gps-tracks/health` — `tracks_by_source` содержит
   ключи `enduro_russia` и `wikiloc` с ненулевыми значениями.
7. Smoke в UI: открыть `/enduro/`, включить «Публичные треки»,
   проверить три чекбокса источников и атрибуции.
8. Зафиксировать результат в `docs/work-items/ET-009/14-deploy-log.md`.

### 7.3 Время простоя

API: ≤ 5 секунд на шаге 3 (стандартный рестарт контейнера).
Pipeline: ≈ 50 минут (последовательные ручные прогоны двух источников).
Pipeline-простой **не влияет** на API; оба независимы.

### 7.4 Cron включается отдельным task'ом

ET-009 **не** активирует автоматический cron. После двух успешных
ручных прогонов подряд DevOps вручную раскомментирует cron-записи
из ET-008 (`/etc/cron.d/enduro-gps`).

### 7.5 Rollback

| Сценарий | Действие | Время |
|---|---|---|
| Откат конфигов (вернуть `enabled: false`) | `git revert <commit>` + `docker compose up -d --no-deps app` | ≈ 2 мин |
| Откат БД (если новые источники запортили данные) | `cp backups/gps_tracks-<date>.sqlite data/gps_tracks.sqlite` + рестарт API | ≈ 1 мин |
| Точечное удаление источника без отката кода | Открыть `config/gps_sources.yaml` на mva154, выставить `enabled: false`, рестарт API (cache-clear) | ≈ 1 мин |
| Удаление треков конкретного источника | `DELETE FROM tracks WHERE sources_json LIKE '%<id>%'` (через ssh + sqlite3) | ≈ 1 мин |

## 8. Cron / scheduled jobs

**Нет** в ET-009. Cron активируется отдельным DevOps-task'ом после
ETC-009 (см. §7.4).

## 9. Ресурсы (CPU / RAM / диск)

### 9.1 API-контейнер

Никаких изменений. Дополнительные source-ID не нагружают endpoint
(только новые значения в `properties.sources`).

### 9.2 gps-collector контейнер (во время прогона)

| Метрика | EnduroRussia | Wikiloc | OSM (для сравнения) |
|---|---|---|---|
| CPU (peak) | < 5% от 1 vCPU | < 5% от 1 vCPU | < 10% |
| RAM (peak) | ≤ 150 МБ | ≤ 150 МБ | ≤ 200 МБ |
| Network egress | ≈ 30 МБ | ≈ 5 МБ | ≈ 100 МБ |
| Длительность | 20–30 мин | 10–25 мин | 1–3 часа (ЦФО) |
| Disk write rate | низкий (≤ 1 МБ/мин) | низкий | средний |

Все три параллельно `gps-collector` cgroup-limit'ы (`cpus: 1.0`,
`mem_limit: 512m`) — никаких изменений по сравнению с ET-008.

### 9.3 Диск

Прирост `data/gps_tracks.sqlite` после первого прогона ET-009:
+20–50 МБ. Снимок backup того же объёма. Не влияет на disk budget.

## 10. Наблюдаемость

| Артефакт | Состояние после ET-009 |
|---|---|
| `GET /api/gps-tracks/health` | Возвращает `tracks_by_source = {osm, enduro_russia, wikiloc}` после первых прогонов |
| `/var/log/enduro-trails/gps-collect.log` | Логи ручных прогонов (через `>> ... 2>&1` при ssh) |
| `pipeline_runs` в БД | Новые записи для `source_id ∈ {enduro_russia, wikiloc}` |
| Docker `docker compose logs app` | Без изменений |

### 10.1 Алерты

Нет новых алертов. Существующие правила ET-008 (cron MAILTO,
db_size_mb > 2 ГБ) применяются как есть.

Опционально (out of scope ET-009): добавить ручную проверку
`/api/gps-tracks/health` в еженедельный operations-review для двух
новых источников.

### 10.2 Logrotate

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

## 11. Безопасность

Никаких изменений по security-модели по сравнению с ET-008:
- XML-парсинг GPX через `defusedxml.ElementTree`;
- скрейпинг — только outgoing;
- cache-clear endpoint остаётся docker-internal через nginx allow/deny.

### 11.1 Новые atack-vectors

| Vector | Митигация |
|---|---|
| Wikiloc возвращает malformed HTML с XSS-payload | Парсер использует regex, не интерпретирует HTML как DOM. JS не исполняется на сервере. Любой malformed HTML — `0 треков` без падения |
| EnduroRussia API возвращает malformed JSON | `httpx.Response.json()` бросает exception → graceful return из generator |
| Wikiloc / EnduroRussia возвращают XML-bomb в GPX | `defusedxml` блокирует billion-laughs (наследуется из ET-008) |
| Поддельный 403 от Wikiloc → DoS pipeline | Graceful-stop ≠ ошибка; следующий прогон попробует снова. Cron-окно (3 дня) > recovery-окна (часы) |

## 12. Влияние на C4 / архитектурную документацию

Изменения для отражения в `docs/architecture/README.md`:

- Таблица «Внешние источники pipeline» (lines 53-58 в текущем README):
  - `EnduroRussia.ru`: `ADR-010 (proposed/blocked)` → `ADR-010 (accepted)`;
  - добавить строку `Wikiloc | HTML + GPX | proprietary (некоммерческое использование) | ADR-012 (accepted) | да`;
  - `ttrails.ru`: без изменений.

Изменения для отражения в `docs/architecture/adr/README.md`:

- ADR-010: `status` updated to `accepted`;
- ADR-012: новая строка таблицы;
- ADR-013: новая строка таблицы (с ссылкой на ET-009).

C4 mmd-диаграмм в проекте нет (ET-008 §12 явно зафиксировано). ET-009
не создаёт диаграмм — изменение «активация existing source»
выражается в текстовом README.

## 13. Вывод

ET-009 — **minimal-change** на инфра-уровне:
- 0 новых сервисов / 0 новых БД / 0 новых cron / 0 новых env / 0 новых портов;
- Все изменения локализованы в `config/*.yaml`, `src/web/style*.json`,
  тестовых фикстурах и `src/api/gps_tracks/sources/wikiloc.py` (8 строк
  для `max_tracks_per_run`);
- Деплой = git pull + рестарт API + один ручной прогон;
- Rollback = `git revert` или ssh-правка `enabled: false`.

Эскалация: **не требуется** (`arch:major-change` не выставлен, ADR-013 §«Классификация»).
