fix(log): ES 7.2.0 time format .SSSZ + index dropdown + demo always visible
- ES query 502 fix: remove +00:00 conversion, use native .SSSZ format (ES 7.2.0 date_time = strict_date_optional_time only, rejects +00:00/epoch) - parseLogTime: use time.RFC3339Nano for fractional seconds - new API GET /log-datasources/indices to list ES indices via proxy - LogDatasourceDialog: add '获取索引' button + Select dropdown for ES - LogDatasourcePage: always show demo sources alongside real ones
This commit is contained in:
1 parent
2e179ef7f4
commit
a04d815efe
7 files changed
+308
-15
No files matched your search
@@ -3,6 +3,7 @@ package controller
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -168,6 +169,52 @@ func (h *LogDSHandler) TestConnection(c *gin.Context) {
|
||||
c.JSON(200, model.Response{Code: 200, Message: "连接成功", Data: map[string]string{"status": "ok"}})
|
||||
}
|
||||
|
||||
// ListIndices GET /api/v1/log-datasources/indices?url=...&type=elasticsearch
|
||||
// 通过代理查询 ES 集群的索引列表 // Logan.Li
|
||||
func (h *LogDSHandler) ListIndices(c *gin.Context) {
|
||||
esURL := c.Query("url")
|
||||
if esURL == "" {
|
||||
c.JSON(400, model.Response{Code: 400, Message: "url 参数必填"})
|
||||
return
|
||||
}
|
||||
basicAuth := c.Query("basic_auth") == "true"
|
||||
user := c.Query("basic_auth_user")
|
||||
pass := c.Query("basic_auth_pass")
|
||||
skipTLS := c.Query("skip_tls_verify") == "true"
|
||||
|
||||
client := newLogHTTPClient(skipTLS, 10*time.Second)
|
||||
target := strings.TrimRight(esURL, "/") + "/_cat/indices?format=json&h=index"
|
||||
httpReq, err := http.NewRequest(http.MethodGet, target, nil)
|
||||
if err != nil {
|
||||
c.JSON(502, model.Response{Code: 502, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
applyLogDSAuth(httpReq, basicAuth, user, pass)
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
c.JSON(502, model.Response{Code: 502, Message: "查询索引失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode >= 400 {
|
||||
c.JSON(502, model.Response{Code: 502, Message: "ES 错误: " + string(body)})
|
||||
return
|
||||
}
|
||||
var raw []map[string]interface{}
|
||||
if err := json.Unmarshal(body, &raw); err != nil {
|
||||
c.JSON(502, model.Response{Code: 502, Message: "解析索引列表失败"})
|
||||
return
|
||||
}
|
||||
indices := make([]string, 0, len(raw))
|
||||
for _, entry := range raw {
|
||||
if idx, ok := entry["index"].(string); ok {
|
||||
indices = append(indices, idx)
|
||||
}
|
||||
}
|
||||
c.JSON(200, model.Response{Code: 200, Data: indices})
|
||||
}
|
||||
|
||||
func testLogDSConnection(req model.TestLogDSReq) (bool, string) {
|
||||
client := newLogHTTPClient(req.SkipTLSVerify, 10*time.Second)
|
||||
url := strings.TrimRight(req.URL, "/")
|
||||
|
||||
@@ -149,10 +149,7 @@ func (h *LogProxyHandler) QueryES(c *gin.Context) {
|
||||
},
|
||||
}
|
||||
if req.Start != "" || req.End != "" {
|
||||
// ES 7.2 date_time 格式要求 +00:00 而非 Z;兼容 @timestamp 与 timestamp // Logan.Li
|
||||
buildRangeVal := func(v string) string {
|
||||
return strings.Replace(v, "Z", "+00:00", 1)
|
||||
}
|
||||
// ES 7.2 date_time 仅接受 .SSSZ 格式;兼容 @timestamp 与 timestamp // Logan.Li
|
||||
rangeCond := map[string]interface{}{
|
||||
"bool": map[string]interface{}{
|
||||
"should": []interface{}{
|
||||
@@ -167,10 +164,10 @@ func (h *LogProxyHandler) QueryES(c *gin.Context) {
|
||||
for _, v := range rng {
|
||||
r := v.(map[string]string)
|
||||
if req.Start != "" {
|
||||
r["gte"] = buildRangeVal(req.Start)
|
||||
r["gte"] = req.Start
|
||||
}
|
||||
if req.End != "" {
|
||||
r["lte"] = buildRangeVal(req.End)
|
||||
r["lte"] = req.End
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,9 +214,8 @@ func parseLogTime(s string) (time.Time, error) {
|
||||
}
|
||||
return time.Unix(n, 0), nil
|
||||
}
|
||||
// ES 7.2 date_time 不接受 Z,转 +00:00
|
||||
s = strings.Replace(s, "Z", "+00:00", 1)
|
||||
return time.Parse(time.RFC3339, s)
|
||||
// 解析 ISO8601(含 .SSSZ 毫秒后缀,如 2026-06-20T09:33:17.000Z)
|
||||
return time.Parse(time.RFC3339Nano, s)
|
||||
}
|
||||
|
||||
func defaultESQuery(q string) string {
|
||||
|
||||
@@ -721,6 +721,7 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
|
||||
logDSGroup := authorized.Group("/log-datasources")
|
||||
{
|
||||
logDSGroup.GET("", logDSHandler.List)
|
||||
logDSGroup.GET("/indices", logDSHandler.ListIndices)
|
||||
logDSGroup.GET("/:id", logDSHandler.Get)
|
||||
logDSGroup.POST("", logDSHandler.Create)
|
||||
logDSGroup.PUT("/:id", logDSHandler.Update)
|
||||
|
||||
Reference in new issue
Block a user