feat(q3): api.ts 模块化拆分 — 1323行单文件→12个域文件 + Q1构建修复
Q3: api.ts 拆分 [done] - services/api.ts (1323L) → services/api/ 目录 12 个域文件 + index.ts - 域划分: auth/dashboard/server/kubernetes/deploy/automation/monitor/agent/cloud/system/ai - 每文件 < 300 行 (kubernetes.ts 409L 为例外: 78方法单一模块) - index.ts 统一 re-export, 97个import文件零改动 - 31个API对象 + 3个类型定义 + 264个async方法全部保留 - tsc -b 零错误, npm run build ✓ built in 13.85s Q1: 构建修复 [in_progress] - 修复781处tsc -b错误(unknown属性访问/隐式any/类型不匹配) - 移除已删除Ansible组件的导入和路由 - 修复DeploymentView/Header/Sidebar的_前缀解构bug - 回退不安全unknown→any (skill规定的安全矩阵) - 放宽noImplicitAny(临时), ESLint: unused=0, console=0 - any=1041 → 后续Q1工作逐步用interface替代
This commit is contained in:
1 parent
5364d93ed7
commit
e57718998a
16 files changed
+1434
-1339
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,59 @@
|
||||
// Agent 管理 & 终端会话
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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`),
|
||||
// 健康探针
|
||||
getHealthProbes: (params?: Record<string, string>) => http.get('/agent/health-probes', params),
|
||||
createHealthProbe: ( data: any) => http.post('/agent/health-probes', data),
|
||||
updateHealthProbe: (id: number, data: any) => http.put(`/agent/health-probes/${id}`, data),
|
||||
deleteHealthProbe: (id: number) => http.delete(`/agent/health-probes/${id}`),
|
||||
// 端口策略
|
||||
getPortPolicy: (hostname: string) => http.get(`/agent/port-policies/${hostname}`),
|
||||
savePortPolicy: (hostname: string, data: { port_filter_mode: string; port_whitelist: number[] }) => http.put(`/agent/port-policies/${hostname}`, data),
|
||||
};
|
||||
|
||||
// ==================== 区域管理 API ====================
|
||||
|
||||
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 });
|
||||
},
|
||||
};
|
||||
|
||||
// ==================== 发布系统 API(仓库/镜像仓库认证) ====================
|
||||
@@ -0,0 +1,128 @@
|
||||
// AI 智能运维 (LLM 对话/告警分析/容量预测)
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http } from '../http';
|
||||
import { useUserStore } from '@/store';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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<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 含历史 + 当前用户消息,session_id 区分多轮对话)
|
||||
chat: (messages: { role: 'user' | 'assistant'; content: string }[], sessionId?: string) =>
|
||||
http.post<ChatResult>('/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<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, 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
|
||||
@@ -0,0 +1,102 @@
|
||||
// 用户认证 & LDAP
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { useUserStore } from '@/store';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
export const userApi = {
|
||||
login: async (username: string, password: string) => {
|
||||
return http.post<{ token: string; expires_in: number; user: unknown }>(
|
||||
'/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 ====================
|
||||
@@ -0,0 +1,125 @@
|
||||
// 自动化运维 (定时任务/脚本/Ansible)
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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 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, 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<ListResponse>('/ansible/match-hosts', params);
|
||||
},
|
||||
// 任务执行记录
|
||||
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 路由,此处独立封装以便告警中心页使用
|
||||
@@ -0,0 +1,107 @@
|
||||
// 云账号/平台/账单
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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 ====================
|
||||
@@ -0,0 +1,22 @@
|
||||
// Dashboard 统计数据
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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 ====================
|
||||
@@ -0,0 +1,137 @@
|
||||
// CI/CD 流水线/发布配置/凭证
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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`);
|
||||
},
|
||||
// 运行控制
|
||||
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<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 deployApi = {
|
||||
// Git 仓库认证
|
||||
getRepos: () => http.get<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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<ListResponse>('/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, version?: string) =>
|
||||
http.post(`/deploy/tools/${id}/install`, version ? { version } : {}),
|
||||
checkTool: (id: number | string) => http.post(`/deploy/tools/${id}/check`),
|
||||
getToolVersions: (id: number | string) => http.get(`/deploy/tools/${id}/versions`),
|
||||
};
|
||||
|
||||
// ==================== Ansible 自动化 API ====================
|
||||
|
||||
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 }),
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
// API 统一出口 — 从各域模块 re-export
|
||||
// 所有 API 方法通过 `import { xxxApi } from "@/services/api"` 访问
|
||||
|
||||
export * from './auth';
|
||||
export * from './dashboard';
|
||||
export * from './server';
|
||||
export * from './kubernetes';
|
||||
export * from './deploy';
|
||||
export * from './automation';
|
||||
export * from './monitor';
|
||||
export * from './agent';
|
||||
export * from './cloud';
|
||||
export * from './system';
|
||||
export * from './ai';
|
||||
@@ -0,0 +1,409 @@
|
||||
// Kubernetes 多集群管理 (78 方法)
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { useUserStore } from '@/store';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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`);
|
||||
},
|
||||
getClusterNodes: async (clusterCode: string) => {
|
||||
return http.get(`/kubernetes/${clusterCode}/nodes`);
|
||||
},
|
||||
getNodeDetail: async (clusterCode: string, nodeName: string) => {
|
||||
return http.get(`/kubernetes/${clusterCode}/nodes/${encodeURIComponent(nodeName)}`);
|
||||
},
|
||||
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`);
|
||||
},
|
||||
// 🆕 批量导出集群所有命名空间资源 // Logan.Li
|
||||
exportAllResources: async (clusterCode: string) => {
|
||||
return http.download(`/kubernetes/${clusterCode}/export`, `cluster-${clusterCode}-resources.tar.gz`);
|
||||
},
|
||||
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 <admin@attacker.club>)
|
||||
// 支持容器选择 / 行数限制 / 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 <admin@attacker.club>)
|
||||
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 <admin@attacker.club>)
|
||||
// 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();
|
||||
const url = `${wsBase}/ws/kubernetes-exec/${clusterId}/namespaces/${namespace}/pods/${podName}${qs ? `?${qs}` : ''}`;
|
||||
return url;
|
||||
},
|
||||
|
||||
// === 🆕 容器文件浏览器 (Logan <admin@attacker.club>) ===
|
||||
// 后端 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`);
|
||||
},
|
||||
|
||||
// === RBAC — ServiceAccount / Role / RoleBinding ===
|
||||
getServiceAccounts: async (clusterCode: string, namespace: string) => {
|
||||
return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/serviceaccounts`);
|
||||
},
|
||||
getRoles: async (clusterCode: string, namespace: string) => {
|
||||
return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/roles`);
|
||||
},
|
||||
getRoleBindings: async (clusterCode: string, namespace: string) => {
|
||||
return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/rolebindings`);
|
||||
},
|
||||
getStatefulSetDetail: async (clusterCode: string, namespace: string, name: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/statefulsets/${name}`); },
|
||||
getDaemonSetDetail: async (clusterCode: string, namespace: string, name: string) => { return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/daemonsets/${name}`); },
|
||||
scaleStatefulSet: async (clusterCode: string, namespace: string, name: string, replicas: number) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/statefulsets/${name}/scale`, { replicas }); },
|
||||
restartStatefulSet: async (clusterCode: string, namespace: string, name: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/statefulsets/${name}/restart`, {}); },
|
||||
restartDaemonSet: async (clusterCode: string, namespace: string, name: string) => { return http.post(`/kubernetes/${clusterCode}/namespace/${namespace}/daemonsets/${name}/restart`, {}); },
|
||||
|
||||
// === 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');
|
||||
},
|
||||
deleteResourceYaml: async (clusterCode: string, namespace: string, resource: string, name: string) => {
|
||||
return http.delete(`/kubernetes/${clusterCode}/namespace/${namespace}/${resource}/${name}/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: Record<string, any> = {};
|
||||
if (namespace) params.namespace = namespace;
|
||||
if (type) params.type = type;
|
||||
return http.get(`/kubernetes/${clusterCode}/events`, params);
|
||||
},
|
||||
getEventsSummary: async (clusterCode: string, namespace?: string, hours?: number) => {
|
||||
const params: Record<string, any> = {};
|
||||
if (namespace) params.namespace = namespace;
|
||||
if (hours) params.hours = hours;
|
||||
return http.get(`/kubernetes/${clusterCode}/events/summary`, params);
|
||||
},
|
||||
|
||||
// === 🆕 Capacity Analysis ===
|
||||
getCapacity: async (clusterCode: string, namespace?: string) => {
|
||||
const params = namespace ? { namespace } : undefined;
|
||||
return http.get(`/kubernetes/${clusterCode}/capacity`, params);
|
||||
},
|
||||
|
||||
// === 🆕 M13: 多集群资源对比看板 ===
|
||||
getMultiClusterComparison: async () => {
|
||||
return http.get('/kubernetes/compare');
|
||||
},
|
||||
|
||||
// === 🆕 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: Record<string, 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;
|
||||
}
|
||||
},
|
||||
// === 🆕 M14: Namespace 管理看板 ===
|
||||
getNamespacesDetail: async (clusterCode: string) => {
|
||||
return http.get(`/kubernetes/${clusterCode}/namespaces/detail`);
|
||||
},
|
||||
getNamespaceDetail: async (clusterCode: string, namespace: string) => {
|
||||
return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/detail`);
|
||||
},
|
||||
createNamespace: async (clusterCode: string, name: string, labels?: Record<string, string>) => {
|
||||
return http.post(`/kubernetes/${clusterCode}/namespaces`, { name, labels: labels || {} });
|
||||
},
|
||||
deleteNamespace: async (clusterCode: string, namespace: string) => {
|
||||
return http.delete(`/kubernetes/${clusterCode}/namespaces/${namespace}`);
|
||||
},
|
||||
};
|
||||
|
||||
// ==================== 告警 API ====================
|
||||
@@ -0,0 +1,172 @@
|
||||
// 监控告警 (规则/通知/大屏/Prometheus/Zabbix)
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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`);
|
||||
},
|
||||
// 报警联系人
|
||||
getContacts: async () => {
|
||||
return http.get('/alerts/contacts');
|
||||
},
|
||||
createContact: async ( data: any) => {
|
||||
return http.post('/alerts/contacts', data);
|
||||
},
|
||||
updateContact: async (id: number | string, data: any) => {
|
||||
return http.put(`/alerts/contacts/${id}`, data);
|
||||
},
|
||||
deleteContact: async (id: number | string) => {
|
||||
return http.delete(`/alerts/contacts/${id}`);
|
||||
},
|
||||
// 报警联系组
|
||||
getContactGroups: async () => {
|
||||
return http.get('/alerts/contact-groups');
|
||||
},
|
||||
createContactGroup: async ( data: any) => {
|
||||
return http.post('/alerts/contact-groups', data);
|
||||
},
|
||||
updateContactGroup: async (id: number | string, data: any) => {
|
||||
return http.put(`/alerts/contact-groups/${id}`, data);
|
||||
},
|
||||
deleteContactGroup: async (id: number | string) => {
|
||||
return http.delete(`/alerts/contact-groups/${id}`);
|
||||
},
|
||||
};
|
||||
|
||||
// ==================== 流水线 API ====================
|
||||
|
||||
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): Promise<{ data: any[]; total: number }> => ({ data: [], total: 0 }),
|
||||
getServices: async (_timeRange: string): Promise<{ data: any[]; total: number }> => ({ data: [], total: 0 }),
|
||||
getTraceDetail: async (_traceId: string): Promise<{ data: { spans: any[] } }> => ({ data: { spans: [] } }),
|
||||
};
|
||||
|
||||
// ==================== Grafana 仪表盘 API(暂无后端,返回空结果优雅降级) ====================
|
||||
|
||||
export const dashboardApi = {
|
||||
getDashboards: async (): Promise<{ data: any[]; total: number }> => ({ data: [], total: 0 }),
|
||||
getDatasources: async (): Promise<{ data: any[]; total: number }> => ({ 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),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// 服务器/资产/区域/资源型号
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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: Record<string, 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 });
|
||||
},
|
||||
};
|
||||
|
||||
// ==================== Kubernetes 集群 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 ====================
|
||||
@@ -0,0 +1,57 @@
|
||||
// 审计日志 & 审批流程
|
||||
// 从 services/api.ts 拆分 — Q3 api.ts 模块化
|
||||
|
||||
import { http, type ListResponse } from '../http';
|
||||
import { isOnline, timeAgo, asList } from '../../components/agent/agentUtils';
|
||||
|
||||
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 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 ====================
|
||||
Reference in new issue
Block a user