Go CLI: add list user commands framework (#16201)

This commit is contained in:
Jin Hai
2026-06-19 15:09:54 +08:00
committed by GitHub
parent 7214a23614
commit 11499f7bb3
9 changed files with 478 additions and 37 deletions

View File

@@ -395,6 +395,108 @@ func (h *Handler) ShowUserPermission(c *gin.Context) {
success(c, permissions, "")
}
// ListUserDatasets handle show user datasets
func (h *Handler) ListUserDatasets(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
datasets, err := h.service.ListUserDatasets(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, datasets, "")
}
// ListUserAgents handle show user agents
func (h *Handler) ListUserAgents(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
agents, err := h.service.ListUserAgents(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, agents, "")
}
// ListUserChats handle show user chats
func (h *Handler) ListUserChats(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
chats, err := h.service.ListUserChats(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, chats, "")
}
// ListUserSearches handle show user searches
func (h *Handler) ListUserSearches(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
searches, err := h.service.ListUserSearches(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, searches, "")
}
// ListUserModels handle show user models
func (h *Handler) ListUserModels(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
models, err := h.service.ListUserModels(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, models, "")
}
// ListUserFiles handle show user files
func (h *Handler) ListUserFiles(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
files, err := h.service.ListUserFiles(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, files, "")
}
// ShowUsersSummary handle show users summary
func (h *Handler) ShowUsersSummary(c *gin.Context) {
usersSummary, err := h.service.ShowUsersSummary()

View File

@@ -222,6 +222,132 @@ func (s *Service) ShowUserPermission(email string) (map[string]interface{}, erro
return result, nil
}
// ListUserDatasets show user datasets for enterprise edition
func (s *Service) ListUserDatasets(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": "list_user_datasets",
"email": user.Email,
"nickname": user.Nickname,
"error": "'list user datasets' is implemented in enterprise edition",
},
}
return result, nil
}
// ListUserAgents show user agents for enterprise edition
func (s *Service) ListUserAgents(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": "list_user_agents",
"email": user.Email,
"nickname": user.Nickname,
"error": "'list user agents' is implemented in enterprise edition",
},
}
return result, nil
}
// ListUserChats show user chats for enterprise edition
func (s *Service) ListUserChats(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": "list_user_chats",
"email": user.Email,
"nickname": user.Nickname,
"error": "'list user chats' is implemented in enterprise edition",
},
}
return result, nil
}
// ListUserSearches show user searches for enterprise edition
func (s *Service) ListUserSearches(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": "list_user_searches",
"email": user.Email,
"nickname": user.Nickname,
"error": "'list user searches' is implemented in enterprise edition",
},
}
return result, nil
}
// ListUserModels show user models for enterprise edition
func (s *Service) ListUserModels(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": "list_user_models",
"email": user.Email,
"nickname": user.Nickname,
"error": "'list user models' is implemented in enterprise edition",
},
}
return result, nil
}
// ListUserFiles show user files for enterprise edition
func (s *Service) ListUserFiles(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": "list_user_files",
"email": user.Email,
"nickname": user.Nickname,
"error": "'list user files' 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{}{

View File

@@ -443,40 +443,6 @@ func (h *Handler) RevokeAdmin(c *gin.Context) {
successNoData(c, "Admin role revoked")
}
// GetUserDatasets handle get user datasets
func (h *Handler) GetUserDatasets(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
datasets, err := h.service.GetUserDatasets(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, datasets, "")
}
// GetUserAgents handle get user agents
func (h *Handler) GetUserAgents(c *gin.Context) {
username := c.Param("username")
if username == "" {
errorResponse(c, "Username is required", 400)
return
}
agents, err := h.service.GetUserAgents(username)
if err != nil {
errorResponse(c, err.Error(), 500)
return
}
success(c, agents, "")
}
// ListUserAPITokens handle get user API keys
func (h *Handler) ListUserAPITokens(c *gin.Context) {
username := c.Param("username")

View File

@@ -68,8 +68,6 @@ func (r *Router) Setup(engine *gin.Engine) {
protected.PUT("/users/:username/activate", r.handler.UpdateUserActivateStatus)
protected.PUT("/users/:username/admin", r.handler.GrantAdmin)
protected.DELETE("/users/:username/admin", r.handler.RevokeAdmin)
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)
@@ -80,6 +78,12 @@ func (r *Router) Setup(engine *gin.Engine) {
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/:username/datasets", r.handler.ListUserDatasets)
protected.GET("/users/:username/agents", r.handler.ListUserAgents)
protected.GET("/users/:username/chats", r.handler.ListUserChats)
protected.GET("/users/:username/searches", r.handler.ListUserSearches)
protected.GET("/users/:username/models", r.handler.ListUserModels)
protected.GET("/users/:username/files", r.handler.ListUserFiles)
protected.GET("/users/summary", r.handler.ShowUsersSummary)
protected.GET("/users/activity", r.handler.ShowUsersActivity)
protected.GET("/users/reports", r.handler.ListUsersReports)

View File

@@ -2393,6 +2393,211 @@ func (c *CLI) AdminListUserIngestionTasksCommand(cmd *Command) (ResponseIf, erro
return &result, nil
}
func (c *CLI) AdminListUserDatasetsCommand(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/datasets", userName)
resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list user %s datasets: %w", userName, err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list user %s datasets: 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 datasets 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) AdminListUserAgentsCommand(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/agents", userName)
resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list user %s agents: %w", userName, err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list user %s agents: 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 agents 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) AdminListUserChatsCommand(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/chats", userName)
resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list user %s chats: %w", userName, err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list user %s chats: 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 chats 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) AdminListUserSearchesCommand(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/searches", userName)
resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list user %s searches: %w", userName, err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list user %s searches: 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 searches 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) AdminListUserModelsCommand(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/models", userName)
resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list user %s models: %w", userName, err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list user %s models: 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 models 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) AdminListUserFilesCommand(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/files", userName)
resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list user %s files: %w", userName, err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list user %s files: 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 files 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 {

View File

@@ -2578,8 +2578,31 @@ func (p *Parser) parseAdminListUserCommand() (*Command, error) {
cmd.Params["status"] = status
p.nextToken()
}
case TokenDatasets:
p.nextToken()
cmd = NewCommand("admin_list_user_datasets_command")
case TokenAgents:
p.nextToken()
cmd = NewCommand("admin_list_user_agents_command")
case TokenChats:
p.nextToken()
cmd = NewCommand("admin_list_user_chats_command")
case TokenSearches:
p.nextToken()
cmd = NewCommand("admin_list_user_searches_command")
case TokenModels:
p.nextToken()
cmd = NewCommand("admin_list_user_models_command")
case TokenFiles:
p.nextToken()
cmd = NewCommand("admin_list_user_files_command")
default:
return nil, fmt.Errorf("expected INGESTION after USER")
return nil, fmt.Errorf("expected INGESTION or DATASETS or AGENTS or CHATS or SEARCHES or MODELS or FILES after USER")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd.Params["user_name"] = userName

View File

@@ -163,6 +163,18 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
return c.AdminPurgeUsersCommand(cmd)
case "admin_list_user_ingestion_tasks_command":
return c.AdminListUserIngestionTasksCommand(cmd)
case "admin_list_user_datasets_command":
return c.AdminListUserDatasetsCommand(cmd)
case "admin_list_user_agents_command":
return c.AdminListUserAgentsCommand(cmd)
case "admin_list_user_chats_command":
return c.AdminListUserChatsCommand(cmd)
case "admin_list_user_searches_command":
return c.AdminListUserSearchesCommand(cmd)
case "admin_list_user_models_command":
return c.AdminListUserModelsCommand(cmd)
case "admin_list_user_files_command":
return c.AdminListUserFilesCommand(cmd)
case "admin_stop_user_ingestion_tasks_command":
return c.AdminStopUserIngestionTasksCommand(cmd)
case "admin_remove_user_ingestion_tasks_command":

View File

@@ -243,6 +243,8 @@ func (l *Lexer) lookupIdent(ident string) Token {
return Token{Type: TokenOf, Value: ident}
case "AGENTS":
return Token{Type: TokenAgents, Value: ident}
case "SEARCHES":
return Token{Type: TokenSearches, Value: ident}
case "ROLE":
return Token{Type: TokenRole, Value: ident}
case "ROLES":

View File

@@ -54,6 +54,7 @@ const (
TokenChunkStore
TokenOf
TokenAgents
TokenSearches
TokenRole
TokenRoles
TokenDescription