# 🧹 Отдавать файл записи через 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`, а не тип из имени.
- Чужой файл остаётся недостижимым. **Оракул:** прежний тест владения —
  чужая и несуществующая запись отвечают байт в байт одинаково.

## Рамки

Раскладку каталога данных и имена копий не трогаем: она объявлена необратимой.
Ответ на запрос чужого файла не меняется ни кодом, ни телом.
