хранилище, файлы записей и очередь переведены на встроенную PocketBase
- записи, метаданные и файлы съехались под один каталог данных; появилась панель владельца, а gin, goqu, goose и требование CGO ушли - захват задачи стал одним запросом с RETURNING; заведены число попыток, состояние dead и нарастающая пауза вместо признака is_error - имя файла в хранилище задаёт сервис и в журнал не идёт: вместе с идентификатором записи оно собирало бы ссылку на скачивание
This commit is contained in:
+33
-26
@@ -85,16 +85,27 @@ Telegram отправителю.
|
||||
Отсюда возможен выход за пределы каталога хранения — запись файла туда, куда
|
||||
путь не предполагался.
|
||||
|
||||
- **Путь на диске** — `filepath.Join(cfg.Storage.Path, fileId + ext)`, где
|
||||
`fileId` наш UUID, а **`ext` берётся из имени файла отправителя** через
|
||||
`filepath.Ext`. Расширение в путь попадает без проверки списком; `filepath.Ext`
|
||||
режет по последней точке и не пропускает разделитель каталогов, но это
|
||||
единственное, что стоит между входом и именем файла.
|
||||
- **Путь на диске** выбирает хранилище:
|
||||
`data/storage/<коллекция>/<запись>/<имя>`. **Имя задаёт сервис** —
|
||||
`<uuid><расширение>`, — а умолчание PocketBase, строящее имя из имени
|
||||
отправителя, не применяется: имя отправителя в хранилище не попадает.
|
||||
Расширение берётся из имени отправителя через `filepath.Ext` без проверки
|
||||
списком; `filepath.Ext` режет по последней точке и не пропускает разделитель
|
||||
каталогов, но это единственное, что стоит между входом и именем файла.
|
||||
- **Ключ объекта в Object Storage** — то же имя файла, то есть UUID с
|
||||
расширением. Бакет один на все записи, префикса по пользователю нет.
|
||||
- **Каталог** один и плоский: `data/files` целиком, вложенности нет.
|
||||
- **Идентификатор задачи** — UUID v4. Он же единственное, что защищает
|
||||
`GET /api/status/:id`.
|
||||
- **Ссылка на файл** — `/api/files/<коллекция>/<запись>/<имя>`. Поле файла не
|
||||
помечено защищённым, поэтому ссылка сама по себе и есть право пройти по ней, а
|
||||
отзыва у неё нет. Отсюда запрет: **имя файла в хранилище в журнал не пишется**
|
||||
— иначе строка журнала вместе с идентификатором записи собирала бы ссылку
|
||||
целиком и работала бы бессрочно. В журнал идёт расширение своим полем.
|
||||
- **Идентификатор задачи** — 15 знаков, выдаёт хранилище. Он же единственное,
|
||||
что защищает `GET /api/status/:id`.
|
||||
- **Поверхность самого хранилища.** Вместе с переводом наружу выходят
|
||||
`/api/collections/...`, `/api/logs`, `/api/backups`, `/api/settings`,
|
||||
`/api/crons` и панель `/_/`. Правила доступа коллекций оставлены пустыми, то
|
||||
есть доступны они только владельцу панели; проверено прогоном — записи отдают
|
||||
`403`, служебные разделы `401`.
|
||||
|
||||
Целевой периметр добавляет сюда три вещи, и все три — от новых задач:
|
||||
|
||||
@@ -103,15 +114,7 @@ Telegram отправителю.
|
||||
поиск отдавал бы чужую расшифровку тому, кто угадал или добыл тот же файл, и
|
||||
заодно сообщал бы, что запись у кого-то уже есть.
|
||||
- **Файлы фрагментов** (`long-audio-chunking`) ложатся рядом с исходным в тот же
|
||||
плоский каталог — раскладка `data/files` меняется, и это необратимо.
|
||||
- **Раскладку выбирает PocketBase** (`pocketbase-storage`), и плоского каталога
|
||||
не остаётся вовсе: файл ложится в
|
||||
`pb_data/storage/<коллекция>/<запись>/<имя>_<10 случайных символов>` рядом с
|
||||
файлом атрибутов. Имя, данное отправителем, в путь при этом попадает — сегодня
|
||||
от него берётся только расширение. Файл уходит не с диска напрямую, а по ссылке
|
||||
вида `/api/files/<коллекция>/<запись>/<имя>`; закрытым он становится, только если
|
||||
поле помечено защищённым, и тогда нужен отдельный файловый токен. Замер —
|
||||
[research/pocketbase.md](research/pocketbase.md).
|
||||
плоский каталог — раскладка каталога данных меняется, и это необратимо.
|
||||
- **Имя отправляемого документа** (`long-text-delivery`) собирается из
|
||||
идентификатора задачи: имя, данное пользователем, в него не попадает.
|
||||
|
||||
@@ -180,10 +183,14 @@ Telegram отправителю.
|
||||
5. **Статистика потребления** (`usage-accounting`). Текста записей не содержит,
|
||||
но говорит, кто и когда пользовался сервисом и сколько; страница расхода
|
||||
открыта только владельцу.
|
||||
6. **Пароль суперпользователя панели** (`pocketbase-storage`). Открывает все
|
||||
записи, все файлы и всех пользователей разом, то есть стоит вровень с самым
|
||||
чувствительным из списка выше. Второй секрет после токенов пользователей,
|
||||
который лежит **не в конфигурации**: его отпечаток хранит сама база.
|
||||
6. **Пароль владельца от панели.** Открывает все записи, все файлы и всех
|
||||
пользователей разом, то есть стоит вровень с самым чувствительным из списка
|
||||
выше. Второй секрет после токенов пользователей, который лежит **не в
|
||||
конфигурации**: его отпечаток хранит сама база, а задаёт пароль сам владелец
|
||||
по приглашению, которое сервис печатает в журнал при первом запуске. У
|
||||
приглашения тридцать минут жизни, и после того как владелец заведён, оно не
|
||||
печатается вовсе — иначе строка журнала отдавала бы панель всякому его
|
||||
читателю навсегда.
|
||||
|
||||
Тексты расшифровок в логи не пишутся — логируется длина текста и
|
||||
идентификаторы. Имя файла, данное отправителем, из журнала приёма убрано
|
||||
@@ -194,10 +201,10 @@ Telegram отправителю.
|
||||
инварианта приватности из [../CLAUDE.md](../CLAUDE.md), а не незакрытый остаток.
|
||||
Расширение берётся из имени отправителя дословно (`filepath.Ext`), поэтому имя
|
||||
`запись.тайное-слово` отдаёт `тайное-слово`, а `Разговор с Петровым 11.08` —
|
||||
`08`. Оно стоит в собственном имени файла на диске, а путь к файлу логируется.
|
||||
Читает этот журнал владелец сервиса. Нормализация расширения на диске — отдельная
|
||||
работа, задачи на неё пока нет: формат имени файла объявлен необратимым и меняется
|
||||
решением человека.
|
||||
`08`. В журнал оно идёт собственным полем, а не в составе имени файла: по нему
|
||||
прослеживается путь записи. Читает этот журнал владелец сервиса. Нормализация
|
||||
расширения в хранилище — отдельная работа, задачи на неё пока нет: формат имени
|
||||
файла объявлен необратимым и меняется решением человека.
|
||||
|
||||
**Наружу хвост не выходит.** Метки метрик (`file_extension` у
|
||||
`transcriber_input_file_size_bytes`, `source_format` у
|
||||
@@ -236,7 +243,7 @@ Telegram отправителю.
|
||||
замер: `research/` пуст, потолок длины стоит открытым вопросом
|
||||
`architecture.md`, «Долгие записи», — а квот нет и не будет: решено считать расход и показывать его владельцу, а не отказывать
|
||||
(цель `usage-stats`). Перебравшего останавливает разговор или отзыв доступа в
|
||||
Authelia. Рост каталога `data/files` при этом ничем не наблюдается —
|
||||
Authelia. Рост каталога данных при этом ничем не наблюдается —
|
||||
открытый вопрос `architecture.md`.
|
||||
- **Перерасход денег на внешних сервисах.** Распознавание и языковая модель
|
||||
оплачиваются по факту; потолка на пользователя нет по тому же решению.
|
||||
|
||||
Reference in New Issue
Block a user