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 })
}
}