// FlowStack gin 后端 REST 客户端 export class ApiError extends Error { status: number constructor(message: string, status: number) { super(message) this.status = status } } export async function api(path: string, init?: RequestInit): Promise { const res = await fetch(path, { headers: { 'Content-Type': 'application/json', ...(init?.headers || {}) }, ...init, }) if (!res.ok) { let message = `请求失败 (HTTP ${res.status})` try { const body = await res.json() if (body?.error) message = body.error } catch { // 非 JSON 响应体 } throw new ApiError(message, res.status) } return res.json() } // ---- 类型定义(与后端 model 对齐) ---- export interface PipelineNodeDTO { dbId?: number pipelineId?: number nodeId: string type: string name: string positionX: number positionY: number width: number height: number category: string config?: Record } export interface PipelineEdgeDTO { dbId?: number pipelineId?: number edgeId: string source: string target: string label?: string animated?: boolean } export interface PipelineDetail { pipeline: any nodes: PipelineNodeDTO[] edges: PipelineEdgeDTO[] } export interface PipelineRunDTO { id: number runId: string pipelineId: number pipelineName: string branch: string env: string status: string durationMs: number triggerBy?: string triggerType?: string startedAt: string completedAt?: string buildNumber?: number externalRunId?: string externalUrl?: string } export const apiClient = { // 流水线 listPipelines: () => api('/api/pipelines'), getPipeline: (id: number | string) => api(`/api/pipelines/${id}`), createPipeline: (body: any) => api('/api/pipelines', { method: 'POST', body: JSON.stringify(body) }), updatePipeline: (id: number | string, body: any) => api(`/api/pipelines/${id}`, { method: 'PUT', body: JSON.stringify(body) }), deletePipeline: (id: number | string) => api(`/api/pipelines/${id}`, { method: 'DELETE' }), saveNodes: (id: number | string, nodes: PipelineNodeDTO[]) => api(`/api/pipelines/${id}/nodes`, { method: 'PUT', body: JSON.stringify({ nodes }) }), saveEdges: (id: number | string, edges: PipelineEdgeDTO[]) => api(`/api/pipelines/${id}/edges`, { method: 'PUT', body: JSON.stringify({ edges }) }), // 运行 createRun: (pipelineId: number | string, body?: any) => api(`/api/pipelines/${pipelineId}/runs`, { method: 'POST', body: JSON.stringify(body || {}) }), listRuns: (params?: { pipelineId?: number | string; status?: string }) => { const q = new URLSearchParams() if (params?.pipelineId) q.set('pipelineId', String(params.pipelineId)) if (params?.status && params.status !== 'all') q.set('status', params.status) const qs = q.toString() return api(`/api/runs${qs ? `?${qs}` : ''}`) }, getRun: (runId: string) => api(`/api/runs/${runId}`), retryRun: (runId: string) => api(`/api/runs/${runId}/retry`, { method: 'POST' }), // 摘要/设置 getSummary: () => api('/api/summary'), getSettings: () => api('/api/settings'), updateSettings: (body: any) => api('/api/settings', { method: 'PUT', body: JSON.stringify(body) }), // 主机 listHosts: () => api('/api/hosts'), createHost: (b: any) => api('/api/hosts', { method: 'POST', body: JSON.stringify(b) }), updateHost: (id: any, b: any) => api(`/api/hosts/${id}`, { method: 'PUT', body: JSON.stringify(b) }), deleteHost: (id: any) => api(`/api/hosts/${id}`, { method: 'DELETE' }), testHost: (id: any) => api(`/api/hosts/${id}/test`, { method: 'POST' }), // 集群 listClusters: () => api('/api/clusters'), createCluster: (b: any) => api('/api/clusters', { method: 'POST', body: JSON.stringify(b) }), updateCluster: (id: any, b: any) => api(`/api/clusters/${id}`, { method: 'PUT', body: JSON.stringify(b) }), deleteCluster: (id: any) => api(`/api/clusters/${id}`, { method: 'DELETE' }), testCluster: (id: any) => api(`/api/clusters/${id}/test`, { method: 'POST' }), // 凭证 listCredentials: (type?: string) => api(`/api/credentials${type && type !== 'all' ? `?type=${type}` : ''}`), createCredential: (b: any) => api('/api/credentials', { method: 'POST', body: JSON.stringify(b) }), updateCredential: (id: any, b: any) => api(`/api/credentials/${id}`, { method: 'PUT', body: JSON.stringify(b) }), deleteCredential: (id: any) => api(`/api/credentials/${id}`, { method: 'DELETE' }), // 构建节点 listBuildNodes: () => api('/api/build-nodes'), createBuildNode: (b: any) => api('/api/build-nodes', { method: 'POST', body: JSON.stringify(b) }), updateBuildNode: (id: any, b: any) => api(`/api/build-nodes/${id}`, { method: 'PUT', body: JSON.stringify(b) }), deleteBuildNode: (id: any) => api(`/api/build-nodes/${id}`, { method: 'DELETE' }), testBuildNode: (id: any) => api(`/api/build-nodes/${id}/test`, { method: 'POST' }), // 工具 listTools: () => api('/api/tools'), updateTool: (id: any, b: any) => api(`/api/tools/${id}`, { method: 'PUT', body: JSON.stringify(b) }), checkTool: (id: any) => api(`/api/tools/${id}/check`, { method: 'POST' }), installTool: (id: any) => api(`/api/tools/${id}/install`, { method: 'POST' }), // Webhook listWebhooks: () => api('/api/webhooks'), createWebhook: (b: any) => api('/api/webhooks', { method: 'POST', body: JSON.stringify(b) }), updateWebhook: (id: any, b: any) => api(`/api/webhooks/${id}`, { method: 'PUT', body: JSON.stringify(b) }), deleteWebhook: (id: any) => api(`/api/webhooks/${id}`, { method: 'DELETE' }), testWebhook: (id: any) => api(`/api/webhooks/${id}/test`, { method: 'POST' }), // Jenkins listJenkinsServers: () => api('/api/jenkins-servers'), createJenkinsServer: (b: any) => api('/api/jenkins-servers', { method: 'POST', body: JSON.stringify(b) }), updateJenkinsServer: (id: any, b: any) => api(`/api/jenkins-servers/${id}`, { method: 'PUT', body: JSON.stringify(b) }), deleteJenkinsServer: (id: any) => api(`/api/jenkins-servers/${id}`, { method: 'DELETE' }), testJenkinsServer: (id: any) => api(`/api/jenkins-servers/${id}/test`, { method: 'POST' }), listJenkinsJobs: (id: any) => api(`/api/jenkins-servers/${id}/jobs`), importJenkinsJobs: (id: any, jobs: string[]) => api(`/api/jenkins-servers/${id}/import`, { method: 'POST', body: JSON.stringify({ jobs }) }), // AI 助手与批量发布 aiChat: (messages: { role: string; content: string }[]) => api('/api/ai/chat', { method: 'POST', body: JSON.stringify({ messages }) }), aiRelease: (b: { jobs: string[]; branch?: string; env?: string; parallel?: boolean; jenkinsServerId?: number; autoCreate?: boolean }) => api('/api/ai/release', { method: 'POST', body: JSON.stringify(b) }), batchParse: (b: { text: string; refType?: string; fixedRef?: string }) => api('/api/batch/parse', { method: 'POST', body: JSON.stringify(b) }), testLLM: () => api('/api/settings/test-llm', { method: 'POST' }), testLark: () => api('/api/settings/test-lark', { method: 'POST' }), }