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" ) // WorkspaceHandler 工作空间 HTTP 处理程序 type WorkspaceHandler struct { workspaceService *service.WorkspaceService authService *service.AuthService } // NewWorkspaceHandler 创建工作空间处理程序 func NewWorkspaceHandler(workspaceService *service.WorkspaceService, authService *service.AuthService) *WorkspaceHandler { return &WorkspaceHandler{ workspaceService: workspaceService, authService: authService, } } // CreateWorkspace 创建工作空间 // @Summary 创建工作空间 // @Description 创建新的工作空间(Admin 专用,支持 cluster_ids 和初始配额) // @Tags workspace // @Accept json // @Produce json // @Param request body dto.CreateWorkspaceRequest true "创建工作空间请求" // @Success 200 {object} dto.WorkspaceDTO // @Router /workspaces [post] func (h *WorkspaceHandler) CreateWorkspace(w http.ResponseWriter, r *http.Request) { // 检查权限(Admin) if !h.requireAdmin(w, r) { return } var req dto.CreateWorkspaceRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondError(w, http.StatusBadRequest, "Invalid request body", "") return } // 获取创建者 ID userID := GetUserIDFromRequest(r) // 准备配额 quotas := make(map[entity.ResourceType]struct { HardLimit float64 SoftLimit float64 }) if req.CPU != nil { quotas[entity.ResourceCPU] = struct { HardLimit float64 SoftLimit float64 }{req.CPU.HardLimit, req.CPU.SoftLimit} } if req.GPU != nil { quotas[entity.ResourceGPU] = struct { HardLimit float64 SoftLimit float64 }{req.GPU.HardLimit, req.GPU.SoftLimit} } if req.GPUMemory != nil { quotas[entity.ResourceGPUMemory] = struct { HardLimit float64 SoftLimit float64 }{req.GPUMemory.HardLimit, req.GPUMemory.SoftLimit} } workspace, err := h.workspaceService.Create(r.Context(), req.Name, req.Description, userID, req.ClusterIDs, quotas) if err != nil { respondError(w, http.StatusBadRequest, err.Error(), "") return } respondSuccess(w, "", dto.WorkspaceDTOFromEntity(workspace)) } // GetWorkspace 获取工作空间 // @Summary 获取工作空间 // @Description 获取指定工作空间的详细信息和配额 // @Tags workspace // @Accept json // @Produce json // @Param workspace_id path string true "工作空间 ID" // @Success 200 {object} dto.WorkspaceResponse // @Router /workspaces/{workspace_id} [get] func (h *WorkspaceHandler) GetWorkspace(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) workspaceID := vars["workspace_id"] workspace, err := h.workspaceService.GetByID(r.Context(), workspaceID) if err != nil { respondError(w, http.StatusNotFound, "Workspace not found", "") return } // 检查访问权限 if !h.canAccessWorkspace(w, r, workspace.ID) { return } // 获取配额 quotas, _ := h.workspaceService.GetQuotas(r.Context(), workspace.ID) response := dto.WorkspaceResponse{ Workspace: dto.WorkspaceDTOFromEntity(workspace), Quotas: dto.QuotaDTOsFromEntities(quotas), } respondSuccess(w, "", response) } // UpdateWorkspace 更新工作空间 // @Summary 更新工作空间 // @Description 更新工作空间信息(Admin 专用) // @Tags workspace // @Accept json // @Produce json // @Param workspace_id path string true "工作空间 ID" // @Param request body dto.UpdateWorkspaceRequest true "更新工作空间请求" // @Success 200 {object} dto.WorkspaceDTO // @Router /workspaces/{workspace_id} [put] func (h *WorkspaceHandler) UpdateWorkspace(w http.ResponseWriter, r *http.Request) { // 检查权限(Admin) if !h.requireAdmin(w, r) { return } vars := mux.Vars(r) workspaceID := vars["workspace_id"] workspace, err := h.workspaceService.GetByID(r.Context(), workspaceID) if err != nil { respondError(w, http.StatusNotFound, "Workspace not found", "") return } var req dto.UpdateWorkspaceRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondError(w, http.StatusBadRequest, "Invalid request body", "") return } if req.Name != "" { workspace.Name = req.Name } if req.Description != "" { workspace.Description = req.Description } if req.ClusterIDs != nil { workspace.ClusterIDs = req.ClusterIDs } if err := h.workspaceService.Update(r.Context(), workspace); err != nil { respondError(w, http.StatusBadRequest, err.Error(), "") return } respondSuccess(w, "", dto.WorkspaceDTOFromEntity(workspace)) } // DeleteWorkspace 删除工作空间 // @Summary 删除工作空间 // @Description 删除指定工作空间(Admin 专用) // @Tags workspace // @Accept json // @Produce json // @Param workspace_id path string true "工作空间 ID" // @Success 200 // @Router /workspaces/{workspace_id} [delete] func (h *WorkspaceHandler) DeleteWorkspace(w http.ResponseWriter, r *http.Request) { // 检查权限(Admin) if !h.requireAdmin(w, r) { return } vars := mux.Vars(r) workspaceID := vars["workspace_id"] if err := h.workspaceService.Delete(r.Context(), workspaceID); err != nil { respondError(w, http.StatusBadRequest, err.Error(), "") return } respondSuccess(w, "", nil) } // ListWorkspaces 列出所有工作空间 // @Summary 列出所有工作空间 // @Description 获取所有工作空间列表(Admin 专用) // @Tags workspace // @Accept json // @Produce json // @Success 200 {object} dto.WorkspaceListResponse // @Router /workspaces [get] func (h *WorkspaceHandler) ListWorkspaces(w http.ResponseWriter, r *http.Request) { // 检查权限(Admin) if !h.requireAdmin(w, r) { return } workspaces, err := h.workspaceService.List(r.Context()) if err != nil { respondError(w, http.StatusInternalServerError, err.Error(), "") return } respondSuccess(w, "", dto.WorkspaceListResponse{ Workspaces: dto.WorkspaceDTOsFromEntities(workspaces), Total: len(workspaces), }) } // GetWorkspaceQuotas 获取工作空间配额 // @Summary 获取工作空间配额 // @Description 获取指定工作空间的资源配额 // @Tags workspace // @Accept json // @Produce json // @Param workspace_id path string true "工作空间 ID" // @Success 200 {array} dto.QuotaDTO // @Router /workspaces/{workspace_id}/quotas [get] func (h *WorkspaceHandler) GetWorkspaceQuotas(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) workspaceID := vars["workspace_id"] // 检查访问权限 if !h.canAccessWorkspace(w, r, workspaceID) { return } quotas, err := h.workspaceService.GetQuotas(r.Context(), workspaceID) if err != nil { respondError(w, http.StatusInternalServerError, err.Error(), "") return } respondSuccess(w, "", dto.QuotaDTOsFromEntities(quotas)) } // SetWorkspaceQuotas 设置工作空间配额 // @Summary 设置工作空间配额 // @Description 设置指定工作空间的 CPU/GPU/GPU Memory 配额(Admin 专用) // @Tags workspace // @Accept json // @Produce json // @Param workspace_id path string true "工作空间 ID" // @Param request body dto.SetQuotasRequest true "配额设置请求" // @Success 200 {array} dto.QuotaDTO // @Router /workspaces/{workspace_id}/quotas [put] func (h *WorkspaceHandler) SetWorkspaceQuotas(w http.ResponseWriter, r *http.Request) { // 检查权限(Admin) if !h.requireAdmin(w, r) { return } vars := mux.Vars(r) workspaceID := vars["workspace_id"] var req dto.SetQuotasRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondError(w, http.StatusBadRequest, "Invalid request body", "") return } quotas := make(map[entity.ResourceType]struct { HardLimit float64 SoftLimit float64 }) if req.CPU != nil { quotas[entity.ResourceCPU] = struct { HardLimit float64 SoftLimit float64 }{req.CPU.HardLimit, req.CPU.SoftLimit} } if req.GPU != nil { quotas[entity.ResourceGPU] = struct { HardLimit float64 SoftLimit float64 }{req.GPU.HardLimit, req.GPU.SoftLimit} } if req.GPUMemory != nil { quotas[entity.ResourceGPUMemory] = struct { HardLimit float64 SoftLimit float64 }{req.GPUMemory.HardLimit, req.GPUMemory.SoftLimit} } if err := h.workspaceService.SetQuotas(r.Context(), workspaceID, quotas); err != nil { respondError(w, http.StatusBadRequest, err.Error(), "") return } // 返回更新后的配额 updatedQuotas, _ := h.workspaceService.GetQuotas(r.Context(), workspaceID) respondSuccess(w, "", dto.QuotaDTOsFromEntities(updatedQuotas)) } // requireAdmin 检查是否为 Admin func (h *WorkspaceHandler) requireAdmin(w http.ResponseWriter, r *http.Request) bool { userRole := r.Header.Get("X-User-Role") if userRole != string(entity.RoleAdmin) { respondError(w, http.StatusForbidden, "Admin access required", "") return false } return true } // canAccessWorkspace 检查是否可以访问工作空间 func (h *WorkspaceHandler) canAccessWorkspace(w http.ResponseWriter, r *http.Request, workspaceID string) bool { userRole := r.Header.Get("X-User-Role") userWorkspaceID := r.Header.Get("X-Workspace-ID") // Admin 可以访问所有 if userRole == string(entity.RoleAdmin) { return true } // 普通用户只能访问自己的 workspace if userWorkspaceID != workspaceID { respondError(w, http.StatusForbidden, "Access denied", "") return false } return true }