57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
import { Navigate, useLocation } from 'react-router-dom';
|
|
import { portalSessionApi } from '@/api/adminApi';
|
|
import {
|
|
clearSession,
|
|
readSession,
|
|
saveSessionRecovery,
|
|
writeSession,
|
|
type LoginSession,
|
|
type Portal,
|
|
} from '@/api/session';
|
|
|
|
type PortalSessionBoundaryProps = {
|
|
portal: Portal;
|
|
children(session: LoginSession): ReactNode;
|
|
};
|
|
|
|
export function PortalSessionBoundary({ portal, children }: PortalSessionBoundaryProps) {
|
|
const location = useLocation();
|
|
const targetRoute = useRef(`${location.pathname}${location.search}${location.hash}`);
|
|
const [state, setState] = useState<{ checking: boolean; session: LoginSession | null }>({
|
|
checking: true,
|
|
session: readSession(portal),
|
|
});
|
|
|
|
useEffect(() => {
|
|
let active = true;
|
|
setState((current) => ({ ...current, checking: true }));
|
|
portalSessionApi.current(portal)
|
|
.then((session) => {
|
|
if (!active) return;
|
|
writeSession(session);
|
|
setState({ checking: false, session });
|
|
})
|
|
.catch((error: unknown) => {
|
|
if (!active) return;
|
|
clearSession(portal);
|
|
saveSessionRecovery(portal, {
|
|
returnUrl: targetRoute.current,
|
|
message: error instanceof Error ? error.message : '登录会话已失效,请重新登录',
|
|
});
|
|
setState({ checking: false, session: null });
|
|
});
|
|
return () => { active = false; };
|
|
}, [portal]);
|
|
|
|
if (state.checking) {
|
|
return (
|
|
<main aria-busy="true" aria-live="polite" className="page-loading-state">
|
|
<p>正在恢复登录会话…</p>
|
|
</main>
|
|
);
|
|
}
|
|
if (!state.session) return <Navigate to={`/${portal}/login`} replace />;
|
|
return children(state.session);
|
|
}
|