Files
aiops/frontend/src/services/api.ts
T
attacker a3cd0d3910 feat: PTY kubectl终端 + 侧边栏对齐修复 + 构建缓存清理
**kubectl WebShell 修复:**
- 引入 github.com/creack/pty 库实现真正的伪终端
- 替换 bash --noediting + TERM=dumb 方案为 PTY-backed bash
- 支持 xterm-256color、窗口自适应、readline 交互
- 修复键盘输入无响应问题

**侧边栏优化:**
- 独立菜单项高度从 py-1.5 改为 h-8,与父菜单对齐
- 独立菜单图标从 h-3 w-3 改为 h-3.5 w-3.5,统一 14px

**构建优化:**
- run.sh docker up 增加 docker image prune -f 清理悬空镜像
- 每次部署自动清理 builder cache + dangling images

**认证修复(历史):**
- Token 存储:Zustand + localStorage 双写,http.ts 双源 fallback
- YamlViewer/DeploymentDetail token 来源统一为 Zustand store
2026-06-19 11:22:45 +00:00

996 lines
39 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.
// 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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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}`);
},
execPod: async (clusterCode: string, namespace: string, podName: string, command: string[], container?: string) => {
return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/pods/${podName}/exec`, { command, container });
},
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`);
},
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 });
},
};
// ==================== 告警 API ====================
export const alertApi = {
getAlerts: async (params?: { page?: number; page_size?: number; status?: string; severity?: string }) => {
return http.get<ListResponse>('/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<ListResponse>('/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`);
},
};
// ==================== 应用发布 API ====================
export const releaseApi = {
getReleases: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>(`/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<string, string>) => 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<string, string>) => http.get('/agent/alerts', params),
resolveAlert: (id: number) => http.post(`/agent/alerts/${id}/resolve`),
};
// ==================== 区域管理 API ====================
export const regionApi = {
list: async () => {
return http.get<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/terminal-sessions', params);
},
get: async (id: number) => {
return http.get(`/terminal-sessions/${id}`);
},
getRecord: async (id: number) => {
return http.get<string>(`/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 });
},
};
// ==================== Ansible 自动化 API ====================
export const ansibleApi = {
// Playbook 管理
getPlaybooks: async (params?: { page?: number; page_size?: number; keyword?: string }) => {
return http.get<ListResponse>('/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) => {
return http.post(`/ansible/playbooks/${id}/execute`);
},
// 任务执行记录
getJobs: async (params?: { page?: number; page_size?: number; status?: string }) => {
return http.get<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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;
role: 'user' | 'assistant';
content: string;
model?: string;
created_at: string;
}
export const aiApi = {
// 获取 LLM 配置(API Key 已脱敏)
getConfig: () => http.get<LLMConfigData>('/aiops/llm-config'),
// 保存 LLM 配置(api_key 若为脱敏值则后端保留原值)
saveConfig: (data: LLMConfigData) => http.put<LLMConfigData>('/aiops/llm-config', data),
// 测试 LLM 连接(发送 ping)
testConnection: () => http.post('/aiops/llm-config/test'),
// AI 对话(messages 含历史 + 当前用户消息)
chat: (messages: { role: 'user' | 'assistant'; content: string }[]) =>
http.post<ChatResult>('/aiops/chat', { messages }),
// 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,
): Promise<string> => {
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 }),
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;
},
// 获取当前用户对话历史(按时间正序,最近 100 条)
getHistory: () => http.get<{ list: ChatHistoryItem[]; total: number }>('/aiops/chat-history'),
// 清空当前用户对话历史
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 }),
};