M0: scaffold SPA + API monorepo
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
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
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 });
|
||||
}
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user