mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 15:31:05 +08:00
Go CLI: add response output (#16263)
### What problem does this PR solve? Go CLI: add response output
This commit is contained in:
@@ -2042,7 +2042,7 @@ func (c *CLI) AdminShowUserStorageCommand(cmd *Command) (ResponseIf, error) {
|
||||
}
|
||||
|
||||
encodedUserName := common.EncodeEmail(email)
|
||||
apiURL := fmt.Sprintf("/admin/users/%s/admin", encodedUserName)
|
||||
apiURL := fmt.Sprintf("/admin/users/%s/storage", encodedUserName)
|
||||
|
||||
resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil)
|
||||
if err != nil {
|
||||
@@ -2053,7 +2053,7 @@ func (c *CLI) AdminShowUserStorageCommand(cmd *Command) (ResponseIf, error) {
|
||||
return nil, fmt.Errorf("failed to get user storage: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
|
||||
}
|
||||
|
||||
var result CommonDataResponse
|
||||
var result UserStorageResponse
|
||||
if err = json.Unmarshal(resp.Body, &result); err != nil {
|
||||
return nil, fmt.Errorf("get user storage failed: invalid JSON (%w)", err)
|
||||
}
|
||||
@@ -2089,9 +2089,9 @@ func (c *CLI) AdminShowUserQuotaCommand(cmd *Command) (ResponseIf, error) {
|
||||
return nil, fmt.Errorf("failed to get user storage: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
|
||||
}
|
||||
|
||||
var result CommonDataResponse
|
||||
var result UserQuotaResponse
|
||||
if err = json.Unmarshal(resp.Body, &result); err != nil {
|
||||
return nil, fmt.Errorf("get user storage failed: invalid JSON (%w)", err)
|
||||
return nil, fmt.Errorf("get user quota failed: invalid JSON (%w)", err)
|
||||
}
|
||||
|
||||
if result.Code != 0 {
|
||||
@@ -2125,9 +2125,9 @@ func (c *CLI) AdminShowUserIndexCommand(cmd *Command) (ResponseIf, error) {
|
||||
return nil, fmt.Errorf("failed to get user storage: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
|
||||
}
|
||||
|
||||
var result CommonDataResponse
|
||||
var result UserIndexResponse
|
||||
if err = json.Unmarshal(resp.Body, &result); err != nil {
|
||||
return nil, fmt.Errorf("get user storage failed: invalid JSON (%w)", err)
|
||||
return nil, fmt.Errorf("get user index failed: invalid JSON (%w)", err)
|
||||
}
|
||||
|
||||
if result.Code != 0 {
|
||||
|
||||
@@ -88,14 +88,6 @@ func (r *ModelsResponse) PrintOut() {
|
||||
}
|
||||
}
|
||||
|
||||
type UserIndexResponse struct {
|
||||
InternalData CommonDataResponse
|
||||
}
|
||||
|
||||
func (r *UserIndexResponse) TimeCost() float64 {
|
||||
return r.InternalData.Duration
|
||||
}
|
||||
|
||||
type CommonDataResponse struct {
|
||||
Code int `json:"code"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
@@ -116,6 +108,24 @@ func (r *CommonDataResponse) SetOutputFormat(format OutputFormat) {
|
||||
r.OutputFormat = format
|
||||
}
|
||||
|
||||
func (r *CommonDataResponse) orderedMetricTable() []map[string]interface{} {
|
||||
table := make([]map[string]interface{}, 0)
|
||||
if orderRaw, ok := r.Data["_order"]; ok {
|
||||
if orderSlice, ok := orderRaw.([]interface{}); ok {
|
||||
for _, keyRaw := range orderSlice {
|
||||
key := fmt.Sprintf("%v", keyRaw)
|
||||
if value, exists := r.Data[key]; exists {
|
||||
table = append(table, map[string]interface{}{
|
||||
"Metric": key,
|
||||
"Value": value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func (r *CommonDataResponse) PrintOut() {
|
||||
if r.Code == 0 {
|
||||
table := make([]map[string]interface{}, 0)
|
||||
@@ -789,6 +799,142 @@ func chunkDocName(c map[string]interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type UserIndexResponse struct {
|
||||
CommonDataResponse
|
||||
}
|
||||
|
||||
func (r *UserIndexResponse) Type() string {
|
||||
return "user_index"
|
||||
}
|
||||
|
||||
func (r *UserIndexResponse) PrintOut() {
|
||||
if r.Code != 0 {
|
||||
fmt.Println("ERROR")
|
||||
fmt.Printf("%d, %s\n", r.Code, r.Message)
|
||||
return
|
||||
}
|
||||
|
||||
summaryTable := r.orderedMetricTable()
|
||||
indexColumns := []string{"index", "health", "status", "docs.count", "dataset.size", "store.size"}
|
||||
indexTable := make([]map[string]interface{}, 0)
|
||||
indicesRaw, hasIndices := r.Data["indices"]
|
||||
if hasIndices {
|
||||
if indices, ok := indicesRaw.([]interface{}); ok {
|
||||
for _, idx := range indices {
|
||||
if m, ok := idx.(map[string]interface{}); ok {
|
||||
orderedRow := make(map[string]interface{})
|
||||
for _, col := range indexColumns {
|
||||
if v, exists := m[col]; exists {
|
||||
orderedRow[col] = v
|
||||
} else {
|
||||
orderedRow[col] = ""
|
||||
}
|
||||
}
|
||||
indexTable = append(indexTable, orderedRow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.OutputFormat == OutputFormatJSON {
|
||||
payload := make(map[string]interface{})
|
||||
if len(summaryTable) > 0 {
|
||||
payload["summary"] = summaryTable
|
||||
}
|
||||
if len(indexTable) > 0 {
|
||||
payload["indices"] = indexTable
|
||||
}
|
||||
jsonData, err := json.MarshalIndent(payload, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("Error marshaling JSON: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(jsonData))
|
||||
return
|
||||
}
|
||||
|
||||
if len(summaryTable) > 0 {
|
||||
PrintTableSimpleByFormat(summaryTable, r.OutputFormat)
|
||||
}
|
||||
|
||||
if len(indexTable) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Println("Index Details:")
|
||||
PrintTableSimpleByFormatWithOrder(indexTable, indexColumns, r.OutputFormat)
|
||||
} else if hasIndices {
|
||||
fmt.Println()
|
||||
fmt.Println("No indices found for this user.")
|
||||
}
|
||||
}
|
||||
|
||||
type UserStorageResponse struct {
|
||||
CommonDataResponse
|
||||
}
|
||||
|
||||
func (r *UserStorageResponse) Type() string {
|
||||
return "user_storage"
|
||||
}
|
||||
|
||||
func (r *UserStorageResponse) PrintOut() {
|
||||
if r.Code != 0 {
|
||||
fmt.Println("ERROR")
|
||||
fmt.Printf("%d, %s\n", r.Code, r.Message)
|
||||
return
|
||||
}
|
||||
|
||||
summaryTable := r.orderedMetricTable()
|
||||
fileColumns := []string{"name", "size"}
|
||||
fileTable := make([]map[string]interface{}, 0)
|
||||
filesRaw, hasFiles := r.Data["files"]
|
||||
if hasFiles {
|
||||
if files, ok := filesRaw.([]interface{}); ok {
|
||||
for _, f := range files {
|
||||
if m, ok := f.(map[string]interface{}); ok {
|
||||
orderedRow := make(map[string]interface{})
|
||||
for _, col := range fileColumns {
|
||||
if v, exists := m[col]; exists {
|
||||
orderedRow[col] = v
|
||||
} else {
|
||||
orderedRow[col] = ""
|
||||
}
|
||||
}
|
||||
fileTable = append(fileTable, orderedRow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.OutputFormat == OutputFormatJSON {
|
||||
payload := make(map[string]interface{})
|
||||
if len(summaryTable) > 0 {
|
||||
payload["summary"] = summaryTable
|
||||
}
|
||||
if len(fileTable) > 0 {
|
||||
payload["files"] = fileTable
|
||||
}
|
||||
jsonData, err := json.MarshalIndent(payload, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("Error marshaling JSON: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(jsonData))
|
||||
return
|
||||
}
|
||||
|
||||
if len(summaryTable) > 0 {
|
||||
PrintTableSimpleByFormat(summaryTable, r.OutputFormat)
|
||||
}
|
||||
|
||||
if len(fileTable) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Println("Files(Top 10):")
|
||||
PrintTableSimpleByFormatWithOrder(fileTable, fileColumns, r.OutputFormat)
|
||||
} else if hasFiles {
|
||||
fmt.Println()
|
||||
fmt.Println("No files found for this user.")
|
||||
}
|
||||
}
|
||||
|
||||
func truncateStr(s string, maxLen int) string {
|
||||
s = strings.TrimSpace(s)
|
||||
runes := []rune(s)
|
||||
@@ -797,3 +943,33 @@ func truncateStr(s string, maxLen int) string {
|
||||
}
|
||||
return string(runes[:maxLen]) + "..."
|
||||
}
|
||||
|
||||
type UserQuotaResponse struct {
|
||||
CommonDataResponse
|
||||
}
|
||||
|
||||
func (r *UserQuotaResponse) Type() string {
|
||||
return "user_quota"
|
||||
}
|
||||
|
||||
func (r *UserQuotaResponse) PrintOut() {
|
||||
if r.Code != 0 {
|
||||
fmt.Println("ERROR")
|
||||
fmt.Printf("%d, %s\n", r.Code, r.Message)
|
||||
return
|
||||
}
|
||||
|
||||
summaryTable := make([]map[string]interface{}, 0)
|
||||
if rowsRaw, ok := r.Data["rows"]; ok {
|
||||
if rows, ok := rowsRaw.([]interface{}); ok {
|
||||
for _, row := range rows {
|
||||
if m, ok := row.(map[string]interface{}); ok {
|
||||
summaryTable = append(summaryTable, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(summaryTable) > 0 {
|
||||
PrintTableSimpleByFormatWithOrder(summaryTable, []string{"Metric", "Used", "Limit"}, r.OutputFormat)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,8 +188,119 @@ func PrintTableSimpleByFormat(data []map[string]interface{}, format OutputFormat
|
||||
valueWidth = getStringWidth(value)
|
||||
}
|
||||
// Pad to column width
|
||||
padding := colWidths[col] - valueWidth + len(value)
|
||||
rowParts = append(rowParts, fmt.Sprintf(" %-*s ", padding, value))
|
||||
rowParts = append(rowParts, fmt.Sprintf(" %s ", padCell(value, colWidths[col], false)))
|
||||
}
|
||||
fmt.Println("|" + strings.Join(rowParts, "|") + "|")
|
||||
}
|
||||
|
||||
fmt.Println(separator)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintTableSimpleByFormatWithOrder prints data with columns in the specified order
|
||||
func PrintTableSimpleByFormatWithOrder(data []map[string]interface{}, columns []string, format OutputFormat) {
|
||||
if len(data) == 0 {
|
||||
if format == OutputFormatJSON {
|
||||
fmt.Println("[]")
|
||||
} else if format == OutputFormatPlain {
|
||||
fmt.Println("(empty)")
|
||||
} else {
|
||||
fmt.Println("No data to print")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if format == OutputFormatJSON {
|
||||
jsonData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("Error marshaling JSON: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(jsonData))
|
||||
return
|
||||
}
|
||||
|
||||
colIsNumeric := make(map[string]bool)
|
||||
for _, col := range columns {
|
||||
isNumeric := true
|
||||
for _, item := range data {
|
||||
if val, ok := item[col]; ok {
|
||||
if !isNumericValue(val) {
|
||||
isNumeric = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
colIsNumeric[col] = isNumeric
|
||||
}
|
||||
|
||||
colWidths := make(map[string]int)
|
||||
for _, col := range columns {
|
||||
maxWidth := getStringWidth(strings.ToLower(col))
|
||||
for _, item := range data {
|
||||
value := formatValue(item[col])
|
||||
valueWidth := getStringWidth(value)
|
||||
if valueWidth > maxWidth {
|
||||
maxWidth = valueWidth
|
||||
}
|
||||
}
|
||||
if maxWidth > maxColWidth {
|
||||
maxWidth = maxColWidth
|
||||
}
|
||||
if maxWidth < 2 {
|
||||
maxWidth = 2
|
||||
}
|
||||
colWidths[col] = maxWidth
|
||||
}
|
||||
|
||||
if format == OutputFormatPlain {
|
||||
headerParts := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
headerParts = append(headerParts, padCell(strings.ToLower(col), colWidths[col], colIsNumeric[col]))
|
||||
}
|
||||
fmt.Println(strings.Join(headerParts, " "))
|
||||
|
||||
for _, item := range data {
|
||||
rowParts := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
value := formatValue(item[col])
|
||||
valueWidth := getStringWidth(value)
|
||||
if valueWidth > colWidths[col] {
|
||||
runes := []rune(value)
|
||||
value = truncateStringByWidth(runes, colWidths[col])
|
||||
valueWidth = getStringWidth(value)
|
||||
}
|
||||
rowParts = append(rowParts, padCell(value, colWidths[col], colIsNumeric[col]))
|
||||
}
|
||||
fmt.Println(strings.Join(rowParts, " "))
|
||||
}
|
||||
} else {
|
||||
separatorParts := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
separatorParts = append(separatorParts, strings.Repeat("-", colWidths[col]+2))
|
||||
}
|
||||
separator := "+" + strings.Join(separatorParts, "+") + "+"
|
||||
|
||||
fmt.Println(separator)
|
||||
headerParts := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
headerParts = append(headerParts, fmt.Sprintf(" %-*s ", colWidths[col], col))
|
||||
}
|
||||
fmt.Println("|" + strings.Join(headerParts, "|") + "|")
|
||||
fmt.Println(separator)
|
||||
|
||||
for _, item := range data {
|
||||
rowParts := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
value := formatValue(item[col])
|
||||
valueWidth := getStringWidth(value)
|
||||
if valueWidth > colWidths[col] {
|
||||
runes := []rune(value)
|
||||
value = truncateStringByWidth(runes, colWidths[col])
|
||||
valueWidth = getStringWidth(value)
|
||||
}
|
||||
rowParts = append(rowParts, fmt.Sprintf(" %s ", padCell(value, colWidths[col], false)))
|
||||
|
||||
}
|
||||
fmt.Println("|" + strings.Join(rowParts, "|") + "|")
|
||||
}
|
||||
@@ -293,5 +404,3 @@ func isHalfWidth(r rune) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ func (e *elasticsearchEngine) CreateIndexTemplate(ctx context.Context, templateN
|
||||
// Build template body with proper structure
|
||||
templateBody := map[string]interface{}{
|
||||
"index_patterns": []string{indexPattern},
|
||||
"priority": p, // Configurable priority to override existing templates
|
||||
"priority": p, // Configurable priority to override existing templates
|
||||
"template": map[string]interface{}{
|
||||
"settings": templateSettings,
|
||||
"mappings": templateMappings,
|
||||
@@ -376,3 +376,38 @@ func extractErrorReason(bodyBytes []byte) string {
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetIndexStats gets statistics for specified indices using the _cat/indices API
|
||||
// Returns index, health, status, docs.count, store.size, dataset.size for each index
|
||||
func (e *elasticsearchEngine) GetIndexStats(indices []string) ([]map[string]interface{}, error) {
|
||||
if len(indices) == 0 {
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
req := esapi.CatIndicesRequest{
|
||||
Index: indices,
|
||||
Format: "json",
|
||||
H: []string{"index", "health", "status", "docs.count", "store.size", "dataset.size"},
|
||||
}
|
||||
|
||||
res, err := req.Do(context.Background(), e.client)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get index stats: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
if res.StatusCode == 404 {
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
bodyBytes, _ := io.ReadAll(res.Body)
|
||||
return nil, fmt.Errorf("elasticsearch cat indices error: %s, body: %s", res.Status(), string(bodyBytes))
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&results); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode index stats: %w", err)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user