feat(frontend): add Helm chart browser, monitoring, chart-references and values templates pages

Add new frontend pages for the multi-tenant OCDP platform:

- Charts page (/charts): Browse Harbor OCI registries to list Helm chart repositories
  and versions, with deploy modal to launch charts on selected clusters
- Monitoring page (/monitoring): Display cluster metrics (CPU/Memory/GPU usage)
  and per-node details with resource utilization bars
- Chart References page (/chart-references): CRUD for chart metadata references
- Values Templates page (/templates): CRUD for Helm values templates with version
  history and rollback support
- Sidebar: Add Charts navigation, update Storage and Templates links
- api.ts: Add all API client functions (clusterApi, registryApi, instanceApi,
  monitoringApi, storageApi, chartReferenceApi, valuesTemplateApi,
  workspaceApi, userApi) with full TypeScript types

Note: deploy flow and values template rollback not yet end-to-end tested.
This commit is contained in:
Ivan087
2026-04-15 16:59:31 +08:00
parent c5e51ed069
commit 29d0310f03
283 changed files with 24658 additions and 36038 deletions

View File

@ -0,0 +1,143 @@
package service
import (
"context"
"errors"
"github.com/ocdp/cluster-service/internal/domain/entity"
"github.com/ocdp/cluster-service/internal/domain/repository"
)
var (
ErrTemplateNotFound = errors.New("template not found")
ErrTemplateExists = errors.New("template already exists")
)
// ValuesTemplateService Values 模板领域服务
type ValuesTemplateService struct {
valuesTemplateRepo repository.ValuesTemplateRepository
chartRefRepo repository.ChartReferenceRepository
}
// NewValuesTemplateService 创建 Values 模板服务
func NewValuesTemplateService(
valuesTemplateRepo repository.ValuesTemplateRepository,
chartRefRepo repository.ChartReferenceRepository,
) *ValuesTemplateService {
return &ValuesTemplateService{
valuesTemplateRepo: valuesTemplateRepo,
chartRefRepo: chartRefRepo,
}
}
// Create 创建 Values 模板
func (s *ValuesTemplateService) Create(
ctx context.Context,
workspaceID, ownerID, chartRefID, name, description, valuesYAML string,
isDefault bool,
) (*entity.ValuesTemplate, error) {
// 检查 Chart Reference 是否存在
chartRef, err := s.chartRefRepo.GetByID(ctx, chartRefID)
if err != nil {
return nil, errors.New("chart reference not found")
}
// 检查名称是否已存在
existing, _ := s.valuesTemplateRepo.GetByName(ctx, workspaceID, chartRefID, name)
if existing != nil {
return nil, ErrTemplateExists
}
template := entity.NewValuesTemplate(workspaceID, ownerID, chartRef.ID, name, valuesYAML)
template.Description = description
template.IsDefault = isDefault
if err := s.valuesTemplateRepo.Create(ctx, template); err != nil {
return nil, err
}
return template, nil
}
// GetByID 获取 Values 模板
func (s *ValuesTemplateService) GetByID(ctx context.Context, id string) (*entity.ValuesTemplate, error) {
template, err := s.valuesTemplateRepo.GetByID(ctx, id)
if err != nil {
return nil, ErrTemplateNotFound
}
return template, nil
}
// GetByWorkspace 获取工作空间的所有 Values 模板
func (s *ValuesTemplateService) GetByWorkspace(ctx context.Context, workspaceID string) ([]*entity.ValuesTemplate, error) {
return s.valuesTemplateRepo.GetByWorkspace(ctx, workspaceID)
}
// GetByChartReference 获取 Chart Reference 的所有 Values 模板
func (s *ValuesTemplateService) GetByChartReference(ctx context.Context, chartRefID string) ([]*entity.ValuesTemplate, error) {
return s.valuesTemplateRepo.GetByChartReference(ctx, chartRefID)
}
// GetHistory 获取模板的版本历史
func (s *ValuesTemplateService) GetHistory(ctx context.Context, chartRefID, name string) ([]*entity.ValuesTemplate, error) {
return s.valuesTemplateRepo.GetHistory(ctx, chartRefID, name)
}
// Update 更新 Values 模板(创建新版本)
func (s *ValuesTemplateService) Update(
ctx context.Context,
id, description, valuesYAML string,
isDefault bool,
) (*entity.ValuesTemplate, error) {
template, err := s.valuesTemplateRepo.GetByID(ctx, id)
if err != nil {
return nil, ErrTemplateNotFound
}
template.Description = description
template.ValuesYAML = valuesYAML
template.IsDefault = isDefault
if err := s.valuesTemplateRepo.Update(ctx, template); err != nil {
return nil, err
}
// 获取最新版本
return s.valuesTemplateRepo.GetByName(ctx, template.WorkspaceID, template.ChartReferenceID, template.Name)
}
// Delete 删除 Values 模板
func (s *ValuesTemplateService) Delete(ctx context.Context, id string) error {
return s.valuesTemplateRepo.Delete(ctx, id)
}
// List 列出所有 Values 模板(管理员用)
func (s *ValuesTemplateService) List(ctx context.Context) ([]*entity.ValuesTemplate, error) {
return s.valuesTemplateRepo.List(ctx)
}
// Rollback 回滚到指定版本
func (s *ValuesTemplateService) Rollback(ctx context.Context, templateID string) (*entity.ValuesTemplate, error) {
// 获取历史版本模板
oldTemplate, err := s.valuesTemplateRepo.GetByID(ctx, templateID)
if err != nil {
return nil, ErrTemplateNotFound
}
// 重新创建该版本(创建新版本,内容与旧版本相同)
newTemplate := &entity.ValuesTemplate{
WorkspaceID: oldTemplate.WorkspaceID,
OwnerID: oldTemplate.OwnerID,
ChartReferenceID: oldTemplate.ChartReferenceID,
Name: oldTemplate.Name,
Description: oldTemplate.Description,
ValuesYAML: oldTemplate.ValuesYAML,
}
if err := s.valuesTemplateRepo.Update(ctx, newTemplate); err != nil {
return nil, err
}
// 获取最新版本
return s.valuesTemplateRepo.GetByName(ctx, newTemplate.WorkspaceID, newTemplate.ChartReferenceID, newTemplate.Name)
}