удалён вход Telegram, владелец записи стал обязателен в схеме
- убраны клиент бота, транспорт обновлений, отправитель сообщений, сборка входа при старте, секция настроек и зависимость go-telegram-bot-api; из конвейера ушла доставка ответа отправителю — исход виден опросом готовности. Колонки адресата и значение источника остались в схеме: применённые шаги не переписываются - шаг 202608140003 запрещает пустого владельца у аудиозаписи и у файла; существующие строки он не проверяет, и это принято сознательно — искать их надо запросом до выкладки - ревью нашло два пред-существующих дефекта, оба закрыты: пустой второй ответ распознавателя стирал сохранённую расшифровку, а пустая расшифровка перестала быть заметной вместе с убранной доставкой. Попутно поднят golang.org/x/image до v0.45.0 — красный шаг vulns, воспроизводился и на чистом master
This commit is contained in:
+33
-128
@@ -6,12 +6,9 @@
|
||||
записью, что уезжает в ответ и что происходит, когда запись не удалось
|
||||
прочитать. Плюс наличие входов: с каким из них сервис вправе подняться.
|
||||
|
||||
Приём по существу описан пока **только для HTTP** — того, что нормируют
|
||||
проверки. Про вход Telegram нормировано одно: настроен он или нет и что из этого
|
||||
следует для подъёма. Кто допущен к боту и как забирается присланная им запись,
|
||||
требованиями по-прежнему не описано — требование, написанное без проверки, это
|
||||
предположение, а не норма. Первая задача, которая трогает поведение приёма из
|
||||
Telegram, дописывает его сюда.
|
||||
Вход у сервиса один — приём по HTTP, — и описан он тем, что нормируют проверки.
|
||||
Второй вход, Telegram, убран 2026-08-14 вместе со своими требованиями; его
|
||||
возвращение заводит их заново, вместе со связью чата и учётной записи.
|
||||
## Requirements
|
||||
### Requirement: Приём записи по HTTP
|
||||
|
||||
@@ -40,10 +37,12 @@ Telegram, дописывает его сюда.
|
||||
Куда именно ложится принятая запись, приёму не принадлежит: раскладку выбирает
|
||||
хранилище, и нормирует её capability `storage`.
|
||||
|
||||
Владельцем принятой записи приём SHALL назначать предъявителя сессии. Проверка
|
||||
стоит здесь, а не только в схеме хранилища: колонка владельца допускает пустое
|
||||
значение ради записей из Telegram, и приём по HTTP — то место, где
|
||||
обязательность держится.
|
||||
Владельцем принятой записи приём SHALL назначать предъявителя сессии. Обязательность
|
||||
владельца при этом MUST держаться и схемой хранилища: колонка владельца пустого
|
||||
значения не принимает вовсе, и норму эту держит capability `storage`. Проверка в
|
||||
приёме от этого не лишняя — она отвечает отправителю понятным отказом до того, как
|
||||
запись попадёт в память, а схема отвечала бы отказом сохранения после укладки
|
||||
файла.
|
||||
|
||||
Предъявитель, чья сессия не даёт учётной записи пользователя, MUST получать
|
||||
отказ `403` и MUST получать его **до чтения тела** — там же, где стоит отказ по
|
||||
@@ -154,11 +153,9 @@ Telegram, дописывает его сюда.
|
||||
нормирует требование ниже; наружу расширение выходит только приведённым к
|
||||
известному виду — этому отдано отдельное требование.
|
||||
|
||||
Сценарии судят приём по HTTP, потому что имя, данное отправителем, доходит до
|
||||
сервиса только оттуда: из Telegram приходит путь, выданный самим Telegram, а не
|
||||
имя человека. Правка при этом ложится на общий шаг заведения задачи, через
|
||||
который идут оба входа, поэтому своей нормы приём из Telegram здесь не получает —
|
||||
её напишет задача, которая тронет его поведение.
|
||||
Оговорка про второй вход из требования ушла вместе с ним: имя, данное
|
||||
отправителем, доходит до сервиса единственным путём — приёмом по HTTP, — и
|
||||
сценарии судят именно его.
|
||||
|
||||
#### Scenario: Имя записи не видно в журнале принятой записи
|
||||
|
||||
@@ -265,15 +262,15 @@ Telegram, дописывает его сюда.
|
||||
Остановленная запись MUST отдавать рубеж, на котором она остановлена, и MUST
|
||||
нести признак остановки отдельным полем `halted` со значением истины. Машинный
|
||||
текст отказа MUST в ответ не попадать: он принадлежит журналу владельца сервиса,
|
||||
а не отправителю. Отправитель узнаёт о неудаче ответом там, откуда пришла
|
||||
запись, — это нормирует capability `pipeline`.
|
||||
а не отправителю. Этот адрес — **единственное** место, где отправитель узнаёт о
|
||||
неудаче: доставки ответа отправителю у сервиса больше нет, и признак остановки
|
||||
здесь несёт всю обязанность целиком.
|
||||
|
||||
Отказ без сессии MUST не зависеть от того, есть такая запись или нет: иначе по
|
||||
кодам ответа перебирается список заведённых записей.
|
||||
|
||||
Запись, принадлежащая другому, MUST отвечать тем же, чем отвечает неизвестный
|
||||
идентификатор, — кодом `404` и тем же телом. То же MUST относиться к записи без
|
||||
владельца: запись, принятая ботом, по этому адресу не достаётся никому.
|
||||
идентификатор, — кодом `404` и тем же телом.
|
||||
|
||||
#### Scenario: Запись найдена
|
||||
|
||||
@@ -325,117 +322,25 @@ Telegram, дописывает его сюда.
|
||||
### Requirement: Поднятые входы видны наблюдателю
|
||||
|
||||
Сервис SHALL отдавать признак поднятости по каждому входу приёма отдельной
|
||||
метрикой. Признак MUST выставляться при сборке входа и MUST различать поднятый
|
||||
вход и неподнятый.
|
||||
метрикой и MUST выставлять метку только тому входу, который у сервиса есть.
|
||||
Метки убранного входа в метриках MUST не быть вовсе: признак со значением нуля
|
||||
читался бы как «вход есть, но не поднялся», то есть как поломка, а вечная
|
||||
единица рядом с ним — как исправность того, чего нет.
|
||||
|
||||
Требование стоит на том, что иначе потерянный вход не виден ничем: проба
|
||||
здоровья отвечает «сервис работает» и при неподнятом боте, а запись журнала
|
||||
живёт до ротации и вопрос «работает ли вход сейчас» не отвечает. Метрика —
|
||||
единственный канал наблюдения, который у владельца автоматизирован.
|
||||
Проверяемое здесь одно — **набор меток**, и это честнее прежнего. Вход остался
|
||||
один, страница метрик отдаётся тем же сервером, что и приём, и значение нуля у
|
||||
единственной метки недостижимо: чтобы прочитать признак, надо дотянуться до
|
||||
входа, о котором он сообщает. Прежнее обоснование — «иначе потерянный вход не
|
||||
виден ничем» — было верно, пока входов было два; сегодня неподнятый вход виден
|
||||
неудачей чтения самих метрик.
|
||||
|
||||
#### Scenario: Вход Telegram не поднят
|
||||
Различать поднятый и неподнятый вход признак MUST снова, как только входов у
|
||||
сервиса станет больше одного.
|
||||
|
||||
- **GIVEN** сервис поднялся без Telegram
|
||||
#### Scenario: В метриках только оставшийся вход
|
||||
|
||||
- **GIVEN** сервис поднялся
|
||||
- **WHEN** наблюдатель читает метрики
|
||||
- **THEN** признак поднятости входа Telegram равен нулю
|
||||
- **AND** признак поднятости входа HTTP равен единице
|
||||
|
||||
### Requirement: Признак включения решает, поднимается ли вход Telegram
|
||||
|
||||
Намерение владельца SHALL объявляться отдельным признаком включения входа
|
||||
Telegram, а ключ доступа MUST означать только доступ. При выключенном входе
|
||||
сервис MUST подниматься без Telegram и MUST не смотреть на ключ доступа вовсе.
|
||||
При включённом входе пустой ключ MUST быть отказом старта: сообщение называет имя
|
||||
незаполненного ключа и MUST не нести его значения.
|
||||
|
||||
Признак включения MUST быть в настройках задан. Умолчания у него нет: файл, где
|
||||
признака нет вовсе, негоден, и сервис MUST выходить с ошибкой настройки, назвав
|
||||
недостающий ключ. Умолчание здесь было бы угаданным намерением, а признак заведён
|
||||
затем, чтобы намерение объявляли: любое умолчание делает одну из двух ошибок
|
||||
тихой — либо бот молча пропадает, либо файл без признака молча работает.
|
||||
|
||||
Выключенный вход MUST быть назван в журнале **ровно одной** записью уровня `INFO`
|
||||
при старте. Это выбор владельца, а не отклонение, и предупреждать о нём не о чем;
|
||||
предупреждение остаётся за тем, чего владелец не выбирал.
|
||||
|
||||
При включённом входе сервис SHALL подниматься, когда вход поднять не удалось, и
|
||||
MUST продолжать работу оставшимся входом: приём по HTTP, опрос готовности и
|
||||
конвейер расшифровки работают в полном объёме. Неподнятый вход MUST быть назван в
|
||||
журнале **ровно одной** записью уровня `WARN` при старте — с причиной и без
|
||||
значения ключа.
|
||||
|
||||
Исключение одно, и оно проходит по тому, **ответил ли Telegram**. Ответ «такого
|
||||
бота нет» — ошибка настройки: бот по этому ключу не появится ни от ожидания, ни
|
||||
от повтора, и старт MUST кончаться отказом. Сервис, молча потерявший бота после
|
||||
опечатки в ключе, перестаёт отвечать своим отправителям, и узнать об этом было бы
|
||||
неоткуда.
|
||||
|
||||
Всё прочее — недоступность: сеть, DNS, авария Bot API, истёкший срок ожидания.
|
||||
Она MUST не влиять на подъём. Основной вход сервиса — не Telegram, и ронять его
|
||||
целиком из-за чужой аварии нельзя: перезапуск в такую минуту оставил бы без
|
||||
работы и приём по HTTP, и панель, и конвейер, которому Telegram не нужен вовсе.
|
||||
|
||||
Ожидание при сборке MUST быть ограничено сроком. Без него недоступность
|
||||
неотличима от подъёма: обращение к Telegram стоит на пути старта, и молчащий
|
||||
собеседник останавливал бы его бессрочно — без записи, без порта и без пробы
|
||||
здоровья.
|
||||
|
||||
Требование нормирует **наличие входа**, а не приём из него.
|
||||
|
||||
#### Scenario: Вход выключен
|
||||
|
||||
- **GIVEN** в настройках сервиса вход Telegram выключен
|
||||
- **WHEN** сервис запускается
|
||||
- **THEN** он поднимается и принимает записи по HTTP
|
||||
- **AND** конвейер расшифровки работает
|
||||
- **AND** бот не заведён, а в журнале ровно одна запись уровня `INFO` о том, что
|
||||
вход выключен настройкой
|
||||
|
||||
#### Scenario: Вход выключен, а ключ доступа задан
|
||||
|
||||
- **GIVEN** в настройках сервиса вход Telegram выключен
|
||||
- **AND** ключ доступа при этом заполнен
|
||||
- **WHEN** сервис запускается
|
||||
- **THEN** он поднимается без Telegram, и бот не заводится
|
||||
- **AND** к Telegram не уходит ни одного обращения
|
||||
|
||||
#### Scenario: Вход включён, а ключа доступа нет
|
||||
|
||||
- **GIVEN** в настройках сервиса вход Telegram включён
|
||||
- **AND** ключ доступа пуст
|
||||
- **WHEN** сервис запускается
|
||||
- **THEN** старт кончается отказом
|
||||
- **AND** сообщение об отказе называет имя незаполненного ключа
|
||||
|
||||
#### Scenario: Признака включения в настройках нет
|
||||
|
||||
- **GIVEN** в настройках сервиса нет признака включения входа Telegram
|
||||
- **AND** ключ доступа заполнен и Telegram признаёт по нему бота
|
||||
- **WHEN** сервис запускается
|
||||
- **THEN** старт кончается отказом настройки
|
||||
- **AND** сообщение об отказе называет недостающий ключ
|
||||
|
||||
#### Scenario: Вход включён и ключ годен
|
||||
|
||||
- **GIVEN** в настройках сервиса вход Telegram включён
|
||||
- **AND** стоит ключ, по которому Telegram признаёт бота
|
||||
- **WHEN** сервис запускается
|
||||
- **THEN** он поднимается и работает обоими входами
|
||||
|
||||
#### Scenario: Telegram не отвечает
|
||||
|
||||
- **GIVEN** в настройках сервиса вход Telegram включён и ключ непуст
|
||||
- **AND** Telegram недоступен либо не отвечает дольше отведённого срока
|
||||
- **WHEN** сервис запускается
|
||||
- **THEN** он поднимается и принимает записи по HTTP
|
||||
- **AND** бот не заведён, а в журнале запись уровня `WARN` с причиной
|
||||
- **AND** запись не несёт значения ключа
|
||||
|
||||
#### Scenario: Telegram ответил, что такого бота нет
|
||||
|
||||
- **GIVEN** в настройках сервиса вход Telegram включён и ключ непуст
|
||||
- **AND** Telegram отвечает отказом на этот ключ
|
||||
- **WHEN** сервис запускается
|
||||
- **THEN** старт кончается отказом
|
||||
- **AND** ни журнал, ни текст отказа не несут значения ключа
|
||||
- **THEN** признак поднятости несёт метку входа HTTP со значением единицы
|
||||
- **AND** метки убранного входа Telegram в метриках нет вовсе
|
||||
|
||||
|
||||
Reference in New Issue
Block a user