Files
tgex-frontend/components/auth/auth-provider.tsx
2025-11-10 12:07:24 +03:00

155 lines
3.7 KiB
TypeScript

"use client";
// ============================================================================
// Auth Provider
// ============================================================================
import React, {
createContext,
useContext,
useState,
useEffect,
useCallback,
} from "react";
import { useRouter } from "next/navigation";
import { authApi } from "@/lib/api";
import { USE_MOCKS } from "@/lib/utils/constants";
import type { User } from "@/lib/types/api";
interface AuthContextType {
user: User | null;
loading: boolean;
error: string | null;
login: (token: string) => Promise<void>;
loginMock: () => Promise<void>;
logout: () => void;
refreshUser: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
};
interface AuthProviderProps {
children: React.ReactNode;
}
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const router = useRouter();
// Initialize auth state on mount
useEffect(() => {
const initAuth = async () => {
try {
if (!authApi.isAuthenticated()) {
setLoading(false);
return;
}
// Try to get user from storage first
const storedUser = authApi.getUserFromStorage();
if (storedUser) {
setUser(storedUser);
setLoading(false);
return;
}
// If no stored user, fetch from API
const userData = await authApi.me();
setUser(userData);
} catch (err: any) {
console.error("Auth initialization error:", err);
// Clear invalid tokens
authApi.logout();
} finally {
setLoading(false);
}
};
initAuth();
}, []);
// Login with token from Telegram bot
const login = useCallback(
async (token: string) => {
try {
setLoading(true);
setError(null);
const response = await authApi.complete(token);
setUser(response.user);
// Redirect to home
router.push("/");
} catch (err: any) {
const errorMessage = err?.error?.message || "Ошибка авторизации";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
},
[router]
);
// Mock login (for development without backend)
const loginMock = useCallback(async () => {
if (!USE_MOCKS) {
throw new Error("Mock login is only available in mock mode");
}
try {
setLoading(true);
setError(null);
// Use mock token
await login("mock_token_123");
} catch (err: any) {
const errorMessage = err?.error?.message || "Ошибка мок-авторизации";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
}, [login]);
// Logout
const logout = useCallback(() => {
authApi.logout();
setUser(null);
router.push("/login");
}, [router]);
// Refresh user data
const refreshUser = useCallback(async () => {
try {
const userData = await authApi.me();
setUser(userData);
} catch (err) {
console.error("Error refreshing user:", err);
throw err;
}
}, []);
const value: AuthContextType = {
user,
loading,
error,
login,
loginMock,
logout,
refreshUser,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};