Initial commit: Edu Helper (Docker, React, Express, Prisma)

Made-with: Cursor
This commit is contained in:
Константин Лебединский
2026-04-01 21:40:51 +05:00
commit 3dec3ea720
75 changed files with 13806 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import { API_BASE, apiFetch } from "@/lib/utils";
export type UserRole = "TUTOR" | "STUDENT";
export interface AuthUser {
id: number;
username: string;
role: UserRole;
displayName: string | null;
}
interface AuthState {
user: AuthUser | null;
loading: boolean;
refresh: () => Promise<void>;
logout: () => Promise<void>;
setUser: (u: AuthUser | null) => void;
}
const AuthContext = createContext<AuthState | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<AuthUser | null>(null);
const [loading, setLoading] = useState(true);
const refresh = useCallback(async () => {
try {
const data = await apiFetch<{ user: AuthUser }>("/auth/me", { skipAuthRedirect: true });
setUser(data.user);
} catch {
setUser(null);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
refresh();
}, [refresh]);
const logout = useCallback(async () => {
await fetch(`${API_BASE}/auth/logout`, { method: "POST", credentials: "include" });
setUser(null);
window.location.assign("/login");
}, []);
const value = useMemo(
() => ({ user, loading, refresh, logout, setUser }),
[user, loading, refresh, logout]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth(): AuthState {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth must be used within AuthProvider");
return ctx;
}