import { getLoginUrl } from "@/const"; import { trpc } from "@/lib/trpc"; import { TRPCClientError } from "@trpc/client"; import { useCallback, useEffect, useMemo } from "react"; type UseAuthOptions = { redirectOnUnauthenticated?: boolean; redirectPath?: string; }; export function useAuth(options?: UseAuthOptions) { const { redirectOnUnauthenticated = false, redirectPath = getLoginUrl() } = options ?? {}; const utils = trpc.useUtils(); const meQuery = trpc.auth.me.useQuery(undefined, { retry: false, refetchOnWindowFocus: false, }); const logoutMutation = trpc.auth.logout.useMutation({ onSuccess: () => { utils.auth.me.setData(undefined, null); }, }); const logout = useCallback(async () => { try { await logoutMutation.mutateAsync(); } catch (error: unknown) { if ( error instanceof TRPCClientError && error.data?.code === "UNAUTHORIZED" ) { return; } throw error; } finally { utils.auth.me.setData(undefined, null); await utils.auth.me.invalidate(); } }, [logoutMutation, utils]); const state = useMemo(() => { localStorage.setItem( "manus-runtime-user-info", JSON.stringify(meQuery.data) ); return { user: meQuery.data ?? null, loading: meQuery.isLoading || logoutMutation.isPending, error: meQuery.error ?? logoutMutation.error ?? null, isAuthenticated: Boolean(meQuery.data), }; }, [ meQuery.data, meQuery.error, meQuery.isLoading, logoutMutation.error, logoutMutation.isPending, ]); useEffect(() => { if (!redirectOnUnauthenticated) return; if (meQuery.isLoading || logoutMutation.isPending) return; if (state.user) return; if (typeof window === "undefined") return; if (window.location.pathname === redirectPath) return; window.location.href = redirectPath }, [ redirectOnUnauthenticated, redirectPath, logoutMutation.isPending, meQuery.isLoading, state.user, ]); return { ...state, refresh: () => meQuery.refetch(), logout, }; }