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:
@ -0,0 +1,332 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// UserManagementHandler 用户管理 HTTP 处理程序
|
||||
type UserManagementHandler struct {
|
||||
userManagementService *service.UserManagementService
|
||||
authService *service.AuthService
|
||||
workspaceService *service.WorkspaceService
|
||||
}
|
||||
|
||||
// NewUserManagementHandler 创建用户管理处理程序
|
||||
func NewUserManagementHandler(
|
||||
userManagementService *service.UserManagementService,
|
||||
authService *service.AuthService,
|
||||
workspaceService *service.WorkspaceService,
|
||||
) *UserManagementHandler {
|
||||
return &UserManagementHandler{
|
||||
userManagementService: userManagementService,
|
||||
authService: authService,
|
||||
workspaceService: workspaceService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateUser 创建用户(Admin 操作)
|
||||
// @Summary 创建用户
|
||||
// @Description 创建新用户(Admin 专用)
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.CreateUserRequest true "创建用户请求"
|
||||
// @Success 200 {object} dto.UserResponseWithDTO
|
||||
// @Router /admin/users [post]
|
||||
func (h *UserManagementHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.CreateUserRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "Invalid request body", "")
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userManagementService.CreateUser(r.Context(), req.Username, req.Password, req.Email, req.Role, req.WorkspaceID)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err.Error(), "")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取 workspace 名称
|
||||
workspaceName := ""
|
||||
if user.WorkspaceID != "" {
|
||||
ws, _ := h.workspaceService.GetByID(r.Context(), user.WorkspaceID)
|
||||
if ws != nil {
|
||||
workspaceName = ws.Name
|
||||
}
|
||||
}
|
||||
|
||||
respondSuccess(w, "", dto.UserResponseWithDTO{User: dto.UserDTOFromEntity(user, workspaceName)})
|
||||
}
|
||||
|
||||
// GetUser 获取用户
|
||||
// @Summary 获取用户
|
||||
// @Description 获取指定用户信息(Admin 专用)
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path string true "用户 ID"
|
||||
// @Success 200 {object} dto.UserResponseWithDTO
|
||||
// @Router /admin/users/{user_id} [get]
|
||||
func (h *UserManagementHandler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
userID := vars["user_id"]
|
||||
|
||||
user, err := h.userManagementService.GetUser(r.Context(), userID)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusNotFound, "User not found", "")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取 workspace 名称
|
||||
workspaceName := ""
|
||||
if user.WorkspaceID != "" {
|
||||
ws, _ := h.workspaceService.GetByID(r.Context(), user.WorkspaceID)
|
||||
if ws != nil {
|
||||
workspaceName = ws.Name
|
||||
}
|
||||
}
|
||||
|
||||
respondSuccess(w, "", dto.UserResponseWithDTO{User: dto.UserDTOFromEntity(user, workspaceName)})
|
||||
}
|
||||
|
||||
// ListUsers 列出用户
|
||||
// @Summary 列出用户
|
||||
// @Description 获取所有用户列表(Admin 专用),可按 workspace_id 筛选
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param workspace_id query string false "工作空间 ID"
|
||||
// @Success 200 {object} dto.UserListResponse
|
||||
// @Router /admin/users [get]
|
||||
func (h *UserManagementHandler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
workspaceID := r.URL.Query().Get("workspace_id")
|
||||
|
||||
users, err := h.userManagementService.ListUsers(r.Context(), workspaceID)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err.Error(), "")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取所有 workspace 名称
|
||||
workspaceNames := make(map[string]string)
|
||||
workspaces, _ := h.workspaceService.List(r.Context())
|
||||
for _, ws := range workspaces {
|
||||
workspaceNames[ws.ID] = ws.Name
|
||||
}
|
||||
|
||||
respondSuccess(w, "", dto.UserListResponse{
|
||||
Users: dto.UserDTOsFromEntities(users, workspaceNames),
|
||||
Total: len(users),
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户
|
||||
// @Summary 更新用户
|
||||
// @Description 更新用户信息(Admin 专用)
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path string true "用户 ID"
|
||||
// @Param request body dto.UpdateUserRequest true "更新用户请求"
|
||||
// @Success 200 {object} dto.UserResponseWithDTO
|
||||
// @Router /admin/users/{user_id} [put]
|
||||
func (h *UserManagementHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
userID := vars["user_id"]
|
||||
|
||||
user, err := h.userManagementService.GetUser(r.Context(), userID)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusNotFound, "User not found", "")
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.UpdateUserRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "Invalid request body", "")
|
||||
return
|
||||
}
|
||||
|
||||
if req.Email != "" {
|
||||
user.Email = req.Email
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
user.IsActive = *req.IsActive
|
||||
}
|
||||
|
||||
if err := h.userManagementService.UpdateUser(r.Context(), user); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err.Error(), "")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取 workspace 名称
|
||||
workspaceName := ""
|
||||
if user.WorkspaceID != "" {
|
||||
ws, _ := h.workspaceService.GetByID(r.Context(), user.WorkspaceID)
|
||||
if ws != nil {
|
||||
workspaceName = ws.Name
|
||||
}
|
||||
}
|
||||
|
||||
respondSuccess(w, "", dto.UserResponseWithDTO{User: dto.UserDTOFromEntity(user, workspaceName)})
|
||||
}
|
||||
|
||||
// SetUserActive 启用/禁用用户
|
||||
// @Summary 启用/禁用用户
|
||||
// @Description 设置用户是否启用(Admin 专用)
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path string true "用户 ID"
|
||||
// @Param request body dto.SetUserActiveRequest true "启用状态"
|
||||
// @Success 200
|
||||
// @Router /admin/users/{user_id}/active [put]
|
||||
func (h *UserManagementHandler) SetUserActive(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
userID := vars["user_id"]
|
||||
|
||||
var req dto.SetUserActiveRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "Invalid request body", "")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.userManagementService.SetUserActive(r.Context(), userID, req.IsActive); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err.Error(), "")
|
||||
return
|
||||
}
|
||||
|
||||
respondSuccess(w, "", nil)
|
||||
}
|
||||
|
||||
// ChangeUserWorkspace 分配用户到 Workspace
|
||||
// @Summary 分配用户到工作空间
|
||||
// @Description 将用户分配到指定工作空间(Admin 专用)
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path string true "用户 ID"
|
||||
// @Param request body dto.ChangeUserWorkspaceRequest true "工作空间分配请求"
|
||||
// @Success 200
|
||||
// @Router /admin/users/{user_id}/workspace [put]
|
||||
func (h *UserManagementHandler) ChangeUserWorkspace(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
userID := vars["user_id"]
|
||||
|
||||
var req dto.ChangeUserWorkspaceRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "Invalid request body", "")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.userManagementService.ChangeUserWorkspace(r.Context(), userID, req.WorkspaceID); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err.Error(), "")
|
||||
return
|
||||
}
|
||||
|
||||
respondSuccess(w, "", nil)
|
||||
}
|
||||
|
||||
// ResetPassword 重置用户密码(Admin 操作)
|
||||
// @Summary 重置用户密码
|
||||
// @Description 重置指定用户的密码(Admin 专用)
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path string true "用户 ID"
|
||||
// @Param request body dto.ResetPasswordRequest true "重置密码请求"
|
||||
// @Success 200
|
||||
// @Router /admin/users/{user_id}/password [put]
|
||||
func (h *UserManagementHandler) ResetPassword(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
userID := vars["user_id"]
|
||||
|
||||
var req dto.ResetPasswordRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "Invalid request body", "")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.userManagementService.ResetPassword(r.Context(), userID, req.NewPassword); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err.Error(), "")
|
||||
return
|
||||
}
|
||||
|
||||
respondSuccess(w, "", nil)
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
// @Summary 删除用户
|
||||
// @Description 删除指定用户(Admin 专用)
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path string true "用户 ID"
|
||||
// @Success 200
|
||||
// @Router /admin/users/{user_id} [delete]
|
||||
func (h *UserManagementHandler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
// 检查权限(Admin)
|
||||
if !h.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
userID := vars["user_id"]
|
||||
|
||||
if err := h.userManagementService.DeleteUser(r.Context(), userID); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err.Error(), "")
|
||||
return
|
||||
}
|
||||
|
||||
respondSuccess(w, "", nil)
|
||||
}
|
||||
|
||||
// requireAdmin 检查是否为 Admin
|
||||
func (h *UserManagementHandler) 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
|
||||
}
|
||||
Reference in New Issue
Block a user