feat: 工具配置增强、站点拨测与日志中心多项改进

- 工具配置:新增卸载 API/UI、Go 工具链、流水线构建自动安装依赖
- 修复 ListTools VerifyTool 递归栈溢出导致获取工具配置失败
- Node.js Alpine 安装改用 unofficial-builds musl 预编译包与 index.json 解析 LTS
- 站点拨测:详情页路由、告警自动恢复、并发拨测与默认可观测频率
- 日志中心:RBAC、查询构建器、ES/Loki 增强与审计 request/response 展示
- 发布配置:构建节点、流水线远程执行、Deploy RBAC 导航拆分
This commit is contained in:
Logan committed 2026-07-02 07:47:33 +08:00
1 parent cde7bbe540
commit 7e1c8f583c
84 files changed
+7729 -1507

No files matched your search

+66
View File
@@ -5,13 +5,17 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strconv"
"strings"
"time"
"ezops/middleware"
"ezops/model"
"ezops/pkg/logaccess"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@@ -40,6 +44,7 @@ func (h *LogDSHandler) List(c *gin.Context) {
if sources == nil {
sources = []model.LogDataSource{}
}
sources = logaccess.FilterDataSources(h.db, middleware.GetUserID(c), middleware.GetIsAdmin(c), sources)
// 按项目惯例返回 {list,total},前端 listDatasources 取 .list // Logan.Li
c.JSON(200, model.Response{Code: 200, Data: model.ListResponse{List: sources, Total: int64(len(sources))}})
}
@@ -268,6 +273,67 @@ func (h *LogDSHandler) ListIndices(c *gin.Context) {
c.JSON(200, model.Response{Code: 200, Data: indices})
}
// ListIndicesByID GET /api/v1/log-datasources/:id/indices — 按数据源 ID 拉取索引(查询面板用)// Logan.Li
func (h *LogDSHandler) ListIndicesByID(c *gin.Context) {
ds, err := loadLogDS(h, parseUintParam(c.Param("id")))
if err != nil {
c.JSON(404, model.Response{Code: 404, Message: "数据源不存在"})
return
}
if ds.Type != "elasticsearch" {
c.JSON(400, model.Response{Code: 400, Message: "仅 Elasticsearch 数据源支持索引列表"})
return
}
filtered := logaccess.FilterDataSources(h.db, middleware.GetUserID(c), middleware.GetIsAdmin(c), []model.LogDataSource{*ds})
if len(filtered) == 0 {
c.JSON(403, model.Response{Code: 403, Message: "无权访问该数据源"})
return
}
indices, err := fetchESIndices(ds)
if err != nil {
c.JSON(502, model.Response{Code: 502, Message: err.Error()})
return
}
c.JSON(200, model.Response{Code: 200, Data: indices})
}
func fetchESIndices(ds *model.LogDataSource) ([]string, error) {
client := newLogHTTPClient(ds.SkipTLSVerify, 15*time.Second)
target := strings.TrimRight(ds.URL, "/") + "/_cat/indices?format=json&h=index"
httpReq, err := http.NewRequest(http.MethodGet, target, nil)
if err != nil {
return nil, err
}
applyLogDSAuthV2(httpReq, ds)
resp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("查询索引失败: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("ES 错误: %s", string(body))
}
var raw []map[string]interface{}
if err := json.Unmarshal(body, &raw); err != nil {
return nil, fmt.Errorf("解析索引列表失败")
}
indices := make([]string, 0, len(raw))
for _, entry := range raw {
idx, ok := entry["index"].(string)
if !ok || idx == "" || strings.HasPrefix(idx, ".") {
continue
}
indices = append(indices, idx)
}
return indices, nil
}
func parseUintParam(s string) uint {
n, _ := strconv.ParseUint(s, 10, 64)
return uint(n)
}
func testLogDSConnection(req model.TestLogDSReq) (bool, string) {
client := newLogHTTPClient(req.SkipTLSVerify, 10*time.Second)
url := strings.TrimRight(req.URL, "/")