xtraderClient/src/api/request.ts
2026-03-15 15:03:47 +08:00

110 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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