fix: close module access lifecycle gaps
This commit is contained in:
25
src/App.tsx
25
src/App.tsx
@@ -35,6 +35,7 @@ import {
|
||||
AI_MODULE_SELECTION_PATH,
|
||||
getGuardedAiModuleForPath,
|
||||
isAiModuleAllowed,
|
||||
isProgrammingProviderRoute,
|
||||
} from './lib/ai-modules';
|
||||
import { useUserSyncStore } from './stores/user-sync';
|
||||
import { flushPendingAgentSessionSync } from '@/lib/agent-session-sync';
|
||||
@@ -122,10 +123,6 @@ function getReturnPath(location: ReturnType<typeof useLocation>): string {
|
||||
return `${location.pathname}${location.search}`;
|
||||
}
|
||||
|
||||
function isProgrammingRoute(pathname: string): boolean {
|
||||
return getGuardedAiModuleForPath(pathname) === 'programming';
|
||||
}
|
||||
|
||||
function ProtectedLayout({
|
||||
authReady,
|
||||
authRequired,
|
||||
@@ -321,10 +318,24 @@ function App() {
|
||||
useEffect(() => {
|
||||
if (rendererOnlyPreview) return;
|
||||
if (!setupReady) return;
|
||||
if (!programmingModuleAllowed) return;
|
||||
if (!isProgrammingRoute(location.pathname)) return;
|
||||
if (authRequired && !authInitialized) return;
|
||||
if (authRequired && !authenticated) return;
|
||||
if (!isProgrammingProviderRoute(location.pathname)) return;
|
||||
if (
|
||||
getGuardedAiModuleForPath(location.pathname) === 'programming'
|
||||
&& !programmingModuleAllowed
|
||||
) return;
|
||||
initProviders();
|
||||
}, [initProviders, location.pathname, programmingModuleAllowed, rendererOnlyPreview, setupReady]);
|
||||
}, [
|
||||
authInitialized,
|
||||
authRequired,
|
||||
authenticated,
|
||||
initProviders,
|
||||
location.pathname,
|
||||
programmingModuleAllowed,
|
||||
rendererOnlyPreview,
|
||||
setupReady,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (rendererOnlyPreview) return;
|
||||
|
||||
@@ -76,6 +76,10 @@ const PROGRAMMING_ROUTE_PREFIXES = [
|
||||
'/projects',
|
||||
'/sessions',
|
||||
'/models',
|
||||
] as const;
|
||||
|
||||
const PROGRAMMING_PROVIDER_ROUTE_PREFIXES = [
|
||||
...PROGRAMMING_ROUTE_PREFIXES,
|
||||
'/settings',
|
||||
] as const;
|
||||
|
||||
@@ -87,6 +91,10 @@ export function isAiModuleAllowed(moduleId: AiModuleId, access: ModuleAccess): b
|
||||
return access[moduleAccessKeyById[moduleId]];
|
||||
}
|
||||
|
||||
export function isProgrammingProviderRoute(pathname: string): boolean {
|
||||
return PROGRAMMING_PROVIDER_ROUTE_PREFIXES.some((route) => matchesRoute(pathname, route));
|
||||
}
|
||||
|
||||
export function getGuardedAiModuleForPath(pathname: string): AiModuleId | null {
|
||||
if (pathname === '/image-canvas'
|
||||
|| pathname.startsWith('/image-canvas/')
|
||||
|
||||
@@ -236,7 +236,8 @@ async function readCurrentModuleAccess(fallback: ModuleAccess): Promise<ModuleAc
|
||||
const response = await hostApiFetch<ModuleAccessResponse>('/api/auth/me');
|
||||
if (!response.success) return fallback;
|
||||
return normalizeModuleAccess(response.moduleAccess);
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (isTerminalAuthError(error)) throw error;
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
@@ -347,9 +348,25 @@ export const useAuthStore = create<AuthState>()(
|
||||
return;
|
||||
}
|
||||
|
||||
const moduleAccess = await readCurrentModuleAccess(
|
||||
normalizeModuleAccess(state.moduleAccess),
|
||||
);
|
||||
let moduleAccess: ModuleAccess;
|
||||
try {
|
||||
moduleAccess = await readCurrentModuleAccess(
|
||||
normalizeModuleAccess(state.moduleAccess),
|
||||
);
|
||||
} catch (error) {
|
||||
if (!isCurrentAuthSessionEpoch(operationEpoch)) return;
|
||||
if (isTerminalAuthError(error)) {
|
||||
advanceAuthSessionEpoch();
|
||||
set({
|
||||
initialized: true,
|
||||
loading: false,
|
||||
error: '登录已过期,请重新授权。',
|
||||
...getClearedSession(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
moduleAccess = normalizeModuleAccess(state.moduleAccess);
|
||||
}
|
||||
if (!isCurrentAuthSessionEpoch(operationEpoch)) return;
|
||||
set({ initialized: true, loading: false, error: null, moduleAccess });
|
||||
},
|
||||
@@ -383,7 +400,10 @@ export const useAuthStore = create<AuthState>()(
|
||||
});
|
||||
} catch (error) {
|
||||
if (!isCurrentAuthSessionEpoch(operationEpoch)) return;
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const terminal = isTerminalAuthError(error);
|
||||
const message = terminal
|
||||
? '登录已过期,请重新授权。'
|
||||
: (error instanceof Error ? error.message : String(error));
|
||||
advanceAuthSessionEpoch();
|
||||
set({ loading: false, error: message, ...getClearedSession() });
|
||||
throw new Error(message, { cause: error });
|
||||
@@ -431,8 +451,10 @@ export const useAuthStore = create<AuthState>()(
|
||||
return session.accessToken;
|
||||
} catch (error) {
|
||||
if (!isCurrentAuthSessionEpoch(operationEpoch)) return null;
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const terminal = isTerminalAuthError(error);
|
||||
const message = terminal
|
||||
? '登录已过期,请重新授权。'
|
||||
: (error instanceof Error ? error.message : String(error));
|
||||
if (terminal) advanceAuthSessionEpoch();
|
||||
set({
|
||||
initialized: true,
|
||||
|
||||
Reference in New Issue
Block a user