144 lines
3.3 KiB
TypeScript
144 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
createContext,
|
|
useContext,
|
|
useState,
|
|
useEffect,
|
|
useCallback,
|
|
ReactNode,
|
|
} from "react";
|
|
import { useRouter, usePathname } from "next/navigation";
|
|
import {
|
|
AuthUser,
|
|
AuthState,
|
|
LoginCredentials,
|
|
login as authLogin,
|
|
logout as authLogout,
|
|
getAuthState,
|
|
verifyToken,
|
|
} from "@/lib/authService";
|
|
|
|
// Rutas públicas que no requieren autenticación
|
|
const PUBLIC_ROUTES = ["/login"];
|
|
|
|
interface AuthContextType {
|
|
user: AuthUser | null;
|
|
isAuthenticated: boolean;
|
|
isLoading: boolean;
|
|
login: (credentials: LoginCredentials) => Promise<{ success: boolean; error?: string }>;
|
|
logout: () => Promise<void>;
|
|
}
|
|
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
interface AuthProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function AuthProvider({ children }: AuthProviderProps) {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const [authState, setAuthState] = useState<AuthState>({
|
|
isAuthenticated: false,
|
|
user: null,
|
|
token: null,
|
|
});
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
// Verificar autenticación al cargar
|
|
useEffect(() => {
|
|
async function checkAuth() {
|
|
setIsLoading(true);
|
|
|
|
// Obtener estado guardado
|
|
const storedState = getAuthState();
|
|
|
|
if (storedState.isAuthenticated) {
|
|
// Verificar que el token sigue siendo válido
|
|
const isValid = await verifyToken();
|
|
|
|
if (isValid) {
|
|
setAuthState(storedState);
|
|
} else {
|
|
// Token inválido, limpiar
|
|
setAuthState({
|
|
isAuthenticated: false,
|
|
user: null,
|
|
token: null,
|
|
});
|
|
}
|
|
}
|
|
|
|
setIsLoading(false);
|
|
}
|
|
|
|
checkAuth();
|
|
}, []);
|
|
|
|
// Redirección basada en autenticación
|
|
useEffect(() => {
|
|
if (isLoading) return;
|
|
|
|
const isPublicRoute = PUBLIC_ROUTES.includes(pathname);
|
|
|
|
if (!authState.isAuthenticated && !isPublicRoute) {
|
|
// No autenticado y en ruta privada -> redirigir a login
|
|
router.push("/login");
|
|
} else if (authState.isAuthenticated && pathname === "/login") {
|
|
// Autenticado y en login -> redirigir a home
|
|
router.push("/");
|
|
}
|
|
}, [authState.isAuthenticated, isLoading, pathname, router]);
|
|
|
|
// Login
|
|
const login = useCallback(async (credentials: LoginCredentials) => {
|
|
const result = await authLogin(credentials);
|
|
|
|
if (result.success) {
|
|
setAuthState({
|
|
isAuthenticated: true,
|
|
user: result.user || null,
|
|
token: result.token || null,
|
|
});
|
|
return { success: true };
|
|
}
|
|
|
|
return { success: false, error: result.error };
|
|
}, []);
|
|
|
|
// Logout
|
|
const logout = useCallback(async () => {
|
|
await authLogout();
|
|
setAuthState({
|
|
isAuthenticated: false,
|
|
user: null,
|
|
token: null,
|
|
});
|
|
router.push("/login");
|
|
}, [router]);
|
|
|
|
const value: AuthContextType = {
|
|
user: authState.user,
|
|
isAuthenticated: authState.isAuthenticated,
|
|
isLoading,
|
|
login,
|
|
logout,
|
|
};
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
}
|
|
|
|
/**
|
|
* Hook para acceder al contexto de autenticación
|
|
*/
|
|
export function useAuth(): AuthContextType {
|
|
const context = useContext(AuthContext);
|
|
|
|
if (context === undefined) {
|
|
throw new Error("useAuth must be used within an AuthProvider");
|
|
}
|
|
|
|
return context;
|
|
}
|