diff --git a/README.md b/README.md new file mode 100644 index 0000000..066968d --- /dev/null +++ b/README.md @@ -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)), **КОНТУР** (полевая координация). \ No newline at end of file diff --git a/SETUP_GUIDE.md b/SETUP_GUIDE.md new file mode 100644 index 0000000..abf918f --- /dev/null +++ b/SETUP_GUIDE.md @@ -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://: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://: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).* \ No newline at end of file