Add Telegram listener, scheduler, and extended parsers admin UI.
Introduce background scheduling and migrations for analytics ingest, expand parser management and map toolbar UX, and ignore local data/session files from version control. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -18,7 +18,7 @@ flowchart LR
|
||||
| Центр | Контейнеры | Назначение |
|
||||
|-------|------------|------------|
|
||||
| **ЦА** | `ca-db`, `ca-api`, `ca-frontend` | PostgreSQL, ingest API, карта, distribution API |
|
||||
| **ЦП** | `cp-workers` | Парсинг Telegram (`centers/parsing/workers`) |
|
||||
| **ЦП** | `cp-workers` | Парсинг Telegram: real-time listener (Telethon) + batch-задания из Redis |
|
||||
| **Общее** | `redis` | Очередь заданий ЦА → ЦП |
|
||||
|
||||
## Структура monorepo
|
||||
@@ -67,7 +67,7 @@ docker compose up --build
|
||||
| Раздел | Путь | Описание |
|
||||
|--------|------|----------|
|
||||
| **Карта** | `/` | Интерактивная карта событий: навигация по датам (flatpickr), пресеты периода, фильтры региона/темы/источника, подложки Яндекс/OSM/Topo/ESRI, линейка, полноэкранный режим, центрирование по координатам и городам, поиск населённых пунктов (Nominatim). CRUD для ручных объектов (ПКМ). Поддерживает `?eventId=` |
|
||||
| **Парсеры** | `/parsers` | Создание заданий Telegram-парсинга, таблица статусов с автообновлением (5 с), повтор failed-заданий |
|
||||
| **Парсеры** | `/parsers` | Telegram-парсеры с периодическим запуском, настройка интервала, редактирование и удаление; дубликаты по `source_url` не записываются |
|
||||
| **События** | `/events` | Фильтрация, пагинация, просмотр деталей, ссылка «На карте» для событий с координатами |
|
||||
| **Аналитика** | `/analytics` | KPI-карточки, график динамики ingest за 30 дней, топ населённых пунктов и регионов |
|
||||
| **ПИ** | `/consumers` | CRUD подписчиков distribution API, ротация ключей, тест среза через `/api/v1/events` |
|
||||
@@ -108,8 +108,10 @@ docker compose up --build
|
||||
|
||||
| Метод | Путь | Описание |
|
||||
|-------|------|----------|
|
||||
| POST | `/admin/jobs` | Создать задание парсинга (ставится в Redis) |
|
||||
| GET | `/admin/jobs` | Список заданий |
|
||||
| POST | `/admin/jobs` | Создать парсер (сразу в очередь + периодический запуск) |
|
||||
| GET | `/admin/jobs` | Список парсеров |
|
||||
| PATCH | `/admin/jobs/{id}` | Изменить канал/лимит, `interval_seconds`, `is_active` |
|
||||
| DELETE | `/admin/jobs/{id}` | Удалить парсер |
|
||||
| POST | `/admin/jobs/{id}/retry` | Повторить failed-задание |
|
||||
| GET | `/admin/events` | События с фильтрами (`{ items, total }`) |
|
||||
| GET | `/admin/analytics/summary` | KPI-сводка |
|
||||
@@ -148,14 +150,35 @@ curl http://localhost:8080/api/v1/events \
|
||||
-H 'Authorization: Bearer test-pi-api-key-change-me'
|
||||
```
|
||||
|
||||
## Парсинг Telegram
|
||||
|
||||
`cp-workers` объединяет два режима в **одном процессе** (общая сессия `telegram.session`):
|
||||
|
||||
| Режим | Как работает |
|
||||
|-------|----------------|
|
||||
| **Listener (real-time)** | Telethon `NewMessage` / `Album` на активных парсерах (`is_active=true`); новый пост сразу уходит в ingest |
|
||||
| **Batch (по расписанию)** | Планировщик в `ca-api` ставит задание в Redis; воркер забирает последние N постов (`iter_messages`) |
|
||||
|
||||
Дубликаты по `source_url` при ingest пропускаются. Listener не меняет `status` парсера (флаг `listener: true` в ingest).
|
||||
|
||||
Переменные `cp-workers`:
|
||||
|
||||
| Переменная | По умолчанию | Описание |
|
||||
|------------|--------------|----------|
|
||||
| `TELEGRAM_LISTENER_ENABLED` | `true` | Включить real-time listener |
|
||||
| `TELEGRAM_LISTENER_REFRESH_SECONDS` | `60` | Как часто обновлять список каналов из БД |
|
||||
|
||||
Internal API: `GET /internal/listener/subscriptions` — список активных каналов для listener.
|
||||
|
||||
## Поток данных
|
||||
|
||||
1. Аналитик создаёт задание: `POST /admin/jobs`
|
||||
2. `ca-api` ставит задание в Redis (`cp:jobs`)
|
||||
3. `cp-workers` забирает задание, парсит Telegram через существующую сессию
|
||||
4. Результаты отправляются в `POST /internal/ingest`
|
||||
5. События с координатами автоматически появляются на карте как `MapObject`
|
||||
6. Внешние ПИ получают отфильтрованный срез через `/api/v1/events`
|
||||
1. Аналитик создаёт парсер: `POST /admin/jobs` (канал, лимит, интервал в секундах)
|
||||
2. **Listener** сразу подписывается на канал и ingest-ит новые посты
|
||||
3. **Планировщик** `ca-api` периодически ставит batch-задание в Redis (`cp:jobs`)
|
||||
4. `cp-workers` забирает batch-задание, парсит последние N постов
|
||||
5. Результаты → `POST /internal/ingest` (дубликаты по `source_url` пропускаются)
|
||||
6. Новые события с координатами появляются на карте как `MapObject`
|
||||
7. Внешние ПИ получают срез через `/api/v1/events`
|
||||
|
||||
## Миграция EventRecord → Event
|
||||
|
||||
|
||||
Reference in New Issue
Block a user