107 lines
2.8 KiB
TypeScript
107 lines
2.8 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect } from 'react';
|
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
import { clearTokens, getMe, isLoggedIn } from '@/lib/api';
|
|
import { isKeycloakLogoutInProgress, startKeycloakLogin } from '@/lib/keycloak-oidc';
|
|
import { pickAppText } from '@/lib/i18n/core';
|
|
import { useAppI18n } from '@/lib/i18n/provider';
|
|
import { useChatStore } from '@/lib/store';
|
|
|
|
export default function AuthGuard({
|
|
children,
|
|
minHeightClassName = 'min-h-[calc(100vh-3.5rem)]',
|
|
}: {
|
|
children: React.ReactNode;
|
|
minHeightClassName?: string;
|
|
}) {
|
|
const { locale } = useAppI18n();
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const searchParams = useSearchParams();
|
|
const user = useChatStore((s) => s.user);
|
|
const setUser = useChatStore((s) => s.setUser);
|
|
const setIsAuthLoading = useChatStore((s) => s.setIsAuthLoading);
|
|
const isAuthLoading = useChatStore((s) => s.isAuthLoading);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const init = async () => {
|
|
if (!isLoggedIn()) {
|
|
setUser(null);
|
|
if (!cancelled) {
|
|
setIsAuthLoading(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (useChatStore.getState().user) {
|
|
if (!cancelled) {
|
|
setIsAuthLoading(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
setIsAuthLoading(true);
|
|
try {
|
|
const me = await getMe();
|
|
if (cancelled) return;
|
|
setUser(me);
|
|
} catch {
|
|
clearTokens();
|
|
if (cancelled) return;
|
|
setUser(null);
|
|
} finally {
|
|
if (!cancelled) {
|
|
setIsAuthLoading(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
init();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [setIsAuthLoading, setUser]);
|
|
|
|
useEffect(() => {
|
|
if (isAuthLoading) {
|
|
return;
|
|
}
|
|
|
|
const isPublicRoute = pathname === '/login' || pathname === '/register' || pathname === '/auth/callback' || pathname === '/logout/callback';
|
|
const loggedIn = isLoggedIn();
|
|
|
|
if (!loggedIn && !isPublicRoute) {
|
|
if (isKeycloakLogoutInProgress()) {
|
|
return;
|
|
}
|
|
const search = searchParams?.toString();
|
|
const nextPath = search ? `${pathname}?${search}` : pathname;
|
|
void startKeycloakLogin(nextPath);
|
|
return;
|
|
}
|
|
|
|
if (loggedIn && user && isPublicRoute) {
|
|
router.replace('/');
|
|
}
|
|
}, [isAuthLoading, pathname, router, searchParams, user]);
|
|
|
|
if (isAuthLoading) {
|
|
return (
|
|
<div className={`flex ${minHeightClassName} items-center justify-center`}>
|
|
<div className="text-muted-foreground">{pickAppText(locale, '加载中...', 'Loading...')}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const isPublicRoute = pathname === '/login' || pathname === '/register' || pathname === '/auth/callback' || pathname === '/logout/callback';
|
|
if (!isPublicRoute && (!isLoggedIn() || !user)) {
|
|
return null;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|