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,116 @@
package service
import (
"context"
"errors"
"github.com/ocdp/cluster-service/internal/domain/entity"
"github.com/ocdp/cluster-service/internal/domain/repository"
)
var (
ErrStorageNotFound = errors.New("storage backend not found")
ErrStorageExists = errors.New("storage backend already exists")
)
// StorageService 存储后端领域服务
type StorageService struct {
storageRepo repository.StorageRepository
}
// NewStorageService 创建存储后端服务
func NewStorageService(storageRepo repository.StorageRepository) *StorageService {
return &StorageService{
storageRepo: storageRepo,
}
}
// Create 创建存储后端
func (s *StorageService) Create(
ctx context.Context,
workspaceID, ownerID, name string,
storageType entity.StorageType,
config entity.StorageConfig,
description string,
isDefault, isShared bool,
) (*entity.StorageBackend, error) {
// 检查名称是否已存在
existing, _ := s.storageRepo.GetByName(ctx, workspaceID, name)
if existing != nil {
return nil, ErrStorageExists
}
storage := entity.NewStorageBackend(workspaceID, ownerID, name, storageType, config)
storage.Description = description
storage.IsDefault = isDefault
storage.IsShared = isShared
if err := s.storageRepo.Create(ctx, storage); err != nil {
return nil, err
}
return storage, nil
}
// GetByID 获取存储后端
func (s *StorageService) GetByID(ctx context.Context, id string) (*entity.StorageBackend, error) {
storage, err := s.storageRepo.GetByID(ctx, id)
if err != nil {
return nil, ErrStorageNotFound
}
return storage, nil
}
// GetByWorkspace 获取工作空间的所有存储后端
func (s *StorageService) GetByWorkspace(ctx context.Context, workspaceID string) ([]*entity.StorageBackend, error) {
return s.storageRepo.GetByWorkspace(ctx, workspaceID)
}
// GetShared 获取所有共享存储后端
func (s *StorageService) GetShared(ctx context.Context) ([]*entity.StorageBackend, error) {
return s.storageRepo.GetShared(ctx)
}
// GetDefault 获取工作空间的默认存储后端
func (s *StorageService) GetDefault(ctx context.Context, workspaceID string) (*entity.StorageBackend, error) {
return s.storageRepo.GetDefault(ctx, workspaceID)
}
// Update 更新存储后端
func (s *StorageService) Update(
ctx context.Context,
id, name, description string,
storageType entity.StorageType,
config entity.StorageConfig,
isDefault, isShared bool,
) (*entity.StorageBackend, error) {
storage, err := s.storageRepo.GetByID(ctx, id)
if err != nil {
return nil, ErrStorageNotFound
}
if name != "" {
storage.Name = name
}
storage.Description = description
storage.Type = storageType
storage.Config = config
storage.IsDefault = isDefault
storage.IsShared = isShared
if err := s.storageRepo.Update(ctx, storage); err != nil {
return nil, err
}
return storage, nil
}
// Delete 删除存储后端
func (s *StorageService) Delete(ctx context.Context, id string) error {
return s.storageRepo.Delete(ctx, id)
}
// List 列出所有存储后端(管理员用)
func (s *StorageService) List(ctx context.Context) ([]*entity.StorageBackend, error) {
return s.storageRepo.List(ctx)
}