Files
TermorServer/apps/web/src/lib/api.ts

202 lines
4.3 KiB
TypeScript

import { useSessionStore } from '@/stores/session-store'
export type User = {
id: string
username: string
isAdmin: boolean
}
export type Artist = {
id: string
name: string
albumCount: number
coverArtId: string
}
export type Album = {
id: string
artistId: string
artistName: string
title: string
year: number
trackCount: number
coverArtId: string
}
export type Track = {
id: string
albumId: string
artistId: string
title: string
artistName: string
albumTitle: string
trackNumber: number
durationSeconds: number
coverArtId?: string
}
export type ArtistDetail = Artist & {
albums: Album[]
}
export type AlbumDetail = Album & {
tracks: Track[]
}
export type SearchResults = {
artists: Artist[]
albums: Album[]
tracks: Track[]
}
export type ScanStatus = {
scanning: boolean
startedAt?: string
finishedAt?: string
lastError?: string
artists: number
albums: number
tracks: number
}
export type HomePayload = {
recentAlbums: Album[]
artists: Artist[]
}
export type PlaylistSummary = {
id: string
name: string
comment: string
public: boolean
songCount: number
durationSeconds: number
createdAt: string
updatedAt: string
}
export type PlaylistDetail = PlaylistSummary & {
tracks: Track[]
}
const API_BASE = import.meta.env.VITE_API_BASE ?? 'http://localhost:4040'
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const token = useSessionStore.getState().token
const response = await fetch(`${API_BASE}${path}`, {
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...(init?.headers ?? {}),
},
...init,
})
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`)
}
if (response.status === 204) {
return undefined as T
}
return response.json() as Promise<T>
}
export async function login(username: string, password: string) {
return request<{ token: string; user: User }>('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
})
}
export async function fetchHome() {
return request<HomePayload>('/api/home')
}
export async function fetchArtists() {
return request<{ items: Artist[] }>('/api/artists')
}
export async function fetchArtist(id: string) {
return request<ArtistDetail>(`/api/artists/${id}`)
}
export async function fetchAlbums() {
return request<{ items: Album[] }>('/api/albums')
}
export async function fetchAlbum(id: string) {
return request<AlbumDetail>(`/api/albums/${id}`)
}
export async function fetchTracks() {
return request<{ items: Track[] }>('/api/tracks')
}
export async function fetchTrack(id: string) {
return request<Track>(`/api/tracks/${id}`)
}
export async function searchLibrary(query: string) {
return request<SearchResults>(`/api/search?q=${encodeURIComponent(query)}`)
}
export async function fetchScanStatus() {
return request<ScanStatus>('/api/admin/scan-status')
}
export async function triggerScan() {
return request<ScanStatus>('/api/admin/scan', { method: 'POST' })
}
export async function fetchPlaylists() {
return request<{ items: PlaylistSummary[] }>('/api/playlists')
}
export async function fetchPlaylist(id: string) {
return request<PlaylistDetail>(`/api/playlists/${id}`)
}
export async function createPlaylist(input: {
name: string
comment?: string
public?: boolean
trackIds?: string[]
}) {
return request<PlaylistDetail>('/api/playlists', {
method: 'POST',
body: JSON.stringify(input),
})
}
export async function updatePlaylist(
id: string,
input: {
name?: string
comment?: string
public?: boolean
addTrackIds?: string[]
removeTrackIds?: string[]
},
) {
return request<PlaylistDetail>(`/api/playlists/${id}`, {
method: 'PATCH',
body: JSON.stringify(input),
})
}
export async function deletePlaylist(id: string) {
await request<void>(`/api/playlists/${id}`, { method: 'DELETE' })
}
export function coverArtUrl(id: string) {
const token = useSessionStore.getState().token
return `${API_BASE}/api/cover-art/${id}${token ? `?token=${encodeURIComponent(token)}` : ''}`
}
export function streamUrl(id: string) {
const token = useSessionStore.getState().token
return `${API_BASE}/api/stream/${id}${token ? `?token=${encodeURIComponent(token)}` : ''}`
}