110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
import { i18n } from '@/plugins/i18n'
|
||
|
||
/** 请求基础 URL,默认 https://api.xtrader.vip,可通过环境变量 VITE_API_BASE_URL 覆盖 */
|
||
export const BASE_URL =
|
||
(import.meta as { env?: { VITE_API_BASE_URL?: string } }).env?.VITE_API_BASE_URL ??
|
||
'https://api.xtrader.vip'
|
||
|
||
const FALLBACK_BASE =
|
||
typeof window !== 'undefined' ? window.location.origin : 'https://api.xtrader.vip'
|
||
|
||
/** 生成 WebSocket URL,与 REST API 同源 */
|
||
function getWsUrl(path: string): string {
|
||
const base = BASE_URL || FALLBACK_BASE
|
||
const url = new URL(base)
|
||
const protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
|
||
return `${protocol}//${url.host}${path.startsWith('/') ? path : `/${path}`}`
|
||
}
|
||
|
||
/** CLOB WebSocket URL */
|
||
export function getClobWsUrl(): string {
|
||
return getWsUrl('/clob/ws')
|
||
}
|
||
|
||
/** User WebSocket URL(订单/持仓/余额推送) */
|
||
export function getUserWsUrl(): string {
|
||
return getWsUrl('/clob/ws/user')
|
||
}
|
||
|
||
/**
|
||
* 过滤空值后构建 query 对象,供 GET 请求使用
|
||
* - undefined/null 跳过
|
||
* - 空字符串 '' 跳过
|
||
* - 空数组 [] 跳过
|
||
* - NaN 跳过
|
||
*/
|
||
export function buildQuery(
|
||
params: Record<string, string | number | string[] | number[] | undefined | null>,
|
||
): Record<string, string | number | string[] | number[]> {
|
||
const result: Record<string, string | number | string[] | number[]> = {}
|
||
for (const [k, v] of Object.entries(params)) {
|
||
if (v === undefined || v === null) continue
|
||
if (typeof v === 'string' && v === '') continue
|
||
if (Array.isArray(v) && v.length === 0) continue
|
||
if (typeof v === 'number' && !Number.isFinite(v)) continue
|
||
result[k] = v
|
||
}
|
||
return result
|
||
}
|
||
|
||
export interface RequestConfig {
|
||
/** 请求头,如 { 'x-token': token, 'x-user-id': userId } */
|
||
headers?: Record<string, string>
|
||
}
|
||
|
||
/**
|
||
* 带 x-token 等自定义头的 GET 请求
|
||
*/
|
||
export async function get<T = unknown>(
|
||
path: string,
|
||
params?: Record<string, string | number | string[] | number[] | undefined>,
|
||
config?: RequestConfig,
|
||
): Promise<T> {
|
||
const url = new URL(path, BASE_URL || window.location.origin)
|
||
if (params) {
|
||
Object.entries(params).forEach(([key, value]) => {
|
||
if (value === undefined) return
|
||
if (Array.isArray(value)) {
|
||
value.forEach((v) => url.searchParams.append(key, String(v)))
|
||
} else {
|
||
url.searchParams.set(key, String(value))
|
||
}
|
||
})
|
||
}
|
||
const headers: Record<string, string> = {
|
||
'Content-Type': 'application/json',
|
||
'Accept-Language': i18n.global.locale.value as string,
|
||
...config?.headers,
|
||
}
|
||
const res = await fetch(url.toString(), { method: 'GET', headers })
|
||
if (!res.ok) {
|
||
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
||
}
|
||
return res.json() as Promise<T>
|
||
}
|
||
|
||
/**
|
||
* 带 x-token 等自定义头的 POST 请求
|
||
*/
|
||
export async function post<T = unknown>(
|
||
path: string,
|
||
body?: unknown,
|
||
config?: RequestConfig,
|
||
): Promise<T> {
|
||
const url = new URL(path, BASE_URL || window.location.origin)
|
||
const headers: Record<string, string> = {
|
||
'Content-Type': 'application/json',
|
||
'Accept-Language': i18n.global.locale.value as string,
|
||
...config?.headers,
|
||
}
|
||
const res = await fetch(url.toString(), {
|
||
method: 'POST',
|
||
headers,
|
||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||
})
|
||
if (!res.ok) {
|
||
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
||
}
|
||
return res.json() as Promise<T>
|
||
}
|