- сняты повтор слова в записке разведки и та же строка в цитате ADR, убран термин «чекпоинт», разведена цепочка местоимений в паспорте - oidc-login: «зачем» перестало повторять тело, задача заявила пункт 3 «Завершения» цели multi-user — он не был закрыт ни одной её задачей
67 lines
5.6 KiB
Markdown
67 lines
5.6 KiB
Markdown
# 🧹 Перевести хранилище и файлы записей на встроенный PocketBase
|
||
|
||
- **Тип:** chore
|
||
- **Категория:** Очередь
|
||
- **Зачем:** Записи, метаданные и файлы лежат порознь, и владелец не видит их ничем, кроме консоли на сервере: панель PocketBase покажет и то и другое, только если они переедут к ней.
|
||
|
||
PocketBase встраивается библиотекой в тот же бинарник и приносит хранилище,
|
||
учётные записи и панель администратора разом. Конвейер и оба входа работают
|
||
по-прежнему, а снаружи прибавляется панель по адресу `/_/`.
|
||
|
||
Разведка `pocketbase-admin-fit` довод проверила, и 2026-08-11 принято решение
|
||
[перевести хранилище вместе с файлами](../../docs/adr/ADR-2026-08-11-pocketbase-storage-with-admin-panel.md):
|
||
панель показывает файлы и пользователей только тех, что лежат у неё, а половина
|
||
перевода довода не окупает. Замер — [docs/research/pocketbase.md](../../docs/research/pocketbase.md).
|
||
|
||
**Файлы переезжают в раскладку PocketBase**, а плоского каталога `data/files` с
|
||
именами-UUID не остаётся. Это необратимо, и момент перехода назначает человек.
|
||
Вход в этой задаче не трогаем: его переводит `oidc-login`.
|
||
|
||
Данные не переносим — база заводится с чистого листа, и это решение принято
|
||
сознательно.
|
||
|
||
## Затрагивает
|
||
|
||
- таблицы `files` и `transcribe_jobs`, каталог `migrations/` и весь механизм
|
||
goose;
|
||
- `internal/adapter/repo/sqlite` целиком, включая захват задачи через
|
||
`FindAndAcquire`;
|
||
- `internal/contract`, интерфейсы `FileRepository` и `TranscriptJobRepository`;
|
||
- ключ конфигурации `[database] path`, ключ `[storage] path` и раскладка
|
||
каталога `data/`;
|
||
- **формат файла на диске:** запись переезжает в поле коллекции, путь становится
|
||
`pb_data/storage/<коллекция>/<запись>/<имя>_<10 случайных символов>` рядом с
|
||
файлом атрибутов. Имя, данное отправителем, впервые попадает в путь целиком;
|
||
- отдача файла: вместо чтения с диска — ссылка `/api/files/...`, и решение,
|
||
помечать ли поле защищённым;
|
||
- сборка образа: PocketBase тянет свой набор зависимостей, а `mattn/go-sqlite3`
|
||
с его требованием CGO уходит — библиотека ходит в SQLite через
|
||
`modernc.org/sqlite`;
|
||
- пароль суперпользователя панели: где заводится и как попадает на сервер;
|
||
- `docs/database.md` — схема, представление данных и таблица настроек;
|
||
- `CLAUDE.md` — строка стека про CGO и раздел про запреты с путями;
|
||
- `docs/security.md` — раздел «Из чего строятся пути и ключи».
|
||
|
||
## Критерии приёмки
|
||
|
||
- Сервис поднимается на чистом каталоге данных, накатывает свою схему сам и
|
||
принимает запись обоими входами. Оракул — запуск на пустом `data/` и прогон
|
||
записи из Telegram и через `POST /api/audio` до состояния `done`.
|
||
- Захват задачи воркером не выдаёт одну запись двум вызывающим. Оракул — тест
|
||
на трёх параллельных вызовах захвата по одному состоянию: ровно один
|
||
получает запись.
|
||
- Задача, брошенная на середине, достаётся снова по истечении срока захвата.
|
||
Оракул — тест с проставленным задним числом `acquire_time`.
|
||
- Принятая запись видна в панели строкой и скачивается из неё файлом. Оракул —
|
||
прогон записи через `POST /api/audio` на пустом каталоге, затем запрос
|
||
`/api/files/...` за тем же файлом: длина совпадает с загруженной.
|
||
- `docs/database.md` описывает новую схему, а старые упоминания goose и goqu из
|
||
документов канона убраны. Оракул — `task gate`, шаг `docs.py check`.
|
||
|
||
## Рамки
|
||
|
||
Данные прежней базы не переносим и не пытаемся сохранить; выкладку не запускаем;
|
||
смена формата хранения на сервере необратима, и момент перехода назначает
|
||
человек. Вход не трогаем — он на `oidc-login`. Панель наружу закрывает Authelia
|
||
на обратном прокси: это работа выкладки, а не приложения.
|