Handle 401 auth expiry and float playground errors

This commit is contained in:
wangbo 2026-05-14 23:29:38 +08:00
parent 170fd8655c
commit c0cfae475d
2 changed files with 53 additions and 5 deletions

View File

@ -50,6 +50,7 @@ import {
deletePlatform,
deleteTenant,
deleteUserGroup,
GatewayApiError,
getHealth,
listFileStorageChannels,
getFileStorageSettings,
@ -271,7 +272,8 @@ export function App() {
loadedDataKeysRef.current.add('modelRateLimits');
loadedDataKeysRef.current.add('platforms');
})
.catch(() => {
.catch((err) => {
if (handleAuthExpired(err, token)) return;
loadedDataKeysRef.current.delete('modelRateLimits');
loadedDataKeysRef.current.delete('platforms');
});
@ -370,6 +372,7 @@ export function App() {
requestKeys.forEach((key) => loadedDataKeysRef.current.add(key));
setState('ready');
} catch (err) {
if (handleAuthExpired(err, nextToken)) return;
setState('error');
setError(err instanceof Error ? err.message : '加载失败');
} finally {
@ -925,7 +928,7 @@ export function App() {
}
}
function signOut() {
function resetAuthenticatedSession() {
persistAccessToken('');
setToken('');
loadedDataKeysRef.current = new Set(health ? ['health'] : []);
@ -962,6 +965,19 @@ export function App() {
setWalletTransactionTotal(0);
setWorkspaceTransactionQuery(defaultWorkspaceTransactionQuery());
setCoreMessage('');
}
function handleAuthExpired(err: unknown, failedToken: string) {
if (!failedToken || !(err instanceof GatewayApiError) || err.details.status !== 401) return false;
resetAuthenticatedSession();
setError('');
setAuthMode('login');
navigatePath(pathForWorkspaceSection('overview'));
return true;
}
function signOut() {
resetAuthenticatedSession();
navigatePath('/');
}

View File

@ -753,6 +753,9 @@
}
.contentShell[data-page="playground"] {
position: relative;
display: flex;
flex-direction: column;
width: 100vw;
height: 100%;
min-height: 0;
@ -761,6 +764,31 @@
padding: 0;
}
.contentShell[data-page="playground"] > .notice,
.contentShell[data-page="playground"] > .shBadge {
position: absolute;
z-index: 30;
flex: 0 0 auto;
box-shadow: 0 12px 34px rgba(154, 52, 18, 0.12);
}
.contentShell[data-page="playground"] > .shBadge {
top: 10px;
left: 12px;
}
.contentShell[data-page="playground"] > .notice {
top: 38px;
left: 12px;
width: min(680px, calc(100% - 24px));
margin: 0;
}
.contentShell[data-page="playground"] > .playgroundPage {
flex: 1 1 auto;
height: auto;
}
.playgroundPage {
--playground-content-width: 960px;
display: grid;
@ -1396,7 +1424,7 @@
position: sticky;
bottom: 0;
margin: auto auto 0;
padding-top: 18px;
padding: 18px 0 24px;
background: linear-gradient(180deg, rgba(250, 250, 250, 0), var(--surface-subtle) 40%);
}
@ -2223,7 +2251,7 @@
}
.mediaComposerDock {
padding: 16px 40px 28px;
padding: 16px 40px 24px;
background: linear-gradient(180deg, rgba(250, 250, 250, 0), var(--surface-subtle) 26%);
}
@ -2388,7 +2416,11 @@
}
.mediaComposerDock {
padding: 12px 18px 18px;
padding: 12px 18px 20px;
}
.assistantComposerDock {
padding-bottom: 20px;
}
.composerQuickPrompts {