Files
MapMil/README.md
T
gitrusprusandCursor 1d6d54ab9f Refactor map tools and drop SocialParser runtime dependency.
Add filtered map API, creamy-caprice-style toolbar (dates, layers, search), Yandex ru tiles, and store Telegram session in MapMil/data instead of mounting SocialParser.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 20:56:30 +03:00

182 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/`.