From 5f1273292a67c79b2df9058751680d0981ab724b Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Wed, 12 Aug 2026 08:32:25 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D0=B0=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B0=20pocketbase-storage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tasks/BACKLOG.md | 1 - tasks/items/pocketbase-storage.md | 81 ------------------------------- 2 files changed, 82 deletions(-) delete mode 100644 tasks/items/pocketbase-storage.md diff --git a/tasks/BACKLOG.md b/tasks/BACKLOG.md index cc20c90..89afec5 100644 --- a/tasks/BACKLOG.md +++ b/tasks/BACKLOG.md @@ -22,7 +22,6 @@ - [🐞 Убирать записанный файл, когда приём отказал на середине](items/orphan-file-on-failed-intake.md) — Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками. - [🧹 Задать таймауты обращениям к внешним сервисам](items/external-call-timeouts.md) — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата. -- [🧹 Перевести хранилище и файлы записей на встроенный PocketBase](items/pocketbase-storage.md) — Записи, метаданные и файлы лежат порознь, и владелец не видит их ничем, кроме консоли на сервере: панель PocketBase покажет их, только если они переедут к ней. Заодно переписывается захват задачи: сегодня это два запроса без транзакции, а падающая всегда задача падает вечно. - [✨ Пускать в приложение только после входа через OIDC](items/oidc-login.md) — HTTP API открыт наружу без аутентификации: любой из интернета заводит задачи за наши деньги и читает чужие расшифровки по идентификатору. - [✨ Привязать запись к владельцу и отдавать только свои](items/record-ownership.md) — У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать. - [✨ Сопоставить пользователя Telegram с учётной записью](items/telegram-account-link.md) — Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны. diff --git a/tasks/items/pocketbase-storage.md b/tasks/items/pocketbase-storage.md deleted file mode 100644 index 3a3a990..0000000 --- a/tasks/items/pocketbase-storage.md +++ /dev/null @@ -1,81 +0,0 @@ -# 🧹 Перевести хранилище и файлы записей на встроенный 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`. - -Данные не переносим — база заводится с чистого листа, и это решение принято -сознательно. - -**Очередь переписывается тем же заходом.** Разведка `job-queue-choice` 2026-08-11 -[отвергла готовые библиотеки](../../docs/adr/ADR-2026-08-11-queue-as-pocketbase-collection.md): -очередь остаётся своей таблицей, но становится коллекцией PocketBase. Захват -сворачивается в один запрос с `RETURNING`, число попыток ложится колонкой, -нарастающая пауза выражается существующим `delay_time`, а исчерпавшая попытки -задача переходит в состояние «мертва» вместо `is_error = 1`. Сравнение -кандидатов — [docs/research/job-queue.md](../../docs/research/job-queue.md). - -## Затрагивает - -- таблицы `files` и `transcribe_jobs`, каталог `migrations/` и весь механизм - goose; -- `internal/adapter/repo/sqlite` целиком, включая захват задачи через - `FindAndAcquire`; -- состав колонок очереди: прибавляется число попыток, а `is_error` уступает - место состоянию «мертва» в перечне состояний задачи; -- `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` и тест с шагом, падающим на каждой - попытке: после заданного их числа задача не выдаётся, а её состояние видно - отбором. -- Принятая запись видна в панели строкой и скачивается по ссылке - `/api/files/...` тем же файлом. Оракулы — прогон записи через - `POST /api/audio` на пустом каталоге, затем поиск её строки в коллекции задач - на `/_/` по идентификатору и запрос `/api/files/...` за тем же файлом: длина - совпадает с загруженной. -- `docs/database.md` описывает новую схему, а старые упоминания goose и goqu из - документов канона убраны. Оракул — `task gate`, шаг `docs.py check`. - -## Рамки - -Данные прежней базы не переносим и не пытаемся сохранить; выкладку не запускаем; -смена формата хранения на сервере необратима, и момент перехода назначает -человек. Вход не трогаем — он на `oidc-login`. Панель наружу закрывает Authelia -на обратном прокси: это работа выкладки, а не приложения.