feat(libs): create library by name only; root_path derived server-side as BOOKS_DIR/SafeName(name), validated at creation; drop root_path input from admin UI; align changelogs to bilingual template; trim AGENTS.md to rules not derivable from repo layout
This commit is contained in:
@@ -124,8 +124,7 @@ export const api = {
|
||||
deleteUser: (id: number) => apiFetch<void>(`/users/${id}`, { method: "DELETE" }),
|
||||
|
||||
listLibraries: () => apiFetch<Library[]>("/libraries"),
|
||||
createLibrary: (name: string, root_path: string) =>
|
||||
apiFetch<Library>("/libraries", { method: "POST", body: { name, root_path } }),
|
||||
createLibrary: (name: string) => apiFetch<Library>("/libraries", { method: "POST", body: { name } }),
|
||||
scanLibrary: (id: number) => apiFetch<{ accepted: boolean }>(`/libraries/${id}/scan`, { method: "POST" }),
|
||||
uploadBook: (id: number, file: File) => {
|
||||
const fd = new FormData();
|
||||
|
||||
@@ -9,15 +9,13 @@ export default function AdminLibraries() {
|
||||
const qc = useQueryClient();
|
||||
const libsQ = useQuery({ queryKey: ["libraries"], queryFn: api.listLibraries });
|
||||
const [name, setName] = useState("");
|
||||
const [root, setRoot] = useState("");
|
||||
const fileRefs = useRef<Record<number, HTMLInputElement | null>>({});
|
||||
|
||||
const create = useMutation({
|
||||
mutationFn: () => api.createLibrary(name.trim(), root.trim()),
|
||||
mutationFn: () => api.createLibrary(name.trim()),
|
||||
onSuccess: (lib) => {
|
||||
toast("ok", `库「${lib.name}」已创建`);
|
||||
setName("");
|
||||
setRoot("");
|
||||
void qc.invalidateQueries({ queryKey: ["libraries"] });
|
||||
},
|
||||
onError: (e) => toast("err", e instanceof Error ? e.message : "建库失败"),
|
||||
@@ -54,14 +52,13 @@ export default function AdminLibraries() {
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
<h1 className="mb-4 text-lg font-semibold">库管理</h1>
|
||||
<form onSubmit={submit} className={`${card} mb-6 flex max-w-2xl flex-wrap items-center gap-3 p-4`}>
|
||||
<input className={input} placeholder="库名(如 comics)" value={name} onChange={(e) => setName(e.target.value)} />
|
||||
<input
|
||||
className={input + " flex-1"}
|
||||
placeholder="root_path(服务端绝对路径,须位于 BOOKS_DIR 下,如 /data/books/comics)"
|
||||
value={root}
|
||||
onChange={(e) => setRoot(e.target.value)}
|
||||
placeholder="库名(如 comics,服务端目录自动落在 BOOKS_DIR/<库名> 下)"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
<button className={btnPrimary} disabled={create.isPending || !name.trim() || !root.startsWith("/")}>
|
||||
<button className={btnPrimary} disabled={create.isPending || !name.trim()}>
|
||||
{create.isPending ? "创建中…" : "建库"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user