package handlers import ( "net/http" "time" "github.com/ansible-deploy/internal/models" "github.com/ansible-deploy/internal/services" "github.com/gin-gonic/gin" ) // AnsibleHandler API处理器 type AnsibleHandler struct { service *services.AnsibleService } // NewAnsibleHandler 创建处理器 func NewAnsibleHandler(svc *services.AnsibleService) *AnsibleHandler { return &AnsibleHandler{service: svc} } // ListHosts 获取主机列表 func (h *AnsibleHandler) ListHosts(c *gin.Context) { hosts := h.service.ListHosts() c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": hosts, }) } // AddHost 添加主机 func (h *AnsibleHandler) AddHost(c *gin.Context) { var host models.Host if err := c.ShouldBindJSON(&host); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误: " + err.Error(), }) return } if err := h.service.AddHost(host); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "主机添加成功", }) } // DeleteHost 删除主机 func (h *AnsibleHandler) DeleteHost(c *gin.Context) { id := c.Param("id") if err := h.service.DeleteHost(id); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "主机删除成功", }) } // UpdateHost 更新主机 func (h *AnsibleHandler) UpdateHost(c *gin.Context) { id := c.Param("id") var host models.Host if err := c.ShouldBindJSON(&host); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误", }) return } if err := h.service.UpdateHost(id, host); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "主机更新成功", }) } // TestConnection 测试连接 func (h *AnsibleHandler) TestConnection(c *gin.Context) { id := c.Param("id") result, err := h.service.TestConnection(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": result, }) } // ListGroups 获取组列表 func (h *AnsibleHandler) ListGroups(c *gin.Context) { groups := h.service.ListGroups() c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": groups, }) } // CreateGroup 创建组 func (h *AnsibleHandler) CreateGroup(c *gin.Context) { var group models.HostGroup if err := c.ShouldBindJSON(&group); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误", }) return } if err := h.service.CreateGroup(group); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "组创建成功", }) } // DeleteGroup 删除组 func (h *AnsibleHandler) DeleteGroup(c *gin.Context) { name := c.Param("name") if err := h.service.DeleteGroup(name); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "组删除成功", }) } // UpdateGroup 更新组 func (h *AnsibleHandler) UpdateGroup(c *gin.Context) { name := c.Param("name") var group models.HostGroup if err := c.ShouldBindJSON(&group); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误", }) return } if err := h.service.UpdateGroup(name, group); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "组更新成功", }) } // ListPlaybooks 列出Playbooks func (h *AnsibleHandler) ListPlaybooks(c *gin.Context) { playbooks := h.service.ListPlaybooks() c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": playbooks, }) } // GetPlaybook 获取Playbook详情 func (h *AnsibleHandler) GetPlaybook(c *gin.Context) { name := c.Param("name") playbook, err := h.service.GetPlaybook(name) if err != nil { c.JSON(http.StatusNotFound, gin.H{ "code": 404, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": playbook, }) } // ExecutePlaybook 执行Playbook func (h *AnsibleHandler) ExecutePlaybook(c *gin.Context) { var req models.PlaybookExecutionRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误: " + err.Error(), }) return } task, err := h.service.ExecutePlaybook(req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "任务已启动", "taskId": task.ID, }) } // ExecuteCommand 执行命令 func (h *AnsibleHandler) ExecuteCommand(c *gin.Context) { var req models.CommandRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误: " + err.Error(), }) return } results, err := h.service.ExecuteCommand(req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": results, }) } // BatchExecute 批量执行 func (h *AnsibleHandler) BatchExecute(c *gin.Context) { var req models.CommandRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误", }) return } result := h.service.BatchExecute(req) c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "任务已启动", "data": result, }) } // ListTasks 获取任务列表 func (h *AnsibleHandler) ListTasks(c *gin.Context) { tasks := h.service.ListTasks() c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": tasks, }) } // GetTask 获取任务详情 func (h *AnsibleHandler) GetTask(c *gin.Context) { id := c.Param("id") task := h.service.GetTask(id) if task == nil { c.JSON(http.StatusNotFound, gin.H{ "code": 404, "msg": "任务不存在", }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": task, }) } // StreamTaskOutput SSE 流式推送任务日志 func (h *AnsibleHandler) StreamTaskOutput(c *gin.Context) { id := c.Param("id") task := h.service.GetTask(id) if task == nil { c.JSON(http.StatusNotFound, gin.H{"code": 404, "msg": "任务不存在"}) return } c.Header("Content-Type", "text/event-stream") c.Header("Cache-Control", "no-cache") c.Header("Connection", "keep-alive") lastLen := 0 for { // 检查客户端是否断开 if c.Request.Context().Err() != nil { return } task := h.service.GetTask(id) if task == nil { return } output := task.Output if len(output) > lastLen { // 只发送增量 increment := output[lastLen:] lastLen = len(output) c.SSEvent("log", increment) c.Writer.Flush() } if task.Status != "running" { // 任务完成,发送最终状态 c.SSEvent("status", task.Status) c.SSEvent("error", task.Error) c.Writer.Flush() return } // 等 500ms 再推送 select { case <-time.After(500 * time.Millisecond): case <-c.Request.Context().Done(): return } } } // CancelTask 取消任务 func (h *AnsibleHandler) CancelTask(c *gin.Context) { id := c.Param("id") if err := h.service.CancelTask(id); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "任务已取消", }) } // CreatePlaybook 创建Playbook func (h *AnsibleHandler) CreatePlaybook(c *gin.Context) { var req struct { Name string `json:"name" binding:"required"` Content string `json:"content"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误: " + err.Error(), }) return } if req.Content == "" { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "Playbook内容不能为空", }) return } if err := h.service.CreatePlaybook(req.Name, req.Content); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "Playbook创建成功", }) } // DeletePlaybook 删除Playbook func (h *AnsibleHandler) DeletePlaybook(c *gin.Context) { name := c.Param("name") if err := h.service.DeletePlaybook(name); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "Playbook删除成功", }) } // GetPlaybookContent 获取Playbook内容 func (h *AnsibleHandler) GetPlaybookContent(c *gin.Context) { name := c.Param("name") content, err := h.service.GetPlaybookContent(name) if err != nil { c.JSON(http.StatusNotFound, gin.H{ "code": 404, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", "data": content, }) } // UpdatePlaybook 更新Playbook func (h *AnsibleHandler) UpdatePlaybook(c *gin.Context) { name := c.Param("name") var req struct { Content string `json:"content" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "参数错误", }) return } if err := h.service.UpdatePlaybook(name, req.Content); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "Playbook更新成功", }) } // WebSocketLogs WebSocket日志 func (h *AnsibleHandler) WebSocketLogs(c *gin.Context) { taskID := c.Param("taskId") _ = taskID // WebSocket实现需要单独处理,这里返回提示 c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "WebSocket连接", }) }