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>
This commit is contained in:
2026-08-15 22:57:07 +03:00
co-authored by Cursor
parent 5f8ed25759
commit 6dff3c1c3d
11 changed files with 1046 additions and 12 deletions
+154
View File
@@ -0,0 +1,154 @@
# Архитектура ЦА (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) |