From b88fe24aab6eb19567d369b7c0818115e5b14b97 Mon Sep 17 00:00:00 2001 From: Ivan087 Date: Wed, 13 May 2026 16:15:11 +0800 Subject: [PATCH] fix: real K8s replicas in list API, full Helm values in modify YAML editor - Add Replicas field to entity.Instance - Add EnrichReplicas to InstanceService (batch K8s deployment query) - convertInstanceResponse uses instance.Replicas instead of hardcoded 0 - ModifyModal: load full Helm values from values-diff API (Current deployed) - Remove stale loadValuesDiff, use single useEffect for all data loading - Fix YAML lineWidth:0 for no line wrapping --- .../input/http/rest/instance_handler.go | 5 +- backend/internal/domain/entity/instance.go | 1 + .../domain/service/instance_service.go | 18 ++++++ .../instances/components/ModifyModal.tsx | 55 ++++++++----------- 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/backend/internal/adapter/input/http/rest/instance_handler.go b/backend/internal/adapter/input/http/rest/instance_handler.go index 22b55fb..6424aa2 100644 --- a/backend/internal/adapter/input/http/rest/instance_handler.go +++ b/backend/internal/adapter/input/http/rest/instance_handler.go @@ -139,6 +139,9 @@ func (h *InstanceHandler) ListInstances(w http.ResponseWriter, r *http.Request) return } + // Enrich with running replicas from K8s + instances = h.instanceService.EnrichReplicas(r.Context(), clusterID, instances) + responses := make([]*dto.InstanceResponse, 0, len(instances)) for _, instance := range instances { responses = append(responses, convertInstanceResponse(instance, false)) @@ -586,7 +589,7 @@ func convertInstanceResponse(instance *entity.Instance, includeValues bool) *dto LastOperation: string(instance.LastOperation), LastError: instance.LastError, Revision: instance.Revision, - Replicas: 0, + Replicas: instance.Replicas, AllowedActions: []string{"view", "update", "delete"}, CreatedAt: instance.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), UpdatedAt: instance.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), diff --git a/backend/internal/domain/entity/instance.go b/backend/internal/domain/entity/instance.go index 3e422d3..daab238 100644 --- a/backend/internal/domain/entity/instance.go +++ b/backend/internal/domain/entity/instance.go @@ -53,6 +53,7 @@ type Instance struct { Revision int // Helm Release Revision CreatedAt time.Time UpdatedAt time.Time + Replicas int // Running K8s replicas (enriched, not persisted) } // NewInstance 创建新实例 diff --git a/backend/internal/domain/service/instance_service.go b/backend/internal/domain/service/instance_service.go index 926ee22..d1a0c03 100644 --- a/backend/internal/domain/service/instance_service.go +++ b/backend/internal/domain/service/instance_service.go @@ -473,6 +473,24 @@ func (s *InstanceService) ScaleInstance(ctx context.Context, clusterID, instance return instance, nil } +// EnrichReplicas 批量获取实例的 K8s 实际副本数并设置到 entity 上 +func (s *InstanceService) EnrichReplicas(ctx context.Context, clusterID string, instances []*entity.Instance) []*entity.Instance { + if s.scaleClient == nil || len(instances) == 0 { + return instances + } + cluster, err := s.clusterRepo.GetByID(ctx, clusterID) + if err != nil { + return instances + } + for _, inst := range instances { + r, err := s.scaleClient.GetDeploymentReplicas(ctx, cluster, inst.Namespace, inst.Name) + if err == nil { + inst.Replicas = int(r) + } + } + return instances +} + // GetRunningReplicas returns the actual K8s deployment replicas count. func (s *InstanceService) GetRunningReplicas(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) int { if s.scaleClient == nil { diff --git a/frontend/src/features/artifact/instances/components/ModifyModal.tsx b/frontend/src/features/artifact/instances/components/ModifyModal.tsx index f64cf2c..7755fe8 100644 --- a/frontend/src/features/artifact/instances/components/ModifyModal.tsx +++ b/frontend/src/features/artifact/instances/components/ModifyModal.tsx @@ -40,37 +40,47 @@ export const ModifyModal: React.FC = ({ // Values Diff support const [showDiff, setShowDiff] = useState(false); - const [loadingDiff, setLoadingDiff] = useState(false); + const [loadingDiff] = useState(false); const [diffData, setDiffData] = useState<{ current: Record; defaults: Record; } | null>(null); - const [diffError, setDiffError] = useState(null); + const [diffError] = useState(null); - // Fetch full instance detail (list excludes values) and load diff + // Fetch full Helm values (via values-diff API) and instance detail useEffect(() => { setTag(instance.version || ""); setDescription(""); - const loadFullInstance = async () => { + const loadData = async () => { if (instance.clusterId && instance.id) { + // Load values diff first — gives us the full current Helm values try { - const detail = await getInstance( - { clusterId: instance.clusterId, instanceId: instance.id }, - ); - if (detail.values && Object.keys(detail.values).length > 0) { - const currentYaml = stringifyYaml(detail.values, { lineWidth: 0 }); + const data = await getInstanceValuesDiff(instance.clusterId, instance.id); + if (data?.current && Object.keys(data.current).length > 0) { + const currentYaml = stringifyYaml(data.current, { lineWidth: 0 }); setValuesYaml(currentYaml); setOriginalValuesYaml(currentYaml); + setDiffData({ current: data.current, defaults: data.defaults ?? {} }); } } catch (err) { - console.error('[ModifyModal] Failed to fetch instance detail:', err); + console.error('[ModifyModal] Failed to load values diff:', err); + // Fallback: try instance detail + try { + const detail = await getInstance({ clusterId: instance.clusterId, instanceId: instance.id }); + if (detail.values && Object.keys(detail.values).length > 0) { + const y = stringifyYaml(detail.values, { lineWidth: 0 }); + setValuesYaml(y); + setOriginalValuesYaml(y); + } + } catch (err2) { + console.error('[ModifyModal] Failed to load instance detail:', err2); + } } } - loadValuesDiff(); }; - loadFullInstance(); + loadData(); }, [instance]); // Recompute modified keys when valuesYaml or diffData changes @@ -98,24 +108,6 @@ export const ModifyModal: React.FC = ({ } catch { /* ignore parse errors */ } }, [valuesYaml, diffData]); - const loadValuesDiff = async () => { - if (!instance.clusterId || !instance.id) return; - - setLoadingDiff(true); - setDiffError(null); - setDiffData(null); - try { - const data = await getInstanceValuesDiff(instance.clusterId, instance.id); - if (data && data.current && data.defaults) { - setDiffData({ current: data.current, defaults: data.defaults }); - } - } catch (err) { - console.error("[ModifyModal] Failed to load values diff:", err); - setDiffError("Failed to load values diff"); - } finally { - setLoadingDiff(false); - } - }; const applyDefaults = () => { if (!diffData?.defaults) return; @@ -303,9 +295,6 @@ export const ModifyModal: React.FC = ({