feat(interconnect): sync environment variables with original structure and IDs, and translate UI badges
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user