# MapMil Platform (ЦП → ЦА → ПИ) Единая платформа: ЦА (аналитика и карта) + ЦП (парсинг Telegram). ## Архитектура ```mermaid flowchart LR CA[ЦА Analytics Center] CP[ЦП Parsing Center] PI[ПИ External Consumers] CA -->|jobs via Redis| CP CP -->|POST /internal/ingest| CA CA -->|GET /api/v1/events| PI ``` | Центр | Контейнеры | Назначение | |-------|------------|------------| | **ЦА** | `ca-db`, `ca-api`, `ca-frontend` | PostgreSQL, ingest API, карта, distribution API | | **ЦП** | `cp-workers` | Парсинг Telegram (`centers/parsing/workers`) | | **Общее** | `redis` | Очередь заданий ЦА → ЦП | ## Структура monorepo ``` MapMil/ ├── centers/ │ ├── analytics/ │ │ ├── api/ # CA backend (FastAPI + PostgreSQL) │ │ └── frontend/ # CA admin UI (Vue + Leaflet) │ └── parsing/ │ └── workers/ # CP workers (Telethon) ├── contracts/ # Shared schemas (ingest, jobs) ├── data/ # telegram.session (локально, не в git) ├── docker-compose.yml └── .env # TELEGRAM_API_ID, TELEGRAM_API_HASH, … ``` ## Быстрый старт 1. Создайте `.env` из примера и укажите ключи Telegram: ```bash cp .env.example .env # отредактируйте TELEGRAM_API_ID и TELEGRAM_API_HASH ``` 2. Положите сессию Telegram в `data/telegram.session` (файл Telethon SQLite). Если мигрируете со старого SocialParser: ```bash cp ../SocialParser/data/telegram.session data/ ``` 3. Запуск: ```bash docker compose up --build ``` 4. Откройте UI: [http://localhost:8080](http://localhost:8080) ## Admin UI Веб-интерфейс ЦА доступен по тем же адресу. Навигация в шапке: | Раздел | Путь | Описание | |--------|------|----------| | **Карта** | `/` | Интерактивная карта событий: навигация по датам (flatpickr), пресеты периода, фильтры региона/темы/источника, подложки Яндекс/OSM/Topo/ESRI, линейка, полноэкранный режим, центрирование по координатам и городам, поиск населённых пунктов (Nominatim). CRUD для ручных объектов (ПКМ). Поддерживает `?eventId=` | | **Парсеры** | `/parsers` | Создание заданий Telegram-парсинга, таблица статусов с автообновлением (5 с), повтор failed-заданий | | **События** | `/events` | Фильтрация, пагинация, просмотр деталей, ссылка «На карте» для событий с координатами | | **Аналитика** | `/analytics` | KPI-карточки, график динамики ingest за 30 дней, топ населённых пунктов и регионов | | **ПИ** | `/consumers` | CRUD подписчиков distribution API, ротация ключей, тест среза через `/api/v1/events` | ## Сохранение Telegram-сессии **Важно:** существующий файл сессии **не удаляется и не пересоздаётся**. - Локально: `data/telegram.session` (SQLite Telethon, в `.gitignore`) - В Docker `cp-workers`: каталог `./data` монтируется как `/data` (read-write) - Путь в контейнере: `/data/telegram.session` - Переменные из `.env`: `TELEGRAM_API_ID`, `TELEGRAM_API_HASH`, `TELEGRAM_SESSION_PATH=/data/telegram.session` Файл сессии и `.env` не коммитятся. ## API ### Карта | Метод | Путь | Описание | |-------|------|----------| | GET | `/api/health` | Health check | | GET | `/api/map/objects` | Объекты на карте с данными события; фильтры: `date_from`, `date_to`, `on_date`, `region`, `topic`, `source_type`, `search`, `event_id` | | GET | `/api/map/filters` | Справочники для фильтров: `regions`, `topics`, `source_types`, `available_dates`, `date_bounds` | | GET | `/api/objects` | Объекты (legacy, без фильтров события) | | POST | `/api/objects` | Создать объект вручную | #### Инструменты карты (UI) - **Даты:** кнопки ❮❮/❮/❯/❯❯, календарь flatpickr, пресеты периода (1 неделя — 1 год / день) - **Фильтры:** регион, тема, источник (из `/api/map/filters`) - **Подложки:** Яндекс Карты / Спутник (ru_RU), OpenStreetMap, OpenTopoMap, ESRI Satellite - **Инструменты:** линейка (PolylineMeasure), полноэкранный режим, координаты/города, поиск Nominatim - **Точки:** маркеры из ingest + popup; ручные объекты — CRUD и drag - **Обновление:** polling `/api/map/objects` каждые 30 с ### ЦА Admin | Метод | Путь | Описание | |-------|------|----------| | POST | `/admin/jobs` | Создать задание парсинга (ставится в Redis) | | GET | `/admin/jobs` | Список заданий | | POST | `/admin/jobs/{id}/retry` | Повторить failed-задание | | GET | `/admin/events` | События с фильтрами (`{ items, total }`) | | GET | `/admin/analytics/summary` | KPI-сводка | | GET | `/admin/analytics/timeline?days=` | Динамика ingest по дням | | GET | `/admin/analytics/top-localities?limit=` | Топ населённых пунктов | | GET | `/admin/analytics/top-regions?limit=` | Топ регионов | | GET | `/admin/consumers` | Список подписчиков ПИ | | POST | `/admin/consumers` | Создать подписчика ПИ | | PATCH | `/admin/consumers/{id}` | Обновить подписчика | | POST | `/admin/consumers/{id}/rotate-key` | Сменить API-ключ | Пример задания Telegram: ```bash curl -X POST http://localhost:8080/admin/jobs \ -H 'Content-Type: application/json' \ -d '{"source_type":"telegram","source_config":{"channel":"creamy_caprice","limit":50}}' ``` ### ЦП → ЦА (internal) | Метод | Путь | Описание | |-------|------|----------| | POST | `/internal/ingest` | Приём batch событий (заголовок `X-Internal-Token`) | ### ПИ Distribution API | Метод | Путь | Описание | |-------|------|----------| | GET | `/api/v1/events` | События с фильтром по API-ключу | Тестовый consumer `test-pi` создаётся при старте с ключом из `TEST_PI_API_KEY` (по умолчанию `test-pi-api-key-change-me`): ```bash curl http://localhost:8080/api/v1/events \ -H 'Authorization: Bearer test-pi-api-key-change-me' ``` ## Поток данных 1. Аналитик создаёт задание: `POST /admin/jobs` 2. `ca-api` ставит задание в Redis (`cp:jobs`) 3. `cp-workers` забирает задание, парсит Telegram через существующую сессию 4. Результаты отправляются в `POST /internal/ingest` 5. События с координатами автоматически появляются на карте как `MapObject` 6. Внешние ПИ получают отфильтрованный срез через `/api/v1/events` ## Миграция EventRecord → Event | SocialParser | CA Event | |--------------|----------| | `event` | `description` / `title` | | `date` (dd.mm.yy) | `event_date` | | `geolocation` | `latitude`, `longitude` | | `locality` | `locality`, `region` | | `source_url` | `source_url` | | — | `source_type = "telegram"` | ## Остановка ```bash docker compose down ``` Данные PostgreSQL сохраняются в volume `pgdata`. ## Legacy Старые каталоги `backend/` и `frontend/` в корне оставлены для справки; активная разработка — в `centers/`.