Files
pipu-music/docs/api.md
T
mrleo1nidandClaude Opus 5.5 ca106e8eb1
ci / api / api (push) Successful in 46s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m3s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m22s
ci / ts / ts (packages + web) (push) Successful in 1m38s
feat(api): upload queue with quota, tus intake, processing and import (M5.5)
- Migration 00008: upload_batches, upload_files, upload_quota.
- Quota reserved under a row lock when a batch is created; every status
  change goes through one transition that frees space in the same
  transaction. SHA-256 duplicates are rejected before any upload.
- tus 1.0.0 core (HEAD/PATCH) for the author only; offset is the real
  staging file size, data beyond the declared size is refused (ADR-015).
- Processing worker: ffprobe checks, SHA-256 verification, exact and
  Chromaprint duplicates against library and queue, metadata from tags
  or the dropped folder path, possible_duplicate on metadata match, and
  the target path from write_template reusing existing folder spelling.
- Import worker: edited tags written with ffmpeg stream copy, file
  written by storage-agent, point scan of the new file, imported status;
  idempotent and resumed after restart. Stale uploads expire after 24 h,
  orphan staging files are removed at startup.
- API: quota, precheck, batches, my uploads, withdraw, admin list,
  metadata edit, bulk review, retry and queue settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:23:05 +03:00

12 KiB
Raw Blame History

REST API

Источник истины — api/openapi.yaml (OpenAPI 3.0.3 — лучше всего поддерживается oapi-codegen). Этот документ — обзор и соглашения.

Соглашения

  • Базовый путь: /api/v1. Формат — JSON, snake_case.
  • Аутентификация: Authorization: Bearer <access_jwt> (кроме /auth/login, /auth/refresh, /healthz, /stream/* и /covers/* — у них подписанные ссылки).
  • Пагинация — курсорная: ?limit=50&cursor=<opaque> → { "items": [...], "next_cursor": "..." | null }.
  • Ошибки — application/problem+json (RFC 9457):
    { "type": "about:blank", "status": 422, "code": "validation.password_too_short",
      "detail": "password must be at least 8 characters", "params": { "min": 8 },
      "errors": [{ "field": "new_password", "code": "validation.password_too_short" }] }
    
    Клиент показывает перевод code (с подстановкой params), detail — только для логов.
  • Мутирующие запросы с клиента могут нести Idempotency-Key (UUID); повтор в течение 24 ч вернёт тот же ответ.
  • Время — ISO 8601 UTC. Длительности — миллисекунды (*_ms).
  • Rate limit: 429 с Retry-After. /auth/login — 5 попыток / мин на IP+логин.

Основные DTO

// TrackDto
{ "id": "…", "title": "…", "duration_ms": 215000,
  "artists": [{ "id": "…", "name": "…" }],
  "album": { "id": "…", "title": "…", "year": 2019, "cover_url": "/api/v1/covers/…?sig=…",
             "cover_colors": ["#F2A23A", "#1C3B6E", "#35D3FF"] },
  "genres": ["indie rock"],
  "reaction": "like" | "dislike" | null,
  "stream_url": "/api/v1/stream/…?q=auto&exp=…&sig=…",
  "features": { "bpm": 128, "energy": 0.72 }   // для анимации волны, опционально
}

Эндпоинты

Служебные

Метод Путь Описание
GET /healthz, /readyz Живость / готовность
GET /api/v1/server-info Версия сервера, мин. версия клиента, поддерживаемые локали, bootstrap_required

Аутентификация — auth-and-roles.md

Метод Путь Кто Описание
POST /auth/login все {username, password, client_type, device_name} → {access_token, expires_in, refresh_token?, user}; веб получает refresh в cookie
POST /auth/refresh все Ротация refresh → новая пара
POST /auth/logout auth Отзыв текущей семьи токенов
POST /auth/logout-all auth Отзыв всех сессий пользователя
GET /me auth Текущий пользователь, флаги must_change_*
PATCH /me auth display_name, locale, ui_prefs (тема, вид волны и плеера, ambient, кроссфейд, нормализация)
POST /me/password auth {current_password, new_password}; отзывает прочие сессии
POST /me/bootstrap-credentials admin c must_change_credentials {new_username, new_password}
GET /me/sessions · DELETE /me/sessions/{id} auth Активные устройства

Каталог

Метод Путь Описание
GET /artists/{id} Исполнитель + счётчики
GET /artists/{id}/albums Альбомы
GET /artists/{id}/top-tracks Популярные (по прослушиваниям всех пользователей)
GET /albums/{id} Альбом с треками
GET /tracks/{id} Трек
POST /tracks/batch {ids:[]} → треки (для восстановления очереди)
GET `/covers/{albumId}?size=64 300

Поиск — library-and-storage.md

Метод Путь Описание
GET /search/suggest?q= Выпадающий список: до 3 исполнителей, 3 альбомов, 5 треков; < 50 мс
GET `/search?q=&type=all artists
POST /search/click {q, entity_type, entity_id} — сигнал для ранжирования / рекомендаций

Воспроизведение — playback.md

Метод Путь Описание
GET `/stream/{trackId}?q=auto original
GET /tracks/{id}/download-url?q= Подписанная ссылка для офлайн-загрузки
GET /me/playback-state Последний трек, позиция, очередь
PUT /me/playback-state Сохранить (клиент шлёт раз в 10 с и на паузе / закрытии; If-Unmodified-Since для конфликтов)
POST /events Пачка событий {events:[{id, type, track_id, occurred_at, ...}]} до 500 шт. → {accepted, duplicates}

Коллекция

Метод Путь Описание
GET /me/history?cursor= История (сгруппирована по дням на клиенте)
DELETE /me/history Очистить историю (события остаются для статистики, помечаются скрытыми)
GET /me/likes?cursor= · /me/dislikes?cursor= Списки
PUT /me/reactions/tracks/{id} `{reaction: "like"
DELETE /me/reactions/tracks/{id} Снять реакцию
PUT/DELETE /me/reactions/artists/{id} Реакция на исполнителя

Моя волна — recommendations.md

Метод Путь Описание
GET /wave/options Доступные занятия / настроения (ключи для i18n, иконки)
POST /wave/sessions {activity?, mood?, seed?: {type, id}} → {session_id, tracks:[5 TrackDto]}
POST /wave/sessions/{id}/next {feedback:[{track_id, outcome}]} → следующая пачка
PATCH /wave/sessions/{id} Сменить занятие / настроение на лету
DELETE /wave/sessions/{id} Завершить

Мобильное обновление — mobile.md

Метод Путь Описание
GET /app/android/latest {version, version_code, notes_md, size, sha256, download_url, min_supported}
GET /app/android/download/{version} APK (прокси / кэш релиза Gitea)

Админка — права см. auth-and-roles.md

Метод Путь Кто
GET /admin/users?q=&role=&cursor= admin, moderator
POST /admin/users {username, display_name, role} → {user, temporary_password} (пароль генерирует сервер, must_change_password = true) moderator: только role=user; admin: любые
PATCH /admin/users/{id} {display_name?, role?, is_blocked?} role — только admin
POST /admin/users/{id}/reset-password → {temporary_password} moderator — только для user
DELETE /admin/users/{id} admin
GET/POST /admin/sources admin
GET/PATCH/DELETE /admin/sources/{id} admin
POST /admin/sources/{id}/test admin — проверить монтирование и доступ
PUT /admin/sources/{id}/patterns {patterns:[{regex, is_enabled}]} admin
POST /admin/sources/{id}/patterns/preview {patterns, sample?:50} admin — разбор реальных путей без сохранения
POST /admin/sources/{id}/scan {full?: bool} admin
GET /admin/library/status admin, moderator — счётчики, прогресс скана / анализа, ошибки
GET /admin/library/errors?cursor= admin
POST /admin/search/reindex admin
GET /admin/recommender/status admin — версии моделей, метрики
GET /admin/settings · PATCH /admin/settings admin — лимиты очереди, хранилище по умолчанию и пр.
POST /admin/sources/{id}/write-template/suggest → {template, matched_ratio} admin — автоподбор шаблона раскладки
POST /admin/sources/{id}/write-template/verify {template} → {ok, roundtrip_ratio, mismatches[]} admin — проверка обратимости

Предложение треков — uploads.md

Метод Путь Кто Описание
GET /uploads/quota moderator, admin {limit_bytes, used_bytes, per_user_limit_bytes, user_used_bytes, max_file_bytes}
POST /uploads/precheck moderator, admin {files:[{client_id, size, sha256, tags:{artist,title,album,duration_ms}}]} → для каждого accepted / duplicate (+ track) / too_large; ничего не резервирует
POST /uploads moderator, admin Создать пачку {comment, files:[{client_id, name, rel_path, size, sha256}]} → резерв квоты; `{batch_id, files:[{client_id, upload_file_id, tus_url}
tus /uploads/files/{upload_file_id} автор Resumable-загрузка (HEAD/PATCH, ядро tus 1.0.0 — ADR-015)
GET /uploads/mine?status=&cursor= moderator, admin Мои предложения (status — через запятую)
DELETE /uploads/files/{id} автор Отозвать (withdrawn), только до решения
GET /admin/uploads?status=&author=&possible_duplicate=&cursor= admin Очередь, сгруппированная по пачкам
GET /admin/uploads/files/{id}/stream?sig= admin Прослушивание из staging
PATCH /admin/uploads/files/{id} admin Правка metadata, target_source_id; ответ содержит пересчитанный target_rel_path
PATCH /admin/uploads/batches/{id} admin Правка общих полей для всех файлов пачки (исполнитель, альбом, год, обложка)
POST /admin/uploads/review admin `{file_ids:[], decision: "approve"
POST /admin/uploads/files/{id}/retry admin Повторить импорт после import_failed
GET · PATCH /admin/settings/uploads admin Лимиты очереди и хранилище по умолчанию
GET /admin/audit?cursor= admin

Внутренние API

api → recommender (internal-recommender.yaml):

  • POST /internal/wave/batch {user_id, session_id, activity, mood, seed, exclude_ids, feedback, n} → {tracks:[{id, source, score}], model_ver}
  • GET /internal/similar/tracks/{id}, GET /internal/similar/artists/{id}
  • GET /internal/status

api → storage-agent (internal-storage.yaml):

  • POST /internal/mounts {source_id, type, server, export, options} → {status, message}
  • DELETE /internal/mounts/{source_id}
  • GET /internal/mounts — состояние всех точек
  • POST /internal/files {source_id, rel_path, staging_path, sha256} — безопасная запись файла в хранилище (tmp → fsync → проверка хэша → atomic rename); только для accepts_uploads источников
  • GET /internal/files/exists?source_id=&rel_path= — проверка конфликта пути

Внутренние вызовы защищены общим секретом (INTERNAL_TOKEN) и тем, что сервисы не публикуют порты наружу.