# 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 ## Установка и запуск 1. Клонируйте репозиторий 2. Установите зависимости: ```bash go mod tidy ``` 3. Скопируйте образец конфига и заполните его: ```bash cp config.example.toml config.toml ``` 4. Запустите приложение: ```bash go run ./cmd/transcriber -c config.toml ``` Сервер запустится на порту из `[server] port`, по умолчанию 8080. Нужен установленный `ffmpeg`. ### Кого пускают Кто пришёл, сервис узнаёт из заголовка `Remote-User`, который ставит обратный прокси, сходив к Authelia; своего входа у сервиса нет. Кому верить, задаёт перечень доверенных адресов в секции `[auth]`. Подробности — [docs/security.md](docs/security.md), «Что разграничивает доступ»; известные прорехи образца конфига — [docs/conventions/config.md](docs/conventions/config.md). Локально прокси нет, а браузер заголовков не ставит — заголовок подставляет сам сервис по своим настройкам. Второго процесса для этого не нужно: приложение открывают по адресу сервиса. Рецепт — три правки `config.toml` сверху вниз: 1. добавить в перечень доверенных адресов пару петлевых — `127.0.0.1` и `::1`; 2. поставить в секции `[server]` ключ `debug = true`; 3. раскомментировать секцию `[auth.test_headers]` и назвать в ней `Remote-User`. Тот же рецепт записан связным блоком в `config.example.toml`, под перечнем доверенных адресов, — там же названы принимаемые имена заголовков и цена включения. Заполненная секция имитации при выключенном предохранителе роняет старт с именем ключа: сервис с включённым предохранителем называет пришедшего сам, никого не спросив, и в бою этот ключ стоит `false`. ## Деплой Деплой запускается из `pet-project-server`: ```bash 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/access/spec.md)). Больше на этом порту не отвечает ничего: всякий прочий путь получает разметку приложения. Контракт приёма и опроса нормативен и живёт в [openspec/specs/intake/spec.md](openspec/specs/intake/spec.md): поля запроса и ответа, коды и условия. Менять его — необратимое действие ([CLAUDE.md](CLAUDE.md), «Работа»), и второго описания у него быть не должно. ## Состояния задач Перечень состояний, переходы между ними и число воркеров — [docs/database.md](docs/database.md), разделы «Таблицы» и «Представление данных»; как сложен конвейер целиком — [docs/architecture.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](docs/database.md). Панели владельца у сервиса нет: единственное его действие вне экранов — возврат остановленной записи в работу подкомандой оснастки. ```bash go run ./cmd/devtools resume -c config.toml <идентификатор записи> ``` ## Разработка Схему двигают шаги `pressly/goose/v3` — `internal/adapter/repo/sqlite/migrations`, файл на шаг, версия шага — число в начале имени файла. Непринятые шаги накатываются при старте, прежде чем поднимутся входы и стартуют воркеры; отказ шага роняет старт. Применённый шаг не переписывается: изменение — только новым файлом шага. Проверки перед коммитом — одной командой: ```bash task gate ``` Что она гоняет, чем краснеет и какой отказ считается объявленным долгом — [CLAUDE.md](CLAUDE.md), раздел «Гейт».