mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 15:31:05 +08:00
Refactor system/version API to RESTful style (#13956)
### What problem does this PR solve? Refactor version API to RESTful style. Python and go server API also updated. ### Type of change - [x] Refactoring <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Migrated core API endpoints to the `/api/v1/` namespace for improved consistency and organization. * Standardized system version, search, and chat list endpoints under the new API versioning structure. * **New Features** * Added MinIO region configuration support, allowing specification of storage engine regional settings via environment variables or configuration files. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
@@ -1655,7 +1655,7 @@ class RAGFlowClient:
|
||||
if self.server_type == "admin":
|
||||
response = self.http_client.request("GET", "/admin/version", use_api_base=True, auth_kind="admin")
|
||||
else:
|
||||
response = self.http_client.request("GET", "/system/version", use_api_base=False, auth_kind="admin")
|
||||
response = self.http_client.request("GET", "/system/version", use_api_base=True, auth_kind="admin")
|
||||
|
||||
res_json = response.json()
|
||||
if response.status_code == 200:
|
||||
|
||||
42
api/apps/restful_apis/system_api.py
Normal file
42
api/apps/restful_apis/system_api.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
from api.apps import login_required
|
||||
|
||||
from api.utils.api_utils import get_json_result
|
||||
from common.versions import get_ragflow_version
|
||||
|
||||
@manager.route("/system/version", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
def version():
|
||||
"""
|
||||
Get the current version of the application.
|
||||
---
|
||||
tags:
|
||||
- System
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
200:
|
||||
description: Version retrieved successfully.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
description: Version number.
|
||||
"""
|
||||
return get_json_result(data=get_ragflow_version())
|
||||
@@ -29,7 +29,6 @@ from api.utils.api_utils import (
|
||||
server_error_response,
|
||||
generate_confirmation_token,
|
||||
)
|
||||
from common.versions import get_ragflow_version
|
||||
from common.time_utils import current_timestamp, datetime_format
|
||||
from common.log_utils import get_log_levels, set_log_level
|
||||
from timeit import default_timer as timer
|
||||
@@ -39,30 +38,6 @@ from quart import jsonify
|
||||
from api.utils.health_utils import run_health_checks, get_oceanbase_status
|
||||
from common import settings
|
||||
|
||||
|
||||
@manager.route("/version", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
def version():
|
||||
"""
|
||||
Get the current version of the application.
|
||||
---
|
||||
tags:
|
||||
- System
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
200:
|
||||
description: Version retrieved successfully.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
description: Version number.
|
||||
"""
|
||||
return get_json_result(data=get_ragflow_version())
|
||||
|
||||
|
||||
@manager.route("/status", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
def status():
|
||||
|
||||
@@ -69,11 +69,11 @@ func (c *RAGFlowClient) ShowServerVersion(cmd *Command) (ResponseIf, error) {
|
||||
|
||||
if iterations > 1 {
|
||||
// Benchmark mode: multiple iterations
|
||||
return c.HTTPClient.RequestWithIterations("GET", "/system/version", false, "web", nil, nil, iterations)
|
||||
return c.HTTPClient.RequestWithIterations("GET", "/system/version", true, "web", nil, nil, iterations)
|
||||
}
|
||||
|
||||
// Single mode
|
||||
resp, err := c.HTTPClient.Request("GET", "/system/version", false, "web", nil, nil)
|
||||
resp, err := c.HTTPClient.Request("GET", "/system/version", true, "web", nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to show version: %w", err)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func NewSearchHandler(searchService *service.SearchService, userService *service
|
||||
}
|
||||
}
|
||||
|
||||
// ListSearchApps list search apps
|
||||
// ListSearches list search apps
|
||||
// @Summary List Search Apps
|
||||
// @Description Get list of search apps for the current user with filtering, pagination and sorting
|
||||
// @Tags search
|
||||
@@ -53,8 +53,8 @@ func NewSearchHandler(searchService *service.SearchService, userService *service
|
||||
// @Param desc query bool false "descending order (default: true)"
|
||||
// @Param request body service.ListSearchAppsRequest true "filter options including owner_ids"
|
||||
// @Success 200 {object} service.ListSearchAppsResponse
|
||||
// @Router /v1/search/list [post]
|
||||
func (h *SearchHandler) ListSearchApps(c *gin.Context) {
|
||||
// @Router /api/v1/searches [post]
|
||||
func (h *SearchHandler) ListSearches(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
@@ -99,7 +99,7 @@ func (h *SearchHandler) ListSearchApps(c *gin.Context) {
|
||||
}
|
||||
|
||||
// List search apps with filtering
|
||||
result, err := h.searchService.ListSearchApps(userID, keywords, page, pageSize, orderby, desc, req.OwnerIDs)
|
||||
result, err := h.searchService.ListSearches(userID, keywords, page, pageSize, orderby, desc, req.OwnerIDs)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
|
||||
@@ -48,7 +48,7 @@ func (h *SystemHandler) Ping(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
}
|
||||
|
||||
// Health health check
|
||||
// Health check
|
||||
func (h *SystemHandler) Health(c *gin.Context) {
|
||||
c.JSON(200, gin.H{
|
||||
"status": "ok",
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"ragflow/internal/handler"
|
||||
)
|
||||
|
||||
// Router router
|
||||
type Router struct {
|
||||
authHandler *handler.AuthHandler
|
||||
userHandler *handler.UserHandler
|
||||
@@ -196,6 +195,16 @@ func (r *Router) Setup(engine *gin.Engine) {
|
||||
// message.GET("/:memory_id/:message_id/content", r.memoryHandler.GetMessageContent)
|
||||
// }
|
||||
|
||||
chats := v1.Group("/chats")
|
||||
{
|
||||
chats.GET("", r.chatHandler.ListChats)
|
||||
}
|
||||
|
||||
searches := v1.Group("/searches")
|
||||
{
|
||||
searches.GET("", r.searchHandler.ListSearches)
|
||||
}
|
||||
|
||||
file := v1.Group("/files")
|
||||
{
|
||||
file.POST("", r.fileHandler.UploadFile)
|
||||
@@ -220,6 +229,11 @@ func (r *Router) Setup(engine *gin.Engine) {
|
||||
provider.PUT("/:provider_name/instances/:instance_name/models/:model_name", r.providerHandler.EnableOrDisableModel)
|
||||
provider.POST("/:provider_name/instances/:instance_name/models/:model_name", r.providerHandler.ChatToModel)
|
||||
}
|
||||
|
||||
system := v1.Group("/system")
|
||||
{
|
||||
system.GET("/version", r.systemHandler.GetVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// Knowledge base routes
|
||||
@@ -285,7 +299,6 @@ func (r *Router) Setup(engine *gin.Engine) {
|
||||
// Chat routes
|
||||
chat := authorized.Group("/v1/dialog")
|
||||
{
|
||||
chat.GET("/list", r.chatHandler.ListChats)
|
||||
chat.POST("/next", r.chatHandler.ListChatsNext)
|
||||
chat.POST("/set", r.chatHandler.SetDialog)
|
||||
chat.POST("/rm", r.chatHandler.RemoveChats)
|
||||
@@ -306,12 +319,6 @@ func (r *Router) Setup(engine *gin.Engine) {
|
||||
connector.GET("/list", r.connectorHandler.ListConnectors)
|
||||
}
|
||||
|
||||
// Search routes
|
||||
search := authorized.Group("/v1/search")
|
||||
{
|
||||
search.POST("/list", r.searchHandler.ListSearchApps)
|
||||
}
|
||||
|
||||
// File routes
|
||||
file := authorized.Group("/v1/file")
|
||||
{
|
||||
|
||||
@@ -179,6 +179,7 @@ type MinioConfig struct {
|
||||
Password string `mapstructure:"password"` // Secret key
|
||||
Secure bool `mapstructure:"secure"` // Use HTTPS
|
||||
Verify bool `mapstructure:"verify"` // Verify SSL certificates
|
||||
Region string `mapstructure:"region"` // optional
|
||||
Bucket string `mapstructure:"bucket"` // Default bucket (optional)
|
||||
PrefixPath string `mapstructure:"prefix_path"` // Path prefix (optional)
|
||||
}
|
||||
@@ -448,6 +449,9 @@ func FromEnvironments() error {
|
||||
// Minio
|
||||
minioIP := strings.ToLower(os.Getenv("MINIO_IP"))
|
||||
if minioIP != "" {
|
||||
if globalConfig.StorageEngine.Minio == nil {
|
||||
return fmt.Errorf("Minio config not found")
|
||||
}
|
||||
_, port, err := net.SplitHostPort(globalConfig.StorageEngine.Minio.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing host address %s: %v\n", globalConfig.StorageEngine.Minio.Host, err)
|
||||
@@ -458,6 +462,9 @@ func FromEnvironments() error {
|
||||
minioPort := strings.ToLower(os.Getenv("MINIO_PORT"))
|
||||
// println(fmt.Sprintf("MINIO ip and port from env: %s:%s", minioIP, minioPort))
|
||||
if minioPort != "" {
|
||||
if globalConfig.StorageEngine.Minio == nil {
|
||||
return fmt.Errorf("Minio config not found")
|
||||
}
|
||||
ip, _, err := net.SplitHostPort(globalConfig.StorageEngine.Minio.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing host address %s: %v\n", globalConfig.StorageEngine.Minio.Host, err)
|
||||
@@ -465,6 +472,14 @@ func FromEnvironments() error {
|
||||
globalConfig.StorageEngine.Minio.Host = fmt.Sprintf("%s:%s", ip, minioPort)
|
||||
}
|
||||
|
||||
minioRegion := strings.ToLower(os.Getenv("MINIO_REGION"))
|
||||
if minioRegion != "" {
|
||||
if globalConfig.StorageEngine.Minio == nil {
|
||||
return fmt.Errorf("Minio config not found")
|
||||
}
|
||||
globalConfig.StorageEngine.Minio.Region = minioRegion
|
||||
}
|
||||
|
||||
// Language
|
||||
if globalConfig.Language == "" {
|
||||
globalConfig.Language = GetLanguage()
|
||||
@@ -644,6 +659,7 @@ func FromConfigFile(configPath string) error {
|
||||
Secure: minioConfig.GetBool("secure"),
|
||||
PrefixPath: minioConfig.GetString("prefix_path"),
|
||||
Verify: minioConfig.GetBool("verify"),
|
||||
Region: minioConfig.GetString("region"),
|
||||
Bucket: minioConfig.GetString("bucket"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ type ListSearchAppsResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// ListSearchApps list search apps with advanced filtering (equivalent to list_search_app)
|
||||
func (s *SearchService) ListSearchApps(userID string, keywords string, page, pageSize int, orderby string, desc bool, ownerIDs []string) (*ListSearchAppsResponse, error) {
|
||||
// ListSearches list search apps with advanced filtering (equivalent to list_search_app)
|
||||
func (s *SearchService) ListSearches(userID string, keywords string, page, pageSize int, orderby string, desc bool, ownerIDs []string) (*ListSearchAppsResponse, error) {
|
||||
var searches []*entity.Search
|
||||
var total int64
|
||||
var err error
|
||||
|
||||
@@ -71,6 +71,7 @@ func (m *MinioStorage) connect() error {
|
||||
Creds: credentials.NewStaticV4(m.config.User, m.config.Password, ""),
|
||||
Secure: m.config.Secure,
|
||||
Transport: transport,
|
||||
Region: m.config.Region,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to MinIO: %w", err)
|
||||
|
||||
@@ -35,6 +35,7 @@ MEMORY_API_URL = f"/api/{VERSION}/memories"
|
||||
MESSAGE_API_URL = f"/api/{VERSION}/messages"
|
||||
API_APP_URL = f"/{VERSION}/api"
|
||||
SYSTEM_APP_URL = f"/{VERSION}/system"
|
||||
SYSTEM_API_URL = f"/api/{VERSION}/system"
|
||||
LLM_APP_URL = f"/{VERSION}/llm"
|
||||
PLUGIN_APP_URL = f"/{VERSION}/plugin"
|
||||
SEARCHES_URL = f"/api/{VERSION}/searches"
|
||||
@@ -113,7 +114,7 @@ def system_status(auth, params=None, *, headers=HEADERS):
|
||||
|
||||
|
||||
def system_version(auth, params=None, *, headers=HEADERS):
|
||||
res = requests.get(url=f"{HOST_ADDRESS}{SYSTEM_APP_URL}/version", headers=headers, auth=auth, params=params)
|
||||
res = requests.get(url=f"{HOST_ADDRESS}{SYSTEM_API_URL}/version", headers=headers, auth=auth, params=params)
|
||||
return res.json()
|
||||
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ export default {
|
||||
moveFile: `${restAPIv1}/files/move`,
|
||||
|
||||
// system
|
||||
getSystemVersion: `${webAPI}/system/version`,
|
||||
getSystemVersion: `${restAPIv1}/system/version`,
|
||||
getSystemStatus: `${webAPI}/system/status`,
|
||||
getSystemTokenList: `${webAPI}/system/token_list`,
|
||||
createSystemToken: `${webAPI}/system/new_token`,
|
||||
|
||||
Reference in New Issue
Block a user