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" ) // StorageHandler Storage Backend Handler type StorageHandler struct { storageService *service.StorageService } // NewStorageHandler 创建 Storage Handler func NewStorageHandler(storageService *service.StorageService) *StorageHandler { return &StorageHandler{ storageService: storageService, } } // CreateStorage 创建存储后端 // @Summary 创建存储后端 // @Description 新增存储后端配置(NFS/PV/hostPath) // @Tags Storage // @Accept json // @Produce json // @Security BearerAuth // @Param request body dto.CreateStorageRequest true "存储后端信息" // @Success 201 {object} dto.StorageResponse // @Failure 400 {object} dto.ErrorResponse // @Router /storage-backends [post] func (h *StorageHandler) CreateStorage(w http.ResponseWriter, r *http.Request) { var req dto.CreateStorageRequest 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") ownerID := r.Header.Get("X-User-ID") // 构建配置 storageType := entity.StorageType(req.Type) config := entity.StorageConfig{} switch storageType { case entity.StorageTypeNFS: config.NFS = &entity.NFSConfig{ Server: req.NFS.Server, Path: req.NFS.Path, } case entity.StorageTypePV: config.PV = &entity.PVConfig{ StorageClassName: req.PV.StorageClassName, Capacity: req.PV.Capacity, AccessModes: req.PV.AccessModes, } case entity.StorageTypeHostPath: config.HostPath = &entity.HostPathConfig{ Path: req.HostPath.Path, } } // 调用领域服务 storage, err := h.storageService.Create( r.Context(), workspaceID, ownerID, req.Name, storageType, config, req.Description, req.IsDefault, req.IsShared, ) if err != nil { respondError(w, http.StatusBadRequest, "Failed to create storage backend", err.Error()) return } // 返回响应 response := toStorageResponse(storage) respondJSON(w, http.StatusCreated, response) } // GetStorage 获取存储后端详情 // @Summary 获取存储后端 // @Tags Storage // @Produce json // @Security BearerAuth // @Param storage_id path string true "Storage ID" // @Success 200 {object} dto.StorageResponse // @Failure 404 {object} dto.ErrorResponse // @Router /storage-backends/{storage_id} [get] func (h *StorageHandler) GetStorage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) storageID := vars["storage_id"] storage, err := h.storageService.GetByID(r.Context(), storageID) if err != nil { respondError(w, http.StatusNotFound, "Storage backend not found", err.Error()) return } response := toStorageResponse(storage) respondJSON(w, http.StatusOK, response) } // GetAllStorage 获取所有存储后端 // @Summary 列出所有存储后端 // @Tags Storage // @Produce json // @Security BearerAuth // @Success 200 {array} dto.StorageResponse // @Failure 500 {object} dto.ErrorResponse // @Router /storage-backends [get] func (h *StorageHandler) GetAllStorage(w http.ResponseWriter, r *http.Request) { // 获取 workspace_id(从 JWT) workspaceID := r.Header.Get("X-Workspace-ID") role := r.Header.Get("X-User-Role") var storages []*entity.StorageBackend var err error // Admin 可以看到所有,其他用户只看自己 workspace + 共享的 if role == "admin" { storages, err = h.storageService.List(r.Context()) } else if workspaceID != "" { // 获取 workspace 的存储 + 共享存储 workspaceStorages, _ := h.storageService.GetByWorkspace(r.Context(), workspaceID) sharedStorages, _ := h.storageService.GetShared(r.Context()) // 合并去重 seen := make(map[string]bool) for _, s := range workspaceStorages { if !seen[s.ID] { storages = append(storages, s) seen[s.ID] = true } } for _, s := range sharedStorages { if !seen[s.ID] { storages = append(storages, s) seen[s.ID] = true } } } else { // 没有 workspace 的用户只能看到共享存储 storages, err = h.storageService.GetShared(r.Context()) } if err != nil { respondError(w, http.StatusInternalServerError, "Failed to list storage backends", err.Error()) return } responses := make([]*dto.StorageResponse, 0, len(storages)) for _, storage := range storages { responses = append(responses, toStorageResponse(storage)) } respondJSON(w, http.StatusOK, responses) } // UpdateStorage 更新存储后端 // @Summary 更新存储后端 // @Tags Storage // @Accept json // @Produce json // @Security BearerAuth // @Param storage_id path string true "Storage ID" // @Param request body dto.UpdateStorageRequest true "更新内容" // @Success 200 {object} dto.StorageResponse // @Failure 404 {object} dto.ErrorResponse // @Router /storage-backends/{storage_id} [put] func (h *StorageHandler) UpdateStorage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) storageID := vars["storage_id"] var req dto.UpdateStorageRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondError(w, http.StatusBadRequest, "Invalid request body", err.Error()) return } // 构建配置 var storageType entity.StorageType if req.Type != "" { storageType = entity.StorageType(req.Type) } config := entity.StorageConfig{} if storageType == entity.StorageTypeNFS && (req.NFS.Server != "" || req.NFS.Path != "") { config.NFS = &entity.NFSConfig{ Server: req.NFS.Server, Path: req.NFS.Path, } } else if storageType == entity.StorageTypePV && req.PV.StorageClassName != "" { config.PV = &entity.PVConfig{ StorageClassName: req.PV.StorageClassName, Capacity: req.PV.Capacity, AccessModes: req.PV.AccessModes, } } else if storageType == entity.StorageTypeHostPath && req.HostPath.Path != "" { config.HostPath = &entity.HostPathConfig{ Path: req.HostPath.Path, } } storage, err := h.storageService.Update( r.Context(), storageID, req.Name, req.Description, storageType, config, req.IsDefault, req.IsShared, ) if err != nil { respondError(w, http.StatusBadRequest, "Failed to update storage backend", err.Error()) return } response := toStorageResponse(storage) respondJSON(w, http.StatusOK, response) } // DeleteStorage 删除存储后端 // @Summary 删除存储后端 // @Tags Storage // @Produce json // @Security BearerAuth // @Param storage_id path string true "Storage ID" // @Success 204 {string} string "No Content" // @Failure 404 {object} dto.ErrorResponse // @Router /storage-backends/{storage_id} [delete] func (h *StorageHandler) DeleteStorage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) storageID := vars["storage_id"] if err := h.storageService.Delete(r.Context(), storageID); err != nil { respondError(w, http.StatusNotFound, "Failed to delete storage backend", err.Error()) return } w.WriteHeader(http.StatusNoContent) } // toStorageResponse 转换为响应 DTO func toStorageResponse(storage *entity.StorageBackend) *dto.StorageResponse { config := dto.StorageConfigDTO{} if storage.Config.NFS != nil { config.NFS = &dto.NFSConfigDTO{ Server: storage.Config.NFS.Server, Path: storage.Config.NFS.Path, } } if storage.Config.PV != nil { config.PV = &dto.PVConfigDTO{ StorageClassName: storage.Config.PV.StorageClassName, Capacity: storage.Config.PV.Capacity, AccessModes: storage.Config.PV.AccessModes, } } if storage.Config.HostPath != nil { config.HostPath = &dto.HostPathConfigDTO{ Path: storage.Config.HostPath.Path, } } return &dto.StorageResponse{ ID: storage.ID, WorkspaceID: storage.WorkspaceID, OwnerID: storage.OwnerID, Name: storage.Name, Type: string(storage.Type), Config: config, Description: storage.Description, IsDefault: storage.IsDefault, IsShared: storage.IsShared, CreatedAt: storage.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), UpdatedAt: storage.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), } }