feat(k8s): A1 K8s 双路由体系统一 — code 路由为主, ID 路由标记 deprecated

后端:
- 新增 4 条 all-NS code 路由: services/ingresses/cronjobs/nodes
- ListNodes 改用 getK8sClientByCode (兼容 ID+code)
- ID 域资源路由标记 // Deprecated

前端:
- clusterApi/nodeApi/workloadApi 资源方法迁移到 code 路由
- KubernetesClusterDetail/TopologyView/KubernetesNodes 组件从 cluster.id 迁移到 cluster.code
- 修复潜在 bug: ClusterOverviewPage 曾将 code 传给 ID-based getNodes

验证: 16/16 API 测试通过 (7 新 code 路由 + 3 旧 ID 兼容 + 6 回归)
go build=✅ tsc -b=✅ npm build=✅
This commit is contained in:
attacker committed 2026-06-27 04:50:58 +08:00
1 parent bb2b91ebe0
commit 00a98e210d
13 files changed
+241 -39

No files matched your search

+21
View File
@@ -100,6 +100,27 @@ func (h *KubernetesHandler) ListCronJobs(c *gin.Context) {
c.JSON(http.StatusOK, model.Response{Code: 200, Data: cronjobs.Items})
}
// ListCronJobsAllNS lists all CronJobs across all namespaces using clusterCode.
// Canonical route: GET /kubernetes/:clusterCode/cronjobs
func (h *KubernetesHandler) ListCronJobsAllNS(c *gin.Context) {
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
if clusterID == 0 {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "cluster not found"})
return
}
client, _, err := h.getK8sClient(clusterID)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
return
}
cronjobs, err := client.ListCronJobs()
if err != nil {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
return
}
c.JSON(http.StatusOK, model.Response{Code: 200, Data: cronjobs.Items})
}
// CreateCronJob 创建 CronJob
func (h *KubernetesHandler) CreateCronJob(c *gin.Context) {
clusterID, _ := strconv.Atoi(c.Param("id"))
+2 -3
View File
@@ -13,9 +13,8 @@ import (
)
func (h *KubernetesHandler) ListNodes(c *gin.Context) {
clusterID, _ := strconv.Atoi(c.Param("id"))
client, _, err := h.getK8sClient(clusterID)
// 兼容 /kubernetes-clusters/:id/nodes (ID) 和 /kubernetes/:clusterCode/nodes (code)
client, _, err := h.getK8sClientByCode(c)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
return
+88
View File
@@ -99,6 +99,94 @@ func (h *KubernetesHandler) ListIngresses(c *gin.Context) {
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "success", Data: result})
}
// ListServicesAllNS lists all Services across all namespaces using clusterCode.
// Canonical route: GET /kubernetes/:clusterCode/services
func (h *KubernetesHandler) ListServicesAllNS(c *gin.Context) {
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
if clusterID == 0 {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "cluster not found"})
return
}
namespace := c.Query("namespace")
client, _, err := h.getK8sClient(clusterID)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
return
}
svcs, err := client.ListServices()
if err != nil {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
return
}
type SvcSummary struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Type string `json:"type"`
ClusterIP string `json:"cluster_ip"`
Ports int `json:"port_count"`
}
result := []SvcSummary{}
for _, s := range svcs.Items {
if namespace != "" && s.Metadata.Namespace != namespace {
continue
}
result = append(result, SvcSummary{
Namespace: s.Metadata.Namespace,
Name: s.Metadata.Name,
Type: s.Spec.Type,
ClusterIP: s.Spec.ClusterIP,
Ports: len(s.Spec.Ports),
})
}
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "success", Data: result})
}
// ListIngressesAllNS lists all Ingresses across all namespaces using clusterCode.
// Canonical route: GET /kubernetes/:clusterCode/ingresses
func (h *KubernetesHandler) ListIngressesAllNS(c *gin.Context) {
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
if clusterID == 0 {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "cluster not found"})
return
}
namespace := c.Query("namespace")
client, _, err := h.getK8sClient(clusterID)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
return
}
ings, err := client.ListIngresses()
if err != nil {
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
return
}
type IngSummary struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Hosts []string `json:"hosts"`
TLS bool `json:"tls"`
}
result := []IngSummary{}
for _, ing := range ings.Items {
if namespace != "" && ing.Metadata.Namespace != namespace {
continue
}
hosts := []string{}
for _, rule := range ing.Spec.Rules {
if rule.Host != "" {
hosts = append(hosts, rule.Host)
}
}
result = append(result, IngSummary{
Namespace: ing.Metadata.Namespace,
Name: ing.Metadata.Name,
Hosts: hosts,
TLS: len(ing.Spec.TLS) > 0,
})
}
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "success", Data: result})
}
// GetTopology 获取集群拓扑布局 // Logan.Li
func (h *KubernetesHandler) GetTopology(c *gin.Context) {
clusterID := c.Param("id")