fix(web): stop doubling basePath in auth refresh redirects (#39273)

Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
Checo 2026-07-20 20:22:40 +08:00 committed by GitHub
parent 0862641533
commit 3afb9b3230
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 8 deletions

View File

@ -15,6 +15,7 @@ const mocks = vi.hoisted(() => ({
}), }),
headers: vi.fn(), headers: vi.fn(),
resolveServerConsoleApiUrl: vi.fn(), resolveServerConsoleApiUrl: vi.fn(),
basePath: '',
})) }))
vi.mock('@/context/query-client-server', () => ({ vi.mock('@/context/query-client-server', () => ({
@ -29,6 +30,12 @@ vi.mock('@/next/navigation', () => ({
redirect: (url: string) => mocks.redirect(url), redirect: (url: string) => mocks.redirect(url),
})) }))
vi.mock('@/utils/var', () => ({
get basePath() {
return mocks.basePath
},
}))
vi.mock('@/features/account-profile/server', () => ({ vi.mock('@/features/account-profile/server', () => ({
serverUserProfileQueryOptions: () => ({ serverUserProfileQueryOptions: () => ({
queryKey: ['common', 'user-profile'], queryKey: ['common', 'user-profile'],
@ -62,6 +69,7 @@ vi.mock('@/features/system-features/server', () => ({
describe('CommonLayoutHydrationBoundary', () => { describe('CommonLayoutHydrationBoundary', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks() vi.clearAllMocks()
mocks.basePath = ''
mocks.queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }) mocks.queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
mocks.headers.mockResolvedValue( mocks.headers.mockResolvedValue(
new Headers({ new Headers({
@ -126,6 +134,7 @@ describe('CommonLayoutHydrationBoundary', () => {
}) })
it('should redirect unauthorized users to the refresh route with the current path', async () => { it('should redirect unauthorized users to the refresh route with the current path', async () => {
mocks.basePath = '/workflow'
mocks.profileQueryFn.mockRejectedValue( mocks.profileQueryFn.mockRejectedValue(
new Response(JSON.stringify({ code: 'unauthorized' }), { status: 401 }), new Response(JSON.stringify({ code: 'unauthorized' }), { status: 401 }),
) )
@ -150,15 +159,17 @@ describe('CommonLayoutHydrationBoundary', () => {
expect(mocks.redirect).toHaveBeenCalledWith('/auth/refresh?redirect_url=%2F') expect(mocks.redirect).toHaveBeenCalledWith('/auth/refresh?redirect_url=%2F')
}) })
it('should redirect setup errors to install', async () => { it.each([
mocks.profileQueryFn.mockRejectedValue( ['not_setup', '/install'],
new Response(JSON.stringify({ code: 'not_setup' }), { status: 401 }), ['not_init_validated', '/init'],
) ])('should use a basePath-relative destination for %s errors', async (code, destination) => {
mocks.basePath = '/workflow'
mocks.profileQueryFn.mockRejectedValue(new Response(JSON.stringify({ code }), { status: 401 }))
const { CommonLayoutHydrationBoundary } = await import('../hydration-boundary') const { CommonLayoutHydrationBoundary } = await import('../hydration-boundary')
await expect(CommonLayoutHydrationBoundary({ children: null })).rejects.toThrow('NEXT_REDIRECT') await expect(CommonLayoutHydrationBoundary({ children: null })).rejects.toThrow('NEXT_REDIRECT')
expect(mocks.redirect).toHaveBeenCalledWith('/install') expect(mocks.redirect).toHaveBeenCalledWith(destination)
}) })
it('should render children without server prefetch when the server API URL is not resolvable', async () => { it('should render children without server prefetch when the server API URL is not resolvable', async () => {

View File

@ -42,15 +42,15 @@ const getCurrentPath = async () => {
const redirectToAuthRefresh = async () => { const redirectToAuthRefresh = async () => {
const currentPath = await getCurrentPath() const currentPath = await getCurrentPath()
redirect(`${basePath}${AUTH_REFRESH_PATH}?redirect_url=${encodeURIComponent(currentPath)}`) redirect(`${AUTH_REFRESH_PATH}?redirect_url=${encodeURIComponent(currentPath)}`)
} }
const handleProfileError = async (error: unknown) => { const handleProfileError = async (error: unknown) => {
if (!(error instanceof Response)) throw error if (!(error instanceof Response)) throw error
const errorData = await parseConsoleErrorPayload(error) const errorData = await parseConsoleErrorPayload(error)
if (errorData?.code === 'not_setup') redirect(`${basePath}/install`) if (errorData?.code === 'not_setup') redirect('/install')
if (errorData?.code === 'not_init_validated') redirect(`${basePath}/init`) if (errorData?.code === 'not_init_validated') redirect('/init')
if (error.status === 401) await redirectToAuthRefresh() if (error.status === 401) await redirectToAuthRefresh()
throw error throw error