Files
MapMil/centers/analytics/ARCHITECTURE.md
T
gitrusprusandCursor 6dff3c1c3d Document platform architecture and Telegram proxy local setup.
Add developer docs for CA/CP/PI flows and wire cp-workers to the host SOCKS proxy so local Telegram auth and parsing work reliably.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 22:57:07 +03:00

155 lines
5.7 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.
# Архитектура ЦА (Analytics Center)
Центр аналитики — ядро MapMil: PostgreSQL, FastAPI, Vue admin/карта, distribution API для ПИ.
Общий контекст: [docs/architecture-overview.md](../../docs/architecture-overview.md).
ПИ отдельно: [DISTRIBUTION.md](DISTRIBUTION.md).
---
## Общими чертами
ЦА:
- хранит события, jobs, объекты карты, consumers;
- отдаёт единственный UI (`ca-frontend`);
- ставит задания в Redis для ЦП;
- принимает ingest от ЦП;
- отдаёт срезы внешним системам через `/api/v1/events`.
```mermaid
flowchart TB
UI[ca-frontend Vue]
API[ca-api FastAPI]
DB[(PostgreSQL)]
Redis[(Redis)]
CP[CP workers]
UI --> API
API --> DB
API -->|enqueue| Redis
Redis --> CP
CP -->|/internal/*| API
```
Контейнеры: `ca-db`, `ca-api`, `ca-frontend` (+ общий `redis`).
---
## Подробнее
### Структура
```text
centers/analytics/
├── ARCHITECTURE.md
├── DISTRIBUTION.md
├── api/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app/
│ ├── main.py # lifespan: migrations, scheduler, seed
│ ├── database.py
│ ├── models.py
│ ├── schemas.py
│ ├── deps.py # X-Internal-Token
│ ├── seed.py
│ ├── storage.py # uploads
│ ├── routers/
│ │ ├── objects.py # /api/health, /api/objects, media
│ │ ├── map.py # /api/map/*
│ │ ├── admin.py # /admin/*
│ │ ├── internal.py # /internal/* (только ЦП)
│ │ └── v1.py # /api/v1/* (ПИ)
│ └── services/
│ ├── jobs.py # Redis RPUSH
│ ├── scheduler.py # периодический re-queue
│ ├── ingest.py # дедуп + map sync
│ ├── filtering.py
│ ├── map_query.py
│ ├── events_query.py
│ ├── analytics.py
│ └── migrations.py
└── frontend/
├── Dockerfile # Vite build + nginx
├── nginx.conf # proxy /api /admin /internal → ca-api
└── src/
├── views/ # Map, Parsers, Events, Analytics, Consumers
├── components/ # карта, CRUD объектов
├── api/ # HTTP-клиенты
└── router/index.ts
```
### Модели данных
| Модель | Таблица | Назначение |
|--------|--------|------------|
| `Event` | `events` | Нормализованное событие; UK `source_url` |
| `ParseJob` | `parse_jobs` | Конфиг парсера, интервал, статус |
| `MapObject` | `map_objects` | Точка на карте (event или ручная) |
| `ObjectMedia` | `object_media` | Файлы к объектам |
| `Consumer` | `consumers` | Подписчик ПИ (hash ключа) |
| `ConsumerFilter` | `consumer_filters` | Фильтры среза ПИ |
### HTTP-поверхности
| Prefix | Кто вызывает | Содержание |
|--------|--------------|------------|
| `/api/*` | UI, публичный health | Карта, объекты, медиа |
| `/admin/*` | UI admin | Jobs, events, analytics, consumers |
| `/internal/*` | Только ЦП | ingest, job status, listener subscriptions |
| `/api/v1/*` | Внешние клиенты | Events с Bearer-ключом |
Internal защищён заголовком `X-Internal-Token` (`INTERNAL_TOKEN`).
### Jobs и scheduler
1. `POST /admin/jobs` / retry → запись `ParseJob` + `enqueue_job` (`services/jobs.py`).
2. Очередь: `cp:jobs:{family}` из `contracts/queues.py`.
3. `services/scheduler.py` — тик ~30 с, повторная постановка активных jobs по `interval_seconds`.
### Ingest
`POST /internal/ingest` → `services/ingest.py`:
- дедуп по `source_url`;
- создание `Event`;
- при координатах — sync `MapObject`;
- batch обновляет статус job; `listener: true` — нет.
### Frontend (маршруты)
| Path | View |
|------|------|
| `/` | `MapViewPage.vue` |
| `/parsers` | `ParsersView.vue` |
| `/events` | `EventsView.vue` |
| `/analytics` | `AnalyticsView.vue` |
| `/consumers` | `ConsumersView.vue` |
Карта: Leaflet, фильтры дат/региона/темы/источника, CRUD объектов (ПКМ), медиа, таймлайн появления (если включён в UI).
### Nginx
`ca-frontend` слушает `:80` (с хоста `:8080`), проксирует backend-пути на `http://ca-api:8000`. Лимит тела для медиа задаётся в `nginx.conf`.
### Env (ЦА)
| Переменная | Назначение |
|------------|------------|
| `DATABASE_URL` | PostgreSQL |
| `REDIS_URL` | Очереди |
| `INTERNAL_TOKEN` | Auth ЦП ↔ ЦА |
| `TEST_PI_API_KEY` | Seed consumer `test-pi` |
| `UPLOAD_DIR` | Медиа (по умолчанию `/data/uploads`) |
### Типовые точки входа в код
| Задача | Файл |
|--------|------|
| Новый admin endpoint | `routers/admin.py` |
| Логика ingest | `services/ingest.py` |
| Фильтры карты | `services/map_query.py` + `routers/map.py` |
| Новый экран UI | `frontend/src/views/` + `router/index.ts` |
| Поля парсера в форме | `ParsersView.vue` (+ contracts) |