feat: Ansible Playbook 全栈 CRUD + 真实执行 — 替换mock为后端持久化

后端:
- models/automation.go: Playbook 模型新增 Category/Tags 字段; 新增 CreatePlaybookRequest
- handlers/automation.go: 新增 runPlaybookLocal(写临时YML+ansible-playbook --connection=local执行)
  + randomCode(crypto/rand生成唯一code) + List/Get/Create/Update/Delete/Execute Playbook 完整CRUD
  Execute 创建TaskExecution(task_type=playbook)记录,采集stdout/stderr/退出码/耗时
- routes/routes.go: 注册 /playbooks 路由组(7个端点)

前端:
- services/api.ts: ansibleApi 从mock改为真实 /playbooks 后端调用
- AnsibleManager.tsx 完全重写:
  - Playbook管理: 真实数据卡片列表(id/name/category/tags/run_count/timeout) + 搜索/分类筛选
  - 新建/编辑对话框复用(编辑模式预填数据) — 新增编辑入口(原无编辑功能)
  - 查看内容: 终端风格YAML查看器 + 快捷编辑按钮
  - 执行: ConfirmProvider确认 + ansible-playbook本地执行 + toast结果反馈
  - 执行记录tab: 真实TaskExecution记录(任务名/状态/开始时间/耗时/日志查看)
  - 统计卡片: 总数/执行中/今日成功/今日失败(基于真实执行记录)
  - 移除原inventory占位tab(无后端支持,避免非功能按钮)
  - tags JSON数组与前端string[]互转映射
This commit is contained in:
attacker committed 2026-06-14 23:14:16 +08:00
1 parent 2807e4895b
commit ae6af93360
5 files changed
+772 -419

No files matched your search

File diff suppressed because it is too large. Load diff
+19 -7
View File
@@ -531,14 +531,26 @@ export const sqlAuditApi = {
toggleRule: async (ruleId: string, enabled: boolean) => ({ success: true }),
};
// Ansible API(无后端)
// Ansible Playbook API(后端 /playbooks)
export const ansibleApi = {
getPlaybooks: async () => ({ data: mockData.ansiblePlaybooks as any }),
getJobs: async () => ({ data: mockData.ansibleJobs as any }),
getInventories: async () => ({ data: mockData.ansibleInventories as any }),
createPlaybook: async (data: any) => ({ success: true, data: { id: Date.now().toString() } }),
deletePlaybook: async (id: string) => ({ success: true }),
executePlaybook: async (id: string, config: any) => ({ success: true, data: { jobId: Date.now().toString() } }),
getPlaybooks: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/playbooks', params);
},
getPlaybook: async (id: number | string) => {
return http.get(`/playbooks/${id}`);
},
createPlaybook: async (data: any) => {
return http.post('/playbooks', data);
},
updatePlaybook: async (id: number | string, data: any) => {
return http.put(`/playbooks/${id}`, data);
},
deletePlaybook: async (id: number | string) => {
return http.delete(`/playbooks/${id}`);
},
executePlaybook: async (id: number | string) => {
return http.post(`/playbooks/${id}/execute`);
},
};
// Logs API(无后端)