diff --git a/apps/ai-game-creator-shell/src/services/platformSession.ts b/apps/ai-game-creator-shell/src/services/platformSession.ts index b5402ba5d..ba708b774 100644 --- a/apps/ai-game-creator-shell/src/services/platformSession.ts +++ b/apps/ai-game-creator-shell/src/services/platformSession.ts @@ -6,6 +6,11 @@ import { refreshClientAuthAccessToken, } from './clientAuth'; import { getClientServerBaseUrl } from './clientHttp'; +import { + type ClientOperation, + createClientOperation, + transitionClientOperation, +} from './clientOperation'; const ACCESS_TOKEN_STORAGE_KEY = 'genarrative.auth.access-token.v1'; @@ -34,12 +39,20 @@ let committedPlatformSession: CommittedPlatformSession | null = null; let desiredPlatformSession: CommittedPlatformSession | null = null; let platformSessionRefreshPromise: Promise | null = null; +let platformSessionOperation: ClientOperation< + 'auth-transition', + { userId: string | null } +> | null = null; let platformSessionNativeMutationTail: Promise = Promise.resolve(); const platformSessionRefreshListeners = new Set(); const platformSessionGenerationListeners = new Set(); +export function getPlatformSessionOperation() { + return platformSessionOperation; +} + function restoreCommittedAccessToken() { if (committedPlatformSession?.accessToken) { window.localStorage.setItem( @@ -259,15 +272,44 @@ export async function commitAuthenticatedPlatformSession( if (!accessToken) { throw new Error('陶泥儿登录凭据缺失,请重新登录'); } + const operation = createClientOperation( + 'auth-transition', + { userId: user.id }, + { + scope: { apiBaseUrl, sessionGeneration: expectedGeneration }, + deadlineMs: 45_000, + cancellable: false, + }, + ); + platformSessionOperation = transitionClientOperation(operation, 'runner'); return enqueuePlatformSessionNativeMutation(async () => { - const session = await commitPlatformSession( - user, - accessToken, - apiBaseUrl, - expectedGeneration, - ); - if (!session) return null; - return session.generation; + try { + const session = await commitPlatformSession( + user, + accessToken, + apiBaseUrl, + expectedGeneration, + ); + if (!session) { + platformSessionOperation = transitionClientOperation( + operation, + 'unknown', + ); + return null; + } + platformSessionOperation = transitionClientOperation( + operation, + 'success', + { scope: { sessionGeneration: session.generation } }, + ); + return session.generation; + } catch (error) { + platformSessionOperation = transitionClientOperation( + operation, + 'retryable-failure', + ); + throw error; + } }); } @@ -375,30 +417,64 @@ export function subscribePlatformSessionGeneration( } export async function clearCommittedPlatformSession(generation: number) { + const operation = createClientOperation( + 'auth-transition', + { userId: null }, + { + scope: { sessionGeneration: generation }, + deadlineMs: 45_000, + cancellable: false, + }, + ); + platformSessionOperation = transitionClientOperation(operation, 'runner'); return enqueuePlatformSessionNativeMutation(async () => { - if (platformAuthGeneration !== generation) { - await reconcileNativePlatformSessionToCurrentAuthority(); - return; - } - desiredPlatformSession = null; - const nativeGeneration = await reserveNativePlatformSessionGeneration(); try { - await clearNativePlatformSession(nativeGeneration); - } catch { - if (platformAuthGeneration === generation) { - desiredPlatformSession = null; + if (platformAuthGeneration !== generation) { + await reconcileNativePlatformSessionToCurrentAuthority(); + platformSessionOperation = transitionClientOperation( + operation, + 'unknown', + ); + return; } - await reconcileNativePlatformSessionToCurrentAuthority(); - return; + desiredPlatformSession = null; + const nativeGeneration = await reserveNativePlatformSessionGeneration(); + try { + await clearNativePlatformSession(nativeGeneration); + } catch { + if (platformAuthGeneration === generation) { + desiredPlatformSession = null; + } + await reconcileNativePlatformSessionToCurrentAuthority(); + platformSessionOperation = transitionClientOperation( + operation, + 'retryable-failure', + ); + return; + } + if (platformAuthGeneration !== generation) { + await reconcileNativePlatformSessionToCurrentAuthority(); + platformSessionOperation = transitionClientOperation( + operation, + 'unknown', + ); + return; + } + committedPlatformSession = null; + desiredPlatformSession = null; + restoreCommittedAccessToken(); + notifyPlatformSessionGeneration(); + platformSessionOperation = transitionClientOperation( + operation, + 'success', + ); + } catch (error) { + platformSessionOperation = transitionClientOperation( + operation, + 'retryable-failure', + ); + throw error; } - if (platformAuthGeneration !== generation) { - await reconcileNativePlatformSessionToCurrentAuthority(); - return; - } - committedPlatformSession = null; - desiredPlatformSession = null; - restoreCommittedAccessToken(); - notifyPlatformSessionGeneration(); }); } @@ -409,6 +485,7 @@ export function resetPlatformSessionStateForTests() { committedPlatformSession = null; desiredPlatformSession = null; platformSessionRefreshPromise = null; + platformSessionOperation = null; platformSessionNativeMutationTail = Promise.resolve(); platformSessionRefreshListeners.clear(); platformSessionGenerationListeners.clear();