From bdeda51b22dfd78da7570c7d2610603dcaed922f Mon Sep 17 00:00:00 2001 From: duorameng <2997944583@qq.com> Date: Fri, 26 Jun 2026 09:23:20 +0800 Subject: [PATCH] feat(interconnect): sync environment variables with original structure and IDs, and translate UI badges --- internal/controllers/env_controller.go | 87 +++++++++++++++ .../controllers/interconnect_controller.go | 79 +++++++------ internal/router/api_routes.go | 1 + .../interconnect/components/SyncPanel.vue | 104 ++++++++++++------ 4 files changed, 205 insertions(+), 66 deletions(-) diff --git a/internal/controllers/env_controller.go b/internal/controllers/env_controller.go index a804c1a..ff7fb97 100644 --- a/internal/controllers/env_controller.go +++ b/internal/controllers/env_controller.go @@ -2,6 +2,8 @@ package controllers import ( "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/database" + "github.com/engigu/baihu-panel/internal/models" "github.com/engigu/baihu-panel/internal/models/vo" "github.com/engigu/baihu-panel/internal/services" "github.com/engigu/baihu-panel/internal/services/relation" @@ -300,3 +302,88 @@ func (ec *EnvController) GetTags(c *gin.Context) { } utils.Success(c, tags) } + +// BulkSaveEnv 批量保存环境变量 +func (ec *EnvController) BulkSaveEnv(c *gin.Context) { + var reqs []struct { + ID string `json:"id"` + Name string `json:"name" binding:"required"` + Value string `json:"value" binding:"required"` + Remark string `json:"remark"` + Type string `json:"type"` + Hidden *bool `json:"hidden"` + Enabled *bool `json:"enabled"` + } + + if err := c.ShouldBindJSON(&reqs); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + userID := c.GetString("userID") + + for _, req := range reqs { + if req.Type == "secret" { + continue // 二次严苛拦截,机密变量不应下发/保存 + } + + hidden := true + if req.Hidden != nil { + hidden = *req.Hidden + } + enabled := true + if req.Enabled != nil { + enabled = *req.Enabled + } + + var existingEnv *models.EnvironmentVariable + // 优先按 ID 匹配 + if req.ID != "" { + var e models.EnvironmentVariable + if err := database.DB.Where("id = ?", req.ID).First(&e).Error; err == nil { + existingEnv = &e + } + } + // 如果 ID 没找到,按 Name 匹配 + if existingEnv == nil { + var e models.EnvironmentVariable + if err := database.DB.Where("name = ?", req.Name).First(&e).Error; err == nil { + existingEnv = &e + } + } + + if existingEnv != nil { + existingEnv.Name = req.Name + existingEnv.Value = models.BigText(req.Value) + existingEnv.Remark = req.Remark + existingEnv.Type = req.Type + existingEnv.Hidden = &hidden + existingEnv.Enabled = &enabled + database.DB.Save(existingEnv) + + if req.ID != "" && existingEnv.ID != req.ID { + database.DB.Model(existingEnv).Update("id", req.ID) + } + } else { + envVar := &models.EnvironmentVariable{ + ID: req.ID, + Name: req.Name, + Value: models.BigText(req.Value), + Remark: req.Remark, + Type: req.Type, + Hidden: &hidden, + Enabled: &enabled, + UserID: userID, + CreatedAt: models.Now(), + UpdatedAt: models.Now(), + } + if envVar.ID == "" { + envVar.ID = utils.GenerateID() + } + database.DB.Create(envVar) + } + } + + services.GetAgentWSManager().BroadcastTasksToAll() + utils.Success(c, nil) +} diff --git a/internal/controllers/interconnect_controller.go b/internal/controllers/interconnect_controller.go index e331145..916ec26 100644 --- a/internal/controllers/interconnect_controller.go +++ b/internal/controllers/interconnect_controller.go @@ -11,6 +11,7 @@ import ( "time" "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/database" "github.com/engigu/baihu-panel/internal/models" "github.com/engigu/baihu-panel/internal/models/vo" "github.com/engigu/baihu-panel/internal/services" @@ -271,9 +272,13 @@ func (ic *InterconnectController) SyncEnv(c *gin.Context) { var req struct { NodeIDs []string `json:"node_ids" binding:"required"` Envs []struct { - Name string `json:"name"` - Value string `json:"value"` - Remark string `json:"remark"` + ID string `json:"id"` + Name string `json:"name"` + Value string `json:"value"` + Remark string `json:"remark"` + Type string `json:"type"` + Hidden *bool `json:"hidden"` + Enabled *bool `json:"enabled"` } `json:"envs" binding:"required"` } @@ -282,6 +287,27 @@ func (ic *InterconnectController) SyncEnv(c *gin.Context) { return } + // Filter out secret variables securely using the database + var safeEnvs []interface{} + for _, envReq := range req.Envs { + var env models.EnvironmentVariable + query := database.DB + if envReq.ID != "" { + query = query.Where("id = ? OR name = ?", envReq.ID, envReq.Name) + } else { + query = query.Where("name = ?", envReq.Name) + } + if err := query.First(&env).Error; err == nil { + if env.Type == "secret" { + continue // 坚决阻断下发机密数据 + } + } + if envReq.Type == "secret" { + continue // 坚决阻断下发机密数据 + } + safeEnvs = append(safeEnvs, envReq) + } + results := make([]map[string]interface{}, 0) for _, nodeID := range req.NodeIDs { @@ -291,44 +317,31 @@ func (ic *InterconnectController) SyncEnv(c *gin.Context) { continue } - client, apiURL, err := ic.getClientAndURL(node, "/api/v1/env") + client, apiURL, err := ic.getClientAndURL(node, "/api/v1/env/bulk_save") if err != nil { results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "反向隧道未连接"}) continue } - successCount := 0 - for _, env := range req.Envs { - payload := map[string]interface{}{ - "name": env.Name, - "value": env.Value, - "remark": env.Remark, - "type": "normal", - } - payloadBytes, _ := json.Marshal(payload) + payloadBytes, _ := json.Marshal(safeEnvs) - httpReq, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes)) - if err != nil { - continue - } - httpReq.Header.Set("Authorization", "Bearer "+node.Token) - httpReq.Header.Set("Content-Type", "application/json") - - resp, err := client.Do(httpReq) - if err == nil && resp.StatusCode == 200 { - successCount++ - } - if resp != nil { - resp.Body.Close() - } + httpReq, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes)) + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "构建请求失败"}) + continue } + httpReq.Header.Set("Authorization", "Bearer "+node.Token) + httpReq.Header.Set("Content-Type", "application/json") - results = append(results, map[string]interface{}{ - "node_id": nodeID, - "success": true, - "msg": "同步完成", - "count": successCount, - }) + resp, err := client.Do(httpReq) + if err != nil || resp.StatusCode != 200 { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "同步请求失败或超时"}) + } else { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": true, "msg": "同步成功"}) + } + if resp != nil { + resp.Body.Close() + } } utils.Success(c, results) diff --git a/internal/router/api_routes.go b/internal/router/api_routes.go index 4691991..b4def0c 100644 --- a/internal/router/api_routes.go +++ b/internal/router/api_routes.go @@ -116,6 +116,7 @@ func registerEnvRoutes(g *gin.RouterGroup, c *Controllers) { env.GET("/secret-status", c.Env.GetSecretStatus) env.GET("/tags", c.Env.GetTags) env.POST("", c.Env.CreateEnvVar) + env.POST("/bulk_save", c.Env.BulkSaveEnv) env.GET("", c.Env.GetEnvVars) env.GET("/all", c.Env.GetAllEnvVars) env.GET("/:id", c.Env.GetEnvVar) diff --git a/web/src/views/interconnect/components/SyncPanel.vue b/web/src/views/interconnect/components/SyncPanel.vue index a32b5bc..67dcf6d 100644 --- a/web/src/views/interconnect/components/SyncPanel.vue +++ b/web/src/views/interconnect/components/SyncPanel.vue @@ -1,12 +1,11 @@