Backend (Django 6.1 + DRF): - Token auth with a custom User model (register/login/logout/me) - Library models (MediaItem, MediaLocation) and REST endpoints - File list/detail with search, rating filter, sorting, pagination - Multipart upload with optional rating/tags/notes - Range-aware media serving (video seeking) and ffmpeg thumbnails - scan_files management command for the watched folder Frontend (React 19 + Vite + TypeScript): - Catppuccin Mocha design tokens from the design docs - App shell, token persistence, protected routes - Library grid with filters, file detail with custom data editor - Upload page with per-file progress via XHR - Dev proxy to the Django API
62 lines
1.5 KiB
TypeScript
62 lines
1.5 KiB
TypeScript
import { create } from "zustand";
|
|
|
|
import { api, getToken, setToken } from "@/lib/api";
|
|
import type { User } from "@/lib/types";
|
|
|
|
interface AuthState {
|
|
user: User | null;
|
|
ready: boolean;
|
|
login: (username: string, password: string) => Promise<void>;
|
|
register: (username: string, password: string) => Promise<void>;
|
|
logout: () => Promise<void>;
|
|
loadUser: () => Promise<void>;
|
|
}
|
|
|
|
export const useAuth = create<AuthState>((set) => ({
|
|
user: null,
|
|
ready: false,
|
|
|
|
login: async (username, password) => {
|
|
const data = await api<{ token: string }>("/api/auth/token/", {
|
|
method: "POST",
|
|
json: { username, password },
|
|
});
|
|
setToken(data.token);
|
|
const user = await api<User>("/api/auth/me/");
|
|
set({ user, ready: true });
|
|
},
|
|
|
|
register: async (username, password) => {
|
|
const data = await api<{ token: string; user: User }>("/api/auth/register/", {
|
|
method: "POST",
|
|
json: { username, password },
|
|
});
|
|
setToken(data.token);
|
|
set({ user: data.user, ready: true });
|
|
},
|
|
|
|
logout: async () => {
|
|
try {
|
|
await api("/api/auth/logout/", { method: "POST" });
|
|
} catch {
|
|
// The token may already be invalid; clearing it locally is enough.
|
|
}
|
|
setToken(null);
|
|
set({ user: null, ready: true });
|
|
},
|
|
|
|
loadUser: async () => {
|
|
if (!getToken()) {
|
|
set({ user: null, ready: true });
|
|
return;
|
|
}
|
|
try {
|
|
const user = await api<User>("/api/auth/me/");
|
|
set({ user, ready: true });
|
|
} catch {
|
|
setToken(null);
|
|
set({ user: null, ready: true });
|
|
}
|
|
},
|
|
}));
|