Go CLI: refactor (#16299)

```
RAGFlow(api/default)> list dataset 'e93ab2c04ad111f1b17438a74640adcc' documents;
Total: 1

RAGFlow(api/default)> list datasets;


RAGFlow(api/default)> list chats;
Total: 2

RAGFlow(api/default)> list agents;
Total: 1

RAGFlow(api/default)> list searches;
Total: 1

RAGFlow(api/default)> list keys;
+----------------------------------+---------------+----------------------------------+-----------------------------------------------------+---------------+
| beta                             | create_time   | tenant_id                        | token                                               | update_time   |
+----------------------------------+---------------+----------------------------------+-----------------------------------------------------+---------------+
| GKsLEdSUkl76gJz1k_4fJpSQRIlWsiki | 1782285917523 | 2ba4881420fa11f19e9c38a74640adcc | ragflow-JgnarFSCUiV99oOvvMDei7ZzZg1cVlqGd1AMHrHeKE4 | 1782285917523 |
+----------------------------------+---------------+----------------------------------+-----------------------------------------------------+---------------+
RAGFlow(api/default)> create key;
SUCCESS

RAGFlow(api/default)> drop key 'ragflow-aA4R7AuUD158yh2LDh7IDBiqwOKFDKeTwUSQSLVdPdM';
SUCCESS
```

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
Jin Hai
2026-06-24 16:50:40 +08:00
committed by GitHub
parent a8651e7f83
commit 9624f70b22
13 changed files with 516 additions and 302 deletions

View File

@@ -24,7 +24,7 @@ import (
// region AUTH commands
func (p *Parser) parseAdminLoginUser() (*Command, error) {
cmd := NewCommand("login_user")
cmd := NewCommand("admin_login_user")
p.nextToken() // consume LOGIN
if p.curToken.Type != TokenAdmin {
@@ -60,7 +60,7 @@ func (p *Parser) parseAdminLoginUser() (*Command, error) {
}
func (p *Parser) parseAdminLogout() (*Command, error) {
cmd := NewCommand("logout")
cmd := NewCommand("admin_logout")
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
@@ -70,7 +70,7 @@ func (p *Parser) parseAdminLogout() (*Command, error) {
}
func (p *Parser) parseAdminPingServer() (*Command, error) {
cmd := NewCommand("ping_server")
cmd := NewCommand("admin_ping_server")
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
@@ -1837,35 +1837,6 @@ func (p *Parser) parseAdminBenchmarkCommand() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseAdminUserStatement() (*Command, error) {
switch p.curToken.Type {
case TokenPing:
return p.parsePingServer()
case TokenShow:
return p.parseShowCommand()
case TokenCreate:
return p.parseCreateCommand()
case TokenDrop:
return p.parseDropCommand()
case TokenSet:
return p.parseSetCommand()
case TokenUnset:
return p.parseUnsetCommand()
case TokenReset:
return p.parseResetCommand()
case TokenList:
return p.parseListCommand()
case TokenParse:
return p.parseParseCommand()
case TokenImport:
return p.parseImportCommand()
case TokenRetrieve:
return p.parseRetrieveCommand()
default:
return nil, fmt.Errorf("invalid user statement: %s", p.curToken.Value)
}
}
func (p *Parser) parseAdminStartService() (*Command, error) {
p.nextToken() // consume START

View File

@@ -237,7 +237,7 @@ func (c *CLI) executeBenchmarkSilent(cmd *Command, iterations int) []*Response {
resp, err = httpClient.Request("GET", "/system/ping", "web", nil, nil)
case "list_user_datasets":
resp, err = httpClient.Request("POST", "/kb/list", "web", nil, nil)
case "list_datasets":
case "api_list_datasets":
userName, _ := cmd.Params["user_name"].(string)
resp, err = httpClient.Request("GET", fmt.Sprintf("/admin/users/%s/datasets", userName), "admin", nil, nil)
case "search_on_datasets":
@@ -275,7 +275,7 @@ func isSuccess(resp *Response, commandType string) bool {
switch commandType {
case "ping":
return resp.StatusCode == 200 && string(resp.Body) == "pong"
case "list_user_datasets", "list_datasets", "search_on_datasets":
case "list_user_datasets", "api_list_datasets", "search_on_datasets":
// Check status code and JSON response code for dataset commands
if resp.StatusCode != 200 {
return false

View File

@@ -827,25 +827,6 @@ func (c *CLI) RunSingleCommand(command *string) error {
return nil
}
// VerifyAuth verifies authentication if needed
func (c *CLI) NewVerifyAuth(username, password *string) error {
// Otherwise, use username/password authentication
if username == nil {
return fmt.Errorf("username is required")
}
if password == nil {
return fmt.Errorf("password is required")
}
// Create login command with username and password
cmd := NewCommand("login_user")
cmd.Params["email"] = *username
cmd.Params["password"] = *password
_, err := c.ExecuteCommand(cmd)
return err
}
// VerifyAuth verifies authentication if needed
func (c *CLI) VerifyAuth(username, password string) error {
// Otherwise, use username/password authentication
@@ -858,10 +839,11 @@ func (c *CLI) VerifyAuth(username, password string) error {
}
// Create login command with username and password
cmd := NewCommand("login_user")
cmd := NewCommand("login_user_on_startup")
cmd.Params["email"] = username
cmd.Params["password"] = password
_, err := c.ExecuteCommand(cmd)
_, err := c.LoginUserByCommand(cmd)
return err
}

View File

@@ -37,11 +37,11 @@ func (c *CLI) ExecuteCommand(cmd *Command) (ResponseIf, error) {
func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
switch cmd.Type {
case "login_user":
case "admin_login_user":
return c.LoginUserByCommand(cmd)
case "logout":
case "admin_logout":
return c.Logout()
case "ping_server":
case "admin_ping_server":
return c.PingByCommand(cmd)
case "benchmark":
return c.RunBenchmark(cmd)
@@ -284,23 +284,31 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
}
func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
switch cmd.Type {
case "register_user":
case "api_register_user":
return c.RegisterUser(cmd)
case "login_user":
case "api_login_user":
return c.LoginUserByCommand(cmd)
case "logout":
case "api_logout":
return c.Logout()
case "ping_server":
case "api_ping_server":
return c.PingByCommand(cmd)
// Configuration commands
case "list_configs":
case "api_list_configs":
return c.ListConfigs(cmd)
case "set_log_level":
return c.SetLogLevel(cmd)
case "benchmark":
return c.RunBenchmark(cmd)
case "list_datasets":
return c.ListDatasets(cmd)
case "api_list_datasets":
return c.APIListDatasetsCommand(cmd)
case "api_list_dataset_documents":
return c.APIListDatasetDocumentsCommand(cmd)
case "api_list_agents":
return c.APIListAgentsCommand(cmd)
case "api_list_chats":
return c.APIListChatsCommand(cmd)
case "api_list_searches":
return c.APIListSearchesCommand(cmd)
case "list_dataset_documents":
return c.ListDatasetDocumentUserCommand(cmd)
case "search_on_datasets":
@@ -308,12 +316,12 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
case "search_help":
printSearchHelp()
return nil, nil
case "create_token":
return c.CreateToken(cmd)
case "list_tokens":
return c.ListTokens(cmd)
case "drop_token":
return c.DropToken(cmd)
case "api_create_api_key":
return c.APICreateAPIKeyCommand(cmd)
case "api_list_api_keys":
return c.APIListAPIKeysCommand(cmd)
case "api_delete_api_key":
return c.APIDeleteAPIKeyCommand(cmd)
case "set_token":
return c.SetToken(cmd)
case "show_token":

View File

@@ -143,13 +143,13 @@ func (p *Parser) parseUserCommand() (*Command, error) {
switch p.curToken.Type {
case TokenLogin:
return p.parseLoginUser()
return p.parseAPILoginUser()
case TokenLogout:
return p.parseLogout()
return p.parseAPILogout()
case TokenPing:
return p.parsePingServer()
return p.parseAPIPingServer()
case TokenList:
return p.parseListCommand()
return p.parseAPIListCommands()
case TokenShow:
return p.parseShowCommand()
case TokenCreate:
@@ -181,7 +181,7 @@ func (p *Parser) parseUserCommand() (*Command, error) {
case TokenBenchmark:
return p.parseBenchmarkCommand()
case TokenRegister:
return p.parseRegisterCommand()
return p.parseAPIRegisterCommand()
case TokenEnable:
return p.parseEnableCommand()
case TokenDisable:

View File

@@ -180,6 +180,114 @@ func (r *ListDocumentsResponse) PrintOut() {
}
}
type ListAgentsResponse struct {
Code int `json:"code"`
Data map[string]interface{} `json:"data"`
Message string `json:"message"`
Duration float64
OutputFormat OutputFormat
}
func (r *ListAgentsResponse) Type() string {
return "list_agents"
}
func (r *ListAgentsResponse) TimeCost() float64 {
return r.Duration
}
func (r *ListAgentsResponse) SetOutputFormat(format OutputFormat) {
r.OutputFormat = format
}
func (r *ListAgentsResponse) PrintOut() {
if r.Code == 0 {
total := r.Data["total"].(float64)
fmt.Printf("Total: %0.0f\n", total)
docs := r.Data["canvas"].([]interface{})
table := make([]map[string]interface{}, 0)
for _, doc := range docs {
table = append(table, doc.(map[string]interface{}))
}
PrintTableSimpleByFormat(table, r.OutputFormat)
} else {
fmt.Println("ERROR")
fmt.Printf("%d, %s\n", r.Code, r.Message)
}
}
type ListChatsResponse struct {
Code int `json:"code"`
Data map[string]interface{} `json:"data"`
Message string `json:"message"`
Duration float64
OutputFormat OutputFormat
}
func (r *ListChatsResponse) Type() string {
return "list_chats"
}
func (r *ListChatsResponse) TimeCost() float64 {
return r.Duration
}
func (r *ListChatsResponse) SetOutputFormat(format OutputFormat) {
r.OutputFormat = format
}
func (r *ListChatsResponse) PrintOut() {
if r.Code == 0 {
total := r.Data["total"].(float64)
fmt.Printf("Total: %0.0f\n", total)
docs := r.Data["chats"].([]interface{})
table := make([]map[string]interface{}, 0)
for _, doc := range docs {
table = append(table, doc.(map[string]interface{}))
}
PrintTableSimpleByFormat(table, r.OutputFormat)
} else {
fmt.Println("ERROR")
fmt.Printf("%d, %s\n", r.Code, r.Message)
}
}
type ListSearchesResponse struct {
Code int `json:"code"`
Data map[string]interface{} `json:"data"`
Message string `json:"message"`
Duration float64
OutputFormat OutputFormat
}
func (r *ListSearchesResponse) Type() string {
return "list_searches"
}
func (r *ListSearchesResponse) TimeCost() float64 {
return r.Duration
}
func (r *ListSearchesResponse) SetOutputFormat(format OutputFormat) {
r.OutputFormat = format
}
func (r *ListSearchesResponse) PrintOut() {
if r.Code == 0 {
total := r.Data["total"].(float64)
fmt.Printf("Total: %0.0f\n", total)
docs := r.Data["search_apps"].([]interface{})
table := make([]map[string]interface{}, 0)
for _, doc := range docs {
table = append(table, doc.(map[string]interface{}))
}
PrintTableSimpleByFormat(table, r.OutputFormat)
} else {
fmt.Println("ERROR")
fmt.Printf("%d, %s\n", r.Code, r.Message)
}
}
type ChunkResponse struct {
Code int `json:"code"`
Data map[string]interface{} `json:"data"`

View File

@@ -329,9 +329,9 @@ func (c *CLI) RegisterUser(cmd *Command) (ResponseIf, error) {
return &result, nil
}
// ListDatasets lists datasets for current user (user mode)
// APIListDatasetsCommand lists datasets for current user (user mode)
// Returns (result_map, error) - result_map is non-nil for benchmark mode
func (c *CLI) ListDatasets(cmd *Command) (ResponseIf, error) {
func (c *CLI) APIListDatasetsCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
@@ -386,6 +386,186 @@ func (c *CLI) ListDatasets(cmd *Command) (ResponseIf, error) {
return &result, nil
}
func (c *CLI) APIListDatasetDocumentsCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
httpClient := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer]
// Determine auth kind based on whether API token is being used
if httpClient.LoginToken == nil && !httpClient.useAPIToken {
return nil, fmt.Errorf("no authorization")
}
datasetID, ok := cmd.Params["dataset_id"].(string)
if !ok {
return nil, fmt.Errorf("no dataset id")
}
page := 1
pageSize := 10
keywords := ""
returnEmptyMetadata := "true"
url := fmt.Sprintf("/datasets/%s/documents?page=%d&page_size=%d&keywords=%s&return_empty_metadata=%s", datasetID, page, pageSize, keywords, returnEmptyMetadata)
// Normal mode
resp, err := httpClient.Request("GET", url, "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list documents: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list documents: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result ListDocumentsResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("list documents failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// APIListAgentsCommand lists agents
func (c *CLI) APIListAgentsCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
httpClient := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer]
// Determine auth kind based on whether API token is being used
if httpClient.LoginToken == nil && !c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].useAPIToken {
return nil, fmt.Errorf("no authorization")
}
authKind := "web"
if httpClient.useAPIToken {
authKind = "api"
}
if httpClient.LoginToken != nil {
authKind = "web"
}
// Normal mode
resp, err := httpClient.Request("GET", "/agents", authKind, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list agents: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list agents: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result ListAgentsResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("list agents failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// APIListChatsCommand lists chats
func (c *CLI) APIListChatsCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
httpClient := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer]
// Determine auth kind based on whether API token is being used
if httpClient.LoginToken == nil && !c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].useAPIToken {
return nil, fmt.Errorf("no authorization")
}
authKind := "web"
if httpClient.useAPIToken {
authKind = "api"
}
if httpClient.LoginToken != nil {
authKind = "web"
}
// Normal mode
resp, err := httpClient.Request("GET", "/chats", authKind, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list chats: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list chats: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result ListChatsResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("list chats failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// APIListSearchesCommand lists searches
func (c *CLI) APIListSearchesCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
httpClient := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer]
// Determine auth kind based on whether API token is being used
if httpClient.LoginToken == nil && !c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].useAPIToken {
return nil, fmt.Errorf("no authorization")
}
authKind := "web"
if httpClient.useAPIToken {
authKind = "api"
}
if httpClient.LoginToken != nil {
authKind = "web"
}
// Normal mode
resp, err := httpClient.Request("GET", "/searches", authKind, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list searches: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list searches: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result ListSearchesResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("list searches failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// ListDatasetDocumentUserCommand lists dataset documents
func (c *CLI) ListDatasetDocumentUserCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
@@ -734,25 +914,25 @@ func (c *CLI) SearchOnDatasets(cmd *Command) (ResponseIf, error) {
return nil, nil
}
// CreateToken creates a new API token
func (c *CLI) CreateToken(cmd *Command) (ResponseIf, error) {
// APICreateAPIKeyCommand creates a new API key
func (c *CLI) APICreateAPIKeyCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
httpClient := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer]
resp, err := httpClient.Request("POST", "/system/tokens", "web", nil, nil)
resp, err := httpClient.Request("POST", "/system/keys", "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to create token: %w", err)
return nil, fmt.Errorf("failed to create key: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to create token: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
return nil, fmt.Errorf("failed to create key: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var createResult CommonDataResponse
if err = json.Unmarshal(resp.Body, &createResult); err != nil {
return nil, fmt.Errorf("create token failed: invalid JSON (%w)", err)
return nil, fmt.Errorf("create key failed: invalid JSON (%w)", err)
}
if createResult.Code != 0 {
@@ -761,30 +941,30 @@ func (c *CLI) CreateToken(cmd *Command) (ResponseIf, error) {
var result SimpleResponse
result.Code = 0
result.Message = "Token created successfully"
result.Message = "API Key created successfully"
result.Duration = resp.Duration
return &result, nil
}
// ListTokens lists all API tokens for the current user
func (c *CLI) ListTokens(cmd *Command) (ResponseIf, error) {
// APIListAPIKeysCommand lists all API keys for the current user
func (c *CLI) APIListAPIKeysCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
httpClient := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer]
resp, err := httpClient.Request("GET", "/system/tokens", "web", nil, nil)
resp, err := httpClient.Request("GET", "/system/keys", "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list tokens: %w", err)
return nil, fmt.Errorf("failed to list keys: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list tokens: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
return nil, fmt.Errorf("failed to list keys: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result CommonResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("list tokens failed: invalid JSON (%w)", err)
return nil, fmt.Errorf("list keys failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
@@ -794,29 +974,29 @@ func (c *CLI) ListTokens(cmd *Command) (ResponseIf, error) {
return &result, nil
}
// DropToken deletes an API token
func (c *CLI) DropToken(cmd *Command) (ResponseIf, error) {
// APIDeleteAPIKeyCommand deletes an API key
func (c *CLI) APIDeleteAPIKeyCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
token, ok := cmd.Params["token"].(string)
apiKey, ok := cmd.Params["api_key"].(string)
if !ok {
return nil, fmt.Errorf("token not provided")
return nil, fmt.Errorf("key not provided")
}
resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("DELETE", fmt.Sprintf("/system/tokens/%s", token), "web", nil, nil)
resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("DELETE", fmt.Sprintf("/system/keys/%s", apiKey), "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to drop token: %w", err)
return nil, fmt.Errorf("failed to delete key: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to drop token: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
return nil, fmt.Errorf("failed to delete key: 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)
return nil, fmt.Errorf("delete key failed: invalid JSON (%w)", err)
}
if result.Code != 0 {

View File

@@ -17,8 +17,8 @@ func tokenTypeDescription(t int, tok Token) string {
}
// Command parsers
func (p *Parser) parseLogout() (*Command, error) {
cmd := NewCommand("logout")
func (p *Parser) parseAPILogout() (*Command, error) {
cmd := NewCommand("api_logout")
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
@@ -27,8 +27,8 @@ func (p *Parser) parseLogout() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseLoginUser() (*Command, error) {
cmd := NewCommand("login_user")
func (p *Parser) parseAPILoginUser() (*Command, error) {
cmd := NewCommand("api_login_user")
p.nextToken() // consume LOGIN
if p.curToken.Type != TokenUser {
@@ -46,7 +46,8 @@ func (p *Parser) parseLoginUser() (*Command, error) {
// Optional: PASSWORD 'password'
if p.curToken.Type == TokenPassword {
p.nextToken()
password, err := p.parseQuotedString()
var password string
password, err = p.parseQuotedString()
if err != nil {
return nil, err
}
@@ -62,8 +63,8 @@ func (p *Parser) parseLoginUser() (*Command, error) {
return cmd, nil
}
func (p *Parser) parsePingServer() (*Command, error) {
cmd := NewCommand("ping_server")
func (p *Parser) parseAPIPingServer() (*Command, error) {
cmd := NewCommand("api_ping_server")
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
@@ -72,8 +73,8 @@ func (p *Parser) parsePingServer() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseRegisterCommand() (*Command, error) {
cmd := NewCommand("register_user")
func (p *Parser) parseAPIRegisterCommand() (*Command, error) {
cmd := NewCommand("api_register_user")
if err := p.expectPeek(TokenUser); err != nil {
return nil, err
@@ -119,42 +120,28 @@ func (p *Parser) parseRegisterCommand() (*Command, error) {
return cmd, nil
}
// LIST CONFIGS;
// LIST PROVIDER 'provider_name' MODELS;
// LIST PROVIDER 'provider_name' INSTANCE 'instance_name' MODELS
// LIST MODELS;
func (p *Parser) parseListCommand() (*Command, error) {
func (p *Parser) parseAPIListCommands() (*Command, error) {
p.nextToken() // consume LIST
switch p.curToken.Type {
case TokenVars:
p.nextToken()
// Semicolon is optional for SHOW TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("list_variables"), nil
case TokenConfigs:
p.nextToken()
// Semicolon is optional for SHOW TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("list_configs"), nil
case TokenEnvs:
p.nextToken()
// Semicolon is optional for SHOW TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("list_environments"), nil
return p.parseAPIListConfigs()
case TokenDatasets:
return p.parseListDatasets()
case TokenDocuments:
return p.parseListDatasetDocuments()
return p.parseAPIListDatasets()
case TokenDataset:
return p.parseAPIListDatasetDocuments()
case TokenAgents:
return p.parseListAgents()
case TokenTokens:
return p.parseListTokens()
return p.parseAPIListAgents()
case TokenChats:
return p.parseAPIListChats()
case TokenSearches:
return p.parseAPIListSearches()
case TokenKeys:
return p.parseAPIListAPIKeys()
case TokenModel:
return p.parseListModelProviders()
case TokenSupported:
@@ -171,13 +158,6 @@ func (p *Parser) parseListCommand() (*Command, error) {
return p.parseListDefaultModels()
case TokenAvailable:
return p.parseListAvailableProviders()
case TokenChats:
p.nextToken()
// Semicolon is optional for SHOW TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("list_user_chats"), nil
case TokenFiles:
return p.parseListFiles()
case TokenQuotedString:
@@ -189,8 +169,19 @@ func (p *Parser) parseListCommand() (*Command, error) {
}
}
func (p *Parser) parseListDatasets() (*Command, error) {
cmd := NewCommand("list_datasets")
// LIST CONFIGS;
func (p *Parser) parseAPIListConfigs() (*Command, error) {
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_configs"), nil
}
func (p *Parser) parseAPIListDatasets() (*Command, error) {
cmd := NewCommand("api_list_datasets")
p.nextToken() // consume DATASETS
// Semicolon is optional for UNSET TOKEN
@@ -200,13 +191,9 @@ func (p *Parser) parseListDatasets() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseListDatasetDocuments() (*Command, error) {
p.nextToken() // consume DOCUMENTS
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
// LIST DATASET 'dataset_name' DOCUMENTS;
func (p *Parser) parseAPIListDatasetDocuments() (*Command, error) {
p.nextToken() // consume DATASET
datasetID, err := p.parseQuotedString()
if err != nil {
@@ -214,17 +201,53 @@ func (p *Parser) parseListDatasetDocuments() (*Command, error) {
}
p.nextToken()
cmd := NewCommand("list_dataset_documents")
if p.curToken.Type != TokenDocuments {
return nil, fmt.Errorf("expected DOCUMENTS")
}
cmd := NewCommand("api_list_dataset_documents")
cmd.Params["dataset_id"] = datasetID
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIListAgents() (*Command, error) {
p.nextToken() // consume AGENTS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_agents"), nil
}
func (p *Parser) parseAPIListChats() (*Command, error) {
p.nextToken() // consume CHATS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_chats"), nil
}
func (p *Parser) parseAPIListSearches() (*Command, error) {
p.nextToken() // consume SEARCHES
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_searches"), nil
}
func (p *Parser) parseGetMetadata() (*Command, error) {
p.nextToken() // consume METADATA
@@ -273,38 +296,10 @@ func (p *Parser) parseGetMetadata() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseListAgents() (*Command, error) {
p.nextToken() // consume AGENTS
if p.curToken.Type == TokenSemicolon {
return NewCommand("list_user_agents"), nil
}
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("list_agents")
cmd.Params["user_name"] = userName
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseListTokens() (*Command, error) {
p.nextToken() // consume TOKENS
cmd := NewCommand("list_tokens")
// Semicolon is optional for UNSET TOKEN
func (p *Parser) parseAPIListAPIKeys() (*Command, error) {
p.nextToken() // consume KEYS
cmd := NewCommand("api_list_api_keys")
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
@@ -567,8 +562,8 @@ func (p *Parser) parseCreateCommand() (*Command, error) {
return p.parseCreateDataset()
case TokenChat:
return p.parseCreateChat()
case TokenToken:
return p.parseCreateToken()
case TokenKey:
return p.parseAPICreateKey()
case TokenChunkStore:
return p.parseCreateChunkStore()
case TokenMetadata:
@@ -596,15 +591,15 @@ func (p *Parser) parseAddCommand() (*Command, error) {
}
}
func (p *Parser) parseCreateToken() (*Command, error) {
p.nextToken() // consume TOKEN
func (p *Parser) parseAPICreateKey() (*Command, error) {
p.nextToken() // consume KEY
// Semicolon is optional for UNSET TOKEN
// Semicolon is optional for UNSET KEY
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("create_token"), nil
return NewCommand("api_create_api_key"), nil
}
// Internal CLI for GO
@@ -1213,8 +1208,8 @@ func (p *Parser) parseDropCommand() (*Command, error) {
return p.parseDropDataset()
case TokenChat:
return p.parseDropChat()
case TokenToken:
return p.parseDropToken()
case TokenKey:
return p.parseAPIDeleteAPIKey()
case TokenChunkStore:
return p.parseDropChunkStore()
case TokenMetadata:
@@ -1262,30 +1257,18 @@ func (p *Parser) parseRemoveCommand() (*Command, error) {
}
}
func (p *Parser) parseDropToken() (*Command, error) {
p.nextToken() // consume TOKEN
func (p *Parser) parseAPIDeleteAPIKey() (*Command, error) {
p.nextToken() // consume KEY
tokenValue, err := p.parseQuotedString()
apiKey, err := p.parseQuotedString()
if err != nil {
return nil, err
}
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("api_delete_api_key")
cmd.Params["api_key"] = apiKey
cmd := NewCommand("drop_token")
cmd.Params["token"] = tokenValue
cmd.Params["user_name"] = userName
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
@@ -3647,7 +3630,7 @@ func (p *Parser) parseBenchmarkCommand() (*Command, error) {
func (p *Parser) parseUserStatement() (*Command, error) {
switch p.curToken.Type {
case TokenPing:
return p.parsePingServer()
return p.parseAPIPingServer()
case TokenDelete:
return p.parseDeleteCommand()
case TokenShow:
@@ -3663,7 +3646,7 @@ func (p *Parser) parseUserStatement() (*Command, error) {
case TokenReset:
return p.parseResetCommand()
case TokenList:
return p.parseListCommand()
return p.parseAPIListCommands()
case TokenParse:
return p.parseParseCommand()
case TokenImport:

View File

@@ -26,16 +26,7 @@ import (
"github.com/gin-gonic/gin"
)
// ListTokens list all API tokens for the current user's tenant
// @Summary List API Tokens
// @Description List all API tokens for the current user's tenant
// @Tags system
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/system/tokens [get]
func (h *SystemHandler) ListTokens(c *gin.Context) {
func (h *SystemHandler) ListAPIKeys(c *gin.Context) {
// Get current user from context
user, exists := c.Get("user")
if !exists {
@@ -68,12 +59,12 @@ func (h *SystemHandler) ListTokens(c *gin.Context) {
tenantID := tenants[0].TenantID
// Get tokens for the tenant
tokens, err := h.systemService.ListAPITokens(tenantID)
// Get keys for the tenant
keys, err := h.systemService.ListAPIKeys(tenantID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "Failed to list tokens",
"message": "Failed to list keys",
})
return
}
@@ -81,21 +72,11 @@ func (h *SystemHandler) ListTokens(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "success",
"data": tokens,
"data": keys,
})
}
// CreateToken creates a new API token for the current user's tenant
// @Summary Create API Token
// @Description Generate a new API token for the current user's tenant
// @Tags system
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param name query string false "Name of the token"
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/system/tokens [post]
func (h *SystemHandler) CreateToken(c *gin.Context) {
func (h *SystemHandler) CreateKey(c *gin.Context) {
// Get current user from context
user, exists := c.Get("user")
if !exists {
@@ -129,7 +110,7 @@ func (h *SystemHandler) CreateToken(c *gin.Context) {
tenantID := tenants[0].TenantID
// Parse request
var req service.CreateAPITokenRequest
var req service.CreateAPIKeyRequest
if err := c.ShouldBind(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
@@ -138,12 +119,12 @@ func (h *SystemHandler) CreateToken(c *gin.Context) {
return
}
// Create token
token, err := h.systemService.CreateAPIToken(tenantID, &req)
// Create key
key, err := h.systemService.CreateAPIKey(tenantID, &req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "Failed to create token",
"message": "Failed to create key",
})
return
}
@@ -151,21 +132,11 @@ func (h *SystemHandler) CreateToken(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "success",
"data": token,
"data": key,
})
}
// DeleteToken deletes an API token
// @Summary Delete API Token
// @Description Remove an API token for the current user's tenant
// @Tags system
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param token path string true "The API token to remove"
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/system/tokens/{token} [delete]
func (h *SystemHandler) DeleteToken(c *gin.Context) {
func (h *SystemHandler) DeleteKey(c *gin.Context) {
// Get current user from context
user, exists := c.Get("user")
if !exists {
@@ -198,21 +169,21 @@ func (h *SystemHandler) DeleteToken(c *gin.Context) {
tenantID := tenants[0].TenantID
// Get token from path parameter
token := c.Param("token")
if token == "" {
// Get key from path parameter
key := c.Param("key")
if key == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "Token is required",
"message": "Key is required",
})
return
}
// Delete token
if err := h.systemService.DeleteAPIToken(tenantID, token); err != nil {
// Delete key
if err = h.systemService.DeleteAPIKey(tenantID, key); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "Failed to delete token",
"message": "Failed to delete key",
})
return
}

View File

@@ -489,7 +489,7 @@ func (h *DocumentHandler) ListDocuments(c *gin.Context) {
userID := c.GetString("user_id")
if !h.datasetService.Accessible(datasetID, userID) {
jsonError(c, common.CodeAuthenticationError, "No authorization.")
jsonError(c, common.CodeAuthenticationError, "No authorization to access the dataset.")
return
}

View File

@@ -554,11 +554,21 @@ func (r *Router) Setup(engine *gin.Engine) {
tokens := system.Group("/tokens")
{
// list tokens /api/v1/system/tokens GET
tokens.GET("", r.systemHandler.ListTokens)
tokens.GET("", r.systemHandler.ListAPIKeys)
// create token /api/v1/system/tokens POST
tokens.POST("", r.systemHandler.CreateToken)
// delete token /api/v1/system/tokens/:token DELETE
tokens.DELETE("/:token", r.systemHandler.DeleteToken)
tokens.POST("", r.systemHandler.CreateKey)
// delete token /api/v1/system/tokens/:key DELETE
tokens.DELETE("/:key", r.systemHandler.DeleteKey)
}
keys := system.Group("/keys")
{
// list keys /api/v1/system/keys GET
keys.GET("", r.systemHandler.ListAPIKeys)
// create key /api/v1/system/keys POST
keys.POST("", r.systemHandler.CreateKey)
// delete key /api/v1/system/keys/:key DELETE
keys.DELETE("/:key", r.systemHandler.DeleteKey)
}
}
}

View File

@@ -22,8 +22,8 @@ import (
"ragflow/internal/utility"
)
// TokenResponse token response
type TokenResponse struct {
// APIKeyResponse key response
type APIKeyResponse struct {
TenantID string `json:"tenant_id"`
Token string `json:"token"`
DialogID *string `json:"dialog_id,omitempty"`
@@ -33,83 +33,82 @@ type TokenResponse struct {
UpdateTime *int64 `json:"update_time,omitempty"`
}
// ListAPITokens list all API tokens for a tenant
func (s *SystemService) ListAPITokens(tenantID string) ([]*TokenResponse, error) {
// ListAPIKeys list all API keys for a tenant
func (s *SystemService) ListAPIKeys(tenantID string) ([]*APIKeyResponse, error) {
APITokenDAO := dao.NewAPITokenDAO()
tokens, err := APITokenDAO.GetByTenantID(tenantID)
keys, err := APITokenDAO.GetByTenantID(tenantID)
if err != nil {
return nil, err
}
responses := make([]*TokenResponse, len(tokens))
for i, token := range tokens {
beta := token.Beta
responses := make([]*APIKeyResponse, len(keys))
for i, key := range keys {
beta := key.Beta
if beta == nil || *beta == "" {
generatedBeta := utility.GenerateBetaAPIToken(utility.GenerateAPIToken())
if err := dao.DB.Model(&entity.APIToken{}).
Where("tenant_id = ? AND token = ?", tenantID, token.Token).
if err = dao.DB.Model(&entity.APIToken{}).
Where("tenant_id = ? AND token = ?", tenantID, key.Token).
Updates(map[string]interface{}{
"beta": generatedBeta,
}).Error; err != nil {
return nil, err
}
beta = &generatedBeta
token.Beta = beta
key.Beta = beta
}
responses[i] = &TokenResponse{
TenantID: token.TenantID,
Token: token.Token,
DialogID: token.DialogID,
Source: token.Source,
responses[i] = &APIKeyResponse{
TenantID: key.TenantID,
Token: key.Token,
DialogID: key.DialogID,
Source: key.Source,
Beta: beta,
CreateTime: token.CreateTime,
UpdateTime: token.UpdateTime,
CreateTime: key.CreateTime,
UpdateTime: key.UpdateTime,
}
}
return responses, nil
}
// CreateAPITokenRequest create token request
type CreateAPITokenRequest struct {
// CreateAPIKeyRequest create key request
type CreateAPIKeyRequest struct {
Name string `json:"name" form:"name"`
}
// CreateAPIToken creates a new API token for a tenant
func (s *SystemService) CreateAPIToken(tenantID string, req *CreateAPITokenRequest) (*TokenResponse, error) {
// CreateAPIKey creates a new API key for a tenant
func (s *SystemService) CreateAPIKey(tenantID string, req *CreateAPIKeyRequest) (*APIKeyResponse, error) {
APITokenDAO := dao.NewAPITokenDAO()
// Generate token and beta values
// token: "ragflow-" + secrets.token_urlsafe(32)
// Generate key and beta values
// key: "ragflow-" + secrets.token_urlsafe(32)
APIToken := utility.GenerateAPIToken()
// beta: generate_confirmation_token().replace("ragflow-", "")[:32]
betaAPIKey := utility.GenerateBetaAPIToken(utility.GenerateAPIToken())
APITokenData := &entity.APIToken{
APIKeyData := &entity.APIToken{
TenantID: tenantID,
Token: APIToken,
Beta: &betaAPIKey,
}
if err := APITokenDAO.Create(APITokenData); err != nil {
if err := APITokenDAO.Create(APIKeyData); err != nil {
return nil, err
}
return &TokenResponse{
TenantID: APITokenData.TenantID,
Token: APITokenData.Token,
DialogID: APITokenData.DialogID,
Source: APITokenData.Source,
Beta: APITokenData.Beta,
CreateTime: APITokenData.CreateTime,
UpdateTime: APITokenData.UpdateTime,
return &APIKeyResponse{
TenantID: APIKeyData.TenantID,
Token: APIKeyData.Token,
DialogID: APIKeyData.DialogID,
Source: APIKeyData.Source,
Beta: APIKeyData.Beta,
CreateTime: APIKeyData.CreateTime,
UpdateTime: APIKeyData.UpdateTime,
}, nil
}
// DeleteAPIToken deletes an API token by tenant ID and token value
func (s *SystemService) DeleteAPIToken(tenantID, token string) error {
func (s *SystemService) DeleteAPIKey(tenantID, key string) error {
APITokenDAO := dao.NewAPITokenDAO()
_, err := APITokenDAO.DeleteByTenantIDAndToken(tenantID, token)
_, err := APITokenDAO.DeleteByTenantIDAndToken(tenantID, key)
return err
}

View File

@@ -54,6 +54,7 @@ type ChatWithKBNames struct {
// ListChatsResponse list chats response
type ListChatsResponse struct {
Total int64 `json:"total"`
Chats []*ChatWithKBNames `json:"chats"`
}
@@ -107,6 +108,7 @@ func (s *ChatService) ListChats(userID, status, keywords string, page, pageSize
}
return &ListChatsResponse{
Total: total,
Chats: chatsWithKBNames,
}, nil
}