Merge pull request #3437 from imfangwenjie/fix/api-base-fetch

fix(frontend): use configured API base for direct requests
This commit is contained in:
Wesley Liddick
2026-06-29 09:23:13 +08:00
committed by GitHub
13 changed files with 72 additions and 45 deletions
+5 -4
View File
@@ -4,7 +4,7 @@
* - Dashboard overview (raw path)
*/
import { apiClient } from '../client'
import { apiClient, buildGatewayUrl } from '../client'
import type { PaginatedResponse } from '@/types'
export type OpsQueryMode = 'auto' | 'raw' | 'preagg'
@@ -593,9 +593,10 @@ export function subscribeQPS(onMessage: (data: any) => void, options: SubscribeQ
isConnecting = true
setStatus(hasConnectedOnce ? 'reconnecting' : 'connecting')
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const wsBaseUrl = options.wsBaseUrl || import.meta.env.VITE_WS_BASE_URL || window.location.host
const wsURL = new URL(`${protocol}//${wsBaseUrl}/api/v1/admin/ops/ws/qps`)
const wsBaseUrl = options.wsBaseUrl || import.meta.env.VITE_WS_BASE_URL
const wsURL = wsBaseUrl
? new URL(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${wsBaseUrl}/api/v1/admin/ops/ws/qps`)
: new URL(buildGatewayUrl('/api/v1/admin/ops/ws/qps').replace(/^http/, 'ws'))
// Do NOT put admin JWT in the URL query string (it can leak via access logs, proxies, etc).
// Browsers cannot set Authorization headers for WebSockets, so we pass the token via
+4 -4
View File
@@ -6,13 +6,13 @@
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import type { ApiResponse } from '@/types'
import { getLocale } from '@/i18n'
import { getAPIBaseURL } from './url'
export { buildApiUrl, buildGatewayUrl } from './url'
// ==================== Axios Instance Configuration ====================
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api/v1'
export const apiClient: AxiosInstance = axios.create({
baseURL: API_BASE_URL,
baseURL: getAPIBaseURL(),
withCredentials: true,
timeout: 30000,
headers: {
@@ -203,7 +203,7 @@ apiClient.interceptors.response.use(
try {
// Call refresh endpoint directly to avoid circular dependency
const refreshResponse = await axios.post(
`${API_BASE_URL}/auth/refresh`,
`${getAPIBaseURL()}/auth/refresh`,
{ refresh_token: refreshToken },
{ headers: { 'Content-Type': 'application/json' } }
)
+2 -1
View File
@@ -2,10 +2,11 @@
* Setup API endpoints
*/
import axios from 'axios'
import { buildGatewayUrl } from './url'
// Create a separate client for setup endpoints (not under /api/v1)
const setupClient = axios.create({
baseURL: '',
baseURL: buildGatewayUrl('/').replace(/\/+$/, ''),
timeout: 30000,
headers: {
'Content-Type': 'application/json'
+33
View File
@@ -0,0 +1,33 @@
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api/v1'
function normalizePath(path: string): string {
return path.startsWith('/') ? path : `/${path}`
}
export function getAPIBaseURL(): string {
return String(API_BASE_URL || '/api/v1')
}
export function buildApiUrl(path: string): string {
const base = getAPIBaseURL().replace(/\/+$/, '')
let suffix = normalizePath(path)
if (suffix === '/api/v1') {
suffix = ''
} else if (suffix.startsWith('/api/v1/')) {
suffix = suffix.slice('/api/v1'.length)
}
return `${base}${suffix}`
}
export function buildGatewayUrl(path: string): string {
const suffix = normalizePath(path)
try {
const origin =
typeof window === 'undefined'
? new URL(getAPIBaseURL()).origin
: new URL(getAPIBaseURL(), window.location.origin).origin
return `${origin}${suffix}`
} catch {
return suffix
}
}