refactor(api): standardize songs endpoint
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s
- Backend: /api/songs returns PaginatedResponse envelope - Frontend: Updated SongsPage, AdminSongsPage, AdminSequencesPage, BandPage to consume envelope
This commit is contained in:
parent
c860075681
commit
c0e3e2a7e2
6 changed files with 24 additions and 14 deletions
|
|
@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query
|
|||
from sqlmodel import Session, select, func
|
||||
from database import get_session
|
||||
from models import Song, User, Tag, EntityTag, Show, Performance, Rating
|
||||
from schemas import SongCreate, SongRead, SongReadWithStats, SongUpdate, TagRead, PerformanceReadWithShow
|
||||
from schemas import SongCreate, SongRead, SongReadWithStats, SongUpdate, TagRead, PerformanceReadWithShow, PaginatedResponse, PaginationMeta
|
||||
from auth import get_current_user
|
||||
from services.stats import get_song_stats
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ def create_song(song: SongCreate, session: Session = Depends(get_session), curre
|
|||
session.refresh(db_song)
|
||||
return db_song
|
||||
|
||||
@router.get("/", response_model=List[SongRead])
|
||||
@router.get("/", response_model=PaginatedResponse[SongRead])
|
||||
def read_songs(
|
||||
offset: int = 0,
|
||||
limit: int = Query(default=100, le=1000),
|
||||
|
|
@ -34,13 +34,23 @@ def read_songs(
|
|||
if vertical_entity:
|
||||
query = query.where(Song.vertical_id == vertical_entity.id)
|
||||
else:
|
||||
return []
|
||||
return PaginatedResponse(data=[], meta=PaginationMeta(total=0, limit=limit, offset=offset))
|
||||
|
||||
if sort == "times_played":
|
||||
query = query.outerjoin(Performance).group_by(Song.id).order_by(func.count(Performance.id).desc())
|
||||
query = query.outerjoin(Performance).group_by(Song.id)
|
||||
|
||||
# Calculate total count before pagination
|
||||
total = session.exec(select(func.count()).select_from(query.subquery())).one()
|
||||
|
||||
if sort == "times_played":
|
||||
query = query.order_by(func.count(Performance.id).desc())
|
||||
|
||||
songs = session.exec(query.offset(offset).limit(limit)).all()
|
||||
return songs
|
||||
|
||||
return PaginatedResponse(
|
||||
data=songs,
|
||||
meta=PaginationMeta(total=total, limit=limit, offset=offset)
|
||||
)
|
||||
|
||||
@router.get("/{slug}", response_model=SongReadWithStats)
|
||||
def read_song(slug: str, session: Session = Depends(get_session)):
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ async function getTopSongs(verticalSlug: string) {
|
|||
next: { revalidate: 60 }
|
||||
})
|
||||
if (!res.ok) return []
|
||||
return res.json()
|
||||
const data = await res.json()
|
||||
return data.data || []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ async function getSongs(verticalSlug: string) {
|
|||
next: { revalidate: 60 }
|
||||
})
|
||||
if (!res.ok) return []
|
||||
return res.json()
|
||||
const data = await res.json()
|
||||
return data.data || []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ export default function AdminSequencesPage() {
|
|||
})
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setAllSongs(data.songs || data)
|
||||
setAllSongs(data.data || [])
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch songs", e)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export default function AdminSongsPage() {
|
|||
})
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setSongs(data.songs || data)
|
||||
setSongs(data.data || [])
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch songs", e)
|
||||
|
|
|
|||
|
|
@ -21,12 +21,10 @@ export default function SongsPage() {
|
|||
fetch(`${getApiUrl()}/songs/?limit=1000`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (!Array.isArray(data)) {
|
||||
console.error("API Error: Expected array but got:", data)
|
||||
return
|
||||
}
|
||||
// Handle envelope
|
||||
const songData = data.data || []
|
||||
// Sort alphabetically
|
||||
const sorted = data.sort((a: Song, b: Song) => a.title.localeCompare(b.title))
|
||||
const sorted = songData.sort((a: Song, b: Song) => a.title.localeCompare(b.title))
|
||||
setSongs(sorted)
|
||||
})
|
||||
.catch(console.error)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue