feat(web): 增加单点登录分流与本地应急入口

开启统一认证后,正常登录入口直接跳转认证中心,并将本地账号入口收敛到 /login 应急页;未开启统一认证时继续保留工作台内原有登录方式。

影响范围仅限 Web 登录路由、认证入口展示和相关测试,不修改认证接口或 OpenAPI。风险主要在运行时身份状态判断,已通过 141 项前端测试、生产构建、无缓存类型检查及浏览器分支验证。
This commit is contained in:
2026-07-31 13:43:06 +08:00
parent cae97b6f77
commit 88c971564a
10 changed files with 169 additions and 14 deletions
+33 -3
View File
@@ -131,6 +131,7 @@ import {
} from './lib/oidc-browser-session';
import {
consumeOIDCCallbackError,
loginEntryAction,
loadOIDCRuntimeConfiguration,
startOIDCLogin,
startOIDCLogout,
@@ -138,6 +139,7 @@ import {
import { runTask, type RunTaskOptions } from './lib/run-task';
import { AdminPage } from './pages/AdminPage';
import { ApiDocsPage } from './pages/ApiDocsPage';
import { EmergencyLoginPage } from './pages/EmergencyLoginPage';
import { HomePage } from './pages/HomePage';
import { ModelsPage } from './pages/ModelsPage';
import { PlaygroundPage } from './pages/PlaygroundPage';
@@ -686,6 +688,10 @@ export function App() {
const response = await request();
persistAccessToken(response.accessToken);
setToken(response.accessToken);
if (activePage === 'login') {
navigatePath(pathForWorkspaceSection('overview'));
return;
}
await ensureRouteData(response.accessToken, true);
} catch (err) {
setState('error');
@@ -1308,9 +1314,25 @@ export function App() {
});
}
function showLogin() {
setAuthMode('login');
navigatePath(pathForWorkspaceSection('overview'));
async function showLogin() {
setState('loading');
setError('');
try {
const configuration = await loadOIDCRuntimeConfiguration(true);
const identityEnabled = configuration.enabled && configuration.oidcLogin;
setOIDCEnabled(identityEnabled);
if (loginEntryAction(identityEnabled) === 'oidc') {
setOIDCCallbackError(null);
await startOIDCLogin(configuration);
return;
}
setState('idle');
setAuthMode('login');
navigatePath(pathForWorkspaceSection('overview'));
} catch (err) {
setState('error');
setError(err instanceof Error ? err.message : '登录入口加载失败');
}
}
function currentRouteState(): AppRouteState {
@@ -1411,6 +1433,14 @@ export function App() {
/>
)}
{activePage === 'models' && <ModelsPage data={data} />}
{activePage === 'login' && (
<EmergencyLoginPage
loginForm={loginForm}
state={state}
onLoginChange={setLoginForm}
onSubmitLogin={submitLogin}
/>
)}
{activePage === 'workspace' && (
isAuthenticated ? (
<WorkspacePage