111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
'use client';
|
||
|
||
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||
import { getCurrentUser, refreshToken, User } from '@/api/auth';
|
||
|
||
interface AuthContextType {
|
||
user: User | null;
|
||
loading: boolean;
|
||
login: (token: string, userData?: User) => Promise<void>;
|
||
logout: () => void;
|
||
refreshUser: () => Promise<void>;
|
||
}
|
||
|
||
const AuthContext = createContext<AuthContextType | null>(null);
|
||
|
||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||
const [user, setUser] = useState<User | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
const loadUser = async () => {
|
||
try {
|
||
const token = typeof window !== 'undefined' ? localStorage.getItem('access_token') : null;
|
||
if (!token) {
|
||
setUser(null);
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
try {
|
||
const userData = await getCurrentUser();
|
||
setUser(userData);
|
||
} catch (firstError) {
|
||
// При 401 пробуем обновить токен и повторить загрузку (на случай, если interceptor не сработал)
|
||
const refresh = typeof window !== 'undefined' ? localStorage.getItem('refresh_token') : null;
|
||
if (refresh) {
|
||
try {
|
||
const { access } = await refreshToken(refresh);
|
||
if (access && typeof window !== 'undefined') {
|
||
localStorage.setItem('access_token', access);
|
||
const userData = await getCurrentUser();
|
||
setUser(userData);
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
} catch (_) {
|
||
// refresh не удался — выходим
|
||
}
|
||
}
|
||
console.error('[AuthContext] Error loading user:', firstError);
|
||
if (typeof window !== 'undefined') {
|
||
localStorage.removeItem('access_token');
|
||
localStorage.removeItem('refresh_token');
|
||
}
|
||
setUser(null);
|
||
}
|
||
} catch (error) {
|
||
console.error('[AuthContext] Error loading user:', error);
|
||
if (typeof window !== 'undefined') {
|
||
localStorage.removeItem('access_token');
|
||
localStorage.removeItem('refresh_token');
|
||
}
|
||
setUser(null);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
loadUser();
|
||
}, []);
|
||
|
||
const login = async (token: string, userData?: User) => {
|
||
if (typeof window !== 'undefined') {
|
||
localStorage.setItem('access_token', token);
|
||
}
|
||
// Если данные пользователя уже есть, используем их (быстрее)
|
||
if (userData) {
|
||
setUser(userData);
|
||
setLoading(false);
|
||
} else {
|
||
// Иначе загружаем с сервера
|
||
await loadUser();
|
||
}
|
||
};
|
||
|
||
const logout = () => {
|
||
if (typeof window !== 'undefined') {
|
||
localStorage.removeItem('access_token');
|
||
localStorage.removeItem('refresh_token');
|
||
}
|
||
setUser(null);
|
||
};
|
||
|
||
const refreshUser = async () => {
|
||
await loadUser();
|
||
};
|
||
|
||
return (
|
||
<AuthContext.Provider value={{ user, loading, login, logout, refreshUser }}>
|
||
{children}
|
||
</AuthContext.Provider>
|
||
);
|
||
}
|
||
|
||
export function useAuth() {
|
||
const context = useContext(AuthContext);
|
||
if (!context) {
|
||
throw new Error('useAuth must be used within AuthProvider');
|
||
}
|
||
return context;
|
||
}
|