Merge pull request #3560 from StarryKira/feat/issue-3557-oauth-email

feat: fix OAuth email completion flow
This commit is contained in:
Wesley Liddick
2026-06-30 10:55:00 +08:00
committed by GitHub
7 changed files with 177 additions and 9 deletions
+13
View File
@@ -20,11 +20,24 @@ describe('API Client', () => {
afterEach(() => {
vi.restoreAllMocks()
vi.unstubAllEnvs()
})
// --- 请求拦截器 ---
describe('请求拦截器', () => {
it('规范化相对 API base,避免在回调页拼出相对 v1 路径', async () => {
vi.resetModules()
vi.stubEnv('VITE_API_BASE_URL', 'api/v1')
const mod = await import('@/api/client')
expect(mod.apiClient.defaults.baseURL).toBe('/api/v1')
expect(mod.buildApiUrl('/auth/oauth/github/callback?code=abc')).toBe(
'/api/v1/auth/oauth/github/callback?code=abc'
)
})
it('自动附加 Authorization 头', async () => {
localStorage.setItem('auth_token', 'my-jwt-token')
+15 -5
View File
@@ -1,20 +1,30 @@
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api/v1'
const DEFAULT_API_BASE_URL = '/api/v1'
const API_BASE_URL = normalizeAPIBaseURL(import.meta.env.VITE_API_BASE_URL)
function normalizePath(path: string): string {
return path.startsWith('/') ? path : `/${path}`
}
function normalizeAPIBaseURL(value: unknown): string {
const raw = String(value || DEFAULT_API_BASE_URL).trim() || DEFAULT_API_BASE_URL
const withoutTrailingSlash = raw.replace(/\/+$/, '')
if (/^[a-z][a-z\d+.-]*:\/\//i.test(withoutTrailingSlash) || withoutTrailingSlash.startsWith('//')) {
return withoutTrailingSlash
}
return normalizePath(withoutTrailingSlash)
}
export function getAPIBaseURL(): string {
return String(API_BASE_URL || '/api/v1')
return API_BASE_URL
}
export function buildApiUrl(path: string): string {
const base = getAPIBaseURL().replace(/\/+$/, '')
let suffix = normalizePath(path)
if (suffix === '/api/v1') {
if (suffix === DEFAULT_API_BASE_URL) {
suffix = ''
} else if (suffix.startsWith('/api/v1/')) {
suffix = suffix.slice('/api/v1'.length)
} else if (suffix.startsWith(`${DEFAULT_API_BASE_URL}/`)) {
suffix = suffix.slice(DEFAULT_API_BASE_URL.length)
}
return `${base}${suffix}`
}
@@ -152,6 +152,7 @@ import { useRoute, useRouter } from 'vue-router'
import { useClipboard } from '@/composables/useClipboard'
import { useAppStore, useAuthStore } from '@/stores'
import { apiClient } from '@/api/client'
import { buildApiUrl } from '@/api/url'
import {
exchangePendingOAuthCompletion,
persistOAuthTokenContext,
@@ -256,8 +257,6 @@ function readPendingEmailOAuthProvider(): 'github' | 'google' | null {
function redirectProviderCallbackToBackend(provider: 'github' | 'google'): void {
if (typeof window === 'undefined') return
const apiBase = (import.meta.env.VITE_API_BASE_URL as string | undefined) || '/api/v1'
const normalized = apiBase.replace(/\/$/, '')
const params = new URLSearchParams()
for (const [key, value] of Object.entries(route.query)) {
if (Array.isArray(value)) {
@@ -269,7 +268,7 @@ function redirectProviderCallbackToBackend(provider: 'github' | 'google'): void
}
}
const suffix = params.toString() ? `?${params.toString()}` : ''
window.location.href = `${normalized}/auth/oauth/${provider}/callback${suffix}`
window.location.href = buildApiUrl(`/auth/oauth/${provider}/callback${suffix}`)
}
async function finalizeTokenResponse(tokenResponse: OAuthTokenResponse, redirect: string) {