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.
152 lines
4.1 KiB
Go
152 lines
4.1 KiB
Go
package rest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"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"
|
|
)
|
|
|
|
// UserHandler 用户 HTTP 处理程序
|
|
type UserHandler struct {
|
|
authService *service.AuthService
|
|
workspaceService *service.WorkspaceService
|
|
}
|
|
|
|
// NewUserHandler 创建用户处理程序
|
|
func NewUserHandler(authService *service.AuthService, workspaceService *service.WorkspaceService) *UserHandler {
|
|
return &UserHandler{
|
|
authService: authService,
|
|
workspaceService: workspaceService,
|
|
}
|
|
}
|
|
|
|
// GetCurrentUser 获取当前用户信息
|
|
// @Summary 获取当前用户信息
|
|
// @Description 获取当前登录用户的基本信息
|
|
// @Tags user
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} dto.UserResponseWithDTO
|
|
// @Router /users/me [get]
|
|
func (h *UserHandler) GetCurrentUser(w http.ResponseWriter, r *http.Request) {
|
|
userID := GetUserIDFromRequest(r)
|
|
if userID == "" {
|
|
respondError(w, http.StatusUnauthorized, "Not authenticated", "")
|
|
return
|
|
}
|
|
|
|
user, err := h.authService.GetUserByID(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)})
|
|
}
|
|
|
|
// ChangePassword 修改当前用户密码
|
|
// @Summary 修改当前用户密码
|
|
// @Description 修改当前登录用户的密码
|
|
// @Tags user
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body dto.ChangePasswordRequest true "修改密码请求"
|
|
// @Success 200
|
|
// @Router /users/me/password [put]
|
|
func (h *UserHandler) ChangePassword(w http.ResponseWriter, r *http.Request) {
|
|
userID := GetUserIDFromRequest(r)
|
|
if userID == "" {
|
|
respondError(w, http.StatusUnauthorized, "Not authenticated", "")
|
|
return
|
|
}
|
|
|
|
var req dto.ChangePasswordRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
respondError(w, http.StatusBadRequest, "Invalid request body", "")
|
|
return
|
|
}
|
|
|
|
err := h.authService.ChangePassword(r.Context(), userID, req.OldPassword, req.NewPassword)
|
|
if err != nil {
|
|
respondError(w, http.StatusBadRequest, err.Error(), "")
|
|
return
|
|
}
|
|
|
|
respondSuccess(w, "Password changed successfully", map[string]string{"message": "Password changed successfully"})
|
|
}
|
|
|
|
// GetCurrentUserWorkspace 获取当前用户所属的 Workspace
|
|
// @Summary 获取当前用户所属工作空间
|
|
// @Description 获取当前用户所属工作空间的详细信息和配额
|
|
// @Tags user
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} dto.WorkspaceResponse
|
|
// @Router /users/me/workspace [get]
|
|
func (h *UserHandler) GetCurrentUserWorkspace(w http.ResponseWriter, r *http.Request) {
|
|
userID := GetUserIDFromRequest(r)
|
|
if userID == "" {
|
|
respondError(w, http.StatusUnauthorized, "Not authenticated", "")
|
|
return
|
|
}
|
|
|
|
user, err := h.authService.GetUserByID(r.Context(), userID)
|
|
if err != nil {
|
|
respondError(w, http.StatusNotFound, "User not found", "")
|
|
return
|
|
}
|
|
|
|
// Admin 没有 workspace
|
|
if user.Role == entity.RoleAdmin {
|
|
respondSuccess(w, "", nil)
|
|
return
|
|
}
|
|
|
|
if user.WorkspaceID == "" {
|
|
respondSuccess(w, "", nil)
|
|
return
|
|
}
|
|
|
|
workspace, err := h.workspaceService.GetByID(r.Context(), user.WorkspaceID)
|
|
if err != nil {
|
|
respondError(w, http.StatusNotFound, "Workspace not found", "")
|
|
return
|
|
}
|
|
|
|
// 获取配额
|
|
quotas, _ := h.workspaceService.GetQuotas(r.Context(), workspace.ID)
|
|
|
|
response := dto.WorkspaceResponse{
|
|
Workspace: dto.WorkspaceDTOFromEntity(workspace),
|
|
Quotas: dto.QuotaDTOsFromEntities(quotas),
|
|
}
|
|
|
|
respondSuccess(w, "", response)
|
|
}
|
|
|
|
// GetUserIDFromRequest 从请求中获取用户 ID
|
|
func GetUserIDFromRequest(r *http.Request) string {
|
|
// 尝试从 Header 获取(由中间件设置)
|
|
userID := r.Header.Get("X-User-ID")
|
|
if userID != "" {
|
|
return userID
|
|
}
|
|
|
|
// 尝试从 Context 获取(安全类型断言)
|
|
if uid, ok := r.Context().Value("user_id").(string); ok {
|
|
return uid
|
|
}
|
|
return ""
|
|
} |