mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
7e9cb60763
Move the Subsonic + Navidrome protocol clients and the library IPC facade (subsonic*, navidrome*, library.ts + tests) from src/api/ into src/lib/api/, the feature-free infra layer (plan M4, decision §10.3: shared client core → lib/). Pure move: deep-path import specifiers @/api/* -> @/lib/api/* across ~150 consumers; no behaviour change. Domain REST (lyrics/events) and cover/analysis infra stay in src/api/ pending their own M4 placement. tsc 0, lint 0/0, full suite 319 files / 2353 tests green.
65 lines
2.3 KiB
TypeScript
65 lines
2.3 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react';
|
|
import type { TFunction } from 'i18next';
|
|
import {
|
|
ndListLibraries,
|
|
ndListUsers,
|
|
type NdLibrary,
|
|
type NdUser,
|
|
} from '@/lib/api/navidromeAdmin';
|
|
|
|
interface UseUserMgmtDataResult {
|
|
users: NdUser[];
|
|
libraries: NdLibrary[];
|
|
loading: boolean;
|
|
loadError: string | null;
|
|
load: () => Promise<void>;
|
|
}
|
|
|
|
/**
|
|
* Fetch and keep the Navidrome admin users + libraries lists in sync.
|
|
*
|
|
* The two requests are **sequential, not parallel**: some nginx setups
|
|
* with churning upstream keep-alive drop one of two parallel TLS
|
|
* connections. Doing users first then libraries keeps us on one
|
|
* connection at a time and pairs cleanly with the `nd_retry` backoff on
|
|
* the Rust side.
|
|
*
|
|
* Tauri's `invoke` rejects with a bare `string` (our Rust commands
|
|
* return `Err(String)`), so the error normalisation surfaces the real
|
|
* cause (e.g. `tls handshake eof`) instead of falling back to the
|
|
* generic i18n string.
|
|
*/
|
|
export function useUserMgmtData(serverUrl: string, token: string, t: TFunction): UseUserMgmtDataResult {
|
|
const [users, setUsers] = useState<NdUser[]>([]);
|
|
const [libraries, setLibraries] = useState<NdLibrary[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
|
|
const load = useCallback(async () => {
|
|
setLoading(true);
|
|
setLoadError(null);
|
|
try {
|
|
const list = await ndListUsers(serverUrl, token);
|
|
const libs = await ndListLibraries(serverUrl, token).catch(() => [] as NdLibrary[]);
|
|
setUsers([...list].sort((a, b) => a.userName.localeCompare(b.userName)));
|
|
setLibraries([...libs].sort((a, b) => a.name.localeCompare(b.name)));
|
|
} catch (e) {
|
|
const raw = typeof e === 'string'
|
|
? e
|
|
: (e instanceof Error && e.message)
|
|
? e.message
|
|
: '';
|
|
const prefix = t('settings.userMgmtLoadError');
|
|
setLoadError(raw ? `${prefix} ${raw}` : prefix);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [serverUrl, token, t]);
|
|
|
|
// React Compiler set-state-in-effect rule: state set from an async result resolved in this effect.
|
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
useEffect(() => { void load(); }, [load]);
|
|
|
|
return { users, libraries, loading, loadError, load };
|
|
}
|