docs: решено перевести хранилище и файлы записей на PocketBase

- разведка pocketbase-admin-fit ответила замером панели версии 0.39.10:
  записка в docs/research/pocketbase.md, решение — в ADR
- панель показывает файлы и пользователей только своих, поэтому файлы
  переезжают в её раскладку, а вход идёт через её провайдера OIDC
- периметр расширился панелью на /_/ и паролем суперпользователя;
  закрывает её Authelia на прокси, задачи в беклоге у этого нет
This commit is contained in:
av
2026-08-11 13:03:38 +03:00
parent ba7b4f37a6
commit 10ffe8bec3
10 changed files with 299 additions and 34 deletions
+2 -2
View File
@@ -25,8 +25,8 @@
- [🧹 Задать таймауты обращениям к внешним сервисам](items/external-call-timeouts.md) — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата.
- [🔬 Админка PocketBase: данные, пользователи и файлы](items/pocketbase-admin-fit.md) — Переход на PocketBase решён ради его панели администратора, а что она даёт по данным, пользователям и файлам на диске — не проверено.
- [🔬 Очередь задач: своя таблица или готовая библиотека](items/job-queue-choice.md) — Очередь написана вручную: захват двумя запросами без транзакции, протухание временем, опрос раз в секунду вхолостую тремя воркерами.
- [🧹 Перевести хранилище на встроенный PocketBase](items/pocketbase-storage.md) — Из трёх доводов за перевод остался один: учётные записи ушли к Authelia, страницу статистики рисует само приложение, а панель администратора не проверена.
- [✨ Пускать в приложение только после входа через OIDC](items/oidc-login.md) — HTTP API открыт наружу без аутентификации: любой из интернета заводит задачи за наши деньги и читает чужие расшифровки по идентификатору.
- [🧹 Перевести хранилище и файлы записей на встроенный PocketBase](items/pocketbase-storage.md) — Записи, метаданные и файлы лежат порознь, и владелец не видит их ничем: панель PocketBase покажет и то и другое, только если они переедут к ней.
- [✨ Пускать в приложение только после входа через OIDC](items/oidc-login.md) — HTTP API открыт наружу без аутентификации: любой из интернета заводит задачи за наши деньги и читает чужие расшифровки по идентификатору. Ответ провайдера разбирает PocketBase, а не наш код.
- [✨ Привязать запись к владельцу и отдавать только свои](items/record-ownership.md) — У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать.
- [✨ Сопоставить пользователя Telegram с учётной записью](items/telegram-account-link.md) — Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны.
- [✨ Свести приём и чтение записей к одному контракту для приложения](items/json-api-for-spa.md) — Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем.
+16 -4
View File
@@ -2,7 +2,7 @@
- **Тип:** feature
- **Категория:** Очередь
- **Зачем:** HTTP API открыт наружу без аутентификации: любой из интернета заводит задачи за наши деньги и читает чужие расшифровки по идентификатору.
- **Зачем:** HTTP API открыт наружу без аутентификации: любой из интернета заводит задачи за наши деньги и читает чужие расшифровки по идентификатору. Ответ провайдера разбирает PocketBase, а не наш код.
- **Теги:** goal:multi-user, question
Двигает пункт 1 «Завершения» цели: неаутентифицированный запрос к записям не
@@ -13,12 +13,22 @@
здесь ещё нет: после входа видно всё, что видно сейчас, — этим занимается
`record-ownership`.
**Ответ провайдера разбирает PocketBase, а не наш код** — решено 2026-08-11
([adr](../../docs/adr/ADR-2026-08-11-pocketbase-storage-with-admin-panel.md)).
У её коллекции пользователей настраивается провайдер `oidc` с адресами Authelia,
и учётные записи заводятся сами; проверено на версии 0.39.10,
[docs/research/pocketbase.md](../../docs/research/pocketbase.md). Отсюда порядок:
задача идёт после `pocketbase-storage`, до неё настраивать нечего.
## Затрагивает
- новые эндпоинты входа и выхода, обработка ответа провайдера;
- настройка провайдера `oidc` у коллекции пользователей PocketBase: адреса
Authelia, идентификатор клиента, секрет, соответствие полей учётной записи;
- эндпоинты входа и выхода, которые PocketBase приносит своими;
- `POST /api/audio` и `GET /api/status/:id` — оба уходят за аутентификацию;
- `GET /health` и `GET /metrics` — решить и записать, остаются ли открытыми;
- хранение сессии: таблица или подписанная кука;
- хранение сессии: её ведёт PocketBase, и решить надо, чем она предъявляется
приложению;
- секция конфигурации под провайдера: адрес, идентификатор клиента, секрет;
- `docs/security.md` — периметр меняется, и первая его строка перестаёт быть
верной;
@@ -42,7 +52,9 @@
Владельца у записи здесь не заводим и выборку не сужаем: после входа видно
столько же, сколько сейчас. Инвариант «бот отвечает только тем, кто в белом
списке» не трогаем — он живёт до `telegram-account-link`.
списке» не трогаем — он живёт до `telegram-account-link`. Панель администратора
тоже не трогаем: наружу её закрывает Authelia на обратном прокси, а это работа
выкладки.
## Вопросы
+30 -13
View File
@@ -1,18 +1,21 @@
# 🧹 Перевести хранилище на встроенный PocketBase
# 🧹 Перевести хранилище и файлы записей на встроенный PocketBase
- **Тип:** chore
- **Категория:** Очередь
- **Зачем:** Из трёх доводов за перевод остался один: учётные записи ушли к Authelia, страницу статистики рисует само приложение, а панель администратора не проверена.
- **Зачем:** Записи, метаданные и файлы лежат порознь, и владелец не видит их ничем: панель PocketBase покажет и то и другое, только если они переедут к ней.
PocketBase встраивается библиотекой в тот же бинарник и приносит хранилище,
учётные записи и панель администратора разом. Наблюдаемое поведение сервиса
после перевода не меняется: те же два входа, тот же конвейер, тот же текст на
выходе.
учётные записи и панель администратора разом. Конвейер и оба входа работают
по-прежнему, а снаружи прибавляется панель по адресу `/_/`.
Из трёх доводов 2026-08-11 осталось полтора: учётные записи заводит Authelia по
OIDC, страницу статистики рисует само приложение. Довод про панель
администратора проверяет разведка `pocketbase-admin-fit`, и её ответ решает,
берётся эта задача или уходит в `REJECTED.md`.
Разведка `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`.
Данные не переносим — база заводится с чистого листа, и это решение принято
сознательно.
@@ -24,10 +27,20 @@ OIDC, страницу статистики рисует само приложе
- `internal/adapter/repo/sqlite` целиком, включая захват задачи через
`FindAndAcquire`;
- `internal/contract`, интерфейсы `FileRepository` и `TranscriptJobRepository`;
- ключ конфигурации `[database] path` и раскладка каталога `data/`;
- ключ конфигурации `[database] path`, ключ `[storage] path` и раскладка
каталога `data/`;
- **формат файла на диске:** запись переезжает в поле коллекции, путь становится
`pb_data/storage/<коллекция>/<запись>/<имя>_<10 случайных символов>` рядом с
файлом атрибутов. Имя, данное отправителем, впервые попадает в путь целиком;
- отдача файла: вместо чтения с диска — ссылка `/api/files/...`, и решение,
помечать ли поле защищённым;
- сборка образа: PocketBase тянет свой набор зависимостей, а `mattn/go-sqlite3`
с его требованием CGO может уйти;
- `docs/database.md` — схема, представление данных и таблица настроек.
с его требованием CGO уходит — библиотека ходит в SQLite через
`modernc.org/sqlite`;
- пароль суперпользователя панели: где заводится и как попадает на сервер;
- `docs/database.md` — схема, представление данных и таблица настроек;
- `CLAUDE.md` — строка стека про CGO и раздел про запреты с путями;
- `docs/security.md` — раздел «Из чего строятся пути и ключи».
## Критерии приёмки
@@ -39,6 +52,9 @@ OIDC, страницу статистики рисует само приложе
получает запись.
- Задача, брошенная на середине, достаётся снова по истечении срока захвата.
Оракул — тест с проставленным задним числом `acquire_time`.
- Принятая запись видна в панели строкой и скачивается из неё файлом. Оракул —
прогон записи через `POST /api/audio` на пустом каталоге, затем запрос
`/api/files/...` за тем же файлом: длина совпадает с загруженной.
- `docs/database.md` описывает новую схему, а старые упоминания goose и goqu из
документов канона убраны. Оракул — `task gate`, шаг `docs.py check`.
@@ -46,4 +62,5 @@ OIDC, страницу статистики рисует само приложе
Данные прежней базы не переносим и не пытаемся сохранить; выкладку не запускаем;
смена формата хранения на сервере необратима, и момент перехода назначает
человек.
человек. Вход не трогаем — он на `oidc-login`. Панель наружу закрывает Authelia
на обратном прокси: это работа выкладки, а не приложения.