ci / api / api (push) Successful in 48s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 58s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m17s
ci / ts / ts (packages + web) (push) Successful in 2m1s
ci / db / db (push) Successful in 1m4s
GET /app/clients now returns up to 8 Gitea releases per OS so users can download older installers while they still exist after prune. Co-authored-by: Cursor <cursoragent@cursor.com>
13 KiB
13 KiB
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} |
Реакция на исполнителя |
| GET/POST | /me/playlists |
Сводки личных плейлистов; создание {name, description, track_ids} |
| GET/PUT/DELETE | /me/playlists/{id} |
Метаданные и треки; атомарная замена с revision; удаление. Подробно — playlists.md |
Моя волна — 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/clients |
{version, version_code, notes_md, clients:[{client, filename, size, sha256?, download_url, older:[{version, filename, size, download_url}]}]} — до 8 релизов на платформу |
| GET | /app/download/{client}/{version} |
файл клиента этой версии (прокси / кэш релиза Gitea) |
| 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 |
| POST | /admin/users/{id}/reset-recommendations → 204: рекомендации с нуля (профиль, статистика, показы волны) |
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} |
автор | Докачка (HEAD/PATCH, ядро tus 1.0.0) |
| 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) и тем, что сервисы не публикуют порты наружу.