приём и чтение записей сведены к одному контракту приложения
- адреса приложения переехали в своё пространство `/app/`, опрос готовности убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи, текст — отдельным адресом названного вида - заведена единая точка отображения доменной ошибки и слой, приводящий к той же форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением - у записи появились имя файла отправителя, длительность и размер своими колонками, а у ленты владельца — свой индекс: без него страница сканировала весь архив сервиса
This commit is contained in:
+20
-9
@@ -15,7 +15,7 @@
|
||||
[conventions/go-linters.md](conventions/go-linters.md).
|
||||
|
||||
- [intake](../openspec/specs/intake/spec.md) — **приём по HTTP плюс наличие
|
||||
входов**: приём и опрос за сессией, имя отправителя не доходит ни до
|
||||
входов**: приём за сессией, имя отправителя не доходит ни до
|
||||
хранилища, ни до журнала, метка метрики несёт только известное расширение, а
|
||||
наблюдатель видит единственный поднятый вход. Задачи
|
||||
`http-handler-tests-never-green` и `no-user-filename-in-log` 2026-08-11,
|
||||
@@ -38,6 +38,13 @@
|
||||
целиком и вложением, как из сохранённого строится структура реплик без
|
||||
повторной оплаты и почему разбор формата провайдера не доходит до конвейера.
|
||||
Задача `record-centric-model` 2026-08-14;
|
||||
- [archive](../openspec/specs/archive/spec.md) — **архив своих записей глазами
|
||||
приложения**: пространство адресов `/app/` и единая форма отказа с
|
||||
машиночитаемым кодом, пределы, которыми сервис ограничивает загрузку, и само
|
||||
чтение — страница записей ключом, карточка без текста и текст названного вида.
|
||||
Здесь же обязанность, переехавшая с убранного опроса готовности: причину
|
||||
остановки владелец записи узнаёт карточкой. Задача `json-api-for-spa`
|
||||
2026-08-15;
|
||||
- [access](../openspec/specs/access/spec.md) — кто пришёл в сервис и пускают ли
|
||||
его дальше: вход через внешнего провайдера OIDC, чем предъявляется сессия, что
|
||||
её прекращает и какие адреса остаются открытыми. Задача `oidc-login`
|
||||
@@ -82,7 +89,7 @@
|
||||
|
||||
| Компонент | Где | Что делает |
|
||||
| --- | --- | --- |
|
||||
| HTTP API | `internal/controller/http` | Приём файла и опрос статуса задачи |
|
||||
| HTTP API | `internal/controller/http` | Адреса приложения под корнем `/app/`: приём записи, страница своих записей, карточка, текст названного вида, пределы сервера и «кто вошёл» |
|
||||
| Воркеры | `internal/controller/worker` | Пул одинаковых потоков: каждый берёт любую пригодную запись и опрашивает базу. Число — настройкой, ноль законен |
|
||||
| Сервис расшифровки | `internal/service` | Конвейер: приём, приведение, отправка, опрос, завершение. Шаг выбирается по рубежу записи |
|
||||
| Конвертер и метаданные | `internal/adapter/{converter,metaviewer}/ffmpeg` | `ffmpeg` в ogg/vorbis, `ffprobe` для длительности |
|
||||
@@ -139,15 +146,15 @@
|
||||
|
||||
| Зависимость | Падает | Отвечает медленно | Молчит | Отдаёт мусор |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Yandex SpeechKit | Шаг возвращает ошибку, запись остаётся на повтор | Захват держится час, запись не двигается; по истечении предела простоя она останавливается с причиной «застряла», не теряя идентификатора операции | Операция вечно `in progress`, повтор каждые 5 секунд — до предела простоя в сутки | Пустой текст — запись доходит до конечного рубежа без расшифровки, и в журнале стоит запись «может стать проблемой» с идентификатором записи; опрос готовности отдаёт рубеж `done` без поля текста |
|
||||
| Yandex SpeechKit | Шаг возвращает ошибку, запись остаётся на повтор | Захват держится час, запись не двигается; по истечении предела простоя она останавливается с причиной «застряла», не теряя идентификатора операции | Операция вечно `in progress`, повтор каждые 5 секунд — до предела простоя в сутки | Пустой текст — запись доходит до конечного рубежа без расшифровки, и в журнале стоит запись «может стать проблемой» с идентификатором записи; карточка записи отдаёт рубеж `done` с пустым перечнем доступных видов текста |
|
||||
| ↳ *остановка сервиса* | Принятие операции от отмены защищено своим пределом в 10 секунд: операцию там могли принять и начать считать деньги, а потерянный идентификатор заставил бы повтор оплатить ту же запись второй раз. Заливка в Object Storage отменяется штатно — её повтор бесплатен, объект ложится под тем же ключом | — | — | — |
|
||||
| Yandex Object Storage | Заливка падает, запись остаётся на рубеже `normalized` | То же, что падение: висит до конца захвата | — | SpeechKit не прочитает объект и вернёт отказ операции |
|
||||
| ffmpeg, ffprobe | Запись останавливается признаком с текстом «сбой конвертации файла» — рубеж при этом сохраняется, и снятие признака продолжает с него. Остановка сервиса — исход другой: процесс убивают контекстом, запись остаётся на повтор и отказа не тратит | Конвейер стоит: вызов синхронный | — | Выходной файл пуст, отказ вылезет на распознавании |
|
||||
| Хранилище (файл на диске) | Приложение не стартует либо шаг падает на каждом запросе | Блокировка записи держит воркеры | — | — |
|
||||
| Диск | Запись файла падает, задача не заводится | — | — | — |
|
||||
|
||||
- **Кто заметит отказ и когда:** тот, кто загрузил запись, — опросом готовности:
|
||||
остановленная запись отдаёт признак остановки. Владелец — по метрике
|
||||
- **Кто заметит отказ и когда:** тот, кто загрузил запись, — карточкой записи:
|
||||
остановленная запись отдаёт признак остановки и её причину. Владелец — по метрике
|
||||
`transcriber_worker_job_count` с меткой `error="true"`, и метка `stage`
|
||||
называет рубеж, с которого запись взята: с появлением пула одинаковых воркеров
|
||||
имя потока перестало что-либо значить, а разрез по шагу — единственное, чем
|
||||
@@ -177,11 +184,15 @@
|
||||
| Чтение времени | `internal/clock` — `Now` даёт метку в UTC, `Start` — начало измерения длительности; `time.Now` вне пакета запрещён правилом линтера |
|
||||
| Метрики | `internal/metrics`, префикс имени `transcriber_` |
|
||||
| Значения метки формата | `internal/metrics.FormatLabel` — приводит расширение к закрытому перечню, прочее заменяет на `other`; нормирует спека `intake` |
|
||||
| Отображение доменной ошибки в ответ | `internal/controller/http.mapDomainError` — код, машиночитаемый код отказа и сообщение человеку; ветвь по умолчанию определена, новая ветвь заводится добавлением сюда. Отказы, рождённые слоями библиотеки (предел тела, ограничитель частоты, неизвестный путь), к той же форме приводит слой `OneErrorForm`, стоящий снаружи всех прочих |
|
||||
| Состояния отбора списка | `internal/entity.ListFilter` вместе с `WorkingStages` и `TerminalStages` — предикаты выводятся из дескриптора рубежа, а не пишутся строкой запроса |
|
||||
| Уборка имени файла отправителя | `internal/entity.SanitizeOriginalFilename` — режет по пределу и убирает управляющие знаки; зовёт её приём |
|
||||
|
||||
Единых точек, которых **нет** и которые ожидались бы: идентификаторы
|
||||
генерируются вызовом `uuid.NewString()` по месту, отображения доменной ошибки в
|
||||
код HTTP-ответа нет — обработчик решает сам. Время из этого перечня ушло
|
||||
2026-08-13: его читает `internal/clock`, и запрет держит линтер.
|
||||
генерируются вызовом `uuid.NewString()` по месту. Время из этого перечня ушло
|
||||
2026-08-13: его читает `internal/clock`, и запрет держит линтер; отображение
|
||||
доменной ошибки — 2026-08-15 задачей `json-api-for-spa`, и до неё обработчик
|
||||
решал сам: опрос отвечал `404` на упавшую базу, а приём — `500` на негодный файл.
|
||||
|
||||
## Деплой
|
||||
|
||||
@@ -214,7 +225,7 @@
|
||||
входит в гейт и слоем в сборку образа. Пишет это `spa-skeleton`; во что
|
||||
обходится слой Node в образе, не замерялось. Не решено, брать ли готовый набор
|
||||
компонентов.
|
||||
- **Уведомления.** Пользователь веба узнаёт о готовности только опросом.
|
||||
- **Уведомления.** Пользователь веба узнаёт о готовности только опросом карточки.
|
||||
Доставку решено брать внешнюю — apprise как отправитель, ntfy как канал; Web
|
||||
Push с VAPID отвергнут. Появляется внешняя зависимость, которой сегодня нет, и
|
||||
текст расшифровки начинает уходить на сторону — сдвиг периметра
|
||||
|
||||
Reference in New Issue
Block a user