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.
229 lines
7.2 KiB
Go
229 lines
7.2 KiB
Go
package rest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/ocdp/cluster-service/internal/adapter/input/http/dto"
|
|
"github.com/ocdp/cluster-service/internal/domain/entity"
|
|
"github.com/ocdp/cluster-service/internal/domain/service"
|
|
)
|
|
|
|
// ChartReferenceHandler Chart Reference Handler
|
|
type ChartReferenceHandler struct {
|
|
chartRefService *service.ChartReferenceService
|
|
}
|
|
|
|
// NewChartReferenceHandler 创建 Chart Reference Handler
|
|
func NewChartReferenceHandler(chartRefService *service.ChartReferenceService) *ChartReferenceHandler {
|
|
return &ChartReferenceHandler{
|
|
chartRefService: chartRefService,
|
|
}
|
|
}
|
|
|
|
// CreateChartReference 创建 Chart 引用
|
|
// @Summary 创建 Chart 引用
|
|
// @Description 新增 Chart 引用配置
|
|
// @Tags Chart References
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param request body dto.CreateChartReferenceRequest true "Chart 引用信息"
|
|
// @Success 201 {object} dto.ChartReferenceResponse
|
|
// @Failure 400 {object} dto.ErrorResponse
|
|
// @Router /chart-references [post]
|
|
func (h *ChartReferenceHandler) CreateChartReference(w http.ResponseWriter, r *http.Request) {
|
|
var req dto.CreateChartReferenceRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
respondError(w, http.StatusBadRequest, "Invalid request body", err.Error())
|
|
return
|
|
}
|
|
|
|
// 获取用户信息
|
|
workspaceID := r.Header.Get("X-Workspace-ID")
|
|
|
|
chartRef, err := h.chartRefService.Create(
|
|
r.Context(),
|
|
workspaceID,
|
|
req.RegistryID,
|
|
req.Repository,
|
|
req.ChartName,
|
|
req.Description,
|
|
)
|
|
if err != nil {
|
|
respondError(w, http.StatusBadRequest, "Failed to create chart reference", err.Error())
|
|
return
|
|
}
|
|
|
|
response := toChartReferenceResponse(chartRef)
|
|
respondJSON(w, http.StatusCreated, response)
|
|
}
|
|
|
|
// GetChartReference 获取 Chart 引用详情
|
|
// @Summary 获取 Chart 引用
|
|
// @Tags Chart References
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param chart_reference_id path string true "Chart Reference ID"
|
|
// @Success 200 {object} dto.ChartReferenceResponse
|
|
// @Failure 404 {object} dto.ErrorResponse
|
|
// @Router /chart-references/{chart_reference_id} [get]
|
|
func (h *ChartReferenceHandler) GetChartReference(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
chartRefID := vars["chart_reference_id"]
|
|
|
|
chartRef, err := h.chartRefService.GetByID(r.Context(), chartRefID)
|
|
if err != nil {
|
|
respondError(w, http.StatusNotFound, "Chart reference not found", err.Error())
|
|
return
|
|
}
|
|
|
|
response := toChartReferenceResponse(chartRef)
|
|
respondJSON(w, http.StatusOK, response)
|
|
}
|
|
|
|
// GetAllChartReferences 获取所有 Chart 引用
|
|
// @Summary 列出所有 Chart 引用
|
|
// @Tags Chart References
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Success 200 {array} dto.ChartReferenceResponse
|
|
// @Failure 500 {object} dto.ErrorResponse
|
|
// @Router /chart-references [get]
|
|
func (h *ChartReferenceHandler) GetAllChartReferences(w http.ResponseWriter, r *http.Request) {
|
|
workspaceID := r.Header.Get("X-Workspace-ID")
|
|
role := r.Header.Get("X-User-Role")
|
|
|
|
var chartRefs []*dto.ChartReferenceResponse
|
|
|
|
// Admin 可以看到所有,其他用户只看自己 workspace
|
|
if role == "admin" {
|
|
allChartRefs, err := h.chartRefService.List(r.Context())
|
|
if err != nil {
|
|
respondError(w, http.StatusInternalServerError, "Failed to list chart references", err.Error())
|
|
return
|
|
}
|
|
for _, cr := range allChartRefs {
|
|
chartRefs = append(chartRefs, toChartReferenceResponse(cr))
|
|
}
|
|
} else if workspaceID != "" {
|
|
workspaceChartRefs, err := h.chartRefService.GetByWorkspace(r.Context(), workspaceID)
|
|
if err != nil {
|
|
respondError(w, http.StatusInternalServerError, "Failed to list chart references", err.Error())
|
|
return
|
|
}
|
|
for _, cr := range workspaceChartRefs {
|
|
chartRefs = append(chartRefs, toChartReferenceResponse(cr))
|
|
}
|
|
}
|
|
|
|
respondJSON(w, http.StatusOK, chartRefs)
|
|
}
|
|
|
|
// UpdateChartReference 更新 Chart 引用
|
|
// @Summary 更新 Chart 引用
|
|
// @Tags Chart References
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param chart_reference_id path string true "Chart Reference ID"
|
|
// @Param request body dto.UpdateChartReferenceRequest true "更新内容"
|
|
// @Success 200 {object} dto.ChartReferenceResponse
|
|
// @Failure 404 {object} dto.ErrorResponse
|
|
// @Router /chart-references/{chart_reference_id} [put]
|
|
func (h *ChartReferenceHandler) UpdateChartReference(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
chartRefID := vars["chart_reference_id"]
|
|
|
|
var req dto.UpdateChartReferenceRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
respondError(w, http.StatusBadRequest, "Invalid request body", err.Error())
|
|
return
|
|
}
|
|
|
|
isEnabled := true
|
|
if req.IsEnabled != nil {
|
|
isEnabled = *req.IsEnabled
|
|
}
|
|
|
|
chartRef, err := h.chartRefService.Update(
|
|
r.Context(),
|
|
chartRefID,
|
|
req.RegistryID,
|
|
req.Repository,
|
|
req.ChartName,
|
|
req.Description,
|
|
isEnabled,
|
|
)
|
|
if err != nil {
|
|
respondError(w, http.StatusBadRequest, "Failed to update chart reference", err.Error())
|
|
return
|
|
}
|
|
|
|
response := toChartReferenceResponse(chartRef)
|
|
respondJSON(w, http.StatusOK, response)
|
|
}
|
|
|
|
// DeleteChartReference 删除 Chart 引用
|
|
// @Summary 删除 Chart 引用
|
|
// @Tags Chart References
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param chart_reference_id path string true "Chart Reference ID"
|
|
// @Success 204 {string} string "No Content"
|
|
// @Failure 404 {object} dto.ErrorResponse
|
|
// @Router /chart-references/{chart_reference_id} [delete]
|
|
func (h *ChartReferenceHandler) DeleteChartReference(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
chartRefID := vars["chart_reference_id"]
|
|
|
|
if err := h.chartRefService.Delete(r.Context(), chartRefID); err != nil {
|
|
respondError(w, http.StatusNotFound, "Failed to delete chart reference", err.Error())
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
// GetChartReferencesByRegistry 获取 Registry 的所有 Chart 引用
|
|
// @Summary 获取 Registry 的 Chart 引用
|
|
// @Tags Chart References
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param registry_id path string true "Registry ID"
|
|
// @Success 200 {array} dto.ChartReferenceResponse
|
|
// @Failure 500 {object} dto.ErrorResponse
|
|
// @Router /registries/{registry_id}/chart-references [get]
|
|
func (h *ChartReferenceHandler) GetChartReferencesByRegistry(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
registryID := vars["registry_id"]
|
|
|
|
chartRefs, err := h.chartRefService.GetByRegistry(r.Context(), registryID)
|
|
if err != nil {
|
|
respondError(w, http.StatusInternalServerError, "Failed to list chart references", err.Error())
|
|
return
|
|
}
|
|
|
|
responses := make([]*dto.ChartReferenceResponse, 0, len(chartRefs))
|
|
for _, cr := range chartRefs {
|
|
responses = append(responses, toChartReferenceResponse(cr))
|
|
}
|
|
|
|
respondJSON(w, http.StatusOK, responses)
|
|
}
|
|
|
|
// toChartReferenceResponse 转换为响应 DTO
|
|
func toChartReferenceResponse(chartRef *entity.ChartReference) *dto.ChartReferenceResponse {
|
|
return &dto.ChartReferenceResponse{
|
|
ID: chartRef.ID,
|
|
WorkspaceID: chartRef.WorkspaceID,
|
|
RegistryID: chartRef.RegistryID,
|
|
Repository: chartRef.Repository,
|
|
ChartName: chartRef.ChartName,
|
|
Description: chartRef.Description,
|
|
IsEnabled: chartRef.IsEnabled,
|
|
CreatedAt: chartRef.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
|
UpdatedAt: chartRef.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
|
}
|
|
} |