Track scanner status for the web API and Subsonic-compatible scan endpoints, add authenticated cover art serving, and wire album artwork into the web UI. Keep Subsonic auth limited to legacy password mode for now so behavior stays honest with the current bcrypt-based user storage.
76 lines
1.7 KiB
TypeScript
76 lines
1.7 KiB
TypeScript
import { useSessionStore } from '@/stores/session-store'
|
|
|
|
export type User = {
|
|
id: string
|
|
username: string
|
|
isAdmin: boolean
|
|
}
|
|
|
|
export type HomePayload = {
|
|
recentAlbums: Array<{
|
|
id: string
|
|
artistId: string
|
|
artistName: string
|
|
title: string
|
|
year: number
|
|
trackCount: number
|
|
coverArtId: string
|
|
}>
|
|
artists: Array<{
|
|
id: string
|
|
name: string
|
|
albumCount: number
|
|
}>
|
|
}
|
|
|
|
export type Track = {
|
|
id: string
|
|
albumId: string
|
|
artistId: string
|
|
title: string
|
|
artistName: string
|
|
albumTitle: string
|
|
trackNumber: number
|
|
durationSeconds: number
|
|
}
|
|
|
|
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}`)
|
|
}
|
|
|
|
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 fetchTracks() {
|
|
return request<{ items: Track[] }>('/api/tracks')
|
|
}
|
|
|
|
export function coverArtUrl(id: string) {
|
|
const token = useSessionStore.getState().token
|
|
return `${API_BASE}/api/cover-art/${id}${token ? `?token=${encodeURIComponent(token)}` : ''}`
|
|
}
|