feat(tradein/ops): алерты GlitchTip доезжают в Telegram #2915

Merged
lekss361 merged 1 commit from feat/tradein-glitchtip-telegram-alerts into main 2026-08-15 19:56:41 +00:00
Owner

Зачем

Мониторинг сейчас нем полностью. Замер 15.08.2026:

  • alerts_projectalert и alerts_alertrecipientпусты: правил алертов не заведено ни одного;
  • EMAIL_URL=consolemail:// — письма печатаются в stdout (чинится отдельно, #2914);
  • то есть упавший сервис не сообщит об этом никому и никуда.

Этот PR даёт второй канал — Telegram-тему поддержки, независимую от почты.

Как

GlitchTip умеет получателя типа webhook и шлёт Slack-совместимый JSON. Telegram такой формат не понимает, поэтому нужен переводчик. Он поставлен на стороне «Меры», где уже есть Telegram-клиент с поддержкой тем.

Новый эндпоинт:

POST /api/v1/trade-in/ops/glitchtip-webhook?secret=<TRADEIN_INTERNAL_AUTH_SECRET>

Секрет едет в query, а не в заголовке, по факту, проверенному по исходникам GlitchTip 6.1.6 внутри живого контейнера: apps/alerts/webhooks.py делает session.post(url, json=...) без единого заголовка — прислать X-Internal-Auth-Secret он физически не может. Механизм сверки тот же (secrets.compare_digest), новый секрет не заводится.

Аутентификация здесь fail-closed: пустой секрет, токен или chat_id → 503. Это намеренно отличается от fail-open в rbac (#2213): молча принимать неаутентифицированные вебхуки хуже, чем не принимать их вовсе.

Формы payload у issue-алертов и uptime-алертов совпадают ({"text", "attachments":[...]}) — обработаны обе, неизвестная форма пересылается как есть с пометкой. Лимит Telegram 4096 символов: длинное тело обрезается, а не роняет запрос.

Сеть

glitchtip-worker (именно он шлёт вебхуки, не web) добавлен в сеть shared — иначе он не видит tradein-backend вовсе: раньше worker жил только в default, а backend «Меры» — в shared и tradein-net.

Проверка

  • pytest tests/test_glitchtip_webhook.py15 passed (успешная пересылка с замоканным клиентом, отказ без секрета, обрезка длинного текста, неизвестная форма payload).
  • Полный backend-suite — 4561 passed, 23 skipped, 0 падений.
  • ruff check — чисто; docker-compose.prod.yml разобран pyyaml — OK.

Что нужно сделать руками после мержа

1. В файл окружения бэкенда «Меры» на VPS добавить:

TELEGRAM_ALERTS_CHAT_ID=-1004443088679
TELEGRAM_ALERTS_TOPIC_ID=158

TELEGRAM_BOT_TOKEN там уже есть (бот поддержки живой). Отдельно стоит проверить, что TRADEIN_INTERNAL_AUTH_SECRET выставлен одинаково в обоих стеках — если он пуст, эндпоинт будет честно отдавать 503, а не притворяться работающим.

2. В интерфейсе GlitchTip (errors.gendsgn.ru) создать правило алерта — по проекту, а не по организации (ProjectAlert в модели привязан к проекту). У правила есть флаг uptime — «слать при падении uptime-монитора»; он направляет и падения мониторов через тех же получателей. Получатель: тип Webhook, URL —

http://tradein-backend:8000/api/v1/trade-in/ops/glitchtip-webhook?secret=<секрет>

3. Проверка сквозняка изнутри worker'а. В образе glitchtip/glitchtip:6.1.6 нет ни curl, ни wget — только python3:

docker exec glitchtip-worker python3 -c "
import urllib.request, json
data = json.dumps({'text':'проверка','attachments':[{'title':'smoke','title_link':'https://errors.gendsgn.ru/','text':'ручная проверка'}]}).encode()
req = urllib.request.Request('http://tradein-backend:8000/api/v1/trade-in/ops/glitchtip-webhook?secret=СЕКРЕТ',
                             data=data, headers={'Content-Type':'application/json'}, method='POST')
print(urllib.request.urlopen(req, timeout=10).read())
"

Ожидается {"status":"ok"} и сообщение в теме алертов. 401 — секреты в двух стеках разошлись; 503 — одна из трёх настроек пуста; отказ соединения — не доехала правка сети.

## Зачем Мониторинг сейчас нем полностью. Замер 15.08.2026: - `alerts_projectalert` и `alerts_alertrecipient` — **пусты**: правил алертов не заведено ни одного; - `EMAIL_URL=consolemail://` — письма печатаются в stdout (чинится отдельно, #2914); - то есть упавший сервис не сообщит об этом никому и никуда. Этот PR даёт второй канал — Telegram-тему поддержки, независимую от почты. ## Как GlitchTip умеет получателя типа webhook и шлёт Slack-совместимый JSON. Telegram такой формат не понимает, поэтому нужен переводчик. Он поставлен на стороне «Меры», где уже есть Telegram-клиент с поддержкой тем. Новый эндпоинт: ``` POST /api/v1/trade-in/ops/glitchtip-webhook?secret=<TRADEIN_INTERNAL_AUTH_SECRET> ``` Секрет едет в query, а не в заголовке, по факту, проверенному по исходникам GlitchTip 6.1.6 внутри живого контейнера: `apps/alerts/webhooks.py` делает `session.post(url, json=...)` **без единого заголовка** — прислать `X-Internal-Auth-Secret` он физически не может. Механизм сверки тот же (`secrets.compare_digest`), новый секрет не заводится. Аутентификация здесь **fail-closed**: пустой секрет, токен или chat_id → `503`. Это намеренно отличается от fail-open в rbac (#2213): молча принимать неаутентифицированные вебхуки хуже, чем не принимать их вовсе. Формы payload у issue-алертов и uptime-алертов совпадают (`{"text", "attachments":[...]}`) — обработаны обе, неизвестная форма пересылается как есть с пометкой. Лимит Telegram 4096 символов: длинное тело обрезается, а не роняет запрос. ## Сеть `glitchtip-worker` (именно он шлёт вебхуки, не web) добавлен в сеть `shared` — иначе он не видит `tradein-backend` вовсе: раньше worker жил только в `default`, а backend «Меры» — в `shared` и `tradein-net`. ## Проверка - `pytest tests/test_glitchtip_webhook.py` — **15 passed** (успешная пересылка с замоканным клиентом, отказ без секрета, обрезка длинного текста, неизвестная форма payload). - Полный backend-suite — **4561 passed, 23 skipped, 0 падений**. - `ruff check` — чисто; `docker-compose.prod.yml` разобран pyyaml — OK. ## Что нужно сделать руками после мержа **1.** В файл окружения бэкенда «Меры» на VPS добавить: ``` TELEGRAM_ALERTS_CHAT_ID=-1004443088679 TELEGRAM_ALERTS_TOPIC_ID=158 ``` `TELEGRAM_BOT_TOKEN` там уже есть (бот поддержки живой). Отдельно стоит проверить, что `TRADEIN_INTERNAL_AUTH_SECRET` выставлен **одинаково** в обоих стеках — если он пуст, эндпоинт будет честно отдавать 503, а не притворяться работающим. **2.** В интерфейсе GlitchTip (`errors.gendsgn.ru`) создать правило алерта — по проекту, а не по организации (`ProjectAlert` в модели привязан к проекту). У правила есть флаг `uptime` — «слать при падении uptime-монитора»; он направляет и падения мониторов через тех же получателей. Получатель: тип **Webhook**, URL — ``` http://tradein-backend:8000/api/v1/trade-in/ops/glitchtip-webhook?secret=<секрет> ``` **3.** Проверка сквозняка изнутри worker'а. В образе `glitchtip/glitchtip:6.1.6` нет ни curl, ни wget — только python3: ```bash docker exec glitchtip-worker python3 -c " import urllib.request, json data = json.dumps({'text':'проверка','attachments':[{'title':'smoke','title_link':'https://errors.gendsgn.ru/','text':'ручная проверка'}]}).encode() req = urllib.request.Request('http://tradein-backend:8000/api/v1/trade-in/ops/glitchtip-webhook?secret=СЕКРЕТ', data=data, headers={'Content-Type':'application/json'}, method='POST') print(urllib.request.urlopen(req, timeout=10).read()) " ``` Ожидается `{"status":"ok"}` и сообщение в теме алертов. `401` — секреты в двух стеках разошлись; `503` — одна из трёх настроек пуста; отказ соединения — не доехала правка сети.
lekss361 added 1 commit 2026-08-15 19:42:09 +00:00
feat(tradein/ops): приёмник GlitchTip webhook-алертов в Telegram-тему
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m50s
b649caec2d
Мониторинг GlitchTip сейчас нем (alerts_projectalert/alerts_alertrecipient
пусты, EMAIL_URL=consolemail:// печатает письма в stdout, аудит на проде
2026-08-15). GlitchTip умеет получателя типа webhook, но шлёт свой Slack-
совместимый JSON без каких-либо заголовков — Telegram Bot API его не
понимает, нужен адаптер.

- app/api/v1/glitchtip.py: POST /api/v1/trade-in/ops/glitchtip-webhook —
  принимает issue- и uptime-алерты (структурно одинаковый payload у
  GlitchTip 6.1.6, см. docstring), форматирует короткое сообщение
  (проект/заголовок/ссылка/время получения) и шлёт через существующий
  TelegramClient в отдельную тему алертов. Обрезка под лимит Telegram
  (4096 симв.), неизвестная форма payload пересылается как есть с
  пометкой вместо 500.
- Auth: GlitchTip не может слать кастомные заголовки (aiohttp.post без
  headers=) — переиспользуем TRADEIN_INTERNAL_AUTH_SECRET (#2213) как
  query-параметр `secret`, constant-time compare. В отличие от rbac.py
  пустой секрет здесь fail-CLOSED (503), это единственный auth-рубеж пути.
- config.py: TELEGRAM_ALERTS_CHAT_ID / TELEGRAM_ALERTS_TOPIC_ID — намеренно
  отдельные от TELEGRAM_SUPPORT_*, чтобы алерты не лились в топик клиентов.
- rbac.py: путь добавлен в _PUBLIC_PATHS (фиксированный, без секрета в
  самом пути — секрет только в query).
- docker-compose.prod.yml: glitchtip-worker (реально шлёт вебхуки, не
  glitchtip-web) переведён на networks: [default, shared] — без этого
  tradein-backend не резолвится с его стороны (общей сети не было вообще).
lekss361 merged commit 22a1b3aff4 into main 2026-08-15 19:56:41 +00:00
lekss361 deleted branch feat/tradein-glitchtip-telegram-alerts 2026-08-15 19:56:41 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2915
No description provided.