package controller import ( "net/http" "strconv" "time" "ezops/middleware" "ezops/model" "ezops/pkg/scriptrunner" "github.com/gin-gonic/gin" ) // GetScriptRuntime 获取本机脚本运行时能力(Shell/Python/PowerShell) // Logan.Li func (h *AutomationHandler) GetScriptRuntime(c *gin.Context) { rt := scriptrunner.DetectRuntime() c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "success", Data: rt, }) } // ListScripts 获取脚本列表 // @Summary 获取脚本列表 // @Description 获取所有脚本列表 // @Tags 自动化运维 // @Produce json // @Security Bearer // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Success 200 {object} model.Response{data=model.ListResponse} // @Router /scripts [get] func (h *AutomationHandler) ListScripts(c *gin.Context) { var query model.PageQuery c.ShouldBindQuery(&query) var total int64 h.db.Model(&model.Script{}).Count(&total) var scripts []model.Script h.db.Offset(query.GetOffset()).Limit(query.GetLimit()).Find(&scripts) c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "success", Data: model.ListResponse{ List: scripts, Total: total, }, }) } // GetScript 获取脚本详情 // @Summary 获取脚本详情 // @Description 获取指定脚本的详细信息 // @Tags 自动化运维 // @Produce json // @Security Bearer // @Param id path int true "脚本ID" // @Success 200 {object} model.Response{data=model.Script} // @Router /scripts/{id} [get] func (h *AutomationHandler) GetScript(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var script model.Script if err := h.db.First(&script, id).Error; err != nil { c.JSON(http.StatusNotFound, model.Response{ Code: 404, Message: "脚本不存在", }) return } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "success", Data: script, }) } // CreateScript 创建脚本 // @Summary 创建脚本 // @Description 创建新脚本 // @Tags 自动化运维 // @Accept json // @Produce json // @Security Bearer // @Param request body model.CreateScriptRequest true "创建请求" // @Success 200 {object} model.Response{data=model.Script} // @Router /scripts [post] func (h *AutomationHandler) CreateScript(c *gin.Context) { var req model.CreateScriptRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } script := model.Script{ Name: req.Name, Code: req.Code, Description: req.Description, Type: req.Type, Content: req.Content, Params: req.Params, Timeout: req.Timeout, } if script.Type == "" { script.Type = "shell" } if script.Timeout == 0 { script.Timeout = 300 } if script.Code == "" { script.Code = randomCode("sc") } script.CreatedBy = middleware.GetUserID(c) if err := h.db.Create(&script).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "创建失败", }) return } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "创建成功", Data: script, }) } // UpdateScript 更新脚本 // @Summary 更新脚本 // @Description 更新指定脚本 // @Tags 自动化运维 // @Accept json // @Produce json // @Security Bearer // @Param id path int true "脚本ID" // @Param request body model.CreateScriptRequest true "更新请求" // @Success 200 {object} model.Response{data=model.Script} // @Router /scripts/{id} [put] func (h *AutomationHandler) UpdateScript(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var req model.CreateScriptRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } var script model.Script if err := h.db.First(&script, id).Error; err != nil { c.JSON(http.StatusNotFound, model.Response{ Code: 404, Message: "脚本不存在", }) return } updates := map[string]interface{}{ "name": req.Name, "description": req.Description, "type": req.Type, "content": req.Content, "params": req.Params, "timeout": req.Timeout, "version": script.Version + 1, } if err := h.db.Model(&script).Updates(updates).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "更新失败", }) return } h.db.First(&script, id) c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "更新成功", Data: script, }) } // DeleteScript 删除脚本 // @Summary 删除脚本 // @Description 删除指定脚本 // @Tags 自动化运维 // @Produce json // @Security Bearer // @Param id path int true "脚本ID" // @Success 200 {object} model.Response // @Router /scripts/{id} [delete] func (h *AutomationHandler) DeleteScript(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) if err := h.db.Delete(&model.Script{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "删除失败", }) return } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "删除成功", }) } // BatchDeleteScripts 批量删除脚本 // @Summary 批量删除脚本 // @Description 批量删除脚本 // @Tags 自动化运维 // @Accept json // @Produce json // @Security Bearer // @Param request body model.IDsRequest true "删除请求" // @Success 200 {object} model.Response // @Router /scripts/batch-delete [post] func (h *AutomationHandler) BatchDeleteScripts(c *gin.Context) { var req model.IDsRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.Response{ Code: 400, Message: "请求参数错误", }) return } if err := h.db.Delete(&model.Script{}, req.IDs).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "删除失败", }) return } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "删除成功", }) } // ExecuteScript 执行脚本 // @Summary 执行脚本 // @Description 在目标服务器上执行脚本 // @Tags 自动化运维 // @Accept json // @Produce json // @Security Bearer // @Param id path int true "脚本ID" // @Param request body model.ExecuteScriptRequest true "执行请求" // @Success 200 {object} model.Response{data=model.TaskExecution} // @Router /scripts/{id}/execute [post] func (h *AutomationHandler) ExecuteScript(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var script model.Script if err := h.db.First(&script, id).Error; err != nil { c.JSON(http.StatusNotFound, model.Response{ Code: 404, Message: "脚本不存在", }) return } // The request body is optional — when omitted we execute locally. var req model.ExecuteScriptRequest _ = c.ShouldBindJSON(&req) now := time.Now() execution := model.TaskExecution{ TaskType: "script", TaskID: uint(id), TaskName: script.Name, Status: "running", TargetType: req.TargetType, StartedAt: &now, } if err := h.db.Create(&execution).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "创建执行记录失败", }) return } rt := scriptrunner.DetectRuntime() if ok, reason := scriptrunner.CanRun(rt, script.Type); !ok { c.JSON(http.StatusBadRequest, model.Response{ Code: 400, Message: reason, }) return } // 在 AIOps 本机执行(embed 单二进制 / Docker 容器内) output, exitCode, duration := runLocal(script.Type, script.Content, script.Timeout) finishedAt := time.Now() status := "success" if exitCode != 0 { status = "failed" } h.db.Model(&execution).Updates(map[string]interface{}{ "status": status, "output": output, "duration": duration, "finished_at": finishedAt, }) h.db.Model(&script).UpdateColumn("run_count", script.RunCount+1) execution.Output = output execution.Status = status execution.Duration = duration execution.FinishedAt = &finishedAt msg := "执行成功" if status == "failed" { msg = "执行失败" } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: msg, Data: execution, }) } // ListExecutions 获取任务执行记录列表 // @Summary 获取任务执行记录 // @Description 获取脚本/定时任务的执行历史记录(支持 task_type / task_id 筛选) // @Tags 自动化运维 // @Produce json // @Security Bearer // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Param task_type query string false "任务类型 (cron/script)" // @Param task_id query string false "任务ID" // @Success 200 {object} model.Response{data=model.ListResponse} // @Router /task-executions [get] func (h *AutomationHandler) ListExecutions(c *gin.Context) { var query model.PageQuery c.ShouldBindQuery(&query) db := h.db.Model(&model.TaskExecution{}) if taskType := c.Query("task_type"); taskType != "" { db = db.Where("task_type = ?", taskType) } if taskID := c.Query("task_id"); taskID != "" { db = db.Where("task_id = ?", taskID) } var total int64 db.Count(&total) var execs []model.TaskExecution db.Order("created_at DESC").Offset(query.GetOffset()).Limit(query.GetLimit()).Find(&execs) c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "success", Data: model.ListResponse{ List: execs, Total: total, }, }) }