feat: AuthContext, protected routes, admin page, fix VideoPlayer URL
This commit is contained in:
50
frontend/src/context/AuthContext.tsx
Normal file
50
frontend/src/context/AuthContext.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react';
|
||||
import { apiFetch } from '../lib/api';
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string | null;
|
||||
nickname: string;
|
||||
subscriptionLevel?: number;
|
||||
}
|
||||
|
||||
interface AuthContextValue {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
setUser: (u: User | null) => void;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextValue | null>(null);
|
||||
|
||||
interface MeResponse {
|
||||
success: boolean;
|
||||
data: { user: User };
|
||||
}
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
apiFetch<MeResponse>('/auth/me')
|
||||
.then((res) => { if (!cancelled) setUser(res.data.user); })
|
||||
.catch(() => { if (!cancelled) setUser(null); })
|
||||
.finally(() => { if (!cancelled) setLoading(false); });
|
||||
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, loading, setUser }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuthContext(): AuthContextValue {
|
||||
const ctx = useContext(AuthContext);
|
||||
if (!ctx) throw new Error('useAuthContext must be used inside AuthProvider');
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user