From e4ee6b6d9a8fd2b62814a8cd3c364224f204e7fb Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Thu, 24 Sep 2026 11:39:22 +0800 Subject: [PATCH] fix(auth): keep login available during session check failures Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- web/e2e/auth-entry.spec.ts | 12 ++++++++++++ web/src/features/auth/auth-shell.test.tsx | 1 + web/src/features/auth/auth-shell.tsx | 2 +- web/src/i18n/locales/en.json | 1 + web/src/i18n/locales/ru.json | 1 + web/src/i18n/locales/zh.json | 1 + web/src/shared/lib/auth-route.test.ts | 8 ++++++++ web/src/shared/lib/auth-route.ts | 9 ++++++++- 8 files changed, 33 insertions(+), 2 deletions(-) diff --git a/web/e2e/auth-entry.spec.ts b/web/e2e/auth-entry.spec.ts index 2a639b53..ef4f6c3e 100644 --- a/web/e2e/auth-entry.spec.ts +++ b/web/e2e/auth-entry.spec.ts @@ -86,6 +86,18 @@ test.describe('Auth Entry', () => { await expect(page.getByText('Sign in directly with your existing OAuth account')).toHaveCount(0) }) + test('keeps the login form available when the session status check fails', async ({ page }) => { + await page.route('**/api/v1/auth/me', async (route) => { + await route.fulfill({ status: 503, contentType: 'application/json', body: '{"code":503,"msg":"Unavailable"}' }) + }) + + await page.goto('/login') + + await expect(page.getByLabel('Username')).toBeVisible() + await expect(page.getByLabel('Password', { exact: true })).toBeVisible() + await expect(page.getByRole('button', { name: 'Login' })).toBeVisible() + }) + test('keeps configured session bootstrap available in the organization view', async ({ page }) => { await page.route('**/runtime-config.js', async (route) => { await route.fulfill({ diff --git a/web/src/features/auth/auth-shell.test.tsx b/web/src/features/auth/auth-shell.test.tsx index 0e4ea4d7..f8d92c6d 100644 --- a/web/src/features/auth/auth-shell.test.tsx +++ b/web/src/features/auth/auth-shell.test.tsx @@ -9,6 +9,7 @@ describe('AuthShell', () => { expect(html).toContain('dark:bg-slate-950') expect(html).toContain('sticky top-0 hidden h-svh') expect(html).toContain('authShell.heroTitle') + expect(html).toContain('authShell.shortTagline') expect(html).toContain('login-skill-art-light.png') expect(html).toContain('login-skill-art-dark.png') expect(html).toContain('authShell.backHome') diff --git a/web/src/features/auth/auth-shell.tsx b/web/src/features/auth/auth-shell.tsx index 308d2d71..1c1aea75 100644 --- a/web/src/features/auth/auth-shell.tsx +++ b/web/src/features/auth/auth-shell.tsx @@ -48,7 +48,7 @@ export function AuthShell({ children, className }: AuthShellProps) { -

Skills
Build
Together

+

{t('authShell.shortTagline')}

diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index bcfcc801..4c25114a 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1503,6 +1503,7 @@ "heroTitle": "Discover, preserve, and reuse\nyour team's skills", "heroSubtitle": "Start with a SKILL.md.\nTurn experience into a capability your team can share.", "heroFooter": "Bring team knowledge together and unlock skill value", + "shortTagline": "Skills\nBuild\nTogether", "capabilities": "What you can do with SkillHub", "discoverTitle": "Discover skills", "discoverDescription": "Find reusable skills and practices across your team.", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 4fbb14b8..fc0688d7 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1614,6 +1614,7 @@ "heroTitle": "Открывайте и сохраняйте\nнавыки вашей команды", "heroSubtitle": "Начните с SKILL.md.\nПревратите опыт в доступный команде навык.", "heroFooter": "Собирайте знания команды и раскрывайте ценность навыков", + "shortTagline": "Навыки\nобъединяют\nкоманду", "capabilities": "Возможности SkillHub", "discoverTitle": "Поиск навыков", "discoverDescription": "Находите навыки и практики, которые команда может использовать повторно.", diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json index 36d43a7c..f900f983 100644 --- a/web/src/i18n/locales/zh.json +++ b/web/src/i18n/locales/zh.json @@ -1503,6 +1503,7 @@ "heroTitle": "让团队的技能\n被发现、被沉淀、被复用", "heroSubtitle": "从一份 SKILL.md 开始,\n把经验转化为可共享的团队能力。", "heroFooter": "汇聚团队智慧,释放技能价值", + "shortTagline": "让技能\n连接团队", "capabilities": "SkillHub 的能力", "discoverTitle": "发现技能", "discoverDescription": "找到团队可复用的技能包与实践。", diff --git a/web/src/shared/lib/auth-route.test.ts b/web/src/shared/lib/auth-route.test.ts index b164dd06..eb5888d1 100644 --- a/web/src/shared/lib/auth-route.test.ts +++ b/web/src/shared/lib/auth-route.test.ts @@ -62,6 +62,14 @@ describe('auth-route', () => { await expect(redirectAuthenticated({ search: { returnTo: '/dashboard/tokens' } })).resolves.toBeUndefined() }) + it('keeps the login page available when the session status check fails', async () => { + const redirectAuthenticated = createRedirectAuthenticated(async () => { + throw new Error('Session status unavailable') + }) + + await expect(redirectAuthenticated({ search: { returnTo: '/dashboard/tokens' } })).resolves.toBeUndefined() + }) + it('redirects authenticated visitors to the requested local page', async () => { const redirectAuthenticated = createRedirectAuthenticated(async () => ({ userId: 'user-1' })) diff --git a/web/src/shared/lib/auth-route.ts b/web/src/shared/lib/auth-route.ts index 00a21e4a..ada9d746 100644 --- a/web/src/shared/lib/auth-route.ts +++ b/web/src/shared/lib/auth-route.ts @@ -40,7 +40,14 @@ function resolveAuthenticatedReturnTo(value: unknown) { export function createRedirectAuthenticated(getCurrentUser: () => Promise) { return async function redirectAuthenticated({ search }: { search: { returnTo?: string } }) { - if (await getCurrentUser()) { + let user: unknown + try { + user = await getCurrentUser() + } catch { + // A failed status check must not make the login entry unavailable. + return + } + if (user) { throw redirect({ to: resolveAuthenticatedReturnTo(search.returnTo), replace: true }) } }