// API Service for AIOps Platform // 所有模块均调用真实后端 API;暂无后端的模块返回空数据优雅降级 import { http, type ListResponse } from './http'; import { useUserStore } from '@/store'; // ==================== 用户认证 API ==================== export const userApi = { login: async (username: string, password: string) => { return http.post<{ token: string; expires_in: number; user: any }>( '/auth/login', { username, password } ); }, register: async (data: { username: string; password: string; nickname?: string; email?: string }) => { return http.post('/auth/register', data); }, getProfile: async () => { return http.get('/user/profile'); }, updateProfile: async (data: any) => { return http.put('/user/profile', data); }, changePassword: async (data: { old_password: string; new_password: string }) => { return http.put('/user/password', data); }, getUsers: async (params?: { page?: number; page_size?: number; keyword?: string }) => { return http.get('/admin/users', params); }, createUser: async (data: any) => { return http.post('/admin/users', data); }, updateUser: async (id: number | string, data: any) => { return http.put(`/admin/users/${id}`, data); }, deleteUser: async (id: number | string) => { return http.delete(`/admin/users/${id}`); }, batchImport: async (data: { users: any[]; skip_existing?: boolean }) => { return http.post('/admin/users/batch-import', data); }, downloadTemplate: () => { const token = useUserStore.getState().token; const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080/api/v1'; window.open(`${baseUrl}/admin/users/import-template?token=${token}`, '_blank'); }, // 角色列表 // Logan.Li getRoles: async () => { return http.get('/user/roles'); }, // 角色管理(管理员) // Logan.Li createRole: async (data: { name: string; code: string; description?: string; permission_ids?: number[] }) => { return http.post('/admin/roles', data); }, updateRole: async (id: number | string, data: { name?: string; code?: string; description?: string; permission_ids?: number[] }) => { return http.put(`/admin/roles/${id}`, data); }, deleteRole: async (id: number | string) => { return http.delete(`/admin/roles/${id}`); }, // 权限列表(供角色权限选择) getPermissions: async () => { return http.get('/admin/permissions'); }, }; // ==================== LDAP 配置 API ==================== export const ldapApi = { getConfig: async () => { return http.get('/admin/ldap-config'); }, updateConfig: async (data: any) => { return http.put('/admin/ldap-config', data); }, testConnection: async (data?: { test_user?: string; test_password?: string }) => { return http.post('/admin/ldap-config/test', data || {}); }, listUsers: async (ouDn?: string) => { return http.post('/admin/ldap-config/users', { ou_dn: ouDn || '' }); }, batchImport: async (users: any[]) => { return http.post('/admin/ldap-config/import', { users }); }, listOUs: async () => { return http.post('/admin/ldap-config/ou-list', {}); }, createOU: async (data: { ou_name: string; parent_dn?: string; description?: string }) => { return http.post('/admin/ldap-config/create-ou', data); }, createUser: async (data: { username: string; password: string; display_name?: string; email?: string; ou_dn?: string }) => { return http.post('/admin/ldap-config/create-user', data); }, deleteUser: async (dn: string) => { return http.post('/admin/ldap-config/delete-user', { dn }); }, updateUser: async (data: { dn: string; cn?: string; mail?: string; phone?: string; title?: string }) => { return http.post('/admin/ldap-config/update-user', data); }, }; // ==================== 仪表盘 API ==================== export const dashboardStatsApi = { getStats: async () => { return http.get('/dashboard/stats'); }, getAlerts: async (limit?: number) => { return http.get('/dashboard/recent-alerts', limit ? { limit } : undefined); }, getReleases: async (limit?: number) => { return http.get('/dashboard/recent-releases', limit ? { limit } : undefined); }, getMetrics: async (hours?: number) => { return http.get('/dashboard/metrics', hours ? { hours } : undefined); }, }; // ==================== 服务器管理 API ==================== export const serverApi = { getServers: async (params?: { page?: number; page_size?: number; keyword?: string; status?: string }) => { return http.get('/servers', params); }, getServerDetail: async (id: number | string) => { return http.get(`/servers/${id}`); }, getServerStats: async () => { return http.get('/servers/stats'); }, createServer: async (data: any) => { return http.post('/servers', data); }, updateServer: async (id: number | string, data: any) => { return http.put(`/servers/${id}`, data); }, deleteServer: async (id: number | string) => { return http.delete(`/servers/${id}`); }, batchDelete: async (ids: number[]) => { return http.post('/servers/batch-delete', { ids }); }, batchCreate: async (data: { servers: any[]; region_id?: string; auth_type?: string; credential_id?: number; default_port?: number }) => { return http.post('/servers/batch', data); }, getMetrics: async (id: number | string) => { // 后端暂无单机 metrics,用 dashboard metrics return http.get('/dashboard/metrics'); }, // 远程安装 Agent(通过 SSH 执行安装脚本),可选传入凭据 installAgent: async (id: number | string, token: string, creds?: { username?: string; password?: string; ssh_key?: string; auth_type?: string }) => { const body: any = { token }; if (creds) Object.assign(body, creds); return http.post<{ installed: boolean; arch: string; output: string }>(`/servers/${id}/install-agent`, body); }, // 批量远程安装 Agent batchInstallAgent: async (ids: number[], token: string) => { return http.post<{ total: number; success: number; results: any[] }>(`/servers/install-agent?token=${encodeURIComponent(token)}`, { server_ids: ids }); }, }; // ==================== K8s 集群 API ==================== export const kubernetesApi = { getClusters: async () => { return http.get('/kubernetes-clusters'); }, getClusterDetail: async (id: number | string) => { return http.get(`/kubernetes-clusters/${id}/detail`); }, createCluster: async (data: any) => { return http.post('/kubernetes-clusters', data); }, updateCluster: async (id: number | string, data: any) => { return http.put(`/kubernetes-clusters/${id}`, data); }, deleteCluster: async (id: number | string) => { return http.delete(`/kubernetes-clusters/${id}`); }, getNodes: async (clusterId: number | string) => { return http.get(`/kubernetes-clusters/${clusterId}/nodes`); }, getPods: async (clusterId: number | string) => { return http.get(`/kubernetes-clusters/${clusterId}/pods`); }, getDeployments: async (clusterId: number | string) => { return http.get(`/kubernetes-clusters/${clusterId}/deployments`); }, getServices: async (clusterId: number | string) => { return http.get(`/kubernetes-clusters/${clusterId}/services`); }, getIngresses: async (clusterId: number | string) => { return http.get(`/kubernetes-clusters/${clusterId}/ingresses`); }, syncCluster: async (clusterId: number | string) => { return http.post(`/kubernetes-clusters/${clusterId}/sync`); }, getTopology: async (clusterId: number | string) => { // Logan.Li return http.get(`/kubernetes-clusters/${clusterId}/topology`); }, saveTopology: async (clusterId: number | string, layoutData: string) => { // Logan.Li return http.put(`/kubernetes-clusters/${clusterId}/topology`, { layout_data: layoutData }); }, // === 工作负载操作 (Logan.Li) - Kuboard-style routes === getPodLogs: async (clusterCode: string, namespace: string, podName: string, tail: number = 200, container?: string) => { const params = new URLSearchParams({ tail: String(tail) }); if (container) params.set('container', container); return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/logs?${params}`); }, // 🆕 增强版 Pod 日志查询 (Logan ) // 支持容器选择 / 行数限制 / previous 容器日志 / sinceTime 时间范围 getPodLogsEnhanced: async ( clusterCode: string, namespace: string, podName: string, opts: { container?: string; tailLines?: number; // 0 / undefined => 全部 previous?: boolean; sinceTime?: string; // RFC3339,如 2024-01-01T00:00:00Z } = {} ) => { const params = new URLSearchParams(); if (opts.container) params.set('container', opts.container); if (opts.tailLines && opts.tailLines > 0) params.set('tailLines', String(opts.tailLines)); if (opts.previous) params.set('previous', 'true'); if (opts.sinceTime) params.set('sinceTime', opts.sinceTime); const qs = params.toString(); return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/logs${qs ? `?${qs}` : ''}`); }, // 🆕 构建日志流式 URL(tail -f 效果,fetch ReadableStream)(Logan ) buildPodLogsStreamUrl: ( clusterCode: string, namespace: string, podName: string, opts: { container?: string; tailLines?: number; previous?: boolean; sinceTime?: string; } = {} ) => { const params = new URLSearchParams(); params.set('stream', 'true'); if (opts.container) params.set('container', opts.container); if (opts.tailLines && opts.tailLines > 0) params.set('tailLines', String(opts.tailLines)); if (opts.previous) params.set('previous', 'true'); if (opts.sinceTime) params.set('sinceTime', opts.sinceTime); const baseUrl = import.meta.env.VITE_API_BASE_URL || `${window.location.protocol}//${window.location.host}`; return `${baseUrl}/api/v1/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/logs?${params}`; }, execPod: async (clusterCode: string, namespace: string, podName: string, command: string[], container?: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/exec`, { command, container }); }, // 🆕 Pod 级 exec WebSocket URL (Logan ) // ws://host/ws/kubernetes-exec/:clusterId/namespaces/:ns/pods/:pod?container=&command=&token= buildPodExecWsUrl: ( clusterId: number | string, namespace: string, podName: string, container?: string, command?: string ) => { const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const baseUrl = import.meta.env.VITE_API_BASE_URL || `${proto}//${window.location.host}`; const wsBase = baseUrl.replace(/^http/, 'ws').replace('/api/v1', ''); const token = useUserStore.getState().token || ''; const params = new URLSearchParams(); if (container) params.set('container', container); if (command) params.set('command', command); if (token) params.set('token', token); const qs = params.toString(); return `${wsBase}/ws/kubernetes-exec/${clusterId}/namespaces/${namespace}/pods/${podName}${qs ? `?${qs}` : ''}`; }, // === 🆕 容器文件浏览器 (Logan ) === // 后端 API 并行开发中,UI 先行;返回结构兼容 {entries:[{name,isDir,size,modTime}]} 或数组 listPodFiles: async ( clusterCode: string, namespace: string, podName: string, path: string, container?: string ) => { return http.post( `/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/files/list`, { path, container } ); }, // 构建文件下载 URL(带 token,供 window.open / fetch 使用) buildPodFileDownloadUrl: ( clusterCode: string, namespace: string, podName: string, path: string, container?: string ) => { const params = new URLSearchParams({ path }); if (container) params.set('container', container); const token = useUserStore.getState().token || ''; if (token) params.set('token', token); const baseUrl = import.meta.env.VITE_API_BASE_URL || `${window.location.protocol}//${window.location.host}`; return `${baseUrl}/api/v1/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/files/download?${params}`; }, // 读取容器内文本文件(小文件预览) readPodFileText: async ( clusterCode: string, namespace: string, podName: string, path: string, container?: string ) => { const url = kubernetesApi.buildPodFileDownloadUrl(clusterCode, namespace, podName, path, container); const resp = await fetch(url); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); return resp.text(); }, // 上传文件到 Pod 容器内指定目录 uploadPodFile: async ( clusterCode: string, namespace: string, podName: string, targetPath: string, file: File, container?: string, ) => { const formData = new FormData(); formData.append('file', file); formData.append('path', targetPath); if (container) formData.append('container', container); const token = useUserStore.getState().token || localStorage.getItem('token') || ''; const baseUrl = import.meta.env.VITE_API_BASE_URL || `${window.location.protocol}//${window.location.host}`; const resp = await fetch( `${baseUrl}/api/v1/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/files/upload`, { method: 'POST', headers: token ? { Authorization: `Bearer ${token}` } : {}, body: formData, }, ); const json = await resp.json(); if (!resp.ok || json.code !== 200) throw new Error(json.message || `HTTP ${resp.status}`); return json.data; }, scaleDeployment: async (clusterCode: string, namespace: string, name: string, replicas: number) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}/scale`, { replicas }); }, restartDeployment: async (clusterCode: string, namespace: string, name: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}/restart`); }, pauseDeployment: async (clusterCode: string, namespace: string, name: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}/pause`); }, resumeDeployment: async (clusterCode: string, namespace: string, name: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}/resume`); }, updateDeploymentImage: async (clusterCode: string, namespace: string, name: string, image: string, containerName?: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}/update-image`, { image, container_name: containerName }); }, // === HPA 管理 (Logan.Li) === getHPAs: async (clusterCode: string, namespace?: string) => { if (namespace) return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/hpas`); return http.get(`/kubernetes/${clusterCode}/hpas`); // AllNS route }, createHPA: async (clusterCode: string, data: { name: string; namespace: string; targetKind: string; targetName: string; minReplicas: number; maxReplicas: number; cpuPercent: number; memPercent: number }) => { return http.post(`/kubernetes/${clusterCode}/namespace/${data.namespace}/hpas`, data); }, deleteHPA: async (clusterCode: string, namespace: string, name: string) => { return http.delete(`/kubernetes/${clusterCode}/namespace/${namespace}/hpas/${name}`); }, // === CronJob 管理 (Logan.Li) === getCronJobs: async (clusterCode: string, namespace?: string) => { if (namespace) return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/cronjobs`); return http.get(`/kubernetes-clusters/${clusterCode}/cronjobs`); }, createCronJob: async (clusterCode: string, data: { name: string; namespace: string; schedule: string; image: string; command: string[] }) => { return http.post(`/kubernetes/${clusterCode}/namespace/${data.namespace}/cronjobs`, data); }, suspendCronJob: async (clusterCode: string, namespace: string, name: string, suspend: boolean) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/cronjobs/${name}/suspend`, { suspend }); }, triggerCronJob: async (clusterCode: string, namespace: string, name: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/cronjobs/${name}/trigger`); }, deleteCronJob: async (clusterCode: string, namespace: string, name: string) => { return http.delete(`/kubernetes/${clusterCode}/namespace/${namespace}/cronjobs/${name}`); }, // === Namespace-scoped resources (Kuboard-style) === getNamespaceDeployments: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments`); }, getNamespacePods: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/pods`); }, getNamespaceServices: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/services`); }, getNamespaceIngresses: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/ingresses`); }, getNamespaceConfigMaps: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/configmaps`); }, getNamespaceSecrets: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/secrets`); }, // === Namespace (Logan.Li) === getNamespaces: async (clusterCode: string) => { return http.get(`/kubernetes/${clusterCode}/namespaces`); }, // === All-namespace listing (Logan.Li) === getAllDeployments: async (clusterCode: string) => { return http.get(`/kubernetes/${clusterCode}/deployments`); }, getAllPods: async (clusterCode: string) => { return http.get(`/kubernetes/${clusterCode}/pods`); }, // === PVC (存储) === getPVCs: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/pvcs`); }, deletePVC: async (clusterCode: string, namespace: string, name: string) => { return http.delete(`/kubernetes/${clusterCode}/namespace/${namespace}/pvcs/${name}`); }, // === StatefulSet / DaemonSet === getStatefulSets: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/statefulsets`); }, getDaemonSets: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/daemonsets`); }, // === YAML === getResourceYaml: async (clusterCode: string, namespace: string, resource: string, name: string) => { return http.getRaw(`/kubernetes/${clusterCode}/namespace/${namespace}/${resource}/${name}/yaml`); }, updateResourceYaml: async (clusterCode: string, namespace: string, resource: string, name: string, yaml: string) => { return http.putRaw(`/kubernetes/${clusterCode}/namespace/${namespace}/${resource}/${name}/yaml`, yaml, 'application/yaml'); }, // === Export CSV === exportAsCSV: (filename: string, data: any[]) => { if (data.length === 0) return; const headers = Object.keys(data[0]); const csv = [headers.join(','), ...data.map((row: any) => headers.map(h => JSON.stringify(row[h] ?? '')).join(','))].join('\n'); const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; a.click(); }, exportAsJSON: (filename: string, data: any[]) => { const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; a.click(); }, // === 🆕 Pod Metrics (kubectl top pods) === getPodMetrics: async (clusterCode: string, namespace?: string) => { const params = namespace ? { namespace } : undefined; return http.get(`/kubernetes/${clusterCode}/metrics/pods`, params); }, // === 🆕 Events === getEvents: async (clusterCode: string, namespace?: string, type?: string) => { const params: any = {}; if (namespace) params.namespace = namespace; if (type) params.type = type; return http.get(`/kubernetes/${clusterCode}/events`, params); }, // === 🆕 Capacity Analysis === getCapacity: async (clusterCode: string, namespace?: string) => { const params = namespace ? { namespace } : undefined; return http.get(`/kubernetes/${clusterCode}/capacity`, params); }, // === 🆕 YAML Apply === applyYAML: async (clusterCode: string, yaml: string) => { return http.post(`/kubernetes/${clusterCode}/yaml/apply`, { yaml }); }, // === 🆕 ReplicaSet / Revision / Rollback (Logan.Li — Kuboard-style) === // 后端并行开发中,前端按约定路径预先对接;后端就绪后自动生效。 getReplicaSets: async (clusterCode: string, namespace: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/replicasets`); }, getDeploymentRevisions: async (clusterCode: string, namespace: string, name: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}/revisions`); }, rollbackDeployment: async (clusterCode: string, namespace: string, name: string, revision: number) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}/rollback`, { revision }); }, // 单个 Deployment 详情(如后端提供专用端点则用之,否则从列表过滤) getDeployment: async (clusterCode: string, namespace: string, name: string) => { try { return await http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${name}`); } catch { // 降级:从列表中过滤 const res: any = await http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/deployments`); const list = Array.isArray(res) ? res : (res?.list || res?.items || res?.data || []); return list.find((d: any) => d.name === name) || null; } }, }; // ==================== 告警 API ==================== export const alertApi = { getAlerts: async (params?: { page?: number; page_size?: number; status?: string; severity?: string }) => { return http.get('/alerts', params); }, getRules: async () => { return http.get('/alerts/rules'); }, createRule: async (data: any) => { return http.post('/alerts/rules', data); }, updateRule: async (id: number | string, data: any) => { return http.put(`/alerts/rules/${id}`, data); }, deleteRule: async (id: number | string) => { return http.delete(`/alerts/rules/${id}`); }, batchDeleteRules: async (ids: number[]) => { return http.delete('/alerts/rules/batch-delete', { ids }); }, toggleRule: async (id: number | string) => { return http.post(`/alerts/rules/${id}/toggle`); }, acknowledge: async (id: number | string) => { return http.post(`/alerts/${id}/ack`); }, // 通知渠道管理 getNotifyChannels: async () => { return http.get('/alerts/notify-channels'); }, createNotifyChannel: async (data: any) => { return http.post('/alerts/notify-channels', data); }, updateNotifyChannel: async (id: number | string, data: any) => { return http.put(`/alerts/notify-channels/${id}`, data); }, deleteNotifyChannel: async (id: number | string) => { return http.delete(`/alerts/notify-channels/${id}`); }, testNotifyChannel: async (id: number | string) => { return http.post(`/alerts/notify-channels/${id}/test`); }, }; // ==================== 流水线 API ==================== export const pipelineApi = { getPipelines: async (params?: { page?: number; page_size?: number }) => { return http.get('/pipelines', params); }, getPipelineDetail: async (id: number | string) => { return http.get(`/pipelines/${id}`); }, createPipeline: async (data: any) => { return http.post('/pipelines', data); }, updatePipeline: async (id: number | string, data: any) => { return http.put(`/pipelines/${id}`, data); }, deletePipeline: async (id: number | string) => { return http.delete(`/pipelines/${id}`); }, triggerPipeline: async (id: number | string) => { return http.post(`/pipelines/${id}/trigger`); }, getRuns: async (id: number | string) => { return http.get(`/pipelines/${id}/runs`); }, // 运行控制 stopRun: async (pipelineId: number | string, runId: number | string) => { return http.post(`/pipelines/${pipelineId}/runs/${runId}/stop`); }, getRunLog: async (pipelineId: number | string, runId: number | string) => { return http.get(`/pipelines/${pipelineId}/runs/${runId}/log`); }, // 成员管理 getMembers: async (pipelineId: number | string) => { return http.get(`/pipelines/${pipelineId}/members`); }, addMember: async (pipelineId: number | string, data: { username: string; role: string }) => { return http.post(`/pipelines/${pipelineId}/members`, data); }, removeMember: async (pipelineId: number | string, memberId: number | string) => { return http.delete(`/pipelines/${pipelineId}/members/${memberId}`); }, // 组授权 assignGroup: async (pipelineId: number | string, data: { group_id: number; role: string }) => { return http.post(`/pipelines/${pipelineId}/groups`, data); }, }; // ==================== 应用发布 API ==================== export const releaseApi = { getReleases: async (params?: { page?: number; page_size?: number }) => { return http.get('/releases', params); }, createRelease: async (data: any) => { return http.post('/releases', data); }, approveRelease: async (id: number | string) => { return http.post(`/releases/${id}/approve`); }, rejectRelease: async (id: number | string) => { return http.post(`/releases/${id}/reject`); }, deployRelease: async (id: number | string) => { return http.post(`/releases/${id}/deploy`); }, rollbackRelease: async (id: number | string) => { return http.post(`/releases/${id}/rollback`); }, }; // ==================== 定时任务 API ==================== export const cronJobApi = { getJobs: async (params?: { page?: number; page_size?: number }) => { return http.get('/cron-jobs', params); }, getJob: async (id: number | string) => { return http.get(`/cron-jobs/${id}`); }, createJob: async (data: any) => { return http.post('/cron-jobs', data); }, updateJob: async (id: number | string, data: any) => { return http.put(`/cron-jobs/${id}`, data); }, deleteJob: async (id: number | string) => { return http.delete(`/cron-jobs/${id}`); }, toggleJob: async (id: number | string) => { return http.post(`/cron-jobs/${id}/toggle`); }, executeJob: async (id: number | string) => { return http.post(`/cron-jobs/${id}/execute`); }, batchDelete: async (ids: number[]) => { return http.delete('/cron-jobs/batch-delete', { ids }); }, }; // ==================== 脚本 API ==================== export const scriptApi = { getScripts: async (params?: { page?: number; page_size?: number }) => { return http.get('/scripts', params); }, getScript: async (id: number | string) => { return http.get(`/scripts/${id}`); }, createScript: async (data: any) => { return http.post('/scripts', data); }, updateScript: async (id: number | string, data: any) => { return http.put(`/scripts/${id}`, data); }, deleteScript: async (id: number | string) => { return http.delete(`/scripts/${id}`); }, batchDelete: async (ids: number[]) => { return http.post('/scripts/batch-delete', { ids }); }, executeScript: async (id: number | string) => { return http.post(`/scripts/${id}/execute`); }, }; // ==================== 任务执行记录 API ==================== export const taskExecutionApi = { getExecutions: async (params?: { task_type?: string; task_id?: string; page?: number; page_size?: number }) => { return http.get('/task-executions', params); }, }; // ==================== 审批流程 API ==================== export const approvalApi = { getTemplates: async () => { return http.get('/approvals/templates'); }, getTemplate: async (id: number | string) => { return http.get(`/approvals/templates/${id}`); }, createTemplate: async (data: any) => { return http.post('/approvals/templates', data); }, updateTemplate: async (id: number | string, data: any) => { return http.put(`/approvals/templates/${id}`, data); }, deleteTemplate: async (id: number | string) => { return http.delete(`/approvals/templates/${id}`); }, getProcesses: async (params?: { page?: number; page_size?: number; status?: string }) => { return http.get('/approvals/processes', params); }, createProcess: async (data: any) => { return http.post('/approvals/processes', data); }, approveProcess: async (id: number | string, comment?: string) => { return http.post(`/approvals/processes/${id}/approve`, { comment }); }, rejectProcess: async (id: number | string, comment?: string) => { return http.post(`/approvals/processes/${id}/reject`, { comment }); }, }; // ==================== 云管理 API ==================== export const cloudApi = { getAccounts: async () => { return http.get('/cloud-accounts'); }, createAccount: async (data: any) => { return http.post('/cloud-accounts', data); }, updateAccount: async (id: number | string, data: any) => { return http.put(`/cloud-accounts/${id}`, data); }, deleteAccount: async (id: number | string) => { return http.delete(`/cloud-accounts/${id}`); }, // ==================== 云同步 API ==================== // 同步云账号的区域列表到本地 syncRegions: async (id: number | string) => { return http.post(`/cloud-accounts/${id}/sync-regions`); }, // 获取云账号的区域列表 getAccountRegions: async (id: number | string) => { return http.get(`/cloud-accounts/${id}/regions`); }, // 发现指定区域的云主机实例 discoverInstances: async (id: number | string, regions: string[]) => { return http.post<{ list: any[]; total: number; errors: string[] }>(`/cloud-accounts/${id}/discover-instances`, { regions }); }, // 导入选中的云主机实例为本地服务器 importInstances: async (id: number | string, instances: any[], opts?: { default_port?: number; credential_id?: number }) => { return http.post<{ imported: number; skipped: number }>(`/cloud-accounts/${id}/import-instances`, { instances, ...opts }); }, getDomains: async (params?: { page?: number; page_size?: number; keyword?: string }) => { return http.get('/domains', params); }, createDomain: async (data: any) => { return http.post('/domains', data); }, updateDomain: async (id: number | string, data: any) => { return http.put(`/domains/${id}`, data); }, deleteDomain: async (id: number | string) => { return http.delete(`/domains/${id}`); }, }; // ==================== 云平台 API ==================== export const platformApi = { list: async () => { return http.get('/platforms'); }, create: async (data: any) => { return http.post('/platforms', data); }, update: async (id: number | string, data: any) => { return http.put(`/platforms/${id}`, data); }, delete: async (id: number | string) => { return http.delete(`/platforms/${id}`); }, }; // ==================== 云账单 API ==================== export const billingApi = { getStats: async () => { return http.get('/cloud-billings/stats'); }, list: async (params?: { provider?: string; start_date?: string; end_date?: string; page?: number; page_size?: number }) => { return http.get('/cloud-billings', params); }, create: async (data: any) => { return http.post('/cloud-billings', data); }, batchCreate: async (records: any[]) => { return http.post('/cloud-billings/batch', { records }); }, remove: async (id: number | string) => { return http.delete(`/cloud-billings/${id}`); }, getProviders: async () => { return http.get('/cloud-billings/providers'); }, // 预警规则 listAlertRules: async () => { return http.get('/billing-alerts'); }, createAlertRule: async (data: any) => { return http.post('/billing-alerts', data); }, updateAlertRule: async (id: number | string, data: any) => { return http.put(`/billing-alerts/${id}`, data); }, deleteAlertRule: async (id: number | string) => { return http.delete(`/billing-alerts/${id}`); }, toggleAlertRule: async (id: number | string) => { return http.post(`/billing-alerts/${id}/toggle`); }, }; // ==================== Agent 监控 API ==================== export const agentApi = { getLatest: () => http.get('/agent/latest'), getHistory: (hostname: string, hours = 1) => http.get(`/agent/history/${hostname}?hours=${hours}`), createTask: (data: { hostname: string; type: string; command: string; timeout?: number }) => http.post('/agent/tasks', data), listTasks: (params?: Record) => http.get('/agent/tasks', params), // 直连 SSH 执行命令(即时返回) execCommand: (data: { hostname: string; command: string; timeout?: number }) => http.post('/agent/exec', data), // 聚合查询 getAggregate: (params: { hostname: string; metric: string; interval: string; from?: string; to?: string }) => http.get('/agent/aggregate', params), // 触发聚合 aggregate: (data: { hostname: string; metric: string }) => http.post('/agent/aggregate', data), listRules: () => http.get('/agent/rules'), createRule: (data: any) => http.post('/agent/rules', data), updateRule: (id: number, data: any) => http.put(`/agent/rules/${id}`, data), deleteRule: (id: number) => http.delete(`/agent/rules/${id}`), quickSetup: (type?: string) => http.post('/agent/rules/quick-setup', { type: type || 'ecs' }), // Agent 安装/卸载 uninstallAgent: (data: { hostname?: string; server_id?: number }) => http.post('/agent/uninstall', data), batchUninstall: (serverIds: number[]) => http.post('/agent/batch-uninstall', { server_ids: serverIds }), batchUpgrade: (serverIds: number[]) => http.post('/agent/batch-upgrade', { server_ids: serverIds }), listAlerts: (params?: Record) => http.get('/agent/alerts', params), resolveAlert: (id: number) => http.post(`/agent/alerts/${id}/resolve`), }; // ==================== 区域管理 API ==================== export const regionApi = { list: async () => { return http.get('/regions'); }, create: async (data: any) => { return http.post('/regions', data); }, update: async (id: number | string, data: any) => { return http.put(`/regions/${id}`, data); }, delete: async (id: number | string) => { return http.delete(`/regions/${id}`); }, // 批量删除 // Logan.Li batchDelete: async (ids: number[]) => { return http.delete('/regions/batch-delete', { ids }); }, }; // ==================== 资源模型 API ==================== export const resourceModelApi = { list: async () => { return http.get('/resource-models'); }, create: async (data: any) => { return http.post('/resource-models', data); }, update: async (id: number | string, data: any) => { return http.put(`/resource-models/${id}`, data); }, delete: async (id: number | string) => { return http.delete(`/resource-models/${id}`); }, }; // ==================== 审计日志 API ==================== export const auditApi = { getLogs: async (params?: { page?: number; page_size?: number; module?: string; action?: string }) => { return http.get('/audit/logs', params); }, getModules: async () => { return http.get('/audit/modules'); }, getActions: async () => { return http.get('/audit/actions'); }, getStatistics: async () => { return http.get('/audit/statistics'); }, clearLogs: async () => { return http.post('/audit/logs/clear'); }, }; // ==================== 终端会话审计 API(WebSSH 录制) ==================== // Logan.Li export const terminalSessionApi = { list: async (params?: { page?: number; page_size?: number; server_id?: number; username?: string; status?: string }) => { return http.get('/terminal-sessions', params); }, get: async (id: number) => { return http.get(`/terminal-sessions/${id}`); }, getRecord: async (id: number) => { return http.get(`/terminal-sessions/${id}/record`); }, delete: async (id: number) => { return http.delete(`/terminal-sessions/${id}`); }, batchDelete: async (ids: number[]) => { return http.post('/terminal-sessions/batch-delete', { ids }); }, }; // ==================== 发布系统 API(仓库/镜像仓库认证) ==================== export const deployApi = { // Git 仓库认证 getRepos: () => http.get('/deploy/repos'), getRepo: (id: number | string) => http.get(`/deploy/repos/${id}`), createRepo: (data: any) => http.post('/deploy/repos', data), updateRepo: (id: number | string, data: any) => http.put(`/deploy/repos/${id}`, data), deleteRepo: (id: number | string) => http.delete(`/deploy/repos/${id}`), // 镜像仓库认证 getRegistries: () => http.get('/deploy/registries'), getRegistry: (id: number | string) => http.get(`/deploy/registries/${id}`), createRegistry: (data: any) => http.post('/deploy/registries', data), updateRegistry: (id: number | string, data: any) => http.put(`/deploy/registries/${id}`, data), deleteRegistry: (id: number | string) => http.delete(`/deploy/registries/${id}`), // 环境管理 getEnvironments: () => http.get('/deploy/environments'), createEnvironment: (data: any) => http.post('/deploy/environments', data), updateEnvironment: (id: number | string, data: any) => http.put(`/deploy/environments/${id}`, data), deleteEnvironment: (id: number | string) => http.delete(`/deploy/environments/${id}`), // Item Role 权限管理 (Jenkins Role Strategy 风格) getItemRoles: () => http.get('/admin/item-roles'), createItemRole: (data: any) => http.post('/admin/item-roles', data), updateItemRole: (id: number | string, data: any) => http.put(`/admin/item-roles/${id}`, data), deleteItemRole: (id: number | string) => http.delete(`/admin/item-roles/${id}`), getUserItemRoles: (userId: number | string) => http.get(`/admin/users/${userId}/item-roles`), assignItemRole: (userId: number | string, data: { user_id: number; item_role_id: number }) => http.post(`/admin/users/${userId}/item-roles`, data), removeItemRole: (userId: number | string, roleId: number | string) => http.delete(`/admin/users/${userId}/item-roles/${roleId}`), // 用户组管理 getGroups: () => http.get('/deploy/groups'), createGroup: (data: any) => http.post('/deploy/groups', data), updateGroup: (id: number | string, data: any) => http.put(`/deploy/groups/${id}`, data), deleteGroup: (id: number | string) => http.delete(`/deploy/groups/${id}`), getGroupMembers: (id: number | string) => http.get(`/deploy/groups/${id}/members`), addGroupMember: (id: number | string, data: { username: string }) => http.post(`/deploy/groups/${id}/members`, data), removeGroupMember: (id: number | string, memberId: number | string) => http.delete(`/deploy/groups/${id}/members/${memberId}`), // 工具配置 // Logan.Li getTools: () => http.get('/deploy/tools'), getTool: (id: number | string) => http.get(`/deploy/tools/${id}`), updateTool: (id: number | string, data: any) => http.put(`/deploy/tools/${id}`, data), installTool: (id: number | string) => http.post(`/deploy/tools/${id}/install`), checkTool: (id: number | string) => http.post(`/deploy/tools/${id}/check`), }; // ==================== Ansible 自动化 API ==================== export const ansibleApi = { // Playbook 管理 getPlaybooks: async (params?: { page?: number; page_size?: number; keyword?: string }) => { return http.get('/ansible/playbooks', params); }, getPlaybook: async (id: number | string) => { return http.get(`/ansible/playbooks/${id}`); }, createPlaybook: async (data: any) => { return http.post('/ansible/playbooks', data); }, updatePlaybook: async (id: number | string, data: any) => { return http.put(`/ansible/playbooks/${id}`, data); }, deletePlaybook: async (id: number | string) => { return http.delete(`/ansible/playbooks/${id}`); }, // 批量删除 Playbook // Logan.Li batchDeletePlaybooks: async (ids: number[]) => { return http.delete('/ansible/playbooks/batch-delete', { ids }); }, executePlaybook: async (id: number | string, targetHosts?: string[]) => { return http.post(`/ansible/playbooks/${id}/execute`, targetHosts ? { target_hosts: targetHosts } : undefined); }, // 克隆 Playbook 为自定义模板 // Logan.Li clonePlaybook: async (id: number | string, name?: string) => { return http.post(`/ansible/playbooks/${id}/clone`, name ? { name } : undefined); }, // 主机匹配预览 // Logan.Li matchHosts: async (params: { pattern?: string; match_type?: string; page?: number; page_size?: number }) => { return http.get('/ansible/match-hosts', params); }, // 任务执行记录 getJobs: async (params?: { page?: number; page_size?: number; status?: string }) => { return http.get('/ansible/jobs', params); }, getJob: async (id: number | string) => { return http.get(`/ansible/jobs/${id}`); }, // 清单(Inventory)管理 getInventories: async (params?: { page?: number; page_size?: number; keyword?: string }) => { return http.get('/ansible/inventories', params); }, createInventory: async (data: any) => { return http.post('/ansible/inventories', data); }, updateInventory: async (id: number | string, data: any) => { return http.put(`/ansible/inventories/${id}`, data); }, deleteInventory: async (id: number | string) => { return http.delete(`/ansible/inventories/${id}`); }, }; // ==================== 告警中心 API(基于 /alerts 真实路由) ==================== // 说明:alertCenterApi 与 alertApi 共用同一套后端 /alerts 路由,此处独立封装以便告警中心页使用 export const alertCenterApi = { getAlerts: async (params?: { page?: number; page_size?: number; status?: string; severity?: string }) => { return http.get('/alerts', params); }, getRules: async () => { return http.get('/alerts/rules'); }, getChannels: async () => { return http.get('/alerts/notify-channels'); }, acknowledge: async (id: number | string) => { return http.post(`/alerts/${id}/ack`); }, resolve: async (id: number | string) => { return http.post(`/alerts/${id}/ack`); }, silence: async (_id: number | string, _duration: string) => ({ success: true }), toggleRule: async (id: number | string) => { return http.post(`/alerts/rules/${id}/toggle`); }, createRule: async (data: any) => { return http.post('/alerts/rules', data); }, deleteRule: async (id: number | string) => { return http.delete(`/alerts/rules/${id}`); }, // 一键报警 — 后端批量创建默认规则 quickSetup: async (type: string = 'ecs') => { return http.post('/alerts/quick-setup', { type }); }, }; // ==================== 站点拨测 API ==================== export const siteApi = { getStats: async () => { return http.get('/site/stats'); }, getTasks: async () => { return http.get<{ list: any[]; total: number }>('/site/tasks'); }, createTask: async (data: { name: string; url: string; type: string; frequency_sec?: number; timeout_sec?: number; expected_status_code?: number }) => { return http.post('/site/tasks', data); }, deleteTask: async (id: number | string) => { return http.delete(`/site/tasks/${id}`); }, toggleTask: async (id: number | string, enabled: boolean) => { return http.put(`/site/tasks/${id}`, { enabled }); }, }; // ==================== 链路追踪 API(暂无后端,返回空结果优雅降级) ==================== export const traceApi = { getTraces: async (_params: any) => ({ data: [], total: 0 }), getServices: async (_timeRange: string) => ({ data: [], total: 0 }), getTraceDetail: async (_traceId: string) => ({ data: { spans: [] } }), }; // ==================== Grafana 仪表盘 API(暂无后端,返回空结果优雅降级) ==================== export const dashboardApi = { getDashboards: async () => ({ data: [], total: 0 }), getDatasources: async () => ({ data: [], total: 0 }), createDashboard: async (_data: any) => ({ success: false }), updateDashboard: async (_id: number | string, _data: any) => ({ success: false }), deleteDashboard: async (_id: number | string) => ({ success: false }), createDatasource: async (_data: any) => ({ success: false }), }; // ==================== Prometheus 数据源管理 API ==================== // Logan.Li export const promDatasourceApi = { list: () => http.get('/prometheus-datasources'), get: (id: number | string) => http.get(`/prometheus-datasources/${id}`), create: (data: any) => http.post('/prometheus-datasources', data), update: (id: number | string, data: any) => http.put(`/prometheus-datasources/${id}`, data), delete: (id: number | string) => http.delete(`/prometheus-datasources/${id}`), setDefault: (id: number | string) => http.post(`/prometheus-datasources/${id}/default`), testConnection: (data: any) => http.post('/prometheus-datasources/test', data), }; // ==================== Zabbix 数据源管理 API ==================== // Logan.Li export const zabbixDatasourceApi = { list: () => http.get('/zabbix-datasources'), get: (id: number | string) => http.get(`/zabbix-datasources/${id}`), create: (data: any) => http.post('/zabbix-datasources', data), update: (id: number | string, data: any) => http.put(`/zabbix-datasources/${id}`, data), delete: (id: number | string) => http.delete(`/zabbix-datasources/${id}`), setDefault: (id: number | string) => http.post(`/zabbix-datasources/${id}/default`), testConnection: (data: any) => http.post('/zabbix-datasources/test', data), }; // AI 智能助手 API(后端 LLM 配置 + 对话代理,OpenAI 兼容) // Logan.Li export interface LLMConfigData { configured?: boolean; provider: string; model: string; api_key: string; endpoint: string; temperature: number; max_tokens: number; system_prompt: string; enabled: boolean; } export interface ChatResult { content: string; model: string; tokens_used: number; } // 对话历史条目(后端 ChatHistory 序列化) // Logan.Li export interface ChatHistoryItem { id: number; user_id: number; session_id: string; role: 'user' | 'assistant'; content: string; model?: string; created_at: string; } export const aiApi = { // 获取 LLM 配置(API Key 已脱敏) getConfig: () => http.get('/aiops/llm-config'), // 保存 LLM 配置(api_key 若为脱敏值则后端保留原值) saveConfig: (data: LLMConfigData) => http.put('/aiops/llm-config', data), // 测试 LLM 连接(发送 ping) testConnection: () => http.post('/aiops/llm-config/test'), // AI 对话(messages 含历史 + 当前用户消息,session_id 区分多轮对话) chat: (messages: { role: 'user' | 'assistant'; content: string }[], sessionId?: string) => http.post('/aiops/chat', { messages, session_id: sessionId || '' }), // AI 流式对话(SSE)。逐块回调 onChunk,返回完整内容。错误抛出 Error。 // 使用裸 fetch 绕过 http.ts(后者会 await json() 一次性消费) // Logan.Li chatStream: async ( messages: { role: 'user' | 'assistant'; content: string }[], onChunk: (delta: string) => void, signal?: AbortSignal, sessionId?: string, ): Promise => { const token = useUserStore.getState().token; const res = await fetch('/api/v1/aiops/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), }, body: JSON.stringify({ messages, session_id: sessionId || '' }), signal, }); if (res.status === 401) { useUserStore.getState().logout(); window.location.href = '/login'; throw new Error('登录已过期,请重新登录'); } if (!res.body) throw new Error('流式响应不可用'); const reader = res.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; let fullContent = ''; for (;;) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // SSE 事件之间以 \n\n 分隔 const events = buffer.split('\n\n'); buffer = events.pop() ?? ''; for (const evt of events) { const lines = evt.split('\n'); let eventType = 'message'; let dataLines: string[] = []; for (const ln of lines) { if (ln.startsWith('event:')) eventType = ln.slice(6).trim(); else if (ln.startsWith('data:')) dataLines.push(ln.slice(5).trim()); } const data = dataLines.join(''); if (!data) continue; if (eventType === 'error') { // {code, message} try { const obj = JSON.parse(data); throw new Error(obj.message || 'LLM 流式响应错误'); } catch (parseErr) { if (parseErr instanceof Error && parseErr.message) throw parseErr; throw new Error('LLM 流式响应错误'); } } if (eventType === 'done') { // 服务端结束(可能含 model 字段) continue; } // message:data 是 {"content":"..."} try { const obj = JSON.parse(data); if (typeof obj.content === 'string') { fullContent += obj.content; onChunk(obj.content); } } catch { // 非 JSON 数据忽略(如 [DONE] 或注释) } } } return fullContent; }, // 获取当前用户对话历史(按 session_id 过滤,默认最新 session) getHistory: (sessionId?: string) => { const params = sessionId ? `?session_id=${encodeURIComponent(sessionId)}` : ''; return http.get<{ list: ChatHistoryItem[]; total: number }>(`/aiops/chat-history${params}`); }, // 清空当前用户对话历史 clearHistory: () => http.delete('/aiops/chat-history'), }; // ==================== 凭据管理 API(SSH密钥/API Token/密码) ==================== // Logan.Li export const credentialApi = { list: () => http.get<{ list: any[]; total: number }>('/credentials'), create: (data: { name: string; type: string; content: string; description?: string }) => http.post('/credentials', data), update: (id: number | string, data: { name: string; type: string; content?: string; description?: string }) => http.put(`/credentials/${id}`, data), delete: (id: number | string) => http.delete(`/credentials/${id}`), }; // 服务器凭据关联 API export const serverCredentialApi = { assign: (serverId: number | string, credentialId: number) => http.post(`/servers/${serverId}/assign-credential`, { credential_id: credentialId }), };