139 lines
2.9 KiB
TypeScript
139 lines
2.9 KiB
TypeScript
|
|
/**
|
|||
|
|
* TraceStudio API 通讯层
|
|||
|
|
* 统一管理所有后端请求,处理 CORS 和错误
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { API_BASE_URL, WS_BASE_URL } from './base'
|
|||
|
|
|
|||
|
|
if (import.meta.env.DEV) {
|
|||
|
|
console.log('🔗 API Base URL:', API_BASE_URL)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface ApiResponse<T = any> {
|
|||
|
|
data?: T
|
|||
|
|
error?: string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 通用请求方法
|
|||
|
|
*/
|
|||
|
|
async function request<T = any>(
|
|||
|
|
endpoint: string,
|
|||
|
|
options: RequestInit = {}
|
|||
|
|
): Promise<ApiResponse<T>> {
|
|||
|
|
try {
|
|||
|
|
const url = `${API_BASE_URL}${endpoint}`
|
|||
|
|
const response = await fetch(url, {
|
|||
|
|
...options,
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
...options.headers,
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
// Try to include response body (JSON or text) to aid debugging (eg. 422 validation errors)
|
|||
|
|
let bodyText = ''
|
|||
|
|
try {
|
|||
|
|
bodyText = await response.text()
|
|||
|
|
} catch (e) {
|
|||
|
|
bodyText = ''
|
|||
|
|
}
|
|||
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText} ${bodyText}`)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const data = await response.json()
|
|||
|
|
return { data }
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(`API 请求失败 [${endpoint}]:`, error)
|
|||
|
|
return { error: error instanceof Error ? error.message : String(error) }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* GET /api/plugins - 获取所有可用的算子
|
|||
|
|
*/
|
|||
|
|
export async function getPlugins(): Promise<ApiResponse<{
|
|||
|
|
plugins: Record<string, any>
|
|||
|
|
}>> {
|
|||
|
|
return request('/api/plugins', { method: 'GET' })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* POST /api/node/preview - 预览节点输出
|
|||
|
|
*/
|
|||
|
|
export async function previewNode(payload: {
|
|||
|
|
node: any
|
|||
|
|
limit?: number
|
|||
|
|
}): Promise<ApiResponse<{
|
|||
|
|
columns?: string[]
|
|||
|
|
preview?: any[]
|
|||
|
|
error?: string
|
|||
|
|
}>> {
|
|||
|
|
return request('/api/node/preview', {
|
|||
|
|
method: 'POST',
|
|||
|
|
body: JSON.stringify(payload),
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* POST /api/graph/execute - 执行完整的计算图
|
|||
|
|
*/
|
|||
|
|
export async function executeGraph(payload: {
|
|||
|
|
nodes: any[]
|
|||
|
|
edges: any[]
|
|||
|
|
settings?: any
|
|||
|
|
global_context?: any
|
|||
|
|
}): Promise<ApiResponse<{
|
|||
|
|
status: string
|
|||
|
|
message?: string
|
|||
|
|
execution_time?: number
|
|||
|
|
error?: string
|
|||
|
|
}>> {
|
|||
|
|
return request('/api/graph/execute', {
|
|||
|
|
method: 'POST',
|
|||
|
|
body: JSON.stringify(payload),
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* WS /ws/graph/execute - 流式执行图
|
|||
|
|
* 返回 WebSocket 实例,调用方负责监听 message/close/error。
|
|||
|
|
*/
|
|||
|
|
export function executeGraphWS(payload: {
|
|||
|
|
nodes: any[]
|
|||
|
|
edges: any[]
|
|||
|
|
settings?: any
|
|||
|
|
}): WebSocket {
|
|||
|
|
const url = `${WS_BASE_URL}/api/ws/graph/execute`
|
|||
|
|
const ws = new WebSocket(url)
|
|||
|
|
ws.onopen = () => {
|
|||
|
|
try {
|
|||
|
|
ws.send(JSON.stringify(payload))
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('WS 发送初始化消息失败:', e)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ws
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 健康检查
|
|||
|
|
*/
|
|||
|
|
export async function healthCheck(): Promise<ApiResponse<{
|
|||
|
|
status: string
|
|||
|
|
service: string
|
|||
|
|
version: string
|
|||
|
|
}>> {
|
|||
|
|
return request('/', { method: 'GET' })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用户列表
|
|||
|
|
*/
|
|||
|
|
export async function listUsers(): Promise<ApiResponse<{
|
|||
|
|
users: string[]
|
|||
|
|
}>> {
|
|||
|
|
return request('/api/users/list', { method: 'GET' })
|
|||
|
|
}
|