учёт: закрыт переезд хранилища, заведён урожай его ревью
- storage-without-pocketbase закрыта как реализованная: приёмка сошлась по всем
пяти критериям записи и по двенадцати приёмочным свойствам рубрики ревью
дизайна, работа лежит коммитом c9b7765
- урожай триажа ревью развёрнут в одиннадцать записей с тегом партии
review-2026-08-23 и расставлен по зависимости, а не в конец списка
- находка про признак живости воркера слита в stalled-pipeline-metric: у неё та
же причина — вставший конвейер неотличим от простоя
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# 🧹 Отдавать файл записи через http.ServeContent
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь — Стандартная семантика диапазонов нужна проигрыванию записи на экране: браузер тянет звук именно диапазонами
|
||||
- **Зачем:** Отдача файла считает диапазоны байт своей рукой — около 110 строк семантики HTTP, которую стандартная библиотека делает сама, и на этих строках стоит проигрывание записи на экране.
|
||||
- **Теги:** review-2026-08-23
|
||||
|
||||
Отдача файла записи разбирает заголовок `Range`, считает границы, ставит
|
||||
`Content-Range` и код `206` своей рукой — около 110 строк семантики HTTP.
|
||||
`http.ServeContent` делает то же самое: диапазоны, `If-Range`, `Last-Modified`,
|
||||
`ETag`, неверный диапазон с кодом `416`.
|
||||
|
||||
Своя реализация стоит не тем, что длиннее, а тем, что расходится: браузер
|
||||
проигрывает звук именно диапазонами, и задача `play-recording-in-app` встанет
|
||||
поверх этих строк.
|
||||
|
||||
Нашёл проход `review-architecture` (вторая половина R2) ревью change
|
||||
`2026-08-23-storage-without-pocketbase`.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- `internal/controller/http/file.go` — разбор `Range`, ответ `206` и заголовки
|
||||
диапазона;
|
||||
- тип содержимого ответа: он выводится из закрытого перечня форматов и остаётся
|
||||
за нами, `ServeContent` его не выбирает;
|
||||
- открытие копии в `internal/adapter/repo/sqlite/store.go` — `ServeContent`
|
||||
требует `io.ReadSeeker` и время последнего изменения;
|
||||
- публичный контракт HTTP: коды `200`, `206`, `416` и заголовки ответа на
|
||||
запрос файла.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Диапазоны считает стандартная библиотека. **Оракул:** `grep` по
|
||||
`internal/controller/http` — ни одного разбора заголовка `Range` руками,
|
||||
вызов `http.ServeContent` один.
|
||||
- Ответы на диапазон не изменились. **Оракул:** тест контроллера — запрос без
|
||||
`Range` отдаёт `200` и всю длину, запрос `bytes=0-99` отдаёт `206` с
|
||||
`Content-Range`, запрос за пределом длины отдаёт `416`.
|
||||
- Тип содержимого по-прежнему выбирает сервис. **Оракул:** тест на записи с
|
||||
расширением `.html` — ответ несёт `application/octet-stream` и
|
||||
`attachment`, а не тип из имени.
|
||||
- Чужой файл остаётся недостижимым. **Оракул:** прежний тест владения —
|
||||
чужая и несуществующая запись отвечают байт в байт одинаково.
|
||||
|
||||
## Рамки
|
||||
|
||||
Раскладку каталога данных и имена копий не трогаем: она объявлена необратимой.
|
||||
Ответ на запрос чужого файла не меняется ни кодом, ни телом.
|
||||
Reference in New Issue
Block a user