From 20d11648a428bfb5da25e10f5b7fe96bd71b0eac Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Thu, 18 Jun 2026 15:21:44 +0800 Subject: [PATCH] Go: add statistics command (#16119) ### What problem does this PR solve? Prepare for enterprise command ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai --- internal/admin/enterprise_handler.go | 851 ++++++++++++++++++++++++++ internal/admin/enterprise_service.go | 489 +++++++++++++++ internal/admin/handler.go | 282 ++------- internal/admin/router.go | 29 +- internal/admin/service.go | 66 +- internal/cli/admin_command.go | 860 ++++++++++++++++++++++++--- internal/cli/admin_parser.go | 630 +++++++++++++++++++- internal/cli/cli_http.go | 44 +- internal/cli/lexer.go | 28 + internal/cli/parser.go | 2 + internal/cli/types.go | 14 + internal/cli/user_parser.go | 59 -- 12 files changed, 2889 insertions(+), 465 deletions(-) create mode 100644 internal/admin/enterprise_handler.go create mode 100644 internal/admin/enterprise_service.go diff --git a/internal/admin/enterprise_handler.go b/internal/admin/enterprise_handler.go new file mode 100644 index 0000000000..50f9668d86 --- /dev/null +++ b/internal/admin/enterprise_handler.go @@ -0,0 +1,851 @@ +// +// Copyright 2026 The InfiniFlow Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package admin + +import ( + "errors" + "net/http" + "ragflow/internal/common" + "strconv" + + "github.com/gin-gonic/gin" +) + +// ListRoles handle list roles +func (h *Handler) ListRoles(c *gin.Context) { + roles, err := h.service.ListRoles() + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + if roles == nil { + roles = []map[string]interface{}{} + } + + success(c, gin.H{ + "roles": roles, + "total": len(roles), + }, "") +} + +// CreateRoleHTTPRequest create role request +type CreateRoleHTTPRequest struct { + RoleName string `json:"role_name" binding:"required"` + Description string `json:"description"` +} + +// CreateRole handle create role +func (h *Handler) CreateRole(c *gin.Context) { + var req CreateRoleHTTPRequest + if err := c.ShouldBindJSON(&req); err != nil { + errorResponse(c, "Role name is required", 400) + return + } + + role, err := h.service.CreateRole(req.RoleName, req.Description) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, role, "") +} + +// GetRole handle get role +func (h *Handler) GetRole(c *gin.Context) { + roleName := c.Param("role_name") + if roleName == "" { + errorResponse(c, "Role name is required", 400) + return + } + + role, err := h.service.GetRole(roleName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, role, "") +} + +// UpdateRoleHTTPRequest update role request +type UpdateRoleHTTPRequest struct { + Description string `json:"description" binding:"required"` +} + +// UpdateRole handle update role +func (h *Handler) UpdateRole(c *gin.Context) { + roleName := c.Param("role_name") + if roleName == "" { + errorResponse(c, "Role name is required", 400) + return + } + + var req UpdateRoleHTTPRequest + if err := c.ShouldBindJSON(&req); err != nil { + errorResponse(c, "Role description is required", 400) + return + } + + role, err := h.service.UpdateRole(roleName, req.Description) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, role, "") +} + +// DeleteRole handle delete role +func (h *Handler) DeleteRole(c *gin.Context) { + roleName := c.Param("role_name") + if roleName == "" { + errorResponse(c, "Role name is required", 400) + return + } + + if err := h.service.DeleteRole(roleName); err != nil { + errorResponse(c, err.Error(), 500) + return + } + + successNoData(c, "") +} + +// GetRolePermission handle get role permission +func (h *Handler) GetRolePermission(c *gin.Context) { + roleName := c.Param("role_name") + if roleName == "" { + errorResponse(c, "Role name is required", 400) + return + } + + permissions, err := h.service.GetRolePermission(roleName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, permissions, "") +} + +// GrantRolePermissionHTTPRequest grant role permission request +type GrantRolePermissionHTTPRequest struct { + Actions []string `json:"actions" binding:"required"` + Resource string `json:"resource" binding:"required"` +} + +// GrantRolePermission handle grant role permission +func (h *Handler) GrantRolePermission(c *gin.Context) { + roleName := c.Param("role_name") + if roleName == "" { + errorResponse(c, "Role name is required", 400) + return + } + + var req GrantRolePermissionHTTPRequest + if err := c.ShouldBindJSON(&req); err != nil { + errorResponse(c, "Permission is required", 400) + return + } + + result, err := h.service.GrantRolePermission(roleName, req.Actions, req.Resource) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "") +} + +// RevokeRolePermissionHTTPRequest revoke role permission request +type RevokeRolePermissionHTTPRequest struct { + Actions []string `json:"actions" binding:"required"` + Resource string `json:"resource" binding:"required"` +} + +// RevokeRolePermission handle revoke role permission +func (h *Handler) RevokeRolePermission(c *gin.Context) { + roleName := c.Param("role_name") + if roleName == "" { + errorResponse(c, "Role name is required", 400) + return + } + + var req RevokeRolePermissionHTTPRequest + if err := c.ShouldBindJSON(&req); err != nil { + errorResponse(c, "Permission is required", 400) + return + } + + result, err := h.service.RevokeRolePermission(roleName, req.Actions, req.Resource) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "") +} + +type ShowUserActivityRequest struct { + Days int `json:"days"` + Email string `json:"email"` +} + +// ShowUserActivity handle show user activity +func (h *Handler) ShowUserActivity(c *gin.Context) { + var req ShowUserActivityRequest + if err := c.ShouldBindJSON(&req); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + userActivity, err := h.service.ShowUserActivity(req.Email, req.Days) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, userActivity, "") +} + +type ShowUserDatasetSummaryRequest struct { + Dataset string `json:"dataset"` +} + +// ShowUserDatasetSummary handle show user dataset summary +func (h *Handler) ShowUserDatasetSummary(c *gin.Context) { + var req ShowUserDatasetSummaryRequest + if err := c.ShouldBindJSON(&req); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + userDatasetSummary, err := h.service.ShowUserDatasetSummary(username, req.Dataset) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, userDatasetSummary, "") +} + +// ShowUserSummary handle show user summary +func (h *Handler) ShowUserSummary(c *gin.Context) { + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + userSummary, err := h.service.ShowUserSummary(username) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, userSummary, "") +} + +// ShowUserStorage handle show user storage +func (h *Handler) ShowUserStorage(c *gin.Context) { + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + userStorage, err := h.service.ShowUserStorage(username) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, userStorage, "") +} + +// ShowUserQuota handle show user quota +func (h *Handler) ShowUserQuota(c *gin.Context) { + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + userQuota, err := h.service.ShowUserQuota(username) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, userQuota, "") +} + +// ShowUserIndex handle show user index +func (h *Handler) ShowUserIndex(c *gin.Context) { + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + userIndex, err := h.service.ShowUserIndex(username) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, userIndex, "") +} + +// UpdateUserRoleHTTPRequest update user role request +type UpdateUserRoleHTTPRequest struct { + RoleName string `json:"role_name" binding:"required"` +} + +// UpdateUserRole handle update user role +func (h *Handler) UpdateUserRole(c *gin.Context) { + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + var req UpdateUserRoleHTTPRequest + if err := c.ShouldBindJSON(&req); err != nil { + errorResponse(c, "Role name is required", 400) + return + } + + result, err := h.service.UpdateUserRole(username, req.RoleName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "") +} + +// ShowUserPermission handle show user permission +func (h *Handler) ShowUserPermission(c *gin.Context) { + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + permissions, err := h.service.ShowUserPermission(username) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, permissions, "") +} + +// ShowUsersSummary handle show users summary +func (h *Handler) ShowUsersSummary(c *gin.Context) { + usersSummary, err := h.service.ShowUsersSummary() + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersSummary, "") +} + +type ShowUsersActivityRequest struct { + Days *int `json:"days"` + Window *int `json:"window"` +} + +// ShowUsersActivity handle show users activity +func (h *Handler) ShowUsersActivity(c *gin.Context) { + var req ShowUsersActivityRequest + if err := c.ShouldBindJSON(&req); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + usersActivity, err := h.service.ShowUsersActivity(req.Days, req.Window) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersActivity, "") +} + +type ListUsersReportsRequest struct { + Status *string `json:"status"` + Days *int `json:"days"` + Plan *string `json:"plan"` +} + +// ListUsersReports handle show users reports +func (h *Handler) ListUsersReports(c *gin.Context) { + var req ListUsersReportsRequest + if err := c.ShouldBindJSON(&req); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + + var err error + pageIndex := 0 + pageIndexStr := c.Param("page") + if pageIndexStr != "" { + pageIndex, err = strconv.Atoi(pageIndexStr) + if err != nil { + errorResponse(c, "Page index must be an integer", 400) + return + } + } + pageSize := 10 + pageSizeStr := c.Param("page_size") + if pageSizeStr != "" { + pageSize, err = strconv.Atoi(pageSizeStr) + if err != nil { + errorResponse(c, "Page size must be an integer", 400) + return + } + } + + usersReports, err := h.service.ListUsersReports(pageIndex, pageSize, req.Status, req.Plan, req.Days) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersReports, "") +} + +// ListUsersStorage handle show users storage +func (h *Handler) ListUsersStorage(c *gin.Context) { + + var err error + pageIndex := 0 + pageIndexStr := c.Param("page") + if pageIndexStr != "" { + pageIndex, err = strconv.Atoi(pageIndexStr) + if err != nil { + errorResponse(c, "Page index must be an integer", 400) + return + } + } + pageSize := 10 + pageSizeStr := c.Param("page_size") + if pageSizeStr != "" { + pageSize, err = strconv.Atoi(pageSizeStr) + if err != nil { + errorResponse(c, "Page size must be an integer", 400) + return + } + } + top := 10 + topStr := c.Param("top") + if topStr != "" { + top, err = strconv.Atoi(topStr) + if err != nil { + errorResponse(c, "Top must be an integer", 400) + } + } + + usersStorage, err := h.service.ListUsersStorage(pageIndex, pageSize, top) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersStorage, "") +} + +// ListUsersDocuments handle show users documents +func (h *Handler) ListUsersDocuments(c *gin.Context) { + + var err error + pageIndex := 0 + pageIndexStr := c.Param("page") + if pageIndexStr != "" { + pageIndex, err = strconv.Atoi(pageIndexStr) + if err != nil { + errorResponse(c, "Page index must be an integer", 400) + return + } + } + pageSize := 10 + pageSizeStr := c.Param("page_size") + if pageSizeStr != "" { + pageSize, err = strconv.Atoi(pageSizeStr) + if err != nil { + errorResponse(c, "Page size must be an integer", 400) + return + } + } + top := 10 + topStr := c.Param("top") + if topStr != "" { + top, err = strconv.Atoi(topStr) + if err != nil { + errorResponse(c, "Top must be an integer", 400) + } + } + + usersDocuments, err := h.service.ListUsersDocuments(pageIndex, pageSize, top) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersDocuments, "") +} + +// ListUsersIndex handle show users index +func (h *Handler) ListUsersIndex(c *gin.Context) { + + var err error + pageIndex := 0 + pageIndexStr := c.Param("page") + if pageIndexStr != "" { + pageIndex, err = strconv.Atoi(pageIndexStr) + if err != nil { + errorResponse(c, "Page index must be an integer", 400) + return + } + } + pageSize := 10 + pageSizeStr := c.Param("page_size") + if pageSizeStr != "" { + pageSize, err = strconv.Atoi(pageSizeStr) + if err != nil { + errorResponse(c, "Page size must be an integer", 400) + return + } + } + top := 10 + topStr := c.Param("top") + if topStr != "" { + top, err = strconv.Atoi(topStr) + if err != nil { + errorResponse(c, "Top must be an integer", 400) + } + } + + usersIndex, err := h.service.ListUsersIndex(pageIndex, pageSize, top) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersIndex, "") +} + +type ListUsersQuotaRequest struct { + QuotaThreshold *int `json:"quota_threshold"` + Plan *string `json:"plan"` + Days *int `json:"days"` +} + +// ListUsersQuota handle show users quota +func (h *Handler) ListUsersQuota(c *gin.Context) { + var request ListUsersQuotaRequest + if err := c.ShouldBindJSON(&request); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + + var err error + pageIndex := 0 + pageIndexStr := c.Param("page") + if pageIndexStr != "" { + pageIndex, err = strconv.Atoi(pageIndexStr) + if err != nil { + errorResponse(c, "Page index must be an integer", 400) + return + } + } + pageSize := 10 + pageSizeStr := c.Param("page_size") + if pageSizeStr != "" { + pageSize, err = strconv.Atoi(pageSizeStr) + if err != nil { + errorResponse(c, "Page size must be an integer", 400) + return + } + } + top := 10 + topStr := c.Param("top") + if topStr != "" { + top, err = strconv.Atoi(topStr) + if err != nil { + errorResponse(c, "Top must be an integer", 400) + } + } + + usersQuota, err := h.service.ListUsersQuota(pageIndex, pageSize, top, request.QuotaThreshold, request.Plan, request.Days) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersQuota, "") +} + +// ShowUsersQuotaSummary handle show users quota summary +func (h *Handler) ShowUsersQuotaSummary(c *gin.Context) { + usersQuotaSummary, err := h.service.ShowUsersQuotaSummary() + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, usersQuotaSummary, "") +} + +// ShowDataSummary handle show data summary +func (h *Handler) ShowDataSummary(c *gin.Context) { + dataSummary, err := h.service.ShowDataSummary() + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, dataSummary, "") +} + +// ShowDataOrphan handle show data orphan +func (h *Handler) ShowDataOrphan(c *gin.Context) { + dataOrphan, err := h.service.ShowDataOrphan() + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, dataOrphan, "") +} + +// ShowDataStorage handle show data storage +func (h *Handler) ShowDataStorage(c *gin.Context) { + dataStorage, err := h.service.ShowDataStorage() + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, dataStorage, "") +} + +// ShowDataIndex handle show data index +func (h *Handler) ShowDataIndex(c *gin.Context) { + dataIndex, err := h.service.ShowDataIndex() + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, dataIndex, "") +} + +type PurgeOrphanDataRequest struct { + Preview bool `json:"preview"` +} + +// PurgeOrphanData handle purge orphan data +func (h *Handler) PurgeOrphanData(c *gin.Context) { + var request PurgeOrphanDataRequest + if err := c.ShouldBindJSON(&request); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + result, err := h.service.PurgeOrphanData(request.Preview) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Orphan data purged successfully") +} + +type PurgeUserDataRequest struct { + Preview bool `json:"preview"` +} + +// PurgeUserData handle purge user data +func (h *Handler) PurgeUserData(c *gin.Context) { + var request PurgeUserDataRequest + if err := c.ShouldBindJSON(&request); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + username := c.Param("username") + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + result, err := h.service.PurgeUserData(username, request.Preview) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "") +} + +type PurgeUsersDataRequest struct { + Preview bool `json:"preview"` + Days int `json:"days"` + Plan *string `json:"plan"` + UserStatus *string `json:"user_status"` +} + +// PurgeUsersData handle purge users data +func (h *Handler) PurgeUsersData(c *gin.Context) { + var request PurgeUsersDataRequest + if err := c.ShouldBindJSON(&request); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + + result, err := h.service.PurgeUsersData(request.Preview, request.Days, request.Plan, request.UserStatus) + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "User not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "") +} diff --git a/internal/admin/enterprise_service.go b/internal/admin/enterprise_service.go new file mode 100644 index 0000000000..7060209440 --- /dev/null +++ b/internal/admin/enterprise_service.go @@ -0,0 +1,489 @@ +// +// Copyright 2026 The InfiniFlow Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package admin + +import ( + "ragflow/internal/common" + "ragflow/internal/dao" + "ragflow/internal/entity" +) + +// Role management methods + +// ListRoles list all roles +func (s *Service) ListRoles() ([]map[string]interface{}, error) { + // TODO: Implement list roles + return []map[string]interface{}{}, nil +} + +// CreateRole create a new role +func (s *Service) CreateRole(roleName, description string) (map[string]interface{}, error) { + // TODO: Implement create role + return map[string]interface{}{}, nil +} + +// GetRole get role details +func (s *Service) GetRole(roleName string) (map[string]interface{}, error) { + // TODO: Implement get role + return map[string]interface{}{}, nil +} + +// UpdateRole update role +func (s *Service) UpdateRole(roleName, description string) (map[string]interface{}, error) { + // TODO: Implement update role + return map[string]interface{}{}, nil +} + +// DeleteRole delete role +func (s *Service) DeleteRole(roleName string) error { + // TODO: Implement delete role + return nil +} + +// GetRolePermission get role permissions +func (s *Service) GetRolePermission(roleName string) ([]map[string]interface{}, error) { + // TODO: Implement get role permissions + return []map[string]interface{}{}, nil +} + +// GrantRolePermission grant permission to role +func (s *Service) GrantRolePermission(roleName string, actions []string, resource string) (map[string]interface{}, error) { + // TODO: Implement grant role permission + return map[string]interface{}{}, nil +} + +// RevokeRolePermission revoke permission from role +func (s *Service) RevokeRolePermission(roleName string, actions []string, resource string) (map[string]interface{}, error) { + // TODO: Implement revoke role permission + return map[string]interface{}{}, nil +} + +// ShowUserActivity show user activity for enterprise edition +func (s *Service) ShowUserActivity(email string, days int) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "email": user.Email, + "nickname": user.Nickname, + "days": days, + "error": "'show user activity' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUserDatasetSummary show user dataset summary for enterprise edition +func (s *Service) ShowUserDatasetSummary(email, dataset string) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "email": user.Email, + "nickname": user.Nickname, + "dataset": dataset, + "error": "'show user dataset summary' is implemented in enterprise edition", + } + + return result, nil +} + +// GetUserSummary get user summary for enterprise edition +func (s *Service) ShowUserSummary(email string) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "email": user.Email, + "nickname": user.Nickname, + "error": "'show user summary' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUserStorage show user storage for enterprise edition +func (s *Service) ShowUserStorage(email string) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "email": user.Email, + "nickname": user.Nickname, + "error": "'show user storage' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUserQuota show user quota for enterprise edition +func (s *Service) ShowUserQuota(email string) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "email": user.Email, + "nickname": user.Nickname, + "error": "'show user quota' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUserIndex show user index for enterprise edition +func (s *Service) ShowUserIndex(email string) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "email": user.Email, + "nickname": user.Nickname, + "error": "'show user index' is implemented in enterprise edition", + } + + return result, nil +} + +// UpdateUserRole update user role +func (s *Service) UpdateUserRole(email, roleName string) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "command": "update_user_role", + "role": roleName, + "email": user.Email, + "nickname": user.Nickname, + "error": "'update user role' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUserPermission show user permissions for enterprise edition +func (s *Service) ShowUserPermission(email string) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "command": "show_user_permission", + "email": user.Email, + "nickname": user.Nickname, + "error": "'show user permission' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUsersSummary show users summary for enterprise edition +func (s *Service) ShowUsersSummary() (map[string]interface{}, error) { + result := map[string]interface{}{ + "command": "show_users_summary", + "error": "'show users summary' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUsersActivity show users activity for enterprise edition +func (s *Service) ShowUsersActivity(days, windows *int) (map[string]interface{}, error) { + daysInt := 0 + if days != nil { + daysInt = *days + } + windowsInt := 0 + if windows != nil { + windowsInt = *windows + } + result := map[string]interface{}{ + "days": daysInt, + "windows": windowsInt, + "command": "show_users_activity", + "error": "'show users activity' is implemented in enterprise edition", + } + + return result, nil +} + +func (s *Service) ListUsersEnterprise(pageIndex, pageSize int, status, orderBy, plan *string, top, days, quota *int) ([]map[string]interface{}, error) { + item := map[string]interface{}{} + if status != nil { + item["status"] = *status + } + if orderBy != nil { + item["order_by"] = *orderBy + } + if plan != nil { + item["plan"] = *plan + } + if top != nil { + item["top"] = *top + } + if days != nil { + item["days"] = *days + } + if quota != nil { + item["quota"] = *quota + } + + var result []map[string]interface{} + result = append(result, item) + return result, nil +} + +// ListUsersReports list users reports for enterprise edition +func (s *Service) ListUsersReports(pageIndex, pageSize int, status, plan *string, days *int) (map[string]interface{}, error) { + + statusStr := "all" + if status != nil { + statusStr = *status + } + planStr := "all" + daysInt := 0 + if days != nil { + daysInt = *days + } + if plan != nil { + planStr = *plan + } + + result := map[string]interface{}{ + "page_index": pageIndex, + "page_size": pageSize, + "status": statusStr, + "plan": planStr, + "days": daysInt, + "command": "list_users_reports", + "error": "'List users reports' is implemented in enterprise edition", + } + + return result, nil +} + +// ListUsersStorage list users storage for enterprise edition +func (s *Service) ListUsersStorage(pageIndex, pageSize, top int) (map[string]interface{}, error) { + + result := map[string]interface{}{ + "page_index": pageIndex, + "page_size": pageSize, + "top": top, + "command": "list_users_storage", + "error": "'List users storage' is implemented in enterprise edition", + } + + return result, nil +} + +// ListUsersDocuments list users documents for enterprise edition +func (s *Service) ListUsersDocuments(pageIndex, pageSize, top int) (map[string]interface{}, error) { + + result := map[string]interface{}{ + "page_index": pageIndex, + "page_size": pageSize, + "top": top, + "command": "list_users_documents", + "error": "'List users documents' is implemented in enterprise edition", + } + + return result, nil +} + +// ListUsersIndex list users index for enterprise edition +func (s *Service) ListUsersIndex(pageIndex, pageSize, top int) (map[string]interface{}, error) { + + result := map[string]interface{}{ + "page_index": pageIndex, + "page_size": pageSize, + "top": top, + "command": "list_users_index", + "error": "'List users index' is implemented in enterprise edition", + } + + return result, nil +} + +// ListUsersQuota list users quota for enterprise edition +func (s *Service) ListUsersQuota(pageIndex, pageSize, top int, quotaThreshold *int, plan *string, days *int) (map[string]interface{}, error) { + + quotaThresholdInt := 0 + if quotaThreshold != nil { + quotaThresholdInt = *quotaThreshold + } + planStr := "all" + daysInt := 0 + if days != nil { + daysInt = *days + } + if plan != nil { + planStr = *plan + } + + result := map[string]interface{}{ + "page_index": pageIndex, + "page_size": pageSize, + "top": top, + "quota_threshold": quotaThresholdInt, + "plan": planStr, + "days": daysInt, + "command": "list_users_quota", + "error": "'List users quota' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowUsersQuotaSummary show users quota summary for enterprise edition +func (s *Service) ShowUsersQuotaSummary() (map[string]interface{}, error) { + + result := map[string]interface{}{ + "command": "show_users_quota_summary", + "error": "'Show users quota summary' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowDataSummary show data summary for enterprise edition +func (s *Service) ShowDataSummary() (map[string]interface{}, error) { + + result := map[string]interface{}{ + "command": "show_data_summary", + "error": "'Show data summary' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowDataOrphan show data orphan for enterprise edition +func (s *Service) ShowDataOrphan() (map[string]interface{}, error) { + + result := map[string]interface{}{ + "command": "show_data_orphan", + "error": "'Show data orphan' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowDataStorage show data storage for enterprise edition +func (s *Service) ShowDataStorage() (map[string]interface{}, error) { + + result := map[string]interface{}{ + "command": "show_data_storage", + "error": "'Show data storage' is implemented in enterprise edition", + } + + return result, nil +} + +// ShowDataIndex show data index for enterprise edition +func (s *Service) ShowDataIndex() (map[string]interface{}, error) { + + result := map[string]interface{}{ + "command": "show_data_index", + "error": "'Show data index' is implemented in enterprise edition", + } + + return result, nil +} + +// PurgeOrphanData purge orphan data for enterprise edition +func (s *Service) PurgeOrphanData(preview bool) (map[string]interface{}, error) { + + result := map[string]interface{}{ + "command": "purge_orphan_data", + "preview": preview, + "error": "'Purge orphan data' is implemented in enterprise edition", + } + + return result, nil +} + +// PurgeUserData purge user data for enterprise edition +func (s *Service) PurgeUserData(email string, preview bool) (map[string]interface{}, error) { + // Query user by email + var user entity.User + err := dao.DB.Where("email = ?", email).First(&user).Error + if err != nil { + return nil, common.ErrUserNotFound + } + + result := map[string]interface{}{ + "email": user.Email, + "nickname": user.Nickname, + "preview": preview, + "error": "'Purge user data' is implemented in enterprise edition", + } + + return result, nil +} + +// PurgeUsersData purge users data for enterprise edition +func (s *Service) PurgeUsersData(preview bool, days int, userPlan *string, userActivity *string) (map[string]interface{}, error) { + + plan := "all" + activity := "all" + if userPlan != nil { + plan = *userPlan + } + if userActivity != nil { + activity = *userActivity + } + + result := map[string]interface{}{ + "command": "purge_users_data", + "preview": preview, + "days": days, + "plan": plan, + "activity": activity, + "error": "'Purge users data' is implemented in enterprise edition", + } + + return result, nil +} diff --git a/internal/admin/handler.go b/internal/admin/handler.go index 2d99f07518..f60e8cc3ff 100644 --- a/internal/admin/handler.go +++ b/internal/admin/handler.go @@ -205,15 +205,65 @@ func (h *Handler) AuthCheck(c *gin.Context) { successNoData(c, "Admin is authorized") } +// ListUsersRequest list users request +type ListUsersRequest struct { + Enterprise *bool `json:"enterprise"` + UserStatus *string `json:"user_status"` + OrderBy *string `json:"order_by"` + Top *int `json:"top"` + Days *int `json:"days"` + Quota *int `json:"quota"` + Plan *string `json:"plan"` +} + // ListUsers handle list users func (h *Handler) ListUsers(c *gin.Context) { - users, err := h.service.ListUsers() - if err != nil { - errorResponse(c, err.Error(), 500) - return + + var err error + var pageInt int + page := c.Param("page") + if page == "" { + pageInt = 0 + } else { + pageInt, err = strconv.Atoi(page) + if err != nil { + errorResponse(c, "Page must be an integer", 400) + return + } + } + + var pageSizeInt int + pageSize := c.Param("page_size") + if pageSize == "" { + pageSizeInt = 0 + } else { + pageSizeInt, err = strconv.Atoi(pageSize) + if err != nil { + errorResponse(c, "Page size must be an integer", 400) + return + } + } + + var req ListUsersRequest + var users []map[string]interface{} + if err = c.ShouldBindJSON(&req); err != nil { + users, err = h.service.ListUsers(pageInt, pageSizeInt) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, users, "Get all users") + } else { + users, err = h.service.ListUsersEnterprise(pageInt, pageSizeInt, req.UserStatus, req.OrderBy, req.Plan, req.Top, req.Days, req.Quota) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, users, "list users") } - success(c, users, "Get all users") } // CreateUserHTTPRequest create user request @@ -478,228 +528,6 @@ func (h *Handler) DeleteUserAPIToken(c *gin.Context) { successNoData(c, "API key deleted successfully") } -// ListRoles handle list roles -func (h *Handler) ListRoles(c *gin.Context) { - roles, err := h.service.ListRoles() - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - if roles == nil { - roles = []map[string]interface{}{} - } - - success(c, gin.H{ - "roles": roles, - "total": len(roles), - }, "") -} - -// CreateRoleHTTPRequest create role request -type CreateRoleHTTPRequest struct { - RoleName string `json:"role_name" binding:"required"` - Description string `json:"description"` -} - -// CreateRole handle create role -func (h *Handler) CreateRole(c *gin.Context) { - var req CreateRoleHTTPRequest - if err := c.ShouldBindJSON(&req); err != nil { - errorResponse(c, "Role name is required", 400) - return - } - - role, err := h.service.CreateRole(req.RoleName, req.Description) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, role, "") -} - -// GetRole handle get role -func (h *Handler) GetRole(c *gin.Context) { - roleName := c.Param("role_name") - if roleName == "" { - errorResponse(c, "Role name is required", 400) - return - } - - role, err := h.service.GetRole(roleName) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, role, "") -} - -// UpdateRoleHTTPRequest update role request -type UpdateRoleHTTPRequest struct { - Description string `json:"description" binding:"required"` -} - -// UpdateRole handle update role -func (h *Handler) UpdateRole(c *gin.Context) { - roleName := c.Param("role_name") - if roleName == "" { - errorResponse(c, "Role name is required", 400) - return - } - - var req UpdateRoleHTTPRequest - if err := c.ShouldBindJSON(&req); err != nil { - errorResponse(c, "Role description is required", 400) - return - } - - role, err := h.service.UpdateRole(roleName, req.Description) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, role, "") -} - -// DeleteRole handle delete role -func (h *Handler) DeleteRole(c *gin.Context) { - roleName := c.Param("role_name") - if roleName == "" { - errorResponse(c, "Role name is required", 400) - return - } - - if err := h.service.DeleteRole(roleName); err != nil { - errorResponse(c, err.Error(), 500) - return - } - - successNoData(c, "") -} - -// GetRolePermission handle get role permission -func (h *Handler) GetRolePermission(c *gin.Context) { - roleName := c.Param("role_name") - if roleName == "" { - errorResponse(c, "Role name is required", 400) - return - } - - permissions, err := h.service.GetRolePermission(roleName) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, permissions, "") -} - -// GrantRolePermissionHTTPRequest grant role permission request -type GrantRolePermissionHTTPRequest struct { - Actions []string `json:"actions" binding:"required"` - Resource string `json:"resource" binding:"required"` -} - -// GrantRolePermission handle grant role permission -func (h *Handler) GrantRolePermission(c *gin.Context) { - roleName := c.Param("role_name") - if roleName == "" { - errorResponse(c, "Role name is required", 400) - return - } - - var req GrantRolePermissionHTTPRequest - if err := c.ShouldBindJSON(&req); err != nil { - errorResponse(c, "Permission is required", 400) - return - } - - result, err := h.service.GrantRolePermission(roleName, req.Actions, req.Resource) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, result, "") -} - -// RevokeRolePermissionHTTPRequest revoke role permission request -type RevokeRolePermissionHTTPRequest struct { - Actions []string `json:"actions" binding:"required"` - Resource string `json:"resource" binding:"required"` -} - -// RevokeRolePermission handle revoke role permission -func (h *Handler) RevokeRolePermission(c *gin.Context) { - roleName := c.Param("role_name") - if roleName == "" { - errorResponse(c, "Role name is required", 400) - return - } - - var req RevokeRolePermissionHTTPRequest - if err := c.ShouldBindJSON(&req); err != nil { - errorResponse(c, "Permission is required", 400) - return - } - - result, err := h.service.RevokeRolePermission(roleName, req.Actions, req.Resource) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, result, "") -} - -// UpdateUserRoleHTTPRequest update user role request -type UpdateUserRoleHTTPRequest struct { - RoleName string `json:"role_name" binding:"required"` -} - -// UpdateUserRole handle update user role -func (h *Handler) UpdateUserRole(c *gin.Context) { - username := c.Param("username") - if username == "" { - errorResponse(c, "Username is required", 400) - return - } - - var req UpdateUserRoleHTTPRequest - if err := c.ShouldBindJSON(&req); err != nil { - errorResponse(c, "Role name is required", 400) - return - } - - result, err := h.service.UpdateUserRole(username, req.RoleName) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, result, "") -} - -// GetUserPermission handle get user permission -func (h *Handler) GetUserPermission(c *gin.Context) { - username := c.Param("username") - if username == "" { - errorResponse(c, "Username is required", 400) - return - } - - permissions, err := h.service.GetUserPermission(username) - if err != nil { - errorResponse(c, err.Error(), 500) - return - } - - success(c, permissions, "") -} - // GetServices handle get all services func (h *Handler) GetServices(c *gin.Context) { services, err := h.service.ListServices() diff --git a/internal/admin/router.go b/internal/admin/router.go index a42f764d5a..2cd2c47603 100644 --- a/internal/admin/router.go +++ b/internal/admin/router.go @@ -71,6 +71,31 @@ func (r *Router) Setup(engine *gin.Engine) { protected.GET("/users/:username/datasets", r.handler.GetUserDatasets) protected.GET("/users/:username/agents", r.handler.GetUserAgents) + // For enterprise edition + protected.GET("/users/:username/activity", r.handler.ShowUserActivity) + protected.GET("/users/:username/dataset", r.handler.ShowUserDatasetSummary) + protected.GET("/users/:username/summary", r.handler.ShowUserSummary) + protected.GET("/users/:username/storage", r.handler.ShowUserStorage) + protected.GET("/users/:username/quota", r.handler.ShowUserQuota) + protected.GET("/users/:username/index", r.handler.ShowUserIndex) + protected.PUT("/users/:username/role", r.handler.UpdateUserRole) + protected.GET("/users/:username/permission", r.handler.ShowUserPermission) + protected.GET("/users/summary", r.handler.ShowUsersSummary) + protected.GET("/users/activity", r.handler.ShowUsersActivity) + protected.GET("/users/reports", r.handler.ListUsersReports) + protected.GET("/users/storage", r.handler.ListUsersStorage) + protected.GET("/users/documents", r.handler.ListUsersDocuments) + protected.GET("/users/index", r.handler.ListUsersIndex) + protected.GET("/users/quota", r.handler.ListUsersQuota) + protected.GET("/users/quota/summary", r.handler.ShowUsersQuotaSummary) + protected.GET("/data/summary", r.handler.ShowDataSummary) + protected.GET("/data/orphan", r.handler.ShowDataOrphan) + protected.GET("/data/storage", r.handler.ShowDataStorage) + protected.GET("/data/index", r.handler.ShowDataIndex) + protected.DELETE("/data/orphan", r.handler.PurgeOrphanData) + protected.DELETE("/users/:username/data", r.handler.PurgeUserData) + protected.DELETE("/users/data", r.handler.PurgeUsersData) + // API Keys protected.GET("/users/:username/keys", r.handler.ListUserAPITokens) protected.GET("/users/:username/tokens", r.handler.ListUserAPITokens) @@ -89,10 +114,6 @@ func (r *Router) Setup(engine *gin.Engine) { protected.POST("/roles/:role_name/permission", r.handler.GrantRolePermission) protected.DELETE("/roles/:role_name/permission", r.handler.RevokeRolePermission) - // User roles and permissions - protected.PUT("/users/:username/role", r.handler.UpdateUserRole) - protected.GET("/users/:username/permission", r.handler.GetUserPermission) - // Service management protected.GET("/services", r.handler.GetServices) protected.GET("/service_types/:service_type", r.handler.GetServicesByType) diff --git a/internal/admin/service.go b/internal/admin/service.go index 5fa3ac4b29..1889ba4660 100644 --- a/internal/admin/service.go +++ b/internal/admin/service.go @@ -211,8 +211,8 @@ func generateRandomHex(n int) string { } // ListUsers list all users -func (s *Service) ListUsers() ([]map[string]interface{}, error) { - users, _, err := s.userDAO.List(0, 0) +func (s *Service) ListUsers(page, pageSize int) ([]map[string]interface{}, error) { + users, _, err := s.userDAO.List(page*pageSize, pageSize) if err != nil { return nil, err } @@ -1020,68 +1020,6 @@ func (s *Service) DeleteUserAPIToken(username, key string) error { return nil } -// Role management methods - -// ListRoles list all roles -func (s *Service) ListRoles() ([]map[string]interface{}, error) { - // TODO: Implement list roles - return []map[string]interface{}{}, nil -} - -// CreateRole create a new role -func (s *Service) CreateRole(roleName, description string) (map[string]interface{}, error) { - // TODO: Implement create role - return map[string]interface{}{}, nil -} - -// GetRole get role details -func (s *Service) GetRole(roleName string) (map[string]interface{}, error) { - // TODO: Implement get role - return map[string]interface{}{}, nil -} - -// UpdateRole update role -func (s *Service) UpdateRole(roleName, description string) (map[string]interface{}, error) { - // TODO: Implement update role - return map[string]interface{}{}, nil -} - -// DeleteRole delete role -func (s *Service) DeleteRole(roleName string) error { - // TODO: Implement delete role - return nil -} - -// GetRolePermission get role permissions -func (s *Service) GetRolePermission(roleName string) ([]map[string]interface{}, error) { - // TODO: Implement get role permissions - return []map[string]interface{}{}, nil -} - -// GrantRolePermission grant permission to role -func (s *Service) GrantRolePermission(roleName string, actions []string, resource string) (map[string]interface{}, error) { - // TODO: Implement grant role permission - return map[string]interface{}{}, nil -} - -// RevokeRolePermission revoke permission from role -func (s *Service) RevokeRolePermission(roleName string, actions []string, resource string) (map[string]interface{}, error) { - // TODO: Implement revoke role permission - return map[string]interface{}{}, nil -} - -// UpdateUserRole update user role -func (s *Service) UpdateUserRole(username, roleName string) ([]map[string]interface{}, error) { - // TODO: Implement update user role - return []map[string]interface{}{}, nil -} - -// GetUserPermission get user permissions -func (s *Service) GetUserPermission(username string) ([]map[string]interface{}, error) { - // TODO: Implement get user permissions - return []map[string]interface{}{}, nil -} - // ListServices get all services func (s *Service) ListServices() ([]map[string]interface{}, error) { allConfigs := server.GetAllConfigs() diff --git a/internal/cli/admin_command.go b/internal/cli/admin_command.go index 00f2827c99..ec44fbd71f 100644 --- a/internal/cli/admin_command.go +++ b/internal/cli/admin_command.go @@ -732,50 +732,6 @@ func (c *CLI) SetVariable(cmd *Command) (ResponseIf, error) { return &result, nil } -// ListUsers lists all users (admin mode only) -// Returns (result_map, error) - result_map is non-nil for benchmark mode -func (c *CLI) ListUsers(cmd *Command) (ResponseIf, error) { - if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { - return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") - } - - // Check for benchmark iterations - iterations := 1 - if val, ok := cmd.Params["iterations"].(int); ok && val > 1 { - iterations = val - } - - if iterations > 1 { - // Benchmark mode - return raw result for benchmark stats - return c.AdminServerClient.RequestWithIterations("GET", "/admin/users", "admin", nil, nil, iterations) - } - - resp, err := c.AdminServerClient.Request("GET", "/admin/users", "admin", nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to list users: %w", err) - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to list users: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - - var result CommonResponse - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("list users failed: invalid JSON (%w)", err) - } - - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - - for _, user := range result.Data { - delete(user, "create_date") - } - - result.Duration = resp.Duration - return &result, nil -} - // DropUser deletes a user (admin mode only) func (c *CLI) DropUser(cmd *Command) (ResponseIf, error) { if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { @@ -809,39 +765,6 @@ func (c *CLI) DropUser(cmd *Command) (ResponseIf, error) { return &result, nil } -// Show user show user (admin mode only) -func (c *CLI) ShowUser(cmd *Command) (ResponseIf, error) { - if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { - return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") - } - - userName, ok := cmd.Params["user_name"].(string) - if !ok { - return nil, fmt.Errorf("user_name not provided") - } - - resp, err := c.AdminServerClient.Request("GET", fmt.Sprintf("/admin/users/%s", userName), "admin", nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to show user: %w", err) - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to show user: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - - var result CommonDataResponse - - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("show user failed: invalid JSON (%w)", err) - } - - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - result.Duration = resp.Duration - return &result, nil -} - // ListUserDatasets lists datasets for a specific user (admin mode) // Returns (result_map, error) - result_map is non-nil for benchmark mode func (c *CLI) ListUserDatasets(cmd *Command) (ResponseIf, error) { @@ -1612,3 +1535,786 @@ func (c *CLI) AdminRemoveServiceCommand(cmd *Command) (ResponseIf, error) { result.Duration = resp.Duration return &result, nil } + +// Show user show user (admin mode only) +func (c *CLI) AdminShowUserInfoCommand(cmd *Command) (ResponseIf, error) { + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + userName, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + apiURL := fmt.Sprintf("/admin/users/%s", userName) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to show user: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to show user: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("show user failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUserActivityCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + days, ok := cmd.Params["days"].(int) + if !ok { + return nil, fmt.Errorf("days not provided") + } + + email, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + payload := map[string]interface{}{ + "days": days, + "email": email, + } + + apiURL := fmt.Sprintf("/admin/users/%s/activity", email) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to get user activity: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get user activity: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get user activity failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUserSummaryCommand(cmd *Command) (ResponseIf, error) { + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + userName, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + apiURL := fmt.Sprintf("/admin/users/%s/summary", userName) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get statistics: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get user summary: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get user summary failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUserDatasetCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + dataset, ok := cmd.Params["dataset_name"].(string) + if !ok { + return nil, fmt.Errorf("dataset_name not provided") + } + + email, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + payload := map[string]interface{}{ + "dataset": dataset, + } + + apiURL := fmt.Sprintf("/admin/users/%s/dataset", email) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to get user dataset: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get user dataset: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get user dataset failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUserStorageCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + email, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + apiURL := fmt.Sprintf("/admin/users/%s/storage", email) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get user storage: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get user storage: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get user storage failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUserQuotaCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + email, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + apiURL := fmt.Sprintf("/admin/users/%s/quota", email) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get user storage: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get user storage: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get user storage failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUserIndexCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + email, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + apiURL := fmt.Sprintf("/admin/users/%s/index", email) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get user storage: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get user storage: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get user storage failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUserPermissionCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + email, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + apiURL := fmt.Sprintf("/admin/users/%s/permission", email) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get user permission: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get user permission: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get user permission failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUsersSummaryCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + apiURL := "/admin/users/summary" + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get users summary: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get users summary: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get users summary failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowUsersActivityCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + days, ok := cmd.Params["days"].(int) + if !ok { + return nil, fmt.Errorf("days not provided") + } + window, ok := cmd.Params["window"].(int) + if !ok { + return nil, fmt.Errorf("window not provided") + } + + payload := map[string]interface{}{ + "days": days, + "window": window, + } + + apiURL := "/admin/users/activity" + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to get users activity: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get users activity: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get users activity failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// ListUsers lists all users (admin mode only) +// Returns (result_map, error) - result_map is non-nil for benchmark mode +func (c *CLI) AdminListUsersCommand(cmd *Command) (ResponseIf, error) { + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + // Check for benchmark iterations + iterations := 1 + if val, ok := cmd.Params["iterations"].(int); ok && val > 1 { + iterations = val + } + + if iterations > 1 { + // Benchmark mode - return raw result for benchmark stats + return c.AdminServerClient.RequestWithIterations("GET", "/admin/users", "admin", nil, nil, iterations) + } + + resp, err := c.AdminServerClient.Request("GET", "/admin/users", "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to list users: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to list users: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("list users failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + for _, user := range result.Data { + delete(user, "create_date") + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminListUsersConditionCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + var orderBy *string + var userStatus *string + var top *int + var plan *string + var quota *int + var days *int + + orderByStr, ok := cmd.Params["order_by"].(string) + if ok { + orderBy = &orderByStr + } + userStatusStr, ok := cmd.Params["user_status"].(string) + if ok { + userStatus = &userStatusStr + } + topInt, ok := cmd.Params["top"].(int) + if ok { + top = &topInt + } + planStr, ok := cmd.Params["plan"].(string) + if ok { + plan = &planStr + } + quotaInt, ok := cmd.Params["quota"].(int) + if ok { + quota = "aInt + } + daysInt, ok := cmd.Params["days"].(int) + if ok { + days = &daysInt + } + + payload := map[string]interface{}{ + "enterprise": true, + "order_by": orderBy, + "user_status": userStatus, + "top": top, + "plan": plan, + "quota": quota, + "days": days, + } + + resp, err := c.AdminServerClient.Request("GET", "/admin/users", "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to list users: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to list users: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("list users failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowDataSummaryCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + apiURL := "/admin/data/summary" + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get users summary: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get users summary: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get users summary failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowDataOrphanCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + apiURL := "/admin/data/orphan" + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get orphan data: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get orphan data: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get orphan data failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowDataStorageCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + apiURL := "/admin/data/storage" + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get data storage: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get data storage: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get data storage failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowDataIndexCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + apiURL := "/admin/data/index" + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get data index: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get data index: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get data index failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminShowQuotaSummaryCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + apiURL := "/admin/users/quota/summary" + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get users quota summary: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get users quota summary: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("get users quota summary failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminPurgeOrphanCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + preview, ok := cmd.Params["preview"].(bool) + if !ok { + return nil, fmt.Errorf("preview not provided") + } + + payload := map[string]interface{}{ + "preview": preview, + } + + apiURL := "/admin/data/orphan" + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to purge orphan data: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to purge orphan data: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("purge orphan data failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminPurgeUserCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + preview, ok := cmd.Params["preview"].(bool) + if !ok { + return nil, fmt.Errorf("preview not provided") + } + userName, ok := cmd.Params["user_name"].(string) + if !ok { + return nil, fmt.Errorf("user_name not provided") + } + + payload := map[string]interface{}{ + "preview": preview, + } + + apiURL := fmt.Sprintf("/admin/users/%s/data", userName) + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to purge user %s: %w", userName, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to purge user %s: HTTP %d, body: %s", userName, resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("purge user %s failed: invalid JSON (%w)", userName, err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) AdminPurgeUsersCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + var preview bool + var ok bool + var planName string + var planNamePtr *string + var userStatus string + var userStatusPtr *string + var days int + + preview, ok = cmd.Params["preview"].(bool) + if !ok { + return nil, fmt.Errorf("preview not provided") + } + + planName, ok = cmd.Params["plan_name"].(string) + if ok { + planNamePtr = &planName + } + + userStatus, ok = cmd.Params["user_status"].(string) + if ok { + userStatusPtr = &userStatus + } + + days, ok = cmd.Params["days"].(int) + if !ok { + return nil, fmt.Errorf("days not provided") + } + + payload := map[string]interface{}{ + "preview": preview, + "days": days, + "plan": planNamePtr, + "user_status": userStatusPtr, + } + + apiURL := "/admin/users/data" + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to purge users data: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to purge users data: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("purge users data failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} diff --git a/internal/cli/admin_parser.go b/internal/cli/admin_parser.go index 2b1a44bacb..642502b697 100644 --- a/internal/cli/admin_parser.go +++ b/internal/cli/admin_parser.go @@ -137,12 +137,7 @@ func (p *Parser) parseAdminListCommand() (*Command, error) { } return NewCommand("list_services"), nil case TokenUsers: - p.nextToken() - // Semicolon is optional for SHOW TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return NewCommand("list_users"), nil + return p.parseAdminListUsersCommand() case TokenRoles: p.nextToken() // Semicolon is optional for SHOW TOKEN @@ -467,31 +462,11 @@ func (p *Parser) parseAdminShowCommand() (*Command, error) { switch p.curToken.Type { case TokenVersion: - p.nextToken() - // Semicolon is optional for SHOW TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return NewCommand("show_version"), nil - case TokenToken: - p.nextToken() - // Semicolon is optional for SHOW TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return NewCommand("show_token"), nil + return p.parseAdminShowVersionCommand() case TokenCurrent: - p.nextToken() - - // Semicolon is optional for SHOW TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return NewCommand("show_current"), nil - case TokenUser: - return p.parseShowUser() + return p.parseAdminShowCurrentCommand() case TokenRole: - return p.parseShowRole() + return p.parseAdminShowRoleCommand() case TokenVar: return p.parseShowVariable() case TokenService: @@ -504,6 +479,14 @@ func (p *Parser) parseAdminShowCommand() (*Command, error) { return p.parseUserShowAdmin() case TokenAPI: return p.parseUserShowAPI() + case TokenUser: + return p.parseAdminShowUserCommand() + case TokenUsers: + return p.parseAdminShowUsersCommand() + case TokenData: + return p.parseAdminShowDataCommand() + case TokenQuota: + return p.parseAdminShowQuotaCommand() default: return nil, fmt.Errorf("unknown SHOW target: %s", p.curToken.Value) } @@ -545,7 +528,7 @@ func (p *Parser) parseAdminShowUser() (*Command, error) { return cmd, nil } -func (p *Parser) parseAdminShowRole() (*Command, error) { +func (p *Parser) parseAdminShowRoleCommand() (*Command, error) { p.nextToken() // consume ROLE roleName, err := p.parseIdentifier() if err != nil { @@ -1925,3 +1908,590 @@ func (p *Parser) parseAdminRemoveCommand() (*Command, error) { } return cmd, nil } + +// SHOW VERSION; +func (p *Parser) parseAdminShowVersionCommand() (*Command, error) { + p.nextToken() // consume VERSION + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return NewCommand("show_version"), nil +} + +// SHOW CURRENT; +func (p *Parser) parseAdminShowCurrentCommand() (*Command, error) { + p.nextToken() // consume CURRENT + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return NewCommand("show_current"), nil +} + +// SHOW USER 'user@example.com'; +// SHOW USER 'user@example.com' DATASET 'dataset_name'; +// SHOW USER 'user@example.com' SUMMARY; +// SHOW USER 'user@example.com' STORAGE; +// SHOW USER 'user@example.com' QUOTA; +// SHOW USER 'user@example.com' INDEX; +func (p *Parser) parseAdminShowUserCommand() (*Command, error) { + p.nextToken() // consume USER + + userName, err := p.parseQuotedString() + if err != nil { + return nil, err + } + p.nextToken() + + switch p.curToken.Type { + case TokenActivity: + return p.parseAdminShowActivityCommand(userName) + case TokenSummary: + return p.parseAdminShowUserSummaryCommand(userName) + case TokenDataset: + return p.parseAdminShowUserDataSetCommand(userName) + case TokenStorage: + return p.parseAdminShowUserStorageCommand(userName) + case TokenQuota: + return p.parseAdminShowUserQuotaCommand(userName) + case TokenIndex: + return p.parseAdminShowUserIndexCommand(userName) + case TokenPermission: + return p.parseAdminShowUserPermissionCommand(userName) + default: + return p.parseAdminShowUserInfoCommand(userName) + } +} + +// SHOW USER 'user@example.com'; +func (p *Parser) parseAdminShowUserInfoCommand(userName string) (*Command, error) { + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + cmd := NewCommand("admin_show_user_info_command") + cmd.Params["user_name"] = userName + + return cmd, nil +} + +// SHOW USER 'user@example.com' ACTIVITY DAYS 30; +func (p *Parser) parseAdminShowActivityCommand(userName string) (*Command, error) { + p.nextToken() // consume ACTIVITY + + var days int + var err error + + if p.curToken.Type == TokenDays { + p.nextToken() // consume DAYS + days, err = p.parseNumber() + if err != nil { + return nil, err + } + if days < 1 { + return nil, fmt.Errorf("invalid number of DAYS") + } + p.nextToken() + } else { + days = 7 + } + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + cmd := NewCommand("admin_show_user_activity_command") + cmd.Params["user_name"] = userName + cmd.Params["days"] = days + + return cmd, nil +} + +// SHOW USER 'user@example.com' SUMMARY; +func (p *Parser) parseAdminShowUserSummaryCommand(userName string) (*Command, error) { + p.nextToken() // consume SUMMARY + + cmd := NewCommand("admin_show_user_summary_command") + cmd.Params["user_name"] = userName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return cmd, nil +} + +// SHOW USER 'user@example.com' DATASET 'dataset_name'; +func (p *Parser) parseAdminShowUserDataSetCommand(userName string) (*Command, error) { + p.nextToken() // consume DATASET + + var tree = false + var datasetName string + var err error + datasetName, err = p.parseQuotedString() + if err != nil { + return nil, err + } + p.nextToken() + + if p.curToken.Type == TokenTree { + tree = true + p.nextToken() + } + + cmd := NewCommand("admin_show_user_dataset_command") + cmd.Params["user_name"] = userName + if datasetName != "" { + cmd.Params["dataset_name"] = datasetName + } + if tree { + cmd.Params["tree"] = true + } + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return cmd, nil +} + +// SHOW USER 'user@example.com' STORAGE; +func (p *Parser) parseAdminShowUserStorageCommand(userName string) (*Command, error) { + p.nextToken() // consume STORAGE + + cmd := NewCommand("admin_show_user_storage_command") + cmd.Params["user_name"] = userName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return cmd, nil +} + +// SHOW USER 'user@example.com' QUOTA; +func (p *Parser) parseAdminShowUserQuotaCommand(userName string) (*Command, error) { + p.nextToken() // consume QUOTA + + cmd := NewCommand("admin_show_user_quota_command") + cmd.Params["user_name"] = userName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return cmd, nil +} + +// SHOW USER 'user@example.com' INDEX; +func (p *Parser) parseAdminShowUserIndexCommand(userName string) (*Command, error) { + p.nextToken() // consume INDEX + + cmd := NewCommand("admin_show_user_index_command") + cmd.Params["user_name"] = userName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return cmd, nil +} + +// SHOW USER 'user@example.com' PERMISSION; +func (p *Parser) parseAdminShowUserPermissionCommand(userName string) (*Command, error) { + p.nextToken() // consume PERMISSION + + cmd := NewCommand("admin_show_user_permission_command") + cmd.Params["user_name"] = userName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +// SHOW USERS SUMMARY; +// SHOW USERS ACTIVITY; +func (p *Parser) parseAdminShowUsersCommand() (*Command, error) { + p.nextToken() // consume USERS + + switch p.curToken.Type { + case TokenSummary: + p.nextToken() + cmd := NewCommand("admin_show_users_summary_command") + return cmd, nil + case TokenActivity: + return p.parseAdminShowUsersActivityCommand() + default: + return nil, fmt.Errorf("invalid command") + } +} + +// SHOW USERS ACTIVITY WINDOW 2 DAYS 30; +func (p *Parser) parseAdminShowUsersActivityCommand() (*Command, error) { + p.nextToken() // consume ACTIVITY + + var days int + var err error + var windowSize int + +commandLoop: + for { + switch p.curToken.Type { + case TokenDays: + p.nextToken() + days, err = p.parseNumber() + if err != nil { + return nil, err + } + if days < 1 { + return nil, fmt.Errorf("invalid number of DAYS") + } + p.nextToken() + case TokenWindow: + p.nextToken() + windowSize, err = p.parseNumber() + if err != nil { + return nil, err + } + if windowSize < 0 { + return nil, fmt.Errorf("invalid number of WINDOWS") + } + p.nextToken() + case TokenSemicolon: + p.nextToken() + break commandLoop // done + default: + // No more options to process + break commandLoop + } + } + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + cmd := NewCommand("admin_show_users_activity_command") + cmd.Params["days"] = days + cmd.Params["window"] = windowSize + return cmd, nil +} + +// LIST USERS; +// LIST USERS ACTIVE 30 DAYS; // default 7 days +// LIST USERS INACTIVE 30 DAYS; // default 7 days +// LIST USERS STORAGE TOP 10; +// LIST USERS DOCUMENTS TOP 10; +// LIST USERS INDEX TOP 10; +// LIST USERS QUOTA TOP 10; +// LIST USERS QUOTA OVER; +// LIST USERS PLAN 'plan_name' QUOTA OVER DAYS 30; // default 7 days +// LIST USERS PLAN 'plan_name' DAYS 30; // default 7 days +func (p *Parser) parseAdminListUsersCommand() (*Command, error) { + p.nextToken() // consume USERS + + var orderBy string + var userStatus string + var top *int + var plan *string + var quota *int + var days *int + condition := false +commandLoop: + for { + switch p.curToken.Type { + case TokenTop: + condition = true + p.nextToken() + topInt, err := p.parseNumber() + if err != nil { + return nil, err + } + if topInt < 0 { + return nil, fmt.Errorf("invalid number of TOP") + } + p.nextToken() + top = &topInt + case TokenDays: + condition = true + p.nextToken() + daysInt, err := p.parseNumber() + if err != nil { + return nil, err + } + if daysInt < 0 { + return nil, fmt.Errorf("invalid number of DAYS") + } + p.nextToken() + days = &daysInt + case TokenPlan: + condition = true + p.nextToken() + planStr, err := p.parseQuotedString() + if err != nil { + return nil, err + } + if planStr == "" { + return nil, fmt.Errorf("invalid plan") + } + plan = &planStr + p.nextToken() + case TokenQuota: + condition = true + p.nextToken() + quotaInt, err := p.parseNumber() + if err != nil { + return nil, err + } + if quotaInt < 0 { + return nil, fmt.Errorf("invalid number of QUOTA") + } + quota = "aInt + p.nextToken() + case TokenDocuments, TokenIndex, TokenStorage: + condition = true + if orderBy != "" { + return nil, fmt.Errorf("order by already set") + } + orderBy = p.curToken.Value + p.nextToken() + case TokenActive, TokenInactive: + condition = true + userStatus = p.curToken.Value + p.nextToken() + case TokenSemicolon: + p.nextToken() + break commandLoop // done + default: + // No more options to process + break commandLoop + } + } + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + if !condition { + return NewCommand("admin_list_users_command"), nil + } + + cmd := NewCommand("admin_list_users_condition_command") + if orderBy != "" { + cmd.Params["order_by"] = orderBy + } + if userStatus != "" { + cmd.Params["user_status"] = userStatus + } + if top != nil { + cmd.Params["top"] = *top + } + if plan != nil { + cmd.Params["plan"] = *plan + } + if quota != nil { + cmd.Params["quota"] = *quota + } + if days != nil { + cmd.Params["days"] = *days + } + + return cmd, nil +} + +// SHOW DATA SUMMARY; +// SHOW DATA ORPHAN; +// SHOW DATA STORAGE; +// SHOW DATA INDEX; +func (p *Parser) parseAdminShowDataCommand() (*Command, error) { + p.nextToken() // consume ALL + + var cmd *Command + switch p.curToken.Type { + case TokenSummary: + p.nextToken() + cmd = NewCommand("admin_show_data_summary_command") + case TokenOrphan: + p.nextToken() + cmd = NewCommand("admin_show_data_orphan_command") + case TokenStorage: + p.nextToken() + cmd = NewCommand("admin_show_data_storage_command") + case TokenIndex: + p.nextToken() + cmd = NewCommand("admin_show_data_index_command") + default: + return nil, fmt.Errorf("expected SUMMARY, ORPHAN, STORAGE, INDEX, TASKS, QUOTA_SUMMARY after ALL") + } + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +// SHOW QUOTA SUMMARY; +func (p *Parser) parseAdminShowQuotaCommand() (*Command, error) { + p.nextToken() // consume QUOTA + + var cmd *Command + switch p.curToken.Type { + case TokenSummary: + p.nextToken() + cmd = NewCommand("admin_show_quota_summary_command") + default: + return nil, fmt.Errorf("expected SUMMARY after QUOTA") + } + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +// PURGE PREVIEW ORPHAN +// PURGE ORPHAN + +// PURGE PREVIEW USER 'user@example.com'; +// PURGE USER + +// PURGE PREVIEW USERS PLAN 'plan_name' DAYS 30; // default 7 days +// PURGE USERS PLAN 'plan_name' DAYS 30; + +// PURGE PREVIEW USERS INACTIVE PLAN 'plan_name' DAYS 30; +// PURGE USERS INACTIVE PLAN 'plan_name' DAYS 30; +func (p *Parser) parseAdminPurgeCommand() (*Command, error) { + p.nextToken() // consume PURGE + var preview = false + if p.curToken.Type == TokenPreview { + p.nextToken() + preview = true + } + + switch p.curToken.Type { + case TokenOrphan: + return p.parseAdminPurgeOrphanCommand(preview) + case TokenUser: + return p.parseAdminPurgeUserCommand(preview) + case TokenUsers: + return p.parseAdminPurgeUsersCommand(preview) + default: + return nil, fmt.Errorf("expected PREVIEW, USER, USERS after PURGE") + } +} + +// PURGE PREVIEW ORPHAN +// PURGE ORPHAN +func (p *Parser) parseAdminPurgeOrphanCommand(preview bool) (*Command, error) { + p.nextToken() // consume ORPHAN + + cmd := NewCommand("admin_purge_orphan_command") + cmd.Params["preview"] = preview + return cmd, nil +} + +// PURGE PREVIEW USER 'user@example.com'; +// PURGE USER 'user@example.com'; +func (p *Parser) parseAdminPurgeUserCommand(preview bool) (*Command, error) { + p.nextToken() // consume USER + + userName, err := p.parseQuotedString() + if err != nil { + return nil, err + } + cmd := NewCommand("admin_purge_user_command") + cmd.Params["preview"] = preview + cmd.Params["user_name"] = userName + return cmd, nil +} + +// PURGE PREVIEW USERS PLAN 'plan_name' DAYS 30; // default 7 days +// PURGE USERS PLAN 'plan_name' DAYS 30; +// PURGE PREVIEW USERS INACTIVE PLAN 'plan_name' DAYS 30; +// PURGE USERS INACTIVE PLAN 'plan_name' DAYS 30; +func (p *Parser) parseAdminPurgeUsersCommand(preview bool) (*Command, error) { + p.nextToken() // consume USERS + + var userStatus *string = nil + var days *int = nil + var planName *string = nil + +commandLoop: + for { + switch p.curToken.Type { + case TokenPlan: + p.nextToken() + if planName != nil { + return nil, fmt.Errorf("duplicate PLAN after USERS") + } + plan, err := p.parseQuotedString() + if err != nil { + return nil, err + } + planName = &plan + p.nextToken() + case TokenDays: + p.nextToken() + if days != nil { + return nil, fmt.Errorf("duplicate DAYS after USERS") + } + dayCount, err := p.parseNumber() + if err != nil { + return nil, err + } + days = &dayCount + p.nextToken() + case TokenInactive: + p.nextToken() + if userStatus != nil { + return nil, fmt.Errorf("duplicate INACTIVE or ACTIVE after USERS") + } + inactiveStatus := "inactive" + userStatus = &inactiveStatus + case TokenActive: + p.nextToken() + if userStatus != nil { + return nil, fmt.Errorf("duplicate INACTIVE or ACTIVE after USERS") + } + activeStatus := "active" + userStatus = &activeStatus + case TokenSemicolon: + p.nextToken() + break commandLoop // done + default: + // No more options to process + break commandLoop + } + } + + cmd := NewCommand("admin_purge_users_command") + cmd.Params["preview"] = preview + if planName != nil { + cmd.Params["plan_name"] = *planName + } + if userStatus != nil { + cmd.Params["user_status"] = *userStatus + } + if days != nil { + cmd.Params["days"] = *days + } + return cmd, nil +} diff --git a/internal/cli/cli_http.go b/internal/cli/cli_http.go index f23a59ade4..0b6669ea95 100644 --- a/internal/cli/cli_http.go +++ b/internal/cli/cli_http.go @@ -45,8 +45,6 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.PingByCommand(cmd) case "benchmark": return c.RunBenchmark(cmd) - case "list_users": - return c.ListUsers(cmd) case "list_services": return c.ListServices(cmd) case "grant_admin": @@ -67,8 +65,6 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.ShowAdminVersion(cmd) case "show_current": return c.ShowCommonCurrent(cmd) - case "show_user": - return c.ShowUser(cmd) case "list_variables": return c.ListVariables(cmd) case "show_variable": @@ -123,6 +119,46 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.UserShowMessageQueueCommand(cmd) case "admin_remove_service_command": return c.AdminRemoveServiceCommand(cmd) + case "admin_show_user_info_command": + return c.AdminShowUserInfoCommand(cmd) + case "admin_show_user_activity_command": + return c.AdminShowUserActivityCommand(cmd) + case "admin_show_user_summary_command": + return c.AdminShowUserSummaryCommand(cmd) + case "admin_show_user_dataset_command": + return c.AdminShowUserDatasetCommand(cmd) + case "admin_show_user_storage_command": + return c.AdminShowUserStorageCommand(cmd) + case "admin_show_user_quota_command": + return c.AdminShowUserQuotaCommand(cmd) + case "admin_show_user_index_command": + return c.AdminShowUserIndexCommand(cmd) + case "admin_show_user_permission_command": + return c.AdminShowUserPermissionCommand(cmd) + case "admin_show_users_summary_command": + return c.AdminShowUsersSummaryCommand(cmd) + case "admin_show_users_activity_command": + return c.AdminShowUsersActivityCommand(cmd) + case "admin_list_users_command": + return c.AdminListUsersCommand(cmd) + case "admin_list_users_condition_command": + return c.AdminListUsersConditionCommand(cmd) + case "admin_show_quota_summary_command": + return c.AdminShowQuotaSummaryCommand(cmd) + case "admin_show_data_summary_command": + return c.AdminShowDataSummaryCommand(cmd) + case "admin_show_data_orphan_command": + return c.AdminShowDataOrphanCommand(cmd) + case "admin_show_data_storage_command": + return c.AdminShowDataStorageCommand(cmd) + case "admin_show_data_index_command": + return c.AdminShowDataIndexCommand(cmd) + case "admin_purge_orphan_command": + return c.AdminPurgeOrphanCommand(cmd) + case "admin_purge_user_command": + return c.AdminPurgeUserCommand(cmd) + case "admin_purge_users_command": + return c.AdminPurgeUsersCommand(cmd) // TODO: Implement other commands case "show_admin_server": return c.ShowAdminServer(cmd) diff --git a/internal/cli/lexer.go b/internal/cli/lexer.go index ceefad9992..0c4fe24202 100644 --- a/internal/cli/lexer.go +++ b/internal/cli/lexer.go @@ -219,6 +219,8 @@ func (l *Lexer) lookupIdent(ident string) Token { return Token{Type: TokenAlter, Value: ident} case "ACTIVE": return Token{Type: TokenActive, Value: ident} + case "INACTIVE": + return Token{Type: TokenInactive, Value: ident} case "ADMIN": return Token{Type: TokenAdmin, Value: ident} case "SERVER": @@ -487,6 +489,32 @@ func (l *Lexer) lookupIdent(ident string) Token { return Token{Type: TokenPending, Value: ident} case "NOACK": return Token{Type: TokenNoACK, Value: ident} + case "ANALYZE": + return Token{Type: TokenAnalyze, Value: ident} + case "SUMMARY": + return Token{Type: TokenSummary, Value: ident} + case "STORAGE": + return Token{Type: TokenStorage, Value: ident} + case "QUOTA": + return Token{Type: TokenQuota, Value: ident} + case "TREE": + return Token{Type: TokenTree, Value: ident} + case "ORPHAN": + return Token{Type: TokenOrphan, Value: ident} + case "DAYS": + return Token{Type: TokenDays, Value: ident} + case "WINDOW": + return Token{Type: TokenWindow, Value: ident} + case "ACTIVITY": + return Token{Type: TokenActivity, Value: ident} + case "PURGE": + return Token{Type: TokenPurge, Value: ident} + case "PREVIEW": + return Token{Type: TokenPreview, Value: ident} + case "PLAN": + return Token{Type: TokenPlan, Value: ident} + case "DATA": + return Token{Type: TokenData, Value: ident} case "LOG": return Token{Type: TokenLog, Value: ident} case "LEVEL": diff --git a/internal/cli/parser.go b/internal/cli/parser.go index e3ceffb31e..4af45dceae 100644 --- a/internal/cli/parser.go +++ b/internal/cli/parser.go @@ -138,6 +138,8 @@ func (p *Parser) parseAdminCommand() (*Command, error) { return p.parseAdminSaveCommand() case TokenUse: return p.parseAdminUseCommand() + case TokenPurge: + return p.parseAdminPurgeCommand() default: return nil, fmt.Errorf("unknown command: %s", p.curToken.Value) } diff --git a/internal/cli/types.go b/internal/cli/types.go index ebff29bbcf..1979b6c0a8 100644 --- a/internal/cli/types.go +++ b/internal/cli/types.go @@ -41,6 +41,7 @@ const ( TokenUser TokenAlter TokenActive + TokenInactive TokenAdmin TokenServer TokenAPI @@ -175,6 +176,19 @@ const ( TokenPull TokenPending TokenNoACK + TokenAnalyze + TokenSummary + TokenStorage + TokenQuota + TokenTree + TokenOrphan + TokenDays + TokenWindow + TokenActivity + TokenData + TokenPurge + TokenPlan + TokenPreview TokenLog TokenLevel TokenDebug diff --git a/internal/cli/user_parser.go b/internal/cli/user_parser.go index b004a459ce..ffa5e7f27d 100644 --- a/internal/cli/user_parser.go +++ b/internal/cli/user_parser.go @@ -442,12 +442,7 @@ func (p *Parser) parseShowCommand() (*Command, error) { if p.curToken.Type == TokenSemicolon { p.nextToken() } - return NewCommand("show_current"), nil - case TokenUser: - return p.parseShowUser() - case TokenRole: - return p.parseShowRole() case TokenVar: return p.parseShowVariable() case TokenService: @@ -473,60 +468,6 @@ func (p *Parser) parseShowCommand() (*Command, error) { } } -func (p *Parser) parseShowUser() (*Command, error) { - p.nextToken() // consume USER - - // Check for PERMISSION - if p.curToken.Type == TokenPermission { - p.nextToken() - userName, err := p.parseQuotedString() - if err != nil { - return nil, err - } - cmd := NewCommand("show_user_permission") - cmd.Params["user_name"] = userName - p.nextToken() - // Semicolon is optional for SHOW TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return cmd, nil - } - - userName, err := p.parseQuotedString() - if err != nil { - return nil, err - } - - cmd := NewCommand("show_user") - cmd.Params["user_name"] = userName - - p.nextToken() - // Semicolon is optional for UNSET TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return cmd, nil -} - -func (p *Parser) parseShowRole() (*Command, error) { - p.nextToken() // consume ROLE - roleName, err := p.parseIdentifier() - if err != nil { - return nil, err - } - - cmd := NewCommand("show_role") - cmd.Params["role_name"] = roleName - - p.nextToken() - // Semicolon is optional for UNSET TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return cmd, nil -} - func (p *Parser) parseShowVariable() (*Command, error) { p.nextToken() // consume VAR varName, err := p.parseIdentifier()