Контракт ручного повтора уведомлений

Назначение

Ручной retry используется только для заявок, которые уже сохранены в public.broker_leads, но Telegram-уведомление завершилось статусом failed.

Он не является частью публичной формы, не вызывается из браузера посетителя и не заменяет Web3Forms.

До полного smoke-теста сайт сохраняет:

lead_capture:
  mode: "web3forms"
  endpoint: ""

Состав

Шестая миграция:

supabase/migrations/202607130006_broker_lead_notification_manual_retry.sql

Закрытая Edge Function:

supabase/functions/broker-notification-retry/index.ts

Конфигурация функций:

supabase/config.toml

Миграция применяется после пяти предыдущих миграций backend:

  1. 202607070001_create_broker_leads.sql;
  2. 202607130002_broker_leads_v2.sql;
  3. 202607130003_broker_lead_preparation.sql;
  4. 202607130004_broker_lead_notification_summary.sql;
  5. 202607130005_broker_lead_notification_delivery.sql;
  6. 202607130006_broker_lead_notification_manual_retry.sql.

Разрешённый переход статуса

Новый ручной запрос может выполнить только переход:

failed → pending → sending → sent | failed

Новый административный retry запрещён из:

SQL RPC переводит только failed в pending. Он не расширяет допустимые исходные статусы.

Восстановление прерванного retry

Если административная функция уже получила подтверждение SQL RPC, но аварийно завершилась позже, запись может остаться в pending или sending.

Повторный запуск может продолжить такую ранее подтверждённую попытку только при одновременном выполнении условий:

Активный sending не захватывается повторно. Зависший sending может быть восстановлен штатным claim только после 15 минут.

Другой reason code возвращает retry_reason_mismatch. Состояния без ранее подтверждённого ручного retry возвращают retry_not_allowed.

Такое восстановление не увеличивает notification_manual_retry_count и не создаёт второе событие notification_retry_requested.

Причины retry

Свободный комментарий администратора не принимается. Разрешены только коды:

Это предотвращает попадание имени, телефона, текста заявки и других персональных данных в технический журнал.

SQL RPC

public.request_broker_lead_notification_retry(uuid, text, text):

Аутентификация Edge Function

Supabase по умолчанию проверяет JWT до запуска Edge Function. Здесь используется собственный административный bearer token, поэтому в supabase/config.toml задаётся:

[functions.broker-notification-retry]
verify_jwt = false

Это не делает функцию незащищённой: platform JWT заменяется собственной обязательной проверкой NOTIFICATION_ADMIN_TOKEN внутри handler.

Запрещено:

Административная Edge Function

broker-notification-retry требует переменные окружения:

Секреты не размещаются в репозитории, _config.yml, браузерном JavaScript, URL или issue.

Функция:

Формат запроса

{
  "lead_id": "00000000-0000-4000-8000-000000000001",
  "request_id": "00000000-0000-4000-8000-000000000002",
  "reason_code": "telegram_temporary_error"
}

Журналирование

Создаются обезличенные события:

Payload события может содержать только:

Полный payload заявки, Telegram token, chat ID и текст уведомления в событие не записываются.

Контроль очереди

public.broker_lead_notification_queue_health() возвращает только агрегаты по статусу:

Функция доступна только service_role и не возвращает идентификаторы, имена, телефоны, города, источники или тексты заявок.

Ограничения

Telegram API не поддерживает идемпотентный ключ. Если Telegram принял сообщение, но функция аварийно завершилась до фиксации sent, повтор после ручной проверки теоретически может создать дубль. Оператор обязан сверить request ID в Telegram и базе до вызова retry.

failed не повторяется автоматически. Каждая новая попытка требует отдельного подтверждённого административного запроса.

Активационный барьер

Deploy административной функции разрешён только после:

  1. применения всех шести миграций;
  2. проверки verify_jwt = false только для функций с собственной аутентификацией;
  3. проверки прав RPC;
  4. теста в отдельном Telegram-чате;
  5. проверки отсутствия CORS;
  6. проверки неправильного admin token;
  7. проверки восстановления pending и зависшего sending с тем же reason code;
  8. проверки очереди без персональных данных;
  9. фиксации ответственного и процедуры отката в issue №7.

Публичный Supabase endpoint и режим hybrid остаются выключенными до отдельной приёмки основного backend.

ЗаявкаПозвонитьВК