README.md + SETUP_GUIDE.md — документация по образцу РВС

README: назначение/контекст, политика закрытого контура, архитектура,
стек, структура репо, БД (21 таблица), матмодель (контрольные точки),
OSM-геослой, API (12 роутеров), фронт, RBAC (142 юнита, 4 роли/9 прав),
мультипоиск, интеграция КОНТУР, аудит, планы.
SETUP_GUIDE: 18 разделов — требования, Docker, .env (смена секретов),
миграции/сиды, импорт OSM в PostGIS, проверки, пользователи, КОНТУР,
прокси/HTTPS, обновление (up -d для env!), бэкапы, диагностика (11 типовых
проблем), чек-лист развёртывания.
This commit is contained in:
2026-09-09 23:09:03 +03:00
parent 9115a0e7af
commit c44bc46667
2 changed files with 773 additions and 0 deletions
+387
View File
@@ -0,0 +1,387 @@
# ВЕКТОР МЧС — Система определения приоритетных направлений поиска
**Программное средство поддержки принятия решений при поисково-спасательных работах (ПСР) на открытой местности МЧС Республики Беларусь.**
ВЕКТОР рассчитывает приоритетные направления поиска пропавшего человека по матмодели: на основании данных о потерявшемся (возраст, диагнозы, физподготовка, транспорт), обстоятельствах потери (время, причина, направление), среде (рельеф, погода, сезон) и точке последней пропажи (ТНП) система строит концентрическую зону вероятного нахождения и подсвечивает приоритетные направления обследования с учётом рельефа из OSM-данных.
> **Версия документа:** соответствует состоянию репозитория на 9 сентября 2026 г.
> **Репозиторий:** [`Sadmin/vector`](http://192.168.0.106:3000/Sadmin/vector)
> **Развёртывание:** см. [SETUP_GUIDE.md](SETUP_GUIDE.md) — пошаговый гайд по развёртыванию на серверах МЧС.
---
## Оглавление
1. [Назначение и контекст](#1-назначение-и-контекст)
2. [Ключевая политика: полностью закрытый контур](#2-ключевая-политика-полностью-закрытый-контур)
3. [Архитектура](#3-архитектура)
4. [Стек технологий](#4-стек-технологий)
5. [Структура репозитория](#5-структура-репозитория)
6. [База данных](#6-база-данных)
7. [Математическая модель расчёта](#7-математическая-модель-расчёта)
8. [OSM-геослой (PostGIS)](#7-osm-геослой-postgis)
9. [Бэкенд (FastAPI) — структура и API](#8-бэкенд-fastapi--структура-и-api)
10. [Фронтенд (React)](#9-фронтенд-react)
11. [Аутентификация, роли и подразделения (RBAC)](#10-аутентификация-роли-и-подразделения-rbac)
12. [Мультипоиск и операции](#11-мультипоиск-и-операции)
13. [Интеграция с КОНТУР (отправка зон)](#12-интеграция-с-контуром-отправка-зон)
14. [Аудит](#13-аудит)
15. [Тестирование](#14-тестирование)
16. [Справочники](#14-справочники)
17. [Что готово и что в планах](#15-что-готово-и-что-в-планах)
18. [Контакты и сопровождение](#16-контакты-и-сопровождение)
---
## 1. Назначение и контекст
### Что делает ВЕКТОР
ВЕКТОР — рабочий инструмент РЦУ (расчётно-центрального управления) МЧС Республики Беларусь при организации поиска пропавших людей:
- **Карточка поиска** — структурированный ввод данных: субъект (возраст, пол, диагнозы — РАС, эпилепсия, СДВГ, ЗПР, психотип), обстоятельства (причина потери, время, направление движения, надёжность свидетельства), среда (сезон, погода, температура, рельеф), ресурсы (группы, кинолог, транспорт)
- **Матрасчёт** — детерминированный расчёт: максимальный радиус по методике ПСР (КМЧ × коэффициенты), приоритетные направления (сектора 45° с весами), прогноз поведения по психотипу, срочность и критические действия
- **Карта зон** — Leaflet: концентрические круги возможной зоны (0.5R / 0.75R / R) + подсветка приоритетных направлений; слои воды/болот полигонами и ж/д линий из локальной OSM
- **Рекомендации** — тактические рекомендации по профилям случая (проверить водоёмы, обследовать вдоль дорог и т.д.)
- **Мультипоиск** — несколько одновременных поисков, каждое ОУМЧС/Г(Р)ОЧС работает со своими операциями
- **Интеграция с КОНТУР** — отправка зон в полевую систему координации (ATAK + Meshtastic)
- **Архив завершённых поисков** — факт vs прогноз (дистанция, направление, место находки) для калибровки зонального скоринга
- **Администрирование** — пользователи по иерархии МЧС, журнал аудита, статистика
### Методическая основа
Расчёт радиуса основан на статистических таблицах ПСР (поиск и спасение, экстремум-методика): базовая скорость смещения по среднему лесу (НормС) умножается на коэффициенты возраста, диагноза, рельефа, погоды, времени суток, транспорта. Скоринг зон — взвешенная сумма факторов рельефа (лес, вода, дороги, НП) по секторам вокруг ТНП.
### Домен
МЧС осуществляет поиски **только в природных экосистемах** — не в городах. Город учтён в коэффициентах рельефа, но приоритетные зоны строятся для природной среды.
---
## 2. Ключевая политика: полностью закрытый контур
**Все данные о поисках (включая ПДн детей) не покидают контур организации.**
- **ИИ/LLM удалён из проекта полностью** (решение руководства): анализ детерминированный, `backend/services/claude_service.py` и любые облачные API исключены. В `docker-compose` и `.env` НЕТ никаких API-ключей внешних сервисов
- **Расчёт локальный**: вся математика в `services/*.py` (Python), выполняется на сервере за миллисекунды
- **Карты — из локальной OSM**: дамп Geofabrik (беларусь-latest.osm.pbf, ~333 МБ) импортирован в PostGIS внутри организации; бэкенд не ходит в Overpass/интернет (fallback на Overpass остаётся только на случай пустых OSM-таблиц)
- **Фронтенд** тянет OSM-тайлы и иконки Leaflet с CDN — для полностью offline-контура требуется локальный tile-сервер (в планах)
Тест-страж: `backend/tests/test_rules_analysis.py::TestNoLLM` — падает, если в код возвращается внешний ИИ.
---
## 3. Архитектура
```
┌───────────────────────────────────────────────────┐
│ Браузер пользователя │
│ (Chrome / Firefox / Edge — любая ОС) │
└──────────────────────┬────────────────────────────┘
│ HTTP
┌──────────────────────▼────────────────────────────┐
│ vector-frontend (React 18, CRA, порт 3000) │
│ Форма карточки · карта Leaflet · дашборд · админка│
└──────────────────────┬────────────────────────────┘
│ REST JSON :8000
┌──────────────────────▼────────────────────────────┐
│ vector-backend (FastAPI, порт 8000) │
│ ┌──────────────────────────────────────────────┐ │
│ │ Роутеры: auth, cases, analyze, operations, │ │
│ │ admin, admin_users, closed_cases, contour, │ │
│ │ water, geocode, stats, health │ │
│ └──────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Services (математика, без БД/HTTP): │ │
│ │ search_engine (модель), distance_service │ │
│ │ (радиус), scoring_service (сектора), │ │
│ │ rules_analysis (правила), psychotype, │ │
│ │ geo_service → osm_local (PostGIS) │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────┬────────────────────────────┘
│ SQLAlchemy / psycopg2
┌──────────────────────▼────────────────────────────┐
│ vector-postgres (PostgreSQL 16 + PostGIS 3.6) │
│ БД vector_mchs: кейсы, операции, юниты, аудит, │
│ OSM (planet_osm_*: 1 млн дорог, 192 тыс. лесов) │
└───────────────────────────────────────────────────┘
│ (опционально, outbound)
┌──────────────────────▼────────────────────────────┐
│ КОНТУР (CT130, полевая координация ATAK) │
│ POST /api/v1/vector/zones — приоритетные зоны │
└───────────────────────────────────────────────────┘
```
### Принципы архитектуры
- **Чистая математика**: `services/*.py` не знают ни о БД, ни об HTTP — только входные словари на выходе модели. Тонкие роутеры-обёртки в `backend/routers/`
- **Контракт /analyze**: `zone.forest_pct` — доля 0..1 (не проценты!), дистанции — км (в PostGIS SRID 3857 раздувание мерка: real = mercator × cos(lat))
- **Анализ воспроизводим**: тот же вход — тот же выход; прогон модели сохраняется в `analysis_log` кейса и сравнивается с фактом в архиве
---
## 4. Стек технологий
| Слой | Технология | Версия | Назначение |
|------|-----------|--------|-----------|
| Бэкенд | FastAPI | 0.115 | REST API |
| ORM | SQLAlchemy 2.0 + psycopg2 | | БД + миграции Alembic 1.14 |
| БД | PostgreSQL 16 + PostGIS 3.6 | | Данные + гео (PostGIS) |
| OSM | osm2pgsql 2.1 | | Импорт дамп OSM → planet_osm_* |
| Auth | python-jose + passlib | | JWT + bcrypt, server-side сессии |
| Фронт | React 18 (CRA) + react-leaflet | | SPA |
| Карта | Leaflet 1.9 + OSM-тайлы | | Отображение зон |
| Контейнеры | Docker Compose | | 4 сервиса |
Полный список зависимостей — `backend/requirements.txt` и `frontend/package.json`.
---
## 5. Структура репозитория
```
vector/
├── backend/ # FastAPI-приложение
│ ├── main.py # Сборка приложения (12 роутеров)
│ ├── models.py # 21 таблица (SQLAlchemy)
│ ├── schemas.py # Pydantic-схемы
│ ├── database.py # Репозиторий кейсов + статистика
│ ├── audit.py # Журнал аудита + гео-скоуп
│ ├── rbac.py # Роли/права/политика безопасности
│ ├── alembic/ # Миграции (006…009)
│ ├── seed_users.py # Сид первых пользователей
│ ├── seed_units.py # Сид подразделений (РЦУ РЧС → ОУМЧС → Г(Р)ОЧС, 142 юнита)
│ ├── routers/ # 12 модулей API (см. раздел 8)
│ ├── services/ # Адаптеры расчёта
│ └── tests/ # 231 тест (pytest)
├── services/ # Математика (чистые функции)
│ ├── search_engine.py # SearchInput/SearchModel — ядро расчёта
│ ├── distance_service.py # Максимальный радиус (КМЧ × коэффициенты)
│ ├── scoring_service.py # Взвешенный скоринг секторов
│ ├── rules_analysis.py # Правила: срочность, действия, поведение
│ ├── psychotype_service.py # Психотипирование (опрос)
│ ├── geo_service.py # Геоданные секторов (лес/вода/дороги/НП)
│ ├── osm_local.py # Локальный PostGIS-провайдер OSM
│ └── recommendation_service.py
├── frontend/ # React SPA
│ └── src/
│ ├── App.js # Роутинг (/dashboard, /desktop, /users, /admin)
│ ├── constants.js # Единые справочники (место находки, кто нашёл, румбы)
│ ├── api/client.js # apiFetch/apiJson — Bearer + 401-перехват
│ ├── components/ # CaseForm (5 шагов), SearchMap, ProtectedRoute
│ └── pages/ # Dashboard, DesktopSARPage, analysis/, AdminDashboard,
│ # UsersAdmin, ClosedCases, Login
├── scripts/ # Развёртывание и живые проверки
│ ├── b16-import.sh # Импорт OSM-дампа в PostGIS
│ ├── e2e-*.sh # e2e-сценарии
│ ├── eN-live-check.sh # Живые проверки этапов E1–E4
│ └── prod-stamp-backfill.sql
├── docker-compose.yml # 4 сервиса: postgres, adminer, backend, frontend
├── .env.example # Шаблон окружения (без внешних ключей)
├── SETUP_GUIDE.md # Развёртывание на серверах МЧС
├── vector_tasks.md # Живой план работ (B1…B21+)
└── README.md # Этот документ
```
---
## 6. База данных
**PostgreSQL 16 + PostGIS 3.6**, БД `vector_mchs`. Схема управления — Alembic (`backend/alembic/versions/`), актуальная ревизия **009_e3_operations**.
### Основные таблицы
| Таблица | Назначение |
|---------|-----------|
| `cases` | Карточки поисков: субъект, обстоятельства, среда, ТНП, исход |
| `analysis_log` | JSONB-лог прогонов анализа (аудит расчётов) |
| `search_models` | Слой 4: сохранённые матмодели по кейсам (GeoJSON-зоны) |
| `search_teams`, `field_observations`, `areas_checked`, `found_events` | Оперативные слои (состав групп, наблюдения, проверенные квадраты, находки) |
| `reference_priors` | Слой 1: справочные приоритеты |
| `users`, `mchs_units` | Пользователи; иерархия РЦУ РЧС → 6 ОУМЧС → 135 Г(Р)ОЧС |
| `roles`, `permissions`, `role_permissions`, `user_roles` | RBAC: 4 роли, 9 прав, матрица связей |
| `sessions`, `auth_events` | Server-side сессии (sha256-токены), журнал входов |
| `security_settings` | Политика безопасности (10 параметров) |
| `search_operations` | Поисковые операции (статусная машина, привязка к кейсу и КОНТУРу) |
| `audit_events`, `audit_changes` | Неизменяемый журнал аудита, field-level изменения (ретенция 90 дней) |
| `planet_osm_*` | Импорт OSM Беларуси (osm2pgsql, SRID 4326/3857): дороги, леса, вода, НП |
### Политика безопасности (`security_settings`, меняется в админке)
| Параметр | Значение |
|----------|----------|
| Минимальная длина пароля | 8 |
| Сложность пароля | требуются буквы+цифры |
| Срок действия пароля | 90 дней |
| История паролей | 5 (нельзя переиспользовать) |
| Блокировка после неудачных входов | 5 (на 15 минут) |
| Тайм-аут сессии / неактивности | 24 ч / 60 мин |
| Макс. параллельных сессий | 3 (старые отзываются) |
| Ретенция журнала аудита | 90 дней |
---
## 7. Математическая модель расчёта
Ядро — `services/search_engine.py`: `SearchInput` → `SearchModel` (чистая функция, без БД/HTTP).
### Состав расчёта
1. **Максимальный радиус** (`distance_service.py`): НормС (скорость смещения по среднему лесу, км/ч) × коэффициенты возраста / диагноза / рельефа / погоды / времени суток / транспорта, с учётом усталости. Контрольные точки: подросток 14 л, 3 ч, лес = 5.1 км; велосипедист 8 л, 2 ч = 10.8 км
2. **Коэффициенты рельефа** (`terrain_map`): лес/поле/луг/город/неизвестно = 1.0; густой лес = 0.5; болото = 0.4; горы/овраги = 0.6; дорога/тропа = 0.8
3. **Сектора направлений** (`scoring_service.py`): 8 секторов по 45° вокруг ТНП; каждый сектор оценивается по составу рельефа из локальной OSM (лес — доля 0..1, вода/НП — расстояние в км), приоритетные подсвечиваются на карте
4. **Психотип** (`psychotype_service.py`): опрос из формы → доминантный / гармоничный / тревожный / интроверт → модификаторы поведения и рекомендаций
5. **Правила** (`rules_analysis.py`): срочность (critical/high/elevated/normal), критические действия, прогноз поведения по профилям (не_умеет_плавать ×3.0 к водоёмам и т.п.), тактические рекомендации («проверить водоёмы», не «перекрыть»)
6. **Профили, не покрытые моделью** (ДЦП, слабое зрение/слух) — честно помечаются `unmodeled_profiles`, не искажают расчёт
**Коэффициенты меняются только по явному решению руководства** — молча таблицы не трогать.
---
## 7. OSM-геослой (PostGIS)
- Импорт: дамп [Geofabrik](https://download.geofabrik.de/europe/belarus-latest.osm.pbf) (~333 МБ) → `scripts/b16-import.sh` (osm2pgsql --slim, ~4,5 мин, RAM 2 ГБ)
- Содержимое: 1 010 507 дорог, 192 374 леса, 81 772 воды, 2 731 НП
- Провайдер `services/osm_local.py` читает `planet_osm_*`; `geo_service.py` переключается на Overpass только если таблицы пусты
- Полный `/analyze` на локальных данных: ~10 мс (см. `scripts/e2e-b16-timing.sh`)
- Обновление дампа — по решению (раз в квартал / заморозить)
---
## 8. Бэкенд (FastAPI) — структура и API
`backend/main.py` собирает 12 роутеров. Авторизация — Bearer JWT (form-urlencoded логин, OAuth2PasswordRequestForm).
| Префикс | Модуль | Назначение |
|---------|--------|-----------|
| `/api/v1/health` | health.py | Liveness |
| `/api/v1/auth` | auth.py | Логин/логаут/me/смена пароля, блокировки |
| `/api/v1/cases` | cases.py | CRUD карточек поисков |
| `/api/v1/analyze` | analyze.py | Матрасчёт зон (по payload или case_id) |
| `/api/v1/operations` | operations.py | Операции мультипоиска (CRUD, статусная машина, /summary), завершение поиска |
| `/api/v1/operations/{id}/send-to-contour` | contour.py | Отправка зон в КОНТУР; GET areas-checked (прокси) |
| `/api/v1/admin` | admin.py | Список/редактирование кейсов, дашборд-метрики |
| `/api/v1/admin/users` | admin_users.py | Пользователи (создание/роли/юниты/блокировка/сброс пароля) + журнал аудита |
| `/api/v1/closed-cases` | closed_cases.py | Архив завершённых поисков (ручной ввод + прогон модели) |
| `/api/v1/water/{case_id}` | water.py | Вода/болота (полигоны), ж/д (линии) вокруг ТНП |
| `/api/v1/geocode` | geocode.py | Прокси Nominatim (формат «пункт, район, адрес») |
| `/api/v1/stats` | stats.py | Тепловая карта, агрегаты |
Swagger: `http://<сервер>:8000/docs`.
**Синхронизация закрытия**: кейс закрывается через ЛЮБОЙ из путей (`PATCH /admin/cases` или `POST /operations/{id}/complete`) — активная операция кейса автоматически завершается (`completed` + `closed_at`).
---
## 9. Фронтенд (React)
SPA на react-scripts (CRA). Роутинг:
| Путь | Страница |
|------|----------|
| `/dashboard` | Главная: «Активные поиски» (счётчики, карточки операций, «+ Новый поиск», «Пользователи») |
| `/desktop` | Рабочее пространство: форма карточки (5 шагов) + карта + результат анализа |
| `/analysis/:case_id` | Результат анализа: карта зон, КОНТУР-панель, «Завершить поиск» |
| `/users` | Админка пользователей + вкладка «Аудит» |
| `/admin` | Администрирование: Кейсы / Операции / Завершённые поиски |
Компоненты:
- **CaseForm** — 5 шагов: субъект → обстоятельства → среда → координаты (ТНП + геокодирование) → ресурсы; после сохранения автоматически создаётся поисковая операция
- **SearchMap** — концентрические круги (0.5R/0.75R/R) + подсветка приоритетных направлений 45°-секторами (палитра ЦОУ: #ff4d00 / #ff9f1a / #ffd166)
- **Dashboard / UsersAdmin / AdminDashboard** — VCL-density, тёмная тема (#0d1117 / #161b22 / #30363d / #e6edf3)
Единые справочники исхода — `frontend/src/constants.js` (тип места находки, кто нашёл, 8 румбов) — используются во всех точках ввода исхода.
---
## 10. Аутентификация, роли и подразделения (RBAC)
### Иерархия подразделений
РЦУ РЧС (республиканский) → 6 областных ОУМЧС → 135 Г(Р)ОЧС/ПАСО. Справочник — идемпотентный сид `backend/seed_units.py` (источник: справочник подразделений РВС).
### Роли
| Роль | Права | Скоуп видимости |
|------|-------|----------------|
| **admin** (РЦУ РЧС) | Все 9 прав | Все операции и пользователи республики |
| **coordinator** (ОУМЧС) | view/create/update/export/manage_users/view_audit | Своё ОУМЧС + подчинённые Г(Р)ОЧС |
| **operator** (Г(Р)ОЧС) | view/create/update | Свои поиски |
| **observer** | view | Только просмотр |
- Управление пользователями (`manage_users`): РЦУ РЧС — все; ОУМЧС — только своё поддерево (кросс-областные операции отбиваются 403 на бэкенде)
- Созданный пользователь обязан сменить пароль при первом входе (`must_change_password`)
- Сессии server-side: JWT несёт идентификатор, в БД — sha256-хэш; сброс пароля отзывает все сессии
---
## 11. Мультипоиск и операции
`search_operations` — надстройка над кейсами для одновременной работы с несколькими поисками:
- Одна операция на карточку (case_id unique)
- **Статусная машина**: planned → active ⇄ paused → completed → archived (недопустимые переходы → 400)
- Авто-создание операции при сохранении карточки; дашборд показывает активные
- `POST /operations/{id}/complete` — завершение поиска: исход (найден живым/погибшим, дистанция, направление, кто нашёл) → кейс в архив + детерминированный прогон матмодели → сравнение «прогноз vs факт» (accuracy_note)
- `GET /operations/summary` — счётчики для дашборда (активные / всего / завершено за 24 ч)
---
## 12. Интеграция с КОНТУР (отправка зон)
КОНТУР — система полевой координации (ATAK + Meshtastic). ВЕКТОР отдаёт приоритетные зоны (outbound):
1. В операции задаётся `contour_operation_id` (UUID операции в КОНТУРе)
2. `POST /api/v1/operations/{id}/send-to-contour` строит `SearchZones[]` (GeoJSON-полигоны секторов ±22.5°, `zone_id = vec:<НАПР>:<дист>m`, probability по приоритету) и отправляет на `{CONTOUR_API_URL}/api/v1/vector/zones?operation_id=…` с `Bearer CONTOUR_TOKEN`
3. Каждый шаг аудируется (`contour_send_ok` / `contour_send_failed`); ошибка КОНТУРа → 502
4. `GET /api/v1/operations/{id}/areas-checked` — прокси проверенных полевых квадратов (inbound, B17)
Переменные окружения: `CONTOUR_API_URL` (по умолчанию `http://192.168.0.130:8000`), `CONTOUR_TOKEN`.
---
## 13. Аудит
Неизменяемый журнал (append-only):
- `audit_events`: кто, что, когда, IP, user-agent, тип события (login/lockout/user_*/operation_*/contour_send_*/case_closed)
- `audit_changes`: field-level `старое → новое` по каждому изменённому полю
- Ретенция 90 дней (фильтруется при чтении из `security_settings`)
- Доступ: право `view_audit` — вкладка «Аудит» в админке `/users` с фильтром по типу и пагинацией
---
## 15. Что готово и что в планах
### Реализовано
- Форма карточки поиска (5 шагов), справочники, автоподсчёт «прошло времени»
- Детерминированный матрасчёт: радиус, сектора, поведение, срочность, рекомендации — без ИИ
- Локальный OSM-геослой в PostGIS (Беларусь), /analyze ~10 мс, без внешних вызовов
- Карта: концентрические круги + подсветка направлений (дизайн согласованный)
- РБАК: иерархия РЦУ РЧС → ОУМЧС → Г(Р)ОЧС (142 юнита), 4 роли / 9 прав, блокировки, сессии
- Мультипоиск: операции, статусная машина, дашборд «Активные поиски»
- Завершение поиска с внесением исхода, архив завершённых с «прогноз vs факт»
- Аудит 90 дней с field-level деталями
- Интеграция с КОНТУР: отправка зон (контракт B20)
- Журнал аудита пользователей: входы, изменения, отправки
- 231 автотест (pytest), 5 skipped
### В планах
- **E5 (B17)** — приём полевых данных от КОНТУРа: треки групп, проверенные квадраты (areas-checked) на карте операции
- **Живая интеграция с КОНТУР** (CT130) — настройка токена и реальная отправка зон
- **Локальный tile-сервер** — для полностью offline-фронта (сейчас тайлы OSM с CDN в браузере)
- **Калибровка settlement_score** — вкладка населённых пунктов в скоринге секторов (открытый методический вопрос)
- **Калибровка зонального скоринга** по накопленному архиву «прогноз vs факт»
---
## 16. Контакты и сопровождение
- Развёртывание и эксплуатация: см. [SETUP_GUIDE.md](SETUP_GUIDE.md)
- План работ и статус этапов: `vector_tasks.md`
- API-документация: Swagger `http://<сервер>:8000/docs`
- Репозиторий: [`Sadmin/vector`](http://192.168.0.106:3000/Sadmin/vector)
Смежные системы МЧС: **РВС** («Регистрация входящих сообщений», [`Sadmin/rvs-web`](http://192.168.0.106:3000/Sadmin/rvs-web)), **КОНТУР** (полевая координация).
+386
View File
@@ -0,0 +1,386 @@
# ВЕКТОР МЧС — Гайд по развёртыванию на серверах МЧС
**Пошаговое руководство для IT-специалистов МЧС Республики Беларусь по развёртыванию системы определения приоритетных направлений поиска (ВЕКТОР) на серверах организации.**
> **Версия документа:** 1.0 от 9 сентября 2026 г.
> **Репозиторий:** [`Sadmin/vector`](http://192.168.0.106:3000/Sadmin/vector)
---
## Оглавление
1. [Требования к серверу](#1-требования-к-серверу)
2. [Архитектура развёртывания](#2-архитектура-развёртывания)
3. [Подготовка сервера](#3-подготовка-сервера)
4. [Установка Docker и Docker Compose](#4-установка-docker-и-docker-compose)
5. [Получение кода проекта](#5-получение-кода-проекта)
6. [Конфигурация окружения (.env)](#6-конфигурация-окружения-env)
7. [Запуск сервисов](#7-запуск-сервисов)
8. [Инициализация базы данных (миграции, сиды)](#8-инициализация-базы-данных-миграции-сиды)
9. [Загрузка OSM-геослоя (PostGIS)](#9-загрузка-osm-геослоя-postgis)
10. [Проверка работоспособности](#10-проверка-работоспособности)
11. [Создание пользователей и справочника подразделений](#11-создание-пользователей-и-справочника-подразделений)
12. [Настройка интеграции с КОНТУР](#12-настройка-интеграции-с-контуром)
13. [Обратный прокси и HTTPS](#13-обратный-прокси-и-https)
14. [Обновление проекта](#14-обновление-проекта)
15. [Резервное копирование](#15-резервное-копирование)
16. [Мониторинг и логи](#16-мониторинг-и-логи)
17. [Диагностика проблем](#17-диагностика-проблем)
18. [Чек-лист развёртывания](#18-чек-лист-развёртывания)
---
## 1. Требования к серверу
### Минимальные требования
| Параметр | Значение |
|----------|----------|
| ОС | Debian 12/13, Ubuntu 22.04+ (любая с Docker) |
| CPU | 2 vCPU |
| RAM | 4 ГБ (2 ГБ свободно для импорта OSM — см. раздел 9) |
| Диск | 20 ГБ + 1 ГБ под дамп OSM |
| Сеть | Доступ браузеров к серверу по HTTP(S); для первичной загрузки дампа OSM — доступ в интернет (одноразово) |
### Требования безопасности
- Сервер в закрытом контуре организации; **данные ПДн не должны покидать контур** — не настраивать проксирование на внешние сервисы
- Смена всех паролей по умолчанию (см. раздел 6)
- Для полностью offline-сценария без интернета: скачать дамп OSM заранее (раздел 9) и учесть, что фронт тянет OSM-тайлы с CDN — для абсолютного offline нужен локальный tile-сервер (в планах)
---
## 2. Архитектура развёртывания
Docker Compose поднимает 4 сервиса:
| Контейнер | Образ | Порт | Назначение |
|-----------|-------|------|-----------|
| `vector-postgres` | postgres:16 (+PostGIS 3.6 в контейнере) | 5432 | БД `vector_mchs`, данные + OSM |
| `vector-backend` | сборка backend/Dockerfile | 8000 | FastAPI (uvicorn --reload) |
| `vector-frontend` | сборка frontend/Dockerfile | 3000 | React SPA (CRA dev-сервер) |
| `vector-adminer` | adminer | 8080 | Веб-администрирование БД |
Код backend/services и frontend смонтирован volume'ами — правки подхватываются без пересборки (dev-режим).
---
## 3. Подготовка сервера
```bash
# 1. Обновление системы
apt update && apt upgrade -y
# 2. Базовые утилиты
apt install -y curl git ca-certificates gnupg
# 3. (опционально) NTP-синхронизация — важна для корректных меток времени аудита
apt install -y systemd-timesyncd
timedatectl set-ntp true
```
---
## 4. Установка Docker и Docker Compose
```bash
# Официальный скрипт (или репозиторий docker-ce — по политике организации)
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker --version # 24+
docker compose version # v2
```
---
## 5. Получение кода проекта
```bash
mkdir -p /root/vector && cd /root/vector
git clone http://<gitea-host>:3000/Sadmin/vector.git .
```
Ключевые ветки: `master` — рабочая.
---
## 6. Конфигурация окружения (.env)
```bash
cp .env.example .env
```
Обязательные переменные:
```ini
# Подключение к БД (внутри compose — хост postgres)
DATABASE_URL=postgresql://postgres:<СМЕНИТЬ_ПАРОЛЬ>@postgres:5432/vector_mchs
# Секрет JWT — сгенерировать: openssl rand -hex 32
JWT_SECRET=<СЛУЧАЙНАЯ_СТРОКА>
# CORS — адрес(а) фронтенда
CORS_ORIGINS=http://<адрес-сервера>:3000
# Интеграция с КОНТУР (раздел 12; можно отложить)
CONTOUR_API_URL=http://<kontur-host>:8000
CONTOUR_TOKEN=<токен из КОНТУРа>
```
> **НЕ добавлять** никаких ключей внешних ИИ/API — по политике закрытого контура их в проекте нет и быть не должно (тест-страж `TestNoLLM`).
Пароль БД должен совпадать с `POSTGRES_PASSWORD` в `docker-compose.yml` (сервис postgres).
---
## 7. Запуск сервисов
```bash
cd /root/vector
docker compose up -d
docker compose ps # все 4 контейнера Up/healthy
```
Первый старт backend создаёт таблицы через `init_db` (create_all). Порядок применения Alembic-миграций — раздел 8.
---
## 8. Инициализация базы данных (миграции, сиды)
### 8.1 Миграции Alembic
```bash
docker exec vector-backend sh -c 'cd /app/backend && alembic upgrade head'
docker exec vector-backend sh -c 'cd /app/backend && alembic current' # 009_e3_operations
```
Миграции идемпотентны на чистой БД. **Если** БД уже была инициализирована через create_all и `alembic upgrade` падает `DuplicateTable` — применить стемп-скрипт:
```bash
docker exec -i vector-postgres psql -U postgres -d vector_mchs < scripts/prod-stamp-backfill.sql
```
### 8.2 Сид первого администратора
```bash
docker exec vector-backend sh -c 'cd /app && python backend/seed_users.py'
```
Создаётся стартовый администратор (логин `admin`, пароль по умолчанию — сменить при первом входе через форму). Скрипт пропускается, если пользователи уже есть.
### 8.3 Сид подразделений (142 юнита)
```bash
docker exec vector-backend sh -c 'cd /app && python backend/seed_units.py'
```
Создаёт РЦУ РЧС → 6 ОУМЧС → 135 Г(Р)ОЧС/ПАСО (справочник идемпотентный: существующие названия не дублируются). Без него админка пользователей бесполезна (пустые селекты подразделений).
---
## 9. Загрузка OSM-геослоя (PostGIS)
Бэкенд рассчитывает рельеф секторов из локальной OSM. На чистой БД геосервис автоматически fallback'ится на публичный Overpass (медленно, требует интернет) — **для продуктива импортируйте дамп**.
### 9.1 Установка PostGIS в контейнер БД
```bash
docker exec -u root vector-postgres apt update
docker exec -u root vector-postgres apt install -y postgresql-16-postgis-3 osm2pgsql
docker exec vector-postgres psql -U postgres -d vector_mchs -c 'CREATE EXTENSION IF NOT EXISTS postgis;'
```
> Установка пакетом в живой контенер переживает перезапуск контейнера (данные в volume), но слетает при ПЕРЕСОЗДАНИИ контейнера — тогда повторить команды.
### 9.2 Импорт дампа OSM
```bash
# Скачивание (одноразово, ~333 МБ; можно перенести на сервер любым носителем)
wget https://download.geofabrik.de/europe/belarus-latest.osm.pbf -O /tmp/belarus-latest.osm.pbf
# Импорт (~4,5 мин, RAM 2 ГБ, node cache 800)
sh scripts/b16-import.sh
```
Скрипт сам: удаляет старые planet_osm_* таблицы, копирует дамп в контейнер, запускает osm2pgsql (--slim, SRID по умолчанию), выводит список таблиц.
### 9.3 Проверка
```bash
docker exec vector-postgres psql -U postgres -d vector_mchs -t -c "
SELECT 'roads', count(*) FROM planet_osm_roads
UNION ALL SELECT 'forests', count(*) FROM planet_osm_polygon WHERE landuse='forest';"
```
Ожидается ~1 млн дорог, ~190 тыс. лесов.
---
## 10. Проверка работоспособности
```bash
# 1. Health бэкенда
curl -s http://localhost:8000/api/v1/health
# 2. Логин (ТОЛЬКО form-urlencoded!)
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'username=admin&password=<пароль>' | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
# 3. Профиль (9 прав у admin)
curl -s http://localhost:8000/api/v1/auth/me -H "Authorization: Bearer $TOKEN"
# 4. Фронт
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/ # 200
# 5. Полный расчёт (e2e-скрипты в scripts/e2e-*.sh — см. примеры вызовов)
```
В браузере: `http://<адрес>:3000` → дашборд «Активные поиски».
---
## 11. Создание пользователей и справочника подразделений
Пользователи создаются через UI: **Дашборд → Пользователи → «+ Новый пользователь»** (доступ: роль admin РЦУ РЧС или coordinator ОУМЧС в рамках своего поддерева).
- При создании задаётся временный пароль — пользователь обязан сменить его при первом входе
- Роли: РЦУ РЧС / ОУМЧС / Г(Р)ОЧС / Наблюдатель (описание прав — README.md, раздел RBAC)
- Инлайн-операции в таблице: смена роли/подразделения, отключение/включение, сброс пароля (отзывает все сессии)
- Все изменения пишутся в журнал аудита (вкладка «Аудит», ретенция 90 дней)
Иерархию подразделений менять: правкой `backend/seed_units.py` (идемпотентно, kind ∈ rcu/oblast/gor_rayon — ограничение БД `ck_mchs_units_kind`).
---
## 12. Настройка интеграции с КОНТУР
КОНТУР — система полевой координации (ATAK + Meshtastic) на отдельном сервере. ВЕКТОР отправляет в неё приоритетные зоны.
1. В `.env`: `CONTOUR_API_URL` (адрес API КОНТУРа) и `CONTOUR_TOKEN` (выдаётся на стороне КОНТУРа)
2. `docker compose up -d backend` — **пересоздать** контейнер (restart не перечитывает env!)
3. Проверка: операция → «Отправить зоны в КОНТУР» на странице анализа; при недоступности КОНТУРа бэкенд вернёт понятную ошибку 502 и запишет `contour_send_failed` в аудит
Входящие данные (треки групп, проверенные квадраты) — этап B17, в разработке.
---
## 13. Обратный прокси и HTTPS
Для доступа пользователей по HTTPS поставьте nginx/traefik перед фронтендом и API:
```nginx
server {
listen 443 ssl;
server_name vector.mchs.example;
ssl_certificate /etc/ssl/certs/vector.crt;
ssl_certificate_key /etc/ssl/private/vector.key;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
}
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
client_max_body_size 10m;
}
}
```
В `CORS_ORIGINS` укажите итоговый https-адрес. В закрытом контуре допустима работа по HTTP без прокси (порты 3000/8000 напрямую).
---
## 14. Обновление проекта
```bash
cd /root/vector
git pull origin master
# Если менялись миграции:
docker exec vector-backend sh -c 'cd /app/backend && alembic upgrade head'
# Если менялись env-переменные (.env / compose environment) — ПЕРЕСОЗДАТЬ контейнер:
docker compose up -d backend # restart НЕ перечитывает env!
# Обычное обновление кода:
docker restart vector-backend vector-frontend
```
Схема обновления, применяемая при разработке: правки коммитятся в Gitea → на сервере `git pull` → `docker restart`. После рестарта frontend пользователи перелогиниваются.
---
## 15. Резервное копирование
```bash
# Полный дамп БД (данные + OSM-таблицы)
docker exec vector-postgres pg_dump -U postgres -d vector_mchs -F c \
-f /tmp/vector_mchs_$(date +%F).dump
docker cp vector-postgres:/tmp/vector_mchs_$(date +%F).dump /root/backups/
# Только прикладные данные (без тяжёлых planet_osm_* — их восстанавливают импортом дампа):
docker exec vector-postgres pg_dump -U postgres -d vector_mchs \
--exclude-table='planet_osm_*' -F c -f /tmp/vector_app_$(date +%F).dump
```
Рекомендация: ежедневный cron прикладного дампа (он небольшой) + еженедельный полный. Хранить копии вне сервера.
---
## 16. Мониторинг и логи
```bash
docker compose logs -f # все сервисы
docker compose logs -f backend # API + аудит-сообщения
docker compose logs -f frontend # сборка CRA / ошибки компиляции
docker stats # потребление контейнеров
```
Ключевые события в логах backend: `admin closed case ... операция(ий) завершены` (синхронизация закрытия), ошибки расчёта (полный traceback uvicorn).
---
## 17. Диагностика проблем
| Симптом | Причина | Решение |
|---------|---------|---------|
| Логин даёт 422 | JSON вместо form-urlencoded | Логин строго `application/x-www-form-urlencoded` |
| «Session expired or revoked» сразу после логина | Несовпадение sha256-токена | Убедиться, что БД и backend на одной БД; пересоздать сессии |
| Селекты подразделений пусты | Не выполнен сид юнитов | `python backend/seed_units.py` |
| `alembic upgrade` → DuplicateTable | create_all создал таблицы раньше миграций | `scripts/prod-stamp-backfill.sql`, затем upgrade |
| Зоны «всегда вверх-вправо» | Расчёт по case_id терял tnp_lat/lon | Исправлено; при доработках сверять имена полей карточки с входом движка |
| Расчёт падает TypeError float×None | Payload содержит None-поля | SearchInput.to_case_data() выбрасывает None — регресс-тест есть |
| Правка .env не применилась | restart не перечитывает env | `docker compose up -d backend` (пересоздание) |
| Импорт OSM OOM | Мало RAM | Параметры --cache 800 --number-processes 2 уже в скрипте; закрыть лишние сервисы |
| КОНТУР недоступен при отправке зон | Нет связи/токена | curl до CONTOUR_API_URL; проверить CONTOUR_TOKEN; после правки env — up -d |
| Тайлы карты не грузятся | Нет интернета у браузера | Для полного offline — локальный tile-сервер (в планах) |
---
## 18. Чек-лист развёртывания
- [ ] Docker + Compose установлены
- [ ] Код получен (`/root/vector`), `.env` создан из `.env.example`
- [ ] `JWT_SECRET` заменён на случайный; пароль БД сменён (compose + DATABASE_URL)
- [ ] `docker compose up -d` — 4 контейнера Up
- [ ] `alembic current` = `009_e3_operations`
- [ ] `seed_users.py` выполнен, пароль админа сменён при первом входе
- [ ] `seed_units.py` выполнен (142 подразделения в селектах)
- [ ] PostGIS установлен в контейнере БД, extension создан
- [ ] Дамп OSM импортирован (`b16-import.sh`), таблицы заполнены
- [ ] Логин через UI работает, смена пароля обязательна
- [ ] Пробный поиск: карточка → анализ → зоны на карте, радиус соответствует контрольным точкам
- [ ] Создан тестовый пользователь ОУМЧС — скоуп видимости проверен
- [ ] Завершение поиска: исход внесён, кейс в архиве, «прогноз vs факт» отображается
- [ ] (опционально) КОНТУР: env-переменные, пересоздание backend, тестовая отправка зон
- [ ] (опционально) HTTPS/обратный прокси настроен, CORS обновлён
- [ ] Cron резервного копирования настроен, копия выгружена вне сервера
- [ ] Внешние ИИ/API-ключи отсутствуют в `.env` и `docker-compose.yml` (политика закрытого контура)
---
*Сопровождение: репозиторий [`Sadmin/vector`](http://192.168.0.106:3000/Sadmin/vector), план работ — `vector_tasks.md`, описание системы — [README.md](README.md).*