Устройство workflow
На этой странице
- Внутренний сервис
- Изменения графов
- Защита от повторов и неизвестный результат
- Разделение ролей и состояния
- Управление ролью и прежний откат подключения
- Исправление Expression и технический отказ
- Одноразовое продолжение подтверждённого исходного /post
- Точечный откат этого расширения
- Ограничения доказательств
Основной workflow и восемь активных дочерних workflow обновлены через штатные n8n import:workflow и publish:workflow. Точные опубликованные UUID и числа узлов — в README и workflow-index.json. Сохранённые nodes/connections сверены с workflow_history активной версии. Включён один отдельно обозначенный неактивный служебный мост восстановления; архивные mock workflow не включались. Дополнительно опубликован Error Workflow.
| После этапа | Переход |
|---|---|
| Единственный Telegram Trigger | Проверка реального отправителя и реестра |
| Проверка реального отправителя и реестра | Безопасный start |
| Проверка реального отправителя и реестра | Маршрутизатор команд и кнопок |
| Маршрутизатор команд и кнопок | Guards состояния, версии и доставленной копии |
| Guards состояния, версии и доставленной копии | Общий атомарный захват события или версии |
| Общий атомарный захват события или версии | Повторная проверка действующей роли |
| Повторная проверка действующей роли | Поиск, текст, проверка фактов, изображение |
| Поиск, текст, проверка фактов, изображение | Отдельная доставка в каждый личный чат |
| Guards состояния, версии и доставленной копии | Сессия правки конкретного участника |
| Guards состояния, версии и доставленной копии | Захват публикации и отправка сохранённого материала |
| Захват публикации и отправка сохранённого материала | Фиксация результата и уведомление обоих |
Внутренний сервис
bfl-collaboration 1.0.1 расположен в /opt/bfl-content/multiuser. Runtime — тот же официальный образ n8n 2.38.7, зафиксированный по digest; запускается только Node HTTP-сервис. Порт 8084 доступен в Docker-сети, публикации порта на VPS нет. Read-only filesystem, отдельные секреты, отдельная роль PostgreSQL без доступа к credentials и без права менять участников.
n8n обращается по http://bfl-collaboration:8084 с отдельным credential типа httpHeaderAuth. Значения ключей отсутствуют в workflow и в этом архиве. Telegram token читается сервисом из защищённого server-only файла; используется существующий бот и единственный прежний webhook. Renderer остаётся внутренним сервисом.
| Маршрут | Контракт и эффект |
|---|---|
| GET /healthz | Готовность сервиса и БД; не вызывает Telegram/AI. |
| POST /event | Реальный update + execution_id. Сверяет numeric from/chat, private, is_bot=false, роль. Выдаёт подписанный контекст действующего участника, проверку копии и сессии feedback. Не генерирует материалы. |
| POST /internal | Подписывает контекст существующего разрешённого служебного входа владельца. Не является публичной Telegram-командой. |
| POST /execution-context | Связывает дочернее выполнение с подписанным контекстом участника для диагностики и уведомления. |
| POST /execution-failure | Принимает минимальные сведения о техническом отказе, находит адресата по audit и учитывает результат уведомления. |
| POST /authorize | Перед внешним HTTP-запросом заново проверяет active и подпись контекста, пишет результат допуска в аудит. |
| POST /telegram | Ограниченный набор Telegram-операций; проверяет цель и материал, ведёт атомарный журнал доставок, возвращает API-совместимый результат и признак подтверждённой доставки. |
| POST /decision | После сохранённого решения уведомляет участников и старается удалить старые кнопки. Ошибка уведомления не откатывает состояние и не повторяет публикацию. |
| POST /onboarding | Служебное подтверждение и один тестовый комплект без кнопок. Только контекст владельца; сохранённое изображение, без AI и без канала. Дедупликация по multiuser_v1. |
Входящие HTTP-запросы, кроме health, требуют ключ. Подписанный контекст ограничен одним часом и не заменяет повторное чтение роли. Он не экспортируется в документацию. В полном сохранённом execution n8n может находиться временный контекст; редактору не предоставляется учётная запись n8n или доступ к execution/credentials.
Изменения графов
- Trigger сохраняет ID/webhook, теперь называется «Telegram — участники проекта». Unknown допускается только на безопасный
/start; бизнес-парсер проверяет допуск и реальные поля update. - Code-узлы сохраняют доверенный
_bflконтекст. Перед Execute Workflow он передаётся дочернему workflow. Произвольное поле входящего Telegram JSON не создаёт права. - Перед существующими внешними HTTP-запросами вставлены узлы сохранения контекста, повторного допуска и возврата контекста. Содержимое изображения не пересылается в сервис проверки прав.
- Telegram отправки заменены внутренним HTTP-адаптером с сохранением ID и названий соответствующих узлов. Данные об успешной отправке сохраняют прежний native-подобный формат.
- В guards кнопок сначала проверяется фактически доставленная копия участника. Затем используется канонический approval_message_id общей версии для прежнего CAS. Это не отключение message guard.
- Карточки работают аналогично: доставленная копия проверяется по actor/chat/message, а CAS сохраняет проверку первичного item.message_id.
- ForceReply отправляется только инициатору; actor/chat/prompt входят в проверку ответного сообщения и актуальности версии.
- После сохранённого
awaiting_feedback,superseded,rejected,publishedдобавлены уведомления обоим и best-effort удаление кнопок.
Защита от повторов и неизвестный результат
Прежние уникальные индексы событий, draft/version, canonical_url, selection_key и CAS состояния сохранены. Реестр доставок дополнительно имеет unique(purpose, entity_key, recipient_user_id). До сетевого запроса создаётся запись sending; после запроса — sent, rejected, unknown, revoked или not_sent. Повтор с подтверждённой/неизвестной записью не отправляет материал снова.
При 403 одного адресата продолжается доставка второму. Подтверждённая копия любого доступного получателя становится первичной в прежних полях версии; все копии остаются в журнале. Нет транзакции, объединяющей PostgreSQL и Telegram. Разрыв после запроса либо до фиксации подтверждения требует сверки; слепой повтор запрещён. Для первого сообщения прогресса сохранён один ограниченный повтор после явно подтверждённого 429. Поиск, генерация и публикация автоматически ради уведомления не повторяются.
Тексты статусов адресуются инициатору операции; журнал использует отдельную пару operation/actor. Версионный worker сохраняет проверки ранга и захвата статуса. Итоговый материал и решения получают оба активных подписанных участника.
Разделение ролей и состояния
owner_chat_id остаётся ID владельца проекта. Он не заменён ID редактора. created_by_user_id хранит создателя, operation_actor_user_id/chat_id — действующего участника, feedback_actor_user_id/feedback_chat_id — держателя сессии замечаний, получатель — в доставке. Старые owner-копии читаются из прежних полей; для новых доставок fallback не разрешает редактору недоставленную копию.
Прежние лимиты, политика источников, окна 7/30 дней, семантическая проверка и контракты renderer не расширялись. Подпись ≤1024 UTF-16 единиц, заголовок/подзаголовок workflow ≤70/110. Ссылка новости входит в общий бюджет. Точная замена может сохранить готовый file_id; обычная правка сохраняет исходную иллюстрацию, но может пересобрать обложку; явное изменение изображения допускает новую генерацию по прежним правилам.
Управление ролью и прежний откат подключения
На VPS от root:
python3 /opt/bfl-content/multiuser/editorctl.py status
python3 /opt/bfl-content/multiuser/editorctl.py disable
Для повторного включения того же подтверждённого numeric ID — enable. Скрипт не назначает нового человека по username. После active=false старые кнопки/команды не разрешают действия; проверка повторяется непосредственно перед новым внешним запросом. Уже отправленный запрос нельзя отменить задним числом. Публикации не удаляются.
Прежний откат подключения не заменяет откат исправления выражений. Для текущего расширения сначала изучите раздел «Точечный откат этого расширения» ниже.
python3 /opt/bfl-content/multiuser/rollback.py
Обычный откат отключает редактора, сохраняя совместимый обработчик владельца и всю новую историю. --restore-workflows — отдельный точечный возврат исходных workflow без восстановления старого дампа БД; допускается только без активных executions и несовместимых editor-only копий/сессий. Скрипт откажет при таких данных. Добавленные таблицы, колонки, изображения и публикации не удаляет.
Открытая сессия редактора при отзыве автоматически другому человеку не передаётся. Для такой версии нужен разбор состояния владельцем; обходить session guard произвольным reply нельзя.
Исправление Expression и технический отказ
В 37 HTTP-узлах исправлено соседство закрывающих объектных скобок внутри ={{ ... }}. Обычный JavaScript их принимал, установленный n8n Expression выбрасывал ExpressionExtensionError: invalid syntax. До исправления 37 из 44 выражений внутреннего сервиса не разбирались. После дополнения графов все 53 таких выражения проверены реальным Expression из n8n 2.38.7, с сопоставлением структуры результата обычному JS. Содержимое запроса и политика доступа не ослаблены.
Восемь дочерних workflow регистрируют execution/actor в /execution-context перед обработкой и получают исходный контекст обратно. /event, /internal, /authorize также сохраняют ID execution в audit. Обработчик BFLEditorError20260916 получает необработанное исключение и обращается в /execution-failure; он передаёт только execution/workflow ID, имя узла и класс ошибки. Сервис определяет получателя по audit, использует существующий журнал доставки и сохраняет диагностику. Материал и внешний запрос автоматически не повторяются. При невозможности связать actor используется владелец. Это best effort уведомление: общий отказ сети/Telegram/сервиса не даёт гарантии доставки. n8n Error Workflow не обещает обработку аварийного убийства процесса или ручных редакторских тестов.
Одноразовое продолжение подтверждённого исходного /post
Оператор проверил execution 339: настоящий update редактора, роль разрешена, версия уже создана; ни один внешний HTTP узел не выполнялся, нет текста/изображения и delivery ledger. Ошибка статуса в execution 340 также произошла до HTTP. Поэтому запись unknown этого уведомления доказанно относилась к локальному разбору, а не к неизвестному сетевому результату.
В транзакции проверены идентичность события, v1, latest, actor, состояние и отсутствие результата. Прежняя строка переведена из text_generating в text_preparing, разрешён только этот доказанный случай локального сбоя; уникальные индексы/ключи события не удалялись и не сбрасывались. Доказательство с подлинным событием сохранено на сервере в audit (не в экспорте).
Неактивный мост передаёт ссылку на execution. /internal проверяет proof, действующего участника и строку материала; атомарный UPDATE ready → claimed допускает ровно одного исполнителя. Создаётся свежий подписанный контекст того же редактора. Основной workflow возвращает существующую строку в общий узел «Выбрать подтверждение приёма», далее идёт штатный статус и обычная подготовка. Общий контекст заменяет прямые обращения к INSERT, чтобы не создавать вторую версию при продолжении.
| После этапа | Переход |
|---|---|
| Настоящий Telegram post | Участник и parser |
| Участник и parser | Уникальное событие и INSERT версии |
| Серверное доказательство до запроса | Одноразовый claim + active участника |
| Одноразовый claim + active участника | Существующая версия |
| Уникальное событие и INSERT версии | Общий контекст приёма |
| Существующая версия | Общий контекст приёма |
| Общий контекст приёма | Статус инициатору |
| Статус инициатору | Один запрос текста |
| Один запрос текста | Одна иллюстрация и renderer |
| Одна иллюстрация и renderer | Две независимые доставки превью |
| Две независимые доставки превью | Настоящий callback участника |
| Настоящий callback участника | Общий захват публикации |
| Общий захват публикации | Сохранённые фото и подпись в канал |
После успешной подготовки и проверки последующего настоящего callback доказательство отмечено completed; из готовой строки удалён только прежний локальный код ошибки, диагностика осталась в audit. Новый вызов моста с тем же proof блокируется. Привилегии роли БД не расширялись, DDL не менялся.
Точечный откат этого расширения
python3 /opt/bfl-content/editor-post-fix/rollback-extension.py выводит план. --apply возвращает старые девять графов и collaboration 1.0.0, сохраняя исправление 37 выражений; Error Workflow снимается. Скрипт проверяет точные активные UUID и отсутствие выполняющихся работ. Новые строки/публикации, права, credentials и audit не восстанавливаются из старой БД. Скрипт подготовлен, синтаксис и режим плана проверены; откат не применялся. Полный возврат исходного снимка вернёт доказанную синтаксическую ошибку.
Ограничения доказательств
Реально: восстановление прежнего запроса → один текст → одна иллюстрация → обложка → два превью → настоящее нажатие редактора → одна публикация. Изолированно: 26 проверок, включая отказ/отзыв, гонки, неизвестный результат, ForceReply и устаревшие кнопки. Новый /news, реальная доработка/отклонение и реальные внешние аварии не запускались ради теста. Полный restore не выполнялся.
Полные execution payload не выносились в этот архив. После отказа автоматической проверки расширенного экспорта использованы минимальные подтверждённые факты и локальные внедрённые определения, точно сверенные на VPS. Метаданные схемы проверены по хешу. Новые UUID версий прочитаны отдельно после публикации; они могут отличаться от входных UUID import-файла.