fix: K8s集群编辑保存失败+凭据覆盖+去掉Kubectl死按钮

问题:
1. UpdateCluster 用 CreateK8sClusterRequest (code binding:"required"),
   编辑时不传 code → 校验失败 400 → 保存不成功
2. Token/KubeConfig json:"-" 不序列化, 编辑时拿不到原值,
   提交空字符串覆盖真实凭据
3. 操作菜单 Kubectl 是死按钮

修复:
- UpdateCluster 改用 map[string]interface{} 接收, 逐字段更新,
  空值不覆盖 (防止凭据清空)
- 新增 GET /:id/detail 返回 has_token/has_kube_config 脱敏标记
- 编辑弹框拉取详情 API 回填, Token/KubeConfig 留空显示 '已配置' 提示
- 去掉 Kubectl 死按钮, 保留 详情/配置/删除 三项
This commit is contained in:
Logan authored and attacker committed 2026-06-19 15:08:50 +08:00
1 parent 33e58ce25a
commit abf2917fad
5 files changed
+119 -33

No files matched your search

+73 -10
View File
@@ -145,7 +145,8 @@ func (h *K8sHandler) CreateCluster(c *gin.Context) {
func (h *K8sHandler) UpdateCluster(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var req models.CreateK8sClusterRequest
// 用 map 接收,避免 binding:"required" 校验 // Logan.Li
var req map[string]interface{}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, models.Response{
Code: 400,
@@ -163,15 +164,39 @@ func (h *K8sHandler) UpdateCluster(c *gin.Context) {
return
}
updates := map[string]interface{}{
"name": req.Name,
"version": req.Version,
"config_type": req.ConfigType,
"api_server": req.APIServer,
"api_endpoint": req.APIServer,
"token": req.Token,
"kube_config": req.KubeConfig,
"description": req.Description,
// 逐字段更新:空值不覆盖原值(防止凭据被清空) // Logan.Li
updates := map[string]interface{}{}
if v, ok := req["name"]; ok && v != nil && v != "" {
updates["name"] = v
}
if v, ok := req["version"]; ok {
updates["version"] = v
}
if v, ok := req["description"]; ok {
updates["description"] = v
}
if v, ok := req["config_type"]; ok {
updates["config_type"] = v
}
if v, ok := req["api_server"]; ok && v != nil && v != "" {
updates["api_server"] = v
updates["api_endpoint"] = v
}
// Token / KubeConfig:空值不覆盖(前端编辑时可能拿不到原值)
if v, ok := req["token"]; ok && v != nil && v != "" {
updates["token"] = v
}
if v, ok := req["kube_config"]; ok && v != nil && v != "" {
updates["kube_config"] = v
}
if len(updates) == 0 {
c.JSON(http.StatusOK, models.Response{
Code: 200,
Message: "无更新内容",
Data: cluster,
})
return
}
if err := h.db.Model(&cluster).Updates(updates).Error; err != nil {
@@ -191,6 +216,44 @@ func (h *K8sHandler) UpdateCluster(c *gin.Context) {
})
}
// GetClusterDetail 返回集群完整信息(含 token/kube_config,供编辑回填) // Logan.Li
func (h *K8sHandler) GetClusterDetail(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var cluster models.K8sCluster
if err := h.db.First(&cluster, id).Error; err != nil {
c.JSON(http.StatusNotFound, models.Response{
Code: 404,
Message: "集群不存在",
})
return
}
// 返回时手动脱敏:token/kube_config 只返回是否存在标记,不返回原文
c.JSON(http.StatusOK, models.Response{
Code: 200,
Message: "success",
Data: map[string]interface{}{
"id": cluster.ID,
"name": cluster.Name,
"code": cluster.Code,
"version": cluster.Version,
"status": cluster.Status,
"config_type": cluster.ConfigType,
"api_server": cluster.APIServer,
"api_endpoint": cluster.APIEndpoint,
"description": cluster.Description,
"has_token": cluster.Token != "",
"has_kube_config": cluster.KubeConfig != "",
"node_count": cluster.NodeCount,
"pod_count": cluster.PodCount,
"labels": cluster.Labels,
"created_at": cluster.CreatedAt,
"updated_at": cluster.UpdatedAt,
},
})
}
// DeleteCluster 删除集群
// @Summary 删除集群
// @Description 删除指定K8s集群
+1
View File
@@ -236,6 +236,7 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
{
clusters.GET("", k8sHandler.ListClusters)
clusters.GET("/:id", k8sHandler.GetCluster)
clusters.GET("/:id/detail", k8sHandler.GetClusterDetail) // 编辑用详情(含 has_token 标记) // Logan.Li
clusters.POST("", k8sHandler.CreateCluster)
clusters.PUT("/:id", k8sHandler.UpdateCluster)
clusters.DELETE("/:id", k8sHandler.DeleteCluster)