- один факт — один дом: рецепт локального входа, правило чтения X-Forwarded-For, уровень строки журнала и опись опор изъятия сведены к своим домам, копии заменены ссылками - форма [auth.test_headers] выровнена по образцу конфига в семи местах; сценарии intake и archive перестали ссылаться на сессию, которой сервис не выдаёт - поправлены протухшие факты: ключ объекта строит ULID, а не UUID; сверку адреса пира зовут трое, а не двое; обзор capability access знает о задаче 2026-08-23
8.9 KiB
Transcriber Service
Сервис расшифровки аудиозаписей. Вход один — HTTP API.
Возможности
- Приём аудиофайлов через HTTP API
- Конвертация в ogg через ffmpeg
- Распознавание речи через Yandex SpeechKit
- Отслеживание статуса задач расшифровки
- Своё хранилище: SQLite для метаданных и каталог файлов записей рядом с ним; метрики Prometheus
Технологии
- Язык: Go 1.26, CGO не нужен
- HTTP: стандартная библиотека,
net/http - Распознавание: Yandex SpeechKit + Yandex Object Storage (S3)
- Конвертация: ffmpeg
- База данных: SQLite через modernc.org/sqlite, CGO не нужен
- Шаги схемы: pressly/goose/v3, библиотекой — накат при старте
- Файлы записей: свой каталог, подкаталог на запись
- Метрики: prometheus/client_golang
Установка и запуск
- Клонируйте репозиторий
- Установите зависимости:
go mod tidy - Скопируйте образец конфига и заполните его:
cp config.example.toml config.toml - Запустите приложение:
go run ./cmd/transcriber -c config.toml
Сервер запустится на порту из [server] port, по умолчанию 8080. Нужен
установленный ffmpeg.
Кого пускают
Кто пришёл, сервис узнаёт из заголовка Remote-User, который ставит обратный
прокси, сходив к Authelia; своего входа у сервиса нет. Кому верить, задаёт
перечень доверенных адресов в секции [auth]. Подробности —
docs/security.md, «Что разграничивает доступ»; известные
прорехи образца конфига — docs/conventions/config.md.
Локально прокси нет, а браузер заголовков не ставит — заголовок подставляет сам сервис по своим настройкам. Второго процесса для этого не нужно: приложение открывают по адресу сервиса.
Рецепт целиком — связным блоком в config.example.toml, под перечнем доверенных
адресов: там названы три правки, принимаемые имена заголовков и цена включения.
Заполненная секция имитации при выключенном предохранителе роняет
старт с именем ключа: сервис с включённым предохранителем называет пришедшего
сам, никого не спросив, и в бою этот ключ стоит false.
Деплой
Деплой запускается из pet-project-server:
inv pl -- transcriber
Плейбук сам зовёт task image (контракт роли app_image): образ собирается
локально и едет на сервер через docker save/load, реестр не участвует.
Локально образ можно собрать и руками — task image даст transcriber:dev.
HTTP API
Адреса приложения живут под корнем /app: POST /app/audiorecords — приём
записи, GET /app/audiorecords — страница своих записей,
GET /app/audiorecords/{id} — карточка, GET /app/audiorecords/{id}/text —
текст названного вида, GET /app/audiorecords/{id}/file — файл записи названной
копии, GET /app/me — кто пришёл, GET /app/config — пределы, которые сервис
объявляет приложению. Отдельными адресами стоят GET /metrics — метрики
Prometheus с префиксом transcriber_ — и GET /health — проверка живости.
Своего входа у сервиса нет: кто пришёл, называет заголовок обратного прокси
(access). Больше на этом порту не отвечает
ничего: всякий прочий путь получает разметку приложения.
Контракт приёма и опроса нормативен и живёт в openspec/specs/intake/spec.md: поля запроса и ответа, коды и условия. Менять его — необратимое действие (CLAUDE.md, «Работа»), и второго описания у него быть не должно.
Состояния задач
Перечень состояний, переходы между ними и число воркеров — docs/database.md, разделы «Таблицы» и «Представление данных»; как сложен конвейер целиком — docs/architecture.md.
Структура проекта
transcriber/
├── cmd/
│ ├── transcriber/ # Точка входа сервиса: конфиг, миграции, сборка зависимостей, запуск
│ └── devtools/ # Оснастка разработчика: возврат остановленной записи в работу
├── internal/
│ ├── entity/ # Модели: запись, файл, результат распознавания
│ ├── ident/ # Выдача и разбор идентификаторов строк (ULID)
│ ├── contract/ # Интерфейсы адаптеров и репозиториев, типы ошибок
│ ├── config/ # Разбор config.toml
│ ├── metrics/ # Метрики Prometheus
│ ├── service/ # Конвейер расшифровки
│ ├── controller/
│ │ ├── http/ # HTTP-обработчики
│ │ └── worker/ # Фоновые воркеры
│ └── adapter/
│ ├── converter/ffmpeg/ # Конвертация аудио
│ ├── metaviewer/ffmpeg/ # Длительность аудио
│ ├── recognizer/yandex/ # SpeechKit + Object Storage
│ └── repo/sqlite/ # Репозитории, подключение к базе, шаги схемы, каталог файлов
└── data/ # Каталог данных: база и файлы записей вместе
├── transcriber.db # База (создаётся автоматически)
├── migrate.lock # Замок наката схемы
└── records/ # Файлы записей: подкаталог на запись
Хранилище
Таблицы базы — аудиозапись и её приложения. Поля, ключи, правило времени и идентификаторов, раскладка файлов записи и механика захвата задачи воркером — docs/database.md. Панели владельца у сервиса нет: единственное его действие вне экранов — возврат остановленной записи в работу подкомандой оснастки.
go run ./cmd/devtools resume -c config.toml <идентификатор записи>
Разработка
Схему двигают шаги pressly/goose/v3 —
internal/adapter/repo/sqlite/migrations, файл на шаг, версия шага — число в
начале имени файла. Непринятые шаги накатываются при старте, прежде чем поднимутся
входы и стартуют воркеры; отказ шага роняет старт. Применённый шаг не
переписывается: изменение — только новым файлом шага.
Проверки перед коммитом — одной командой:
task gate
Что она гоняет, чем краснеет и какой отказ считается объявленным долгом — CLAUDE.md, раздел «Гейт».