From 1b712be5998c4152bd794398229cb73a75d1acd0 Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Sat, 20 Jun 2026 02:31:07 +0800 Subject: [PATCH] Go CLI: refactor some commands (#16204) ### What problem does this PR solve? - list resources ### Type of change - [x] New Feature (non-breaking change which adds functionality) - [x] Refactoring --------- Signed-off-by: Jin Hai --- internal/admin/enterprise_handler.go | 266 +++++++++++++-- internal/admin/enterprise_service.go | 194 ++++++++++- internal/admin/handler.go | 63 +++- internal/admin/router.go | 113 ++++--- internal/cli/admin_command.go | 487 ++++++++++++++++++++++----- internal/cli/admin_parser.go | 283 +++++++++++----- internal/cli/cli_http.go | 36 +- internal/cli/lexer.go | 4 + internal/cli/parser.go | 2 + internal/cli/types.go | 2 + internal/cli/user_parser.go | 28 +- internal/common/format.go | 13 + 12 files changed, 1186 insertions(+), 305 deletions(-) diff --git a/internal/admin/enterprise_handler.go b/internal/admin/enterprise_handler.go index 9e0481dbbf..004d1acc97 100644 --- a/internal/admin/enterprise_handler.go +++ b/internal/admin/enterprise_handler.go @@ -37,10 +37,7 @@ func (h *Handler) ListRoles(c *gin.Context) { roles = []map[string]interface{}{} } - success(c, gin.H{ - "roles": roles, - "total": len(roles), - }, "") + success(c, roles, "") } // CreateRoleHTTPRequest create role request @@ -119,12 +116,13 @@ func (h *Handler) DeleteRole(c *gin.Context) { return } - if err := h.service.DeleteRole(roleName); err != nil { - errorResponse(c, err.Error(), 500) + role, err := h.service.DeleteRole(roleName) + if err != nil { + errorResponse(c, "Role not found", 404) return } - successNoData(c, "") + success(c, role, "") } // GetRolePermission handle get role permission @@ -202,6 +200,99 @@ func (h *Handler) RevokeRolePermission(c *gin.Context) { success(c, result, "") } +// ListResources handle list role resources +func (h *Handler) ListResources(c *gin.Context) { + resources, err := h.service.ListResources() + if err != nil { + if errors.Is(err, common.ErrUserNotFound) { + errorResponse(c, "Role not found", 404) + return + } + errorResponse(c, err.Error(), 500) + return + } + + success(c, resources, "") +} + +// GetSystemFingerprint handle get system fingerprint +func (h *Handler) GetSystemFingerprint(c *gin.Context) { + fingerprint, err := h.service.GetSystemFingerprint() + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, fingerprint, "") +} + +type SetSystemLicenseRequest struct { + License string `json:"license" binding:"required"` +} + +// SetSystemLicense to set system license +func (h *Handler) SetSystemLicense(c *gin.Context) { + var req SetSystemLicenseRequest + 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 + } + + err := h.service.SetSystemLicense(req.License) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + success(c, nil, "System license set successfully") +} + +// ShowSystemLicense to get system license +func (h *Handler) ShowSystemLicense(c *gin.Context) { + check, ok := c.GetQuery("check") + if !ok { + check = "false" + } + checkFlag, err := strconv.ParseBool(check) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } + systemLicense, err := h.service.ShowSystemLicense(checkFlag) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, systemLicense, "") +} + +type SetSystemLicenseConfigRequest struct { + TimeRecordSaveInterval int64 `json:"value1" binding:"required"` + TimeRecordTaskDuration int64 `json:"value2" binding:"required"` +} + +func (h *Handler) UpdateSystemLicenseConfig(c *gin.Context) { + var req SetSystemLicenseConfigRequest + 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 + } + result, err := h.service.UpdateSystemLicenseConfig(req.TimeRecordSaveInterval, req.TimeRecordTaskDuration) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + success(c, result, "System license config updated successfully") +} + type ShowUserActivityRequest struct { Days int `json:"days"` Email string `json:"email"` @@ -247,7 +338,12 @@ func (h *Handler) ShowUserDatasetSummary(c *gin.Context) { return } - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -268,7 +364,12 @@ func (h *Handler) ShowUserDatasetSummary(c *gin.Context) { // ShowUserSummary handle show user summary func (h *Handler) ShowUserSummary(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -289,7 +390,12 @@ func (h *Handler) ShowUserSummary(c *gin.Context) { // ShowUserStorage handle show user storage func (h *Handler) ShowUserStorage(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -310,7 +416,12 @@ func (h *Handler) ShowUserStorage(c *gin.Context) { // ShowUserQuota handle show user quota func (h *Handler) ShowUserQuota(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -331,7 +442,12 @@ func (h *Handler) ShowUserQuota(c *gin.Context) { // ShowUserIndex handle show user index func (h *Handler) ShowUserIndex(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -357,7 +473,12 @@ type UpdateUserRoleHTTPRequest struct { // UpdateUserRole handle update user role func (h *Handler) UpdateUserRole(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -380,7 +501,12 @@ func (h *Handler) UpdateUserRole(c *gin.Context) { // ShowUserPermission handle show user permission func (h *Handler) ShowUserPermission(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -397,7 +523,12 @@ func (h *Handler) ShowUserPermission(c *gin.Context) { // ListUserDatasets handle show user datasets func (h *Handler) ListUserDatasets(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -414,7 +545,12 @@ func (h *Handler) ListUserDatasets(c *gin.Context) { // ListUserAgents handle show user agents func (h *Handler) ListUserAgents(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -431,7 +567,12 @@ func (h *Handler) ListUserAgents(c *gin.Context) { // ListUserChats handle show user chats func (h *Handler) ListUserChats(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -448,7 +589,12 @@ func (h *Handler) ListUserChats(c *gin.Context) { // ListUserSearches handle show user searches func (h *Handler) ListUserSearches(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -465,7 +611,12 @@ func (h *Handler) ListUserSearches(c *gin.Context) { // ListUserModels handle show user models func (h *Handler) ListUserModels(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -482,7 +633,12 @@ func (h *Handler) ListUserModels(c *gin.Context) { // ListUserFiles handle show user files func (h *Handler) ListUserFiles(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -916,7 +1072,12 @@ func (h *Handler) PurgeUserData(c *gin.Context) { }) return } - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -966,3 +1127,66 @@ func (h *Handler) PurgeUsersData(c *gin.Context) { success(c, result, "") } + +// CreateUserAPIKey handle create tenant API key +func (h *Handler) CreateUserAPIKey(c *gin.Context) { + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } + + apiKey, err := h.service.CreateUserAPIKey(username) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, apiKey, "API key generated successfully") +} + +// DeleteUserAPIKey handle delete user API key +func (h *Handler) DeleteUserAPIKey(c *gin.Context) { + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } + key := c.Param("key") + if username == "" || key == "" { + errorResponse(c, "Username and key are required", 400) + return + } + + result, err := h.service.DeleteUserAPIKey(username, key) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "API key deleted successfully") +} + +// ListUserAPIKeys handle list user API keys +func (h *Handler) ListUserAPIKeys(c *gin.Context) { + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } + if username == "" { + errorResponse(c, "Username is required", 400) + return + } + + result, err := h.service.ListUserAPIKeys(username) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "API keys listed successfully") +} diff --git a/internal/admin/enterprise_service.go b/internal/admin/enterprise_service.go index 4d2f996d9a..ff1d9a9344 100644 --- a/internal/admin/enterprise_service.go +++ b/internal/admin/enterprise_service.go @@ -17,6 +17,7 @@ package admin import ( + "errors" "fmt" "ragflow/internal/common" "ragflow/internal/dao" @@ -27,50 +28,152 @@ import ( // ListRoles list all roles func (s *Service) ListRoles() ([]map[string]interface{}, error) { - // TODO: Implement list roles - return []map[string]interface{}{}, nil + result := []map[string]interface{}{ + { + "command": "list_roles", + "error": "'list roles' is implemented in enterprise edition", + }, + } + + return result, 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 + result := map[string]interface{}{ + "command": "create_role", + "role_name": roleName, + "description": description, + "error": "'create role' is implemented in enterprise edition", + } + + return result, nil } // GetRole get role details func (s *Service) GetRole(roleName string) (map[string]interface{}, error) { - // TODO: Implement get role - return map[string]interface{}{}, nil + result := map[string]interface{}{ + "command": "get_role", + "role_name": roleName, + "error": "'get role' is implemented in enterprise edition", + } + + return result, nil + } // UpdateRole update role func (s *Service) UpdateRole(roleName, description string) (map[string]interface{}, error) { - // TODO: Implement update role - return map[string]interface{}{}, nil + result := map[string]interface{}{ + "command": "update_role", + "role_name": roleName, + "description": description, + "error": "'update role' is implemented in enterprise edition", + } + + return result, nil } // DeleteRole delete role -func (s *Service) DeleteRole(roleName string) error { - // TODO: Implement delete role - return nil +func (s *Service) DeleteRole(roleName string) (map[string]interface{}, error) { + result := map[string]interface{}{ + "command": "delete_role", + "role_name": roleName, + "error": "'delete role' is implemented in enterprise edition", + } + + return result, 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 + result := []map[string]interface{}{ + { + "command": "get_role_permission", + "role_name": roleName, + "error": "'get role permissions' is implemented in enterprise edition", + }, + } + + return result, 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 + result := map[string]interface{}{ + "command": "grant_role_permission", + "role_name": roleName, + "actions": actions, + "resource": resource, + "error": "'grant role permission' is implemented in enterprise edition", + } + + return result, 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 + result := map[string]interface{}{ + "command": "revoke_role_permission", + "role_name": roleName, + "actions": actions, + "resource": resource, + "error": "'revoke role permission' is implemented in enterprise edition", + } + + return result, nil +} + +// ListResources list role resources +func (s *Service) ListResources() (map[string]interface{}, error) { + result := map[string]interface{}{ + "command": "list_resources", + "error": "'list resources for role' is implemented in enterprise edition", + } + + return result, nil +} + +func (s *Service) GetSystemFingerprint() (map[string]interface{}, error) { + result := map[string]interface{}{ + "command": "get_system_fingerprint", + "error": "'get system fingerprint' is implemented in enterprise edition", + } + + return result, nil +} + +func (s *Service) SetSystemLicense(license string) error { + return errors.New("'set system license' is implemented in enterprise edition") +} + +func (s *Service) ShowSystemLicense(check bool) (map[string]interface{}, error) { + var result map[string]interface{} + if check { + result = map[string]interface{}{ + "command": "check_system_license", + "error": "'check system license' is implemented in enterprise edition", + } + + } else { + result = map[string]interface{}{ + "command": "show_system_license", + "error": "'show system license' is implemented in enterprise edition", + } + } + + return result, nil +} + +func (s *Service) UpdateSystemLicenseConfig(timeRecordSaveInterval, timeRecordTaskDuration int64) (map[string]interface{}, error) { + result := map[string]interface{}{ + "command": "update_system_license_config", + "time_record_save_interval": timeRecordSaveInterval, + "time_record_task_duration": timeRecordTaskDuration, + "error": "'update system license config' is implemented in enterprise edition", + } + + return result, nil } // ShowUserActivity show user activity for enterprise edition @@ -626,6 +729,63 @@ func (s *Service) PurgeUsersData(preview bool, days int, userPlan *string, userA return result, nil } +// CreateUserAPIKey create tenant API key for tenant +func (s *Service) CreateUserAPIKey(username string) (map[string]interface{}, error) { + + user, err := s.userDAO.GetByEmail(username) + if err != nil { + return nil, fmt.Errorf("user not found: %w", err) + } + + result := map[string]interface{}{ + "command": "create_user_api_key", + "email": user.Email, + "nickname": user.Nickname, + "error": "'Create user API key' is implemented in enterprise edition", + } + + return result, nil +} + +// DeleteUserAPIKey delete user API key +func (s *Service) DeleteUserAPIKey(username, key string) (map[string]interface{}, error) { + + user, err := s.userDAO.GetByEmail(username) + if err != nil { + return nil, fmt.Errorf("user not found: %w", err) + } + + result := map[string]interface{}{ + "command": "delete_user_api_key", + "email": user.Email, + "nickname": user.Nickname, + "api_key": key, + "error": "'Delete user API key' is implemented in enterprise edition", + } + + return result, nil +} + +// ListUserAPIKeys list user API keys +func (s *Service) ListUserAPIKeys(username string) ([]map[string]interface{}, error) { + + user, err := s.userDAO.GetByEmail(username) + if err != nil { + return nil, fmt.Errorf("user not found: %w", err) + } + + result := []map[string]interface{}{ + { + "command": "list_user_api_keys", + "email": user.Email, + "nickname": user.Nickname, + "error": "'List user API keys' is implemented in enterprise edition", + }, + } + + return result, nil +} + func (s *Service) ListIngestionTasksByCondition(email, status *string) ([]map[string]interface{}, error) { if email == nil && status == nil { diff --git a/internal/admin/handler.go b/internal/admin/handler.go index 21f9c7a666..7d8ceb7ddd 100644 --- a/internal/admin/handler.go +++ b/internal/admin/handler.go @@ -296,7 +296,12 @@ func (h *Handler) CreateUser(c *gin.Context) { // GetUser handle get user func (h *Handler) GetUser(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -317,7 +322,12 @@ func (h *Handler) GetUser(c *gin.Context) { // DeleteUser handle delete user func (h *Handler) DeleteUser(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -344,7 +354,12 @@ type ChangePasswordHTTPRequest struct { // ChangePassword handle change password func (h *Handler) ChangePassword(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -371,7 +386,12 @@ type UpdateActivateStatusHTTPRequest struct { // UpdateUserActivateStatus handle update user activate status func (h *Handler) UpdateUserActivateStatus(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -399,7 +419,12 @@ func (h *Handler) UpdateUserActivateStatus(c *gin.Context) { // GrantAdmin handle grant admin role func (h *Handler) GrantAdmin(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -422,7 +447,12 @@ func (h *Handler) GrantAdmin(c *gin.Context) { // RevokeAdmin handle revoke admin role func (h *Handler) RevokeAdmin(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -445,7 +475,12 @@ func (h *Handler) RevokeAdmin(c *gin.Context) { // ListUserAPITokens handle get user API keys func (h *Handler) ListUserAPITokens(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -462,7 +497,12 @@ func (h *Handler) ListUserAPITokens(c *gin.Context) { // GenerateUserAPIToken handle generate user API key func (h *Handler) GenerateUserAPIToken(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } if username == "" { errorResponse(c, "Username is required", 400) return @@ -479,7 +519,12 @@ func (h *Handler) GenerateUserAPIToken(c *gin.Context) { // DeleteUserAPIToken handle delete user API key func (h *Handler) DeleteUserAPIToken(c *gin.Context) { - username := c.Param("username") + encodedUsername := c.Param("username") + username, err := common.DecodeEmail(encodedUsername) + if err != nil { + errorResponse(c, err.Error(), 400) + return + } key := c.Param("token") if username == "" || key == "" { errorResponse(c, "Username and key are required", 400) diff --git a/internal/admin/router.go b/internal/admin/router.go index f7c7a82bbd..f44c32401c 100644 --- a/internal/admin/router.go +++ b/internal/admin/router.go @@ -69,6 +69,56 @@ func (r *Router) Setup(engine *gin.Engine) { protected.PUT("/users/:username/admin", r.handler.GrantAdmin) protected.DELETE("/users/:username/admin", r.handler.RevokeAdmin) + // Service management + protected.GET("/services", r.handler.GetServices) + protected.GET("/service_types/:service_type", r.handler.GetServicesByType) + protected.GET("/services/:service_id", r.handler.GetService) + protected.DELETE("/services/:service_id", r.handler.ShutdownService) + protected.PUT("/services/:service_id", r.handler.RestartService) + + // Variables/Settings + protected.GET("/variables", r.handler.GetVariables) + protected.PUT("/variables", r.handler.SetVariable) + + // Configs + protected.GET("/configs", r.handler.GetConfigs) + + // Environments + protected.GET("/environments", r.handler.GetEnvironments) + + // Version + protected.GET("/version", r.handler.GetVersion) + + // Log level + protected.GET("/log_level", r.handler.GetLogLevel) + protected.PUT("/log_level", r.handler.SetLogLevel) + + provider := protected.Group("/providers") + { + provider.GET("/", r.handler.ListProviders) + provider.GET("/:provider_name", r.handler.ShowProvider) + provider.GET("/:provider_name/models", r.handler.ListModels) + provider.GET("/:provider_name/models/:model_name", r.handler.ShowModel) + } + + queue := protected.Group("/queue") + { + queue.GET("/", r.handler.ShowMessageQueue) + queue.POST("/messages", r.handler.PublishMessageToQueue) + queue.GET("/messages", r.handler.ListMessagesFromQueue) + queue.PUT("/messages", r.handler.PullMessageFromQueue) + } + + protected.GET("/ingestors", r.handler.ListIngestors) + protected.DELETE("/ingestors", r.handler.ShutdownIngestor) + + // Sandbox + protected.GET("/sandbox/providers", r.handler.ListSandboxProviders) + protected.GET("/sandbox/providers/:provider_id/schema", r.handler.GetSandboxProviderSchema) + protected.GET("/sandbox/config", r.handler.GetSandboxConfig) + protected.POST("/sandbox/config", r.handler.SetSandboxConfig) + protected.POST("/sandbox/test", r.handler.TestSandboxConnection) + // For enterprise edition protected.GET("/users/:username/activity", r.handler.ShowUserActivity) protected.GET("/users/:username/dataset", r.handler.ShowUserDatasetSummary) @@ -102,11 +152,13 @@ func (r *Router) Setup(engine *gin.Engine) { protected.DELETE("/users/data", r.handler.PurgeUsersData) // API Keys - protected.GET("/users/:username/keys", r.handler.ListUserAPITokens) + protected.POST("/users/:username/keys", r.handler.CreateUserAPIKey) + protected.DELETE("/users/:username/keys/:key", r.handler.DeleteUserAPIKey) + protected.GET("/users/:username/keys", r.handler.ListUserAPIKeys) + protected.GET("/users/:username/tokens", r.handler.ListUserAPITokens) - protected.POST("/users/:username/keys", r.handler.GenerateUserAPIToken) + //protected.POST("/users/:username/keys", r.handler.GenerateUserAPIToken) protected.POST("/users/:username/tokens", r.handler.GenerateUserAPIToken) - protected.DELETE("/users/:username/keys/:token", r.handler.DeleteUserAPIToken) protected.DELETE("/users/:username/tokens/:token", r.handler.DeleteUserAPIToken) // Role management @@ -118,33 +170,13 @@ func (r *Router) Setup(engine *gin.Engine) { protected.GET("/roles/:role_name/permission", r.handler.GetRolePermission) protected.POST("/roles/:role_name/permission", r.handler.GrantRolePermission) protected.DELETE("/roles/:role_name/permission", r.handler.RevokeRolePermission) + protected.GET("/roles/resource", r.handler.ListResources) - // Service management - protected.GET("/services", r.handler.GetServices) - protected.GET("/service_types/:service_type", r.handler.GetServicesByType) - protected.GET("/services/:service_id", r.handler.GetService) - protected.DELETE("/services/:service_id", r.handler.ShutdownService) - protected.PUT("/services/:service_id", r.handler.RestartService) - - // Variables/Settings - protected.GET("/variables", r.handler.GetVariables) - protected.PUT("/variables", r.handler.SetVariable) - - // Configs - protected.GET("/configs", r.handler.GetConfigs) - - // Environments - protected.GET("/environments", r.handler.GetEnvironments) - - // Version - protected.GET("/version", r.handler.GetVersion) - - // Sandbox - protected.GET("/sandbox/providers", r.handler.ListSandboxProviders) - protected.GET("/sandbox/providers/:provider_id/schema", r.handler.GetSandboxProviderSchema) - protected.GET("/sandbox/config", r.handler.GetSandboxConfig) - protected.POST("/sandbox/config", r.handler.SetSandboxConfig) - protected.POST("/sandbox/test", r.handler.TestSandboxConnection) + // License + protected.GET("/system/fingerprint", r.handler.GetSystemFingerprint) + protected.POST("/system/license", r.handler.SetSystemLicense) + protected.GET("/system/license", r.handler.ShowSystemLicense) + protected.PUT("/system/license/config", r.handler.UpdateSystemLicenseConfig) // Fingerprint protected.GET("/fingerprint", r.handler.GetFingerprint) @@ -152,32 +184,11 @@ func (r *Router) Setup(engine *gin.Engine) { protected.POST("/license", r.handler.SetLicense) protected.POST("/license/config", r.handler.UpdateLicenseConfig) protected.GET("/license", r.handler.ShowLicense) - // Log level - protected.GET("/log_level", r.handler.GetLogLevel) - protected.PUT("/log_level", r.handler.SetLogLevel) - provider := protected.Group("/providers") - { - provider.GET("/", r.handler.ListProviders) - provider.GET("/:provider_name", r.handler.ShowProvider) - provider.GET("/:provider_name/models", r.handler.ListModels) - provider.GET("/:provider_name/models/:model_name", r.handler.ShowModel) - } - - queue := protected.Group("/queue") - { - queue.GET("/", r.handler.ShowMessageQueue) - queue.POST("/messages", r.handler.PublishMessageToQueue) - queue.GET("/messages", r.handler.ListMessagesFromQueue) - queue.PUT("/messages", r.handler.PullMessageFromQueue) - } - - protected.GET("/ingestors", r.handler.ListIngestors) - protected.DELETE("/ingestors", r.handler.ShutdownIngestor) + // Ingestion tasks protected.DELETE("/ingestion/tasks", r.handler.RemoveIngestionTasks) protected.PUT("/ingestion/tasks", r.handler.StopIngestionTasks) protected.GET("/ingestion/tasks", r.handler.ListIngestionTasks) - } } diff --git a/internal/cli/admin_command.go b/internal/cli/admin_command.go index 6f49dee5e6..8f33154adb 100644 --- a/internal/cli/admin_command.go +++ b/internal/cli/admin_command.go @@ -19,7 +19,7 @@ package cli import ( "encoding/json" "fmt" - "net/url" + "ragflow/internal/common" ) // PingServer pings the server to check if it's alive @@ -58,7 +58,7 @@ func (c *CLI) PingAdmin(cmd *Command) (ResponseIf, error) { // Show admin version to show RAGFlow admin version // Returns benchmark result map if iterations > 1, otherwise prints status -func (c *CLI) ShowAdminVersion(cmd *Command) (ResponseIf, error) { +func (c *CLI) AdminShowVersionCommand(cmd *Command) (ResponseIf, error) { // Get iterations from command params (for benchmark) iterations := 1 if val, ok := cmd.Params["iterations"].(int); ok && val > 1 { @@ -92,8 +92,36 @@ func (c *CLI) ShowAdminVersion(cmd *Command) (ResponseIf, error) { return &result, nil } -// ListRoles to list roles (admin mode only) -func (c *CLI) ListRoles(cmd *Command) (ResponseIf, error) { +// AdminListResourcesCommand to list resources command (admin mode only) +func (c *CLI) AdminListResourcesCommand(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") + } + + resp, err := c.AdminServerClient.Request("GET", "/admin/roles/resource", "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to list resources: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to list resources: 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("list resources failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// AdminListRolesCommand to list roles command (admin mode only) +func (c *CLI) AdminListRolesCommand(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") } @@ -178,21 +206,21 @@ func (c *CLI) ShowRole(cmd *Command) (ResponseIf, error) { return &result, nil } -// CreateRole creates a new role (admin mode only) -func (c *CLI) CreateRole(cmd *Command) (ResponseIf, error) { +// AdminCreateRoleCommand creates a new role (admin mode only) +func (c *CLI) AdminCreateRoleCommand(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") } roleName, ok := cmd.Params["role_name"].(string) if !ok { - return nil, fmt.Errorf("user_name not provided") + return nil, fmt.Errorf("role_name not provided") } - description, ok := cmd.Params["description"].(string) payload := map[string]interface{}{ "role_name": roleName, } + description, ok := cmd.Params["description"].(string) if ok { payload["description"] = description } @@ -206,7 +234,7 @@ func (c *CLI) CreateRole(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("failed to create role: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) } - var result SimpleResponse + var result CommonDataResponse if err = json.Unmarshal(resp.Body, &result); err != nil { return nil, fmt.Errorf("create role failed: invalid JSON (%w)", err) @@ -304,7 +332,10 @@ func (c *CLI) GrantAdmin(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - resp, err := c.AdminServerClient.Request("PUT", fmt.Sprintf("/admin/users/%s/admin", userName), "admin", nil, nil) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/admin", encodedUserName) + + resp, err := c.AdminServerClient.Request("PUT", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to grant admin: %w", err) } @@ -337,7 +368,10 @@ func (c *CLI) RevokeAdmin(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - resp, err := c.AdminServerClient.Request("DELETE", fmt.Sprintf("/admin/users/%s/admin", userName), "admin", nil, nil) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/admin", encodedUserName) + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to revoke admin: %w", err) } @@ -359,8 +393,8 @@ func (c *CLI) RevokeAdmin(cmd *Command) (ResponseIf, error) { return &result, nil } -// CreateUser creates a new user (admin mode only) -func (c *CLI) CreateUser(cmd *Command) (ResponseIf, error) { +// AdminCreateUserCommand creates a new user (admin mode only) +func (c *CLI) AdminCreateUserCommand(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") } @@ -409,6 +443,47 @@ func (c *CLI) CreateUser(cmd *Command) (ResponseIf, error) { return &result, nil } +// AdminCreateUserAPIKeyCommand creates a new user API key (admin mode only) +func (c *CLI) AdminCreateUserAPIKeyCommand(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") + } + + encodedUserName := common.EncodeEmail(userName) + + apiURL := fmt.Sprintf("/admin/users/%s/keys", encodedUserName) + + resp, err := c.AdminServerClient.Request("POST", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to create API key: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to create API key: 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("create API key failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + delete(result.Data, "update_date") + delete(result.Data, "update_time") + delete(result.Data, "create_time") + + result.Duration = resp.Duration + return &result, nil +} + // ActivateUser activates or deactivates a user (admin mode only) func (c *CLI) ActivateUser(cmd *Command) (ResponseIf, error) { if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { @@ -434,7 +509,10 @@ func (c *CLI) ActivateUser(cmd *Command) (ResponseIf, error) { "activate_status": activateStatus, } - resp, err := c.AdminServerClient.Request("PUT", fmt.Sprintf("/admin/users/%s/activate", userName), "admin", nil, payload) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/activate", encodedUserName) + + resp, err := c.AdminServerClient.Request("PUT", apiURL, "admin", nil, payload) if err != nil { return nil, fmt.Errorf("failed to update user status: %w", err) } @@ -482,7 +560,10 @@ func (c *CLI) AlterUserPassword(cmd *Command) (ResponseIf, error) { "new_password": encryptedPassword, } - resp, err := c.AdminServerClient.Request("PUT", fmt.Sprintf("/admin/users/%s/password", userName), "admin", nil, payload) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/password", encodedUserName) + + resp, err := c.AdminServerClient.Request("PUT", apiURL, "admin", nil, payload) if err != nil { return nil, fmt.Errorf("failed to change user password: %w", err) } @@ -691,6 +772,79 @@ func (c *CLI) ShowVariable(cmd *Command) (ResponseIf, error) { return &result, nil } +func (c *CLI) AdminSetLicenseCommand(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") + } + + license, ok := cmd.Params["license"].(string) + if !ok { + return nil, fmt.Errorf("license not provided") + } + + payload := map[string]interface{}{ + "license": license, + } + resp, err := c.AdminServerClient.Request("POST", "/admin/system/license", "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to set license: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to set license: 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("set license 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) AdminSetLicenseConfigCommand(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") + } + + value1, ok := cmd.Params["number1"].(int) + if !ok { + return nil, fmt.Errorf("number1 not provided") + } + value2, ok := cmd.Params["number2"].(int) + if !ok { + return nil, fmt.Errorf("number2 not provided") + } + + payload := map[string]interface{}{ + "value1": value1, + "value2": value2, + } + resp, err := c.AdminServerClient.Request("PUT", "/admin/system/license/config", "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to set license config: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to set license config: 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("set license config failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil +} + // SetVariable updates a system variable (admin mode only). func (c *CLI) SetVariable(cmd *Command) (ResponseIf, error) { if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { @@ -732,8 +886,8 @@ func (c *CLI) SetVariable(cmd *Command) (ResponseIf, error) { return &result, nil } -// DropUser deletes a user (admin mode only) -func (c *CLI) DropUser(cmd *Command) (ResponseIf, error) { +// AdminDropUserCommand deletes a user (admin mode only) +func (c *CLI) AdminDropUserCommand(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") } @@ -743,7 +897,10 @@ func (c *CLI) DropUser(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - resp, err := c.AdminServerClient.Request("DELETE", fmt.Sprintf("/admin/users/%s", userName), "admin", nil, nil) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s", encodedUserName) + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to drop user: %w", err) } @@ -765,6 +922,47 @@ func (c *CLI) DropUser(cmd *Command) (ResponseIf, error) { return &result, nil } +// AdminDropUserAPIKeyCommand drops an API key for a user (admin mode only) +func (c *CLI) AdminDropUserAPIKeyCommand(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") + } + + apiKey, ok := cmd.Params["api_key"].(string) + if !ok { + return nil, fmt.Errorf("api_key not provided") + } + + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/keys/%s", encodedUserName, apiKey) + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to drop API key: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to drop API key: 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("drop API key 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) { @@ -783,12 +981,15 @@ func (c *CLI) ListUserDatasets(cmd *Command) (ResponseIf, error) { iterations = val } + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/datasets", encodedUserName) + if iterations > 1 { // Benchmark mode - return raw result for benchmark stats - return c.AdminServerClient.RequestWithIterations("GET", fmt.Sprintf("/admin/users/%s/datasets", userName), "admin", nil, nil, iterations) + return c.AdminServerClient.RequestWithIterations("GET", apiURL, "admin", nil, nil, iterations) } - resp, err := c.AdminServerClient.Request("GET", fmt.Sprintf("/admin/users/%s/datasets", userName), "admin", nil, nil) + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to list datasets: %w", err) } @@ -838,12 +1039,15 @@ func (c *CLI) ListAgents(cmd *Command) (ResponseIf, error) { iterations = val } + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/agents", encodedUserName) + if iterations > 1 { // Benchmark mode - return raw result for benchmark stats - return c.AdminServerClient.RequestWithIterations("GET", fmt.Sprintf("/admin/users/%s/agents", userName), "admin", nil, nil, iterations) + return c.AdminServerClient.RequestWithIterations("GET", apiURL, "admin", nil, nil, iterations) } - resp, err := c.AdminServerClient.Request("GET", fmt.Sprintf("/admin/users/%s/agents", userName), "admin", nil, nil) + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to list agents: %w", err) } @@ -886,7 +1090,10 @@ func (c *CLI) GrantPermission(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - resp, err := c.AdminServerClient.Request("GET", fmt.Sprintf("/admin/users/%s/keys", userName), "admin", nil, nil) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/keys", encodedUserName) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to list tokens: %w", err) } @@ -993,7 +1200,10 @@ func (c *CLI) AlterUserRole(cmd *Command) (ResponseIf, error) { "role_name": roleName, } - resp, err := c.AdminServerClient.Request("PUT", fmt.Sprintf("/admin/users/%s/role", userName), "admin", nil, payload) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/role", encodedUserName) + + resp, err := c.AdminServerClient.Request("PUT", apiURL, "admin", nil, payload) if err != nil { return nil, fmt.Errorf("failed to alter user role: %w", err) } @@ -1031,7 +1241,10 @@ func (c *CLI) ShowUserPermission(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - resp, err := c.AdminServerClient.Request("GET", fmt.Sprintf("/admin/users/%s/permission", userName), "admin", nil, nil) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/permission", encodedUserName) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to show user permission: %w", err) } @@ -1069,7 +1282,10 @@ func (c *CLI) GenerateAdminToken(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - resp, err := c.AdminServerClient.Request("POST", fmt.Sprintf("/admin/users/%s/keys", userName), "admin", nil, nil) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/keys", encodedUserName) + + resp, err := c.AdminServerClient.Request("POST", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to generate token: %w", err) } @@ -1106,7 +1322,10 @@ func (c *CLI) ListAdminTokens(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - resp, err := c.AdminServerClient.Request("GET", fmt.Sprintf("/admin/users/%s/keys", userName), "admin", nil, nil) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/keys", encodedUserName) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { return nil, fmt.Errorf("failed to list tokens: %w", err) } @@ -1137,47 +1356,6 @@ func (c *CLI) ListAdminTokens(cmd *Command) (ResponseIf, error) { return &result, nil } -// DropToken drops an API token for a user (admin mode only) -func (c *CLI) DropAdminToken(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") - } - - token, ok := cmd.Params["token"].(string) - if !ok { - return nil, fmt.Errorf("token not provided") - } - - // URL encode the token to handle special characters - encodedToken := url.QueryEscape(token) - - resp, err := c.AdminServerClient.Request("DELETE", fmt.Sprintf("/admin/users/%s/keys/%s", userName, encodedToken), "admin", nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to drop token: %w", err) - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to drop token: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - - var result SimpleResponse - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("drop token 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) ListAdminTasks(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") @@ -1536,7 +1714,97 @@ func (c *CLI) AdminRemoveServiceCommand(cmd *Command) (ResponseIf, error) { return &result, nil } -// Show user show user (admin mode only) +// AdminCheckLicenseCommand check license command (admin mode only) +func (c *CLI) AdminCheckLicenseCommand(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 := fmt.Sprintf("/admin/system/license?check=true") + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to check license: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to check license: 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("check license failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil +} + +// AdminShowFingerprintCommand show fingerprint command (admin mode only) +func (c *CLI) AdminShowFingerprintCommand(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 := fmt.Sprintf("/admin/system/fingerprint") + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to show fingerprint: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to show fingerprint: 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 fingerprint failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil +} + +// AdminShowLicenseCommand show license command (admin mode only) +func (c *CLI) AdminShowLicenseCommand(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 := fmt.Sprintf("/admin/system/license") + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to show license: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to show license: 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 license failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil +} + +// AdminShowUserInfoCommand show user info command (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") @@ -1547,7 +1815,8 @@ func (c *CLI) AdminShowUserInfoCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -1592,7 +1861,8 @@ func (c *CLI) AdminShowUserActivityCommand(cmd *Command) (ResponseIf, error) { "email": email, } - apiURL := fmt.Sprintf("/admin/users/%s/activity", email) + encodedUserName := common.EncodeEmail(email) + apiURL := fmt.Sprintf("/admin/users/%s/activity", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, payload) if err != nil { @@ -1626,7 +1896,8 @@ func (c *CLI) AdminShowUserSummaryCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/summary", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/summary", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -1670,7 +1941,8 @@ func (c *CLI) AdminShowUserDatasetCommand(cmd *Command) (ResponseIf, error) { "dataset": dataset, } - apiURL := fmt.Sprintf("/admin/users/%s/dataset", email) + encodedUserName := common.EncodeEmail(email) + apiURL := fmt.Sprintf("/admin/users/%s/dataset", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, payload) if err != nil { @@ -1705,7 +1977,8 @@ func (c *CLI) AdminShowUserStorageCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/storage", email) + encodedUserName := common.EncodeEmail(email) + apiURL := fmt.Sprintf("/admin/users/%s/admin", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -1740,7 +2013,8 @@ func (c *CLI) AdminShowUserQuotaCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/quota", email) + encodedUserName := common.EncodeEmail(email) + apiURL := fmt.Sprintf("/admin/users/%s/quota", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -1775,7 +2049,8 @@ func (c *CLI) AdminShowUserIndexCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/index", email) + encodedUserName := common.EncodeEmail(email) + apiURL := fmt.Sprintf("/admin/users/%s/index", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -1810,7 +2085,8 @@ func (c *CLI) AdminShowUserPermissionCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/permission", email) + encodedUserName := common.EncodeEmail(email) + apiURL := fmt.Sprintf("/admin/users/%s/permission", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -2260,7 +2536,8 @@ func (c *CLI) AdminPurgeUserCommand(cmd *Command) (ResponseIf, error) { "preview": preview, } - apiURL := fmt.Sprintf("/admin/users/%s/data", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/data", encodedUserName) resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, payload) if err != nil { @@ -2404,7 +2681,8 @@ func (c *CLI) AdminListUserDatasetsCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/datasets", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/datasets", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -2438,7 +2716,8 @@ func (c *CLI) AdminListUserAgentsCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/agents", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/agents", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -2472,7 +2751,8 @@ func (c *CLI) AdminListUserChatsCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/chats", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/chats", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -2506,7 +2786,8 @@ func (c *CLI) AdminListUserSearchesCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/searches", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/searches", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -2540,7 +2821,8 @@ func (c *CLI) AdminListUserModelsCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/models", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/models", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -2574,7 +2856,8 @@ func (c *CLI) AdminListUserFilesCommand(cmd *Command) (ResponseIf, error) { return nil, fmt.Errorf("user_name not provided") } - apiURL := fmt.Sprintf("/admin/users/%s/files", userName) + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/files", encodedUserName) resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) if err != nil { @@ -2598,6 +2881,42 @@ func (c *CLI) AdminListUserFilesCommand(cmd *Command) (ResponseIf, error) { return &result, nil } +func (c *CLI) AdminListUserKeysCommand(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") + } + + encodedUserName := common.EncodeEmail(userName) + apiURL := fmt.Sprintf("/admin/users/%s/keys", encodedUserName) + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to list user %s keys: %w", userName, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to list user %s keys: HTTP %d, body: %s", userName, resp.StatusCode, string(resp.Body)) + } + + var result CommonResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("list user %s keys 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) AdminStopUserIngestionTasksCommand(cmd *Command) (ResponseIf, error) { if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { diff --git a/internal/cli/admin_parser.go b/internal/cli/admin_parser.go index 3d58435aca..a3e758cf8a 100644 --- a/internal/cli/admin_parser.go +++ b/internal/cli/admin_parser.go @@ -146,12 +146,9 @@ func (p *Parser) parseAdminListCommand() (*Command, error) { case TokenAgents: return p.parseAdminListAgents() case TokenRoles: - p.nextToken() - // Semicolon is optional for SHOW TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return NewCommand("list_roles"), nil + return p.parseAdminListRoles() + case TokenResources: + return p.parseAdminListResources() case TokenVars: p.nextToken() // Semicolon is optional for SHOW TOKEN @@ -259,6 +256,26 @@ func (p *Parser) parseAdminListAgents() (*Command, error) { return cmd, nil } +func (p *Parser) parseAdminListRoles() (*Command, error) { + p.nextToken() // consume ROLES + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return NewCommand("admin_list_roles_command"), nil +} + +func (p *Parser) parseAdminListResources() (*Command, error) { + p.nextToken() // consume RESOURCES + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return NewCommand("admin_list_resources_command"), nil +} + func (p *Parser) parseAdminListTokens() (*Command, error) { p.nextToken() // consume TOKENS cmd := NewCommand("list_tokens") @@ -378,11 +395,10 @@ func (p *Parser) parseAdminShowCommand() (*Command, error) { return p.parseShowVariable() case TokenCurrent: return p.parseAdminShowCurrentCommand() - // SHOW CURRENT USER - //case TokenFingerprint: - // return p.parseAdminShowFingerprintCommand() - //case TokenLicense : - // return p.parseAdminShowLicenseCommand() + case TokenFingerprint: + return p.parseAdminShowFingerprintCommand() + case TokenLicense: + return p.parseAdminShowLicenseCommand() case TokenProvider: return p.parseShowProvider() case TokenModel: @@ -541,6 +557,28 @@ func (p *Parser) parseCommonShowPoolModel() (*Command, error) { // endregion SHOW commands +// parseAdminCheckCommand +func (p *Parser) parseAdminCheckCommand() (*Command, error) { + p.nextToken() // consume CHECK + switch p.curToken.Type { + case TokenLicense: + return p.parseAdminCheckLicenseCommand() + default: + return nil, fmt.Errorf("unknown CHECK target: %s", p.curToken.Value) + } +} + +func (p *Parser) parseAdminCheckLicenseCommand() (*Command, error) { + p.nextToken() // consume LICENSE + cmd := NewCommand("admin_check_license_command") + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + func (p *Parser) parseAdminStopIngestionTasks() (*Command, error) { p.nextToken() // consume STOP @@ -611,9 +649,9 @@ func (p *Parser) parseAdminCreateCommand() (*Command, error) { switch p.curToken.Type { case TokenUser: - return p.parseCreateUser() + return p.parseAdminCreateUserCommand() case TokenRole: - return p.parseCreateRole() + return p.parseAdminCreateRoleCommand() case TokenModel: return p.parseCreateModelProvider() case TokenDataset: @@ -627,57 +665,72 @@ func (p *Parser) parseAdminCreateCommand() (*Command, error) { } } -func (p *Parser) parseAdminCreateToken() (*Command, error) { - p.nextToken() // consume TOKEN - - // Semicolon is optional for UNSET TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - - return NewCommand("create_token"), nil -} - -func (p *Parser) parseAdminCreateUser() (*Command, error) { +func (p *Parser) parseAdminCreateUserCommand() (*Command, error) { p.nextToken() // consume USER userName, err := p.parseQuotedString() if err != nil { return nil, err } - p.nextToken() - password, err := p.parseQuotedString() - if err != nil { - return nil, err + + var cmd *Command + switch p.curToken.Type { + case TokenQuotedString: + var password string + password, err = p.parseQuotedString() + if err != nil { + return nil, err + } + p.nextToken() + + cmd = NewCommand("admin_create_user_command") + cmd.Params["user_name"] = userName + cmd.Params["password"] = password + cmd.Params["role"] = "user" + case TokenKey: + return p.parseAdminCreateUserAPIKeyCommand(userName) + default: + return nil, fmt.Errorf("expected password or KEY after USER, got %s", p.curToken.Value) } - cmd := NewCommand("create_user") - cmd.Params["user_name"] = userName - cmd.Params["password"] = password - cmd.Params["role"] = "user" - - p.nextToken() // Semicolon is optional for UNSET TOKEN if p.curToken.Type == TokenSemicolon { p.nextToken() } + return cmd, nil } -func (p *Parser) parseAdminCreateRole() (*Command, error) { +// CREATE USER 'user@example.com' KEY; +func (p *Parser) parseAdminCreateUserAPIKeyCommand(userName string) (*Command, error) { + p.nextToken() // consume KEY + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + cmd := NewCommand("admin_create_user_api_key_command") + cmd.Params["user_name"] = userName + + return cmd, nil +} + +func (p *Parser) parseAdminCreateRoleCommand() (*Command, error) { p.nextToken() // consume ROLE - roleName, err := p.parseIdentifier() + roleName, err := p.parseQuotedString() if err != nil { return nil, err } - cmd := NewCommand("create_role") + cmd := NewCommand("admin_create_role_command") cmd.Params["role_name"] = roleName p.nextToken() if p.curToken.Type == TokenDescription { p.nextToken() - description, err := p.parseQuotedString() + var description string + description, err = p.parseQuotedString() if err != nil { return nil, err } @@ -802,70 +855,60 @@ func (p *Parser) parseAdminDropCommand() (*Command, error) { switch p.curToken.Type { case TokenUser: - return p.parseDropUser() + return p.parseAdminDropUserCommand() case TokenRole: - return p.parseDropRole() - case TokenDataset: - return p.parseDropDataset() - case TokenChat: - return p.parseDropChat() - case TokenToken: - return p.parseDropToken() + return p.parseAdminDropRoleCommand() default: return nil, fmt.Errorf("unknown DROP target: %s", p.curToken.Value) } } -func (p *Parser) parseAdminDropToken() (*Command, error) { - p.nextToken() // consume TOKEN +func (p *Parser) parseAdminDropUserCommand() (*Command, error) { + p.nextToken() // consume USER - tokenValue, err := p.parseQuotedString() - if err != nil { - return nil, err + if p.curToken.Type != TokenQuotedString { + return nil, fmt.Errorf("expected USER name, got %s", p.curToken.Value) } - p.nextToken() - if p.curToken.Type != TokenOf { - return nil, fmt.Errorf("expected OF") - } - p.nextToken() - userName, err := p.parseQuotedString() if err != nil { return nil, err } - - cmd := NewCommand("drop_token") - cmd.Params["token"] = tokenValue - cmd.Params["user_name"] = userName - p.nextToken() + + switch p.curToken.Type { + case TokenKey: + return p.parseAdminDropUserAPIKeyCommand(userName) + default: + p.nextToken() + } + + cmd := NewCommand("admin_drop_user_command") + cmd.Params["user_name"] = userName + return cmd, nil +} + +func (p *Parser) parseAdminDropUserAPIKeyCommand(userName string) (*Command, error) { + p.nextToken() // consume KEY + + apiKey, err := p.parseQuotedString() + if err != nil { + return nil, err + } + // Semicolon is optional if p.curToken.Type == TokenSemicolon { p.nextToken() } - return cmd, nil -} -func (p *Parser) parseAdminDropUser() (*Command, error) { - p.nextToken() // consume USER - userName, err := p.parseQuotedString() - if err != nil { - return nil, err - } - - cmd := NewCommand("drop_user") + cmd := NewCommand("admin_drop_user_api_key_command") cmd.Params["user_name"] = userName + cmd.Params["api_key"] = apiKey - p.nextToken() - // Semicolon is optional for UNSET TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } return cmd, nil } -func (p *Parser) parseAdminDropRole() (*Command, error) { +func (p *Parser) parseAdminDropRoleCommand() (*Command, error) { p.nextToken() // consume ROLE roleName, err := p.parseIdentifier() if err != nil { @@ -1253,17 +1296,56 @@ func (p *Parser) parseAdminIdentifierList() ([]string, error) { func (p *Parser) parseAdminSetCommand() (*Command, error) { p.nextToken() // consume SET - if p.curToken.Type == TokenVar { - return p.parseSetVariable() + switch p.curToken.Type { + case TokenLicense: + return p.parseAdminSetLicense() + case TokenVar: + return p.parseAdminSetVariable() + case TokenDefault: + return p.parseAdminSetDefault() + case TokenToken: + return p.parseAdminSetToken() + default: + return nil, fmt.Errorf("unknown SET target: %s", p.curToken.Value) } - if p.curToken.Type == TokenDefault { - return p.parseSetDefault() - } - if p.curToken.Type == TokenToken { - return p.parseSetToken() +} + +func (p *Parser) parseAdminSetLicense() (*Command, error) { + p.nextToken() // consume LICENSE + + if p.curToken.Type == TokenConfig { + p.nextToken() // consume CONFIG + // SET LICENSE CONFIG + cmd := NewCommand("admin_set_license_config_command") + number1, err := p.parseNumber() + if err != nil { + return nil, err + } + p.nextToken() + number2, err := p.parseNumber() + if err != nil { + return nil, err + } + p.nextToken() + cmd.Params["number1"] = number1 + cmd.Params["number2"] = number2 + return cmd, nil } - return nil, fmt.Errorf("unknown SET target: %s", p.curToken.Value) + license, err := p.parseQuotedString() + if err != nil { + return nil, err + } + p.nextToken() + + cmd := NewCommand("admin_set_license_command") + cmd.Params["license"] = license + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil } func (p *Parser) parseAdminSetVariable() (*Command, error) { @@ -1951,7 +2033,7 @@ func (p *Parser) parseAdminShowVersionCommand() (*Command, error) { p.nextToken() } - return NewCommand("show_version"), nil + return NewCommand("admin_show_version_command"), nil } // SHOW CURRENT; @@ -1966,6 +2048,28 @@ func (p *Parser) parseAdminShowCurrentCommand() (*Command, error) { return NewCommand("show_current"), nil } +func (p *Parser) parseAdminShowFingerprintCommand() (*Command, error) { + p.nextToken() // consume FINGERPRINT + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return NewCommand("admin_show_fingerprint_command"), nil +} + +func (p *Parser) parseAdminShowLicenseCommand() (*Command, error) { + p.nextToken() // consume LICENSE + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return NewCommand("admin_show_license_command"), nil +} + // SHOW USER 'user@example.com'; // SHOW USER 'user@example.com' DATASET 'dataset_name'; // SHOW USER 'user@example.com' SUMMARY; @@ -2596,6 +2700,9 @@ func (p *Parser) parseAdminListUserCommand() (*Command, error) { case TokenFiles: p.nextToken() cmd = NewCommand("admin_list_user_files_command") + case TokenKeys: + p.nextToken() + cmd = NewCommand("admin_list_user_keys_command") default: return nil, fmt.Errorf("expected INGESTION or DATASETS or AGENTS or CHATS or SEARCHES or MODELS or FILES after USER") } diff --git a/internal/cli/cli_http.go b/internal/cli/cli_http.go index e0c763cdfc..c12b1f3228 100644 --- a/internal/cli/cli_http.go +++ b/internal/cli/cli_http.go @@ -51,36 +51,48 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.GrantAdmin(cmd) case "revoke_admin": return c.RevokeAdmin(cmd) - case "create_user": - return c.CreateUser(cmd) + case "admin_create_user_command": + return c.AdminCreateUserCommand(cmd) + case "admin_create_user_api_key_command": + return c.AdminCreateUserAPIKeyCommand(cmd) + case "admin_create_role_command": + return c.AdminCreateRoleCommand(cmd) case "activate_user": return c.ActivateUser(cmd) case "alter_user": return c.AlterUserPassword(cmd) - case "drop_user": - return c.DropUser(cmd) + case "admin_drop_user_command": + return c.AdminDropUserCommand(cmd) + case "admin_drop_user_api_key_command": + return c.AdminDropUserAPIKeyCommand(cmd) case "show_service": return c.ShowService(cmd) - case "show_version": - return c.ShowAdminVersion(cmd) + case "admin_show_version_command": + return c.AdminShowVersionCommand(cmd) case "show_current": return c.ShowCommonCurrent(cmd) case "list_variables": return c.ListVariables(cmd) case "show_variable": return c.ShowVariable(cmd) + case "admin_set_license_command": + return c.AdminSetLicenseCommand(cmd) + case "admin_set_license_config_command": + return c.AdminSetLicenseConfigCommand(cmd) case "set_variable": return c.SetVariable(cmd) case "list_user_datasets": return c.ListUserDatasets(cmd) case "list_agents": return c.ListAgents(cmd) + case "admin_list_resources_command": + return c.AdminListResourcesCommand(cmd) + case "admin_list_roles_command": + return c.AdminListRolesCommand(cmd) case "generate_token": return c.GenerateAdminToken(cmd) case "list_tokens": return c.ListAdminTokens(cmd) - case "drop_token": - return c.DropAdminToken(cmd) case "list_available_providers": return c.ListAvailableProviders(cmd) case "show_provider": @@ -119,6 +131,12 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.UserShowMessageQueueCommand(cmd) case "admin_remove_service_command": return c.AdminRemoveServiceCommand(cmd) + case "admin_check_license_command": + return c.AdminCheckLicenseCommand(cmd) + case "admin_show_fingerprint_command": + return c.AdminShowFingerprintCommand(cmd) + case "admin_show_license_command": + return c.AdminShowLicenseCommand(cmd) case "admin_show_user_info_command": return c.AdminShowUserInfoCommand(cmd) case "admin_show_user_activity_command": @@ -175,6 +193,8 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.AdminListUserModelsCommand(cmd) case "admin_list_user_files_command": return c.AdminListUserFilesCommand(cmd) + case "admin_list_user_keys_command": + return c.AdminListUserKeysCommand(cmd) case "admin_stop_user_ingestion_tasks_command": return c.AdminStopUserIngestionTasksCommand(cmd) case "admin_remove_user_ingestion_tasks_command": diff --git a/internal/cli/lexer.go b/internal/cli/lexer.go index 07c03ab936..7bf930f67c 100644 --- a/internal/cli/lexer.go +++ b/internal/cli/lexer.go @@ -367,6 +367,10 @@ func (l *Lexer) lookupIdent(ident string) Token { return Token{Type: TokenRetrieve, Value: ident} case "CURRENT": return Token{Type: TokenCurrent, Value: ident} + case "FINGERPRINT": + return Token{Type: TokenFingerprint, Value: ident} + case "LICENSE": + return Token{Type: TokenLicense, Value: ident} case "VISION": return Token{Type: TokenVision, Value: ident} case "EMBEDDING": diff --git a/internal/cli/parser.go b/internal/cli/parser.go index 5ae6a377d1..b910f244da 100644 --- a/internal/cli/parser.go +++ b/internal/cli/parser.go @@ -92,6 +92,8 @@ func (p *Parser) parseAdminCommand() (*Command, error) { return p.parseAdminListCommand() case TokenShow: return p.parseAdminShowCommand() + case TokenCheck: + return p.parseAdminCheckCommand() case TokenCreate: return p.parseAdminCreateCommand() case TokenDrop: diff --git a/internal/cli/types.go b/internal/cli/types.go index ad38489903..fdd4a71294 100644 --- a/internal/cli/types.go +++ b/internal/cli/types.go @@ -104,6 +104,8 @@ const ( TokenSearch TokenRetrieve TokenCurrent + TokenFingerprint + TokenLicense TokenVision TokenEmbedding TokenRerank diff --git a/internal/cli/user_parser.go b/internal/cli/user_parser.go index 915857b541..9b7e55ab13 100644 --- a/internal/cli/user_parser.go +++ b/internal/cli/user_parser.go @@ -579,7 +579,7 @@ func (p *Parser) parseCreateCommand() (*Command, error) { switch p.curToken.Type { case TokenUser: - return p.parseCreateUser() + return p.parseAdminCreateUserCommand() case TokenRole: return p.parseCreateRole() case TokenModel: @@ -697,32 +697,6 @@ func (p *Parser) parseCreateMetadataStore() (*Command, error) { return NewCommand("create_metadata_store"), nil } -func (p *Parser) parseCreateUser() (*Command, error) { - p.nextToken() // consume USER - userName, err := p.parseQuotedString() - if err != nil { - return nil, err - } - - p.nextToken() - password, err := p.parseQuotedString() - if err != nil { - return nil, err - } - - cmd := NewCommand("create_user") - cmd.Params["user_name"] = userName - cmd.Params["password"] = password - cmd.Params["role"] = "user" - - p.nextToken() - // Semicolon is optional for UNSET TOKEN - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return cmd, nil -} - func (p *Parser) parseCreateRole() (*Command, error) { p.nextToken() // consume ROLE roleName, err := p.parseIdentifier() diff --git a/internal/common/format.go b/internal/common/format.go index b1c1835654..095d1747dc 100644 --- a/internal/common/format.go +++ b/internal/common/format.go @@ -17,6 +17,7 @@ package common import ( + "encoding/base64" "fmt" "regexp" "strings" @@ -72,3 +73,15 @@ func ExtractCompositeName(modelName string) (string, string, string, error) { } return parts[0], parts[1], parts[2], nil } + +func EncodeEmail(email string) string { + return base64.StdEncoding.EncodeToString([]byte(email)) +} + +func DecodeEmail(encoded string) (string, error) { + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return "", err + } + return string(decoded), nil +}