feat: added api conversation + api review

This commit is contained in:
Boris Cherepanov
2026-03-23 12:45:24 +03:00
parent 310f0061d3
commit 5a5dab85d0
3 changed files with 84 additions and 9 deletions

View File

@@ -0,0 +1,32 @@
---
description: REST API запросы — создавать функции в furumiApi.ts (только furumi-node-player/client)
globs: furumi-node-player/client/**/*.{ts,tsx}
alwaysApply: false
---
# REST API в furumi-node-player
**Область действия:** правило применяется только к проекту `furumi-node-player/client/`. В остальных частях репозитория можно использовать другой подход.
Чтобы выполнить REST API запрос, нужно создать функцию, которая принимает необходимые параметры и вызывает `furumiApi`. Все такие функции должны находиться в файле `furumi-node-player/client/src/furumiApi.ts`.
## Правила
1. **Не вызывать `furumiApi` напрямую** из компонентов или других модулей.
2. **Добавлять новую функцию** в `furumiApi.ts` для каждого эндпоинта.
3. Функция принимает нужные параметры и возвращает `Promise` с данными (или `null` при ошибке).
## Пример
```typescript
// furumiApi.ts — добавлять сюда
export async function getSomething(id: string) {
const res = await furumiApi.get(`/something/${id}`).catch(() => null)
return res?.data ?? null
}
```
```typescript
// FurumiPlayer.tsx — использовать функцию, не furumiApi напрямую
const data = await getSomething(id)
```

View File

@@ -1,6 +1,15 @@
import { useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'
import './furumi-player.css'
import { API_ROOT, furumiApi } from './furumiApi'
import {
API_ROOT,
getArtists,
getArtistAlbums,
getAlbumTracks,
getArtistTracks,
searchTracks,
getTrackInfo,
preloadStream,
} from './furumiApi'
import { SearchDropdown } from './components/SearchDropdown'
import { Breadcrumbs } from './components/Breadcrumbs'
import { LibraryList } from './components/LibraryList'
@@ -107,7 +116,7 @@ export function FurumiPlayer() {
setBreadcrumb([{ label: 'Artists', action: showArtists }])
setLibraryLoading(true)
setLibraryError(null)
const artists = (await furumiApi.get('/artists').catch(() => null))?.data ?? null
const artists = await getArtists()
if (!artists) {
setLibraryLoading(false)
setLibraryError('Error')
@@ -133,7 +142,7 @@ export function FurumiPlayer() {
])
setLibraryLoading(true)
setLibraryError(null)
const albums = (await furumiApi.get('/artists/' + artistSlug + '/albums').catch(() => null))?.data ?? null
const albums = await getArtistAlbums(artistSlug)
if (!albums) {
setLibraryLoading(false)
setLibraryError('Error')
@@ -182,7 +191,7 @@ export function FurumiPlayer() {
])
setLibraryLoading(true)
setLibraryError(null)
const tracks = (await furumiApi.get('/albums/' + albumSlug).catch(() => null))?.data ?? null
const tracks = await getAlbumTracks(albumSlug)
if (!tracks) {
setLibraryLoading(false)
setLibraryError('Error')
@@ -252,7 +261,7 @@ export function FurumiPlayer() {
}
async function addAlbumToQueue(albumSlug: string, playFirst?: boolean) {
const tracks = (await furumiApi.get('/albums/' + albumSlug).catch(() => null))?.data ?? null
const tracks = await getAlbumTracks(albumSlug)
if (!tracks || !(tracks as any[]).length) return
const list = tracks as any[]
let firstIdx = queue.length
@@ -272,7 +281,7 @@ export function FurumiPlayer() {
}
async function playAllArtistTracks(artistSlug: string) {
const tracks = (await furumiApi.get('/artists/' + artistSlug + '/tracks').catch(() => null))?.data ?? null
const tracks = await getArtistTracks(artistSlug)
if (!tracks || !(tracks as any[]).length) return
const list = tracks as any[]
clearQueue()
@@ -487,7 +496,7 @@ export function FurumiPlayer() {
return
}
searchTimer = window.setTimeout(async () => {
const results = (await furumiApi.get('/search?q=' + encodeURIComponent(q)).catch(() => null))?.data ?? null
const results = await searchTracks(q)
if (!results || !(results as any[]).length) {
closeSearch()
return
@@ -511,7 +520,7 @@ export function FurumiPlayer() {
{ slug, title: '', artist: '', album_slug: null, duration: null },
true,
)
void furumiApi.get('/stream/' + slug).catch(() => null)
void preloadStream(slug)
}
}
searchSelectRef.current = onSearchSelect
@@ -617,7 +626,7 @@ export function FurumiPlayer() {
const url = new URL(window.location.href)
const urlSlug = url.searchParams.get('t')
if (urlSlug) {
const info = (await furumiApi.get('/tracks/' + urlSlug).catch(() => null))?.data ?? null
const info = await getTrackInfo(urlSlug)
if (info) {
addTrackToQueue(
{

View File

@@ -10,3 +10,37 @@ export const furumiApi = axios.create({
headers: apiKey ? { 'x-api-key': apiKey } : {},
})
export async function getArtists() {
const res = await furumiApi.get('/artists').catch(() => null)
return res?.data ?? null
}
export async function getArtistAlbums(artistSlug: string) {
const res = await furumiApi.get(`/artists/${artistSlug}/albums`).catch(() => null)
return res?.data ?? null
}
export async function getAlbumTracks(albumSlug: string) {
const res = await furumiApi.get(`/albums/${albumSlug}`).catch(() => null)
return res?.data ?? null
}
export async function getArtistTracks(artistSlug: string) {
const res = await furumiApi.get(`/artists/${artistSlug}/tracks`).catch(() => null)
return res?.data ?? null
}
export async function searchTracks(query: string) {
const res = await furumiApi.get(`/search?q=${encodeURIComponent(query)}`).catch(() => null)
return res?.data ?? null
}
export async function getTrackInfo(trackSlug: string) {
const res = await furumiApi.get(`/tracks/${trackSlug}`).catch(() => null)
return res?.data ?? null
}
export async function preloadStream(trackSlug: string) {
await furumiApi.get(`/stream/${trackSlug}`).catch(() => null)
}