mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-29 04:08:12 +08:00
### Summary 1. list and get by id API for builtin DSL 2. add DSL default component param values validation 3. remove all hard code keys for parser config
730 lines
32 KiB
Go
730 lines
32 KiB
Go
//
|
|
// 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.
|
|
//
|
|
|
|
package router
|
|
|
|
import (
|
|
"github.com/gin-gonic/gin"
|
|
"ragflow/internal/common"
|
|
"ragflow/internal/handler"
|
|
)
|
|
|
|
type Router struct {
|
|
authHandler *handler.AuthHandler
|
|
userHandler *handler.UserHandler
|
|
tenantHandler *handler.TenantHandler
|
|
documentHandler *handler.DocumentHandler
|
|
datasetsHandler *handler.DatasetsHandler
|
|
systemHandler *handler.SystemHandler
|
|
chunkHandler *handler.ChunkHandler
|
|
llmHandler *handler.LLMHandler
|
|
chatHandler *handler.ChatHandler
|
|
chatChannelHandler *handler.ChatChannelHandler
|
|
langfuseHandler *handler.LangfuseHandler
|
|
openaiChatHandler *handler.OpenAIChatHandler
|
|
chatSessionHandler *handler.ChatSessionHandler
|
|
connectorHandler *handler.ConnectorHandler
|
|
searchHandler *handler.SearchHandler
|
|
fileHandler *handler.FileHandler
|
|
memoryHandler *handler.MemoryHandler
|
|
mcpHandler *handler.MCPHandler
|
|
mcpServerHandler *handler.MCPServerHandler
|
|
skillSearchHandler *handler.SkillSearchHandler
|
|
providerHandler *handler.ProviderHandler
|
|
agentHandler *handler.AgentHandler
|
|
searchBotHandler *handler.SearchBotHandler
|
|
difyRetrievalHandler *handler.DifyRetrievalHandler
|
|
pluginHandler *handler.PluginHandler
|
|
modelHandler *handler.ModelHandler
|
|
fileCommitHandler *handler.FileCommitHandler
|
|
botHandler *handler.BotHandler
|
|
componentsHandler *handler.ComponentsHandler
|
|
pipelineHandler *handler.PipelineHandler
|
|
}
|
|
|
|
// NewRouter create router
|
|
func NewRouter(
|
|
authHandler *handler.AuthHandler,
|
|
userHandler *handler.UserHandler,
|
|
tenantHandler *handler.TenantHandler,
|
|
documentHandler *handler.DocumentHandler,
|
|
datasetsHandler *handler.DatasetsHandler,
|
|
systemHandler *handler.SystemHandler,
|
|
chunkHandler *handler.ChunkHandler,
|
|
llmHandler *handler.LLMHandler,
|
|
chatHandler *handler.ChatHandler,
|
|
chatChannelHandler *handler.ChatChannelHandler,
|
|
langfuseHandler *handler.LangfuseHandler,
|
|
chatSessionHandler *handler.ChatSessionHandler,
|
|
connectorHandler *handler.ConnectorHandler,
|
|
searchHandler *handler.SearchHandler,
|
|
fileHandler *handler.FileHandler,
|
|
memoryHandler *handler.MemoryHandler,
|
|
mcpHandler *handler.MCPHandler,
|
|
mcpServerHandler *handler.MCPServerHandler,
|
|
skillSearchHandler *handler.SkillSearchHandler,
|
|
providerHandler *handler.ProviderHandler,
|
|
agentHandler *handler.AgentHandler,
|
|
searchBotHandler *handler.SearchBotHandler,
|
|
difyRetrievalHandler *handler.DifyRetrievalHandler,
|
|
pluginHandler *handler.PluginHandler,
|
|
modelHandler *handler.ModelHandler,
|
|
fileCommitHandler *handler.FileCommitHandler,
|
|
openaiChatHandler *handler.OpenAIChatHandler,
|
|
botHandler *handler.BotHandler,
|
|
componentsHandler *handler.ComponentsHandler,
|
|
pipelineHandler *handler.PipelineHandler,
|
|
) *Router {
|
|
return &Router{
|
|
authHandler: authHandler,
|
|
userHandler: userHandler,
|
|
tenantHandler: tenantHandler,
|
|
documentHandler: documentHandler,
|
|
datasetsHandler: datasetsHandler,
|
|
systemHandler: systemHandler,
|
|
chunkHandler: chunkHandler,
|
|
llmHandler: llmHandler,
|
|
chatHandler: chatHandler,
|
|
chatChannelHandler: chatChannelHandler,
|
|
langfuseHandler: langfuseHandler,
|
|
openaiChatHandler: openaiChatHandler,
|
|
chatSessionHandler: chatSessionHandler,
|
|
connectorHandler: connectorHandler,
|
|
searchHandler: searchHandler,
|
|
fileHandler: fileHandler,
|
|
memoryHandler: memoryHandler,
|
|
mcpHandler: mcpHandler,
|
|
mcpServerHandler: mcpServerHandler,
|
|
skillSearchHandler: skillSearchHandler,
|
|
providerHandler: providerHandler,
|
|
agentHandler: agentHandler,
|
|
searchBotHandler: searchBotHandler,
|
|
difyRetrievalHandler: difyRetrievalHandler,
|
|
pluginHandler: pluginHandler,
|
|
modelHandler: modelHandler,
|
|
fileCommitHandler: fileCommitHandler,
|
|
botHandler: botHandler,
|
|
componentsHandler: componentsHandler,
|
|
pipelineHandler: pipelineHandler,
|
|
}
|
|
}
|
|
|
|
// Setup setup routes
|
|
func (r *Router) Setup(engine *gin.Engine) {
|
|
SetupEERouter(engine)
|
|
|
|
// Mark all responses from Go with a header for debugging.
|
|
engine.Use(func(c *gin.Context) {
|
|
c.Header("X-API-Source", "go")
|
|
c.Next()
|
|
})
|
|
|
|
// Log all HTTP requests.
|
|
engine.Use(common.GinLogger())
|
|
|
|
// Health check
|
|
engine.GET("/health", r.systemHandler.Health)
|
|
|
|
// System endpoints
|
|
engine.GET("/v1/system/configs", r.systemHandler.GetConfigs)
|
|
//engine.POST("/v1/user/register", r.userHandler.Register)
|
|
|
|
// User logout endpoint
|
|
engine.GET("/v1/user/logout", r.userHandler.Logout)
|
|
|
|
// OAuth callbacks are invoked by third-party providers and cannot rely on
|
|
// the RAGFlow auth middleware.
|
|
engine.GET("/connectors/gmail/oauth/web/callback", r.connectorHandler.GmailWebOAuthCallback)
|
|
engine.GET("/connectors/google-drive/oauth/web/callback", r.connectorHandler.GoogleDriveWebOAuthCallback)
|
|
engine.GET("/connectors/box/oauth/web/callback", r.connectorHandler.BoxWebOAuthCallback)
|
|
|
|
apiNoAuth := engine.Group("/api/v1")
|
|
{
|
|
apiNoAuth.GET("/system/ping", r.systemHandler.Ping)
|
|
apiNoAuth.GET("/system/config", r.systemHandler.GetConfig)
|
|
apiNoAuth.GET("/system/version", r.systemHandler.GetVersion)
|
|
apiNoAuth.GET("/system/healthz", r.systemHandler.Healthz)
|
|
// Backend runtime language detection. The front end calls this once
|
|
// to choose between Go and Python code paths.
|
|
apiNoAuth.GET("/language", r.systemHandler.Language)
|
|
|
|
// Pipeline catalog. Public static data (shipped with the binary),
|
|
// no auth required. The front end uses it to populate the parser
|
|
// picker without hard-coding the parser_id list.
|
|
// Query: ?type=builtin returns built-in templates (default).
|
|
if r.pipelineHandler != nil {
|
|
apiNoAuth.GET("/pipelines", r.pipelineHandler.ListPipelines)
|
|
apiNoAuth.GET("/pipelines/:id", r.pipelineHandler.GetPipeline)
|
|
}
|
|
|
|
// searchbots
|
|
apiNoAuth.GET("/searchbots/detail", r.searchBotHandler.SearchbotDetail)
|
|
|
|
// User login channels endpoint
|
|
apiNoAuth.GET("/auth/login/channels", r.userHandler.GetLoginChannels)
|
|
|
|
// User login by email endpoint
|
|
apiNoAuth.POST("/auth/login", r.userHandler.LoginByEmail)
|
|
|
|
// OAuth / OIDC login routes. The static "channels" segment is
|
|
// registered before the wildcard, so gin's tree resolves
|
|
// /auth/login/channels to GetLoginChannels and other values to
|
|
// OAuthLogin without conflict.
|
|
apiNoAuth.GET("/auth/login/:channel", r.userHandler.OAuthLogin)
|
|
apiNoAuth.GET("/auth/oauth/:channel/callback", r.userHandler.OAuthChannelCallback)
|
|
|
|
// For EE
|
|
apiNoAuth.GET("/auth/oauth/callback", r.userHandler.OAuthCallback)
|
|
apiNoAuth.GET("/auth/oauth/github/callback", r.userHandler.GitHubAuthCallback)
|
|
apiNoAuth.GET("/auth/oauth/lark/callback", r.userHandler.LarkAuthCallback)
|
|
apiNoAuth.GET("/auth/icbc/callback", r.userHandler.ICBCAuthCallback)
|
|
apiNoAuth.GET("/auth/azure/callback", r.userHandler.AzureAuthCallback)
|
|
apiNoAuth.GET("/auth/azure/login", r.userHandler.AzureAuthLogin)
|
|
apiNoAuth.POST("/auth/register/captcha", r.userHandler.Captcha)
|
|
apiNoAuth.POST("/auth/register/otp", r.userHandler.SendOTP)
|
|
apiNoAuth.POST("/auth/register/otp/verify", r.userHandler.VerifyOTP)
|
|
|
|
// Register
|
|
apiNoAuth.POST("/users", r.userHandler.Register)
|
|
|
|
// Google redirects here after Gmail / Google Drive web OAuth completes.
|
|
apiNoAuth.GET("/connectors/gmail/oauth/web/callback", r.connectorHandler.GmailWebOAuthCallback)
|
|
apiNoAuth.GET("/connectors/google-drive/oauth/web/callback", r.connectorHandler.GoogleDriveWebOAuthCallback)
|
|
apiNoAuth.GET("/connectors/box/oauth/web/callback", r.connectorHandler.BoxWebOAuthCallback)
|
|
// Forgot-password flow (fixes #15282).
|
|
// Routes are intentionally registered before any auth middleware:
|
|
// a user who has forgotten their password is, by definition,
|
|
// unauthenticated.
|
|
apiNoAuth.POST("/auth/password/forgot/captcha", r.userHandler.ForgotCaptcha)
|
|
apiNoAuth.POST("/auth/password/forgot/otp", r.userHandler.ForgotSendOTP)
|
|
apiNoAuth.POST("/auth/password/forgot/otp/verify", r.userHandler.ForgotVerifyOTP)
|
|
apiNoAuth.POST("/auth/password/reset", r.userHandler.ForgotResetPassword)
|
|
|
|
apiNoAuth.GET("/dify/retrieval/health", r.difyRetrievalHandler.HealthCheck)
|
|
}
|
|
|
|
// Beta-token routes. Mirrors python's
|
|
// @login_required(auth_types=AUTH_BETA) on bot_api.py bot endpoints.
|
|
apiBetaAuth := engine.Group("/api/v1")
|
|
apiBetaAuth.Use(r.authHandler.BetaAuthMiddleware())
|
|
{
|
|
searchBotGroup := apiBetaAuth.Group("/searchbots")
|
|
searchBotGroup.POST("/related_questions", r.searchBotHandler.Handle)
|
|
searchBotGroup.POST("/retrieval_test", r.searchBotHandler.RetrievalTest)
|
|
searchBotGroup.POST("/ask", r.searchBotHandler.Ask)
|
|
searchBotGroup.POST("/mindmap", r.searchBotHandler.MindMap)
|
|
|
|
if r.botHandler != nil {
|
|
chatbotGroup := apiBetaAuth.Group("/chatbots")
|
|
betaMW := r.authHandler.BetaAuthMiddleware()
|
|
RegisterChatbotRoutes(chatbotGroup, betaMW, r.botHandler)
|
|
agentbotGroup := apiBetaAuth.Group("/agentbots")
|
|
RegisterAgentbotRoutes(agentbotGroup, betaMW, r.botHandler)
|
|
}
|
|
// Public bot endpoints (authenticated with an SDK beta token, not a session)
|
|
apiBetaAuth.GET("/documents/:id/preview", r.documentHandler.GetDocumentPreview)
|
|
apiBetaAuth.GET("/documents/images/:image_id", r.documentHandler.GetDocumentImage)
|
|
apiBetaAuth.GET("/thumbnails", r.documentHandler.GetThumbnail)
|
|
|
|
// MCP server endpoint — exposes RAGFlow capabilities as MCP tools.
|
|
// Uses BetaAuthMiddleware to resolve the user from the
|
|
// Authorization header.
|
|
if r.mcpServerHandler != nil {
|
|
apiBetaAuth.POST("/mcp", r.mcpServerHandler.HandleMCP)
|
|
}
|
|
}
|
|
|
|
// Protected routes
|
|
authorized := engine.Group("")
|
|
authorized.Use(r.authHandler.AuthMiddleware())
|
|
{
|
|
// User info endpoint
|
|
authorized.GET("/v1/user/info", r.userHandler.Info)
|
|
// User tenant info endpoint
|
|
authorized.GET("/v1/user/tenant_info", r.tenantHandler.TenantInfo)
|
|
// Tenant list endpoint
|
|
authorized.GET("/v1/tenant/list", r.tenantHandler.TenantList)
|
|
// User settings endpoint
|
|
authorized.POST("/v1/user/setting", r.userHandler.Setting)
|
|
// User change password endpoint
|
|
authorized.POST("/v1/user/setting/password", r.userHandler.ChangePassword)
|
|
// User set tenant info endpoint
|
|
authorized.POST("/v1/user/set_tenant_info", r.userHandler.SetTenantInfo)
|
|
|
|
// API v1 route group
|
|
v1 := authorized.Group("/api/v1")
|
|
{
|
|
// Auth routes
|
|
auth := v1.Group("/auth")
|
|
{
|
|
// User logout endpoint
|
|
auth.POST("/logout", r.userHandler.Logout)
|
|
}
|
|
|
|
// Users routes
|
|
users := v1.Group("/users")
|
|
{
|
|
users.GET("/me", r.userHandler.Info)
|
|
// User settings endpoint
|
|
users.PATCH("/me", r.userHandler.Setting)
|
|
// User tenant info endpoint
|
|
users.GET("/me/models", r.tenantHandler.TenantInfo)
|
|
// User set tenant info endpoint
|
|
users.PATCH("/me/models", r.userHandler.SetTenantInfo)
|
|
|
|
// For EE
|
|
users.GET("/me/admin", r.userHandler.IsAdmin)
|
|
users.GET("/me/meta", r.userHandler.GetMeta)
|
|
}
|
|
|
|
user := v1.Group("/settings")
|
|
{
|
|
user.GET("/enable-admin", r.systemHandler.GetEnableAdmin)
|
|
}
|
|
|
|
tenants := v1.Group("/tenants")
|
|
{
|
|
tenants.GET("", r.tenantHandler.TenantList)
|
|
tenants.PATCH("/:tenant_id", r.tenantHandler.AcceptTenantInvite)
|
|
tenants.GET("/:tenant_id/users", r.tenantHandler.ListTenantMembers)
|
|
tenants.POST("/:tenant_id/users", r.tenantHandler.AddTenantMember)
|
|
tenants.DELETE("/:tenant_id/users", r.tenantHandler.RemoveTenantMember)
|
|
}
|
|
|
|
// Tenant routes (per-tenant resources)
|
|
tenant := v1.Group("/tenant")
|
|
{
|
|
tenant.GET("/list", r.tenantHandler.TenantList)
|
|
tenant.POST("/chunk_store", r.tenantHandler.CreateChunkStore) // Internal API only for GO
|
|
tenant.DELETE("/chunk_store", r.tenantHandler.DeleteChunkStore) // Internal API only for GO
|
|
tenant.POST("/metadata_store", r.tenantHandler.CreateMetadataStore) // Internal API only for GO
|
|
tenant.DELETE("/metadata_store", r.tenantHandler.DeleteMetadataStore) // Internal API only for GO
|
|
tenant.POST("/dev_insert_chunks_from_file", r.tenantHandler.InsertChunksFromFile) // Internal API only for GO
|
|
tenant.POST("/dev_insert_metadata_from_file", r.tenantHandler.InsertMetadataFromFile) // Internal API only for GO
|
|
}
|
|
|
|
// Document routes
|
|
documents := v1.Group("/documents")
|
|
{
|
|
documents.POST("/upload", r.documentHandler.UploadInfo)
|
|
documents.GET("", r.documentHandler.ListDocuments)
|
|
documents.GET("/artifact/:filename", r.documentHandler.GetDocumentArtifact)
|
|
documents.GET("/:id", r.documentHandler.GetDocumentByID)
|
|
documents.PUT("/:id", r.documentHandler.UpdateDocument)
|
|
documents.DELETE("/:id", r.documentHandler.DeleteDocument)
|
|
documents.POST("/ingest", r.documentHandler.Ingest)
|
|
}
|
|
|
|
// Chat routes
|
|
chats := v1.Group("/chats")
|
|
{
|
|
chats.GET("", r.chatHandler.ListChats)
|
|
chats.POST("", r.chatHandler.Create)
|
|
chats.DELETE("", r.chatHandler.BulkDeleteChats)
|
|
chats.DELETE("/:chat_id", r.chatHandler.DeleteChat)
|
|
chats.GET("/:chat_id", r.chatHandler.GetChat)
|
|
chats.PUT("/:chat_id", r.chatHandler.UpdateChat)
|
|
chats.PATCH("/:chat_id", r.chatHandler.PatchChat)
|
|
chats.GET("/:chat_id/sessions", r.chatSessionHandler.ListChatSessions)
|
|
chats.POST("/:chat_id/sessions", r.chatSessionHandler.CreateSession)
|
|
chats.DELETE("/:chat_id/sessions", r.chatSessionHandler.DeleteSessions)
|
|
chats.GET("/:chat_id/sessions/:session_id", r.chatSessionHandler.GetSession)
|
|
chats.PATCH("/:chat_id/sessions/:session_id", r.chatSessionHandler.UpdateSession)
|
|
chats.DELETE("/:chat_id/sessions/:session_id/messages/:msg_id", r.chatSessionHandler.DeleteSessionMessage)
|
|
chats.PUT("/:chat_id/sessions/:session_id/messages/:msg_id/feedback", r.chatSessionHandler.UpdateMessageFeedback)
|
|
}
|
|
|
|
chat := v1.Group("/chat")
|
|
{
|
|
chat.POST("/completions", r.chatSessionHandler.ChatCompletions)
|
|
chat.POST("/mindmap", r.chatHandler.MindMap)
|
|
chat.POST("/recommendation", r.chatHandler.Recommendation)
|
|
chat.POST("/audio/speech", r.chatHandler.ChatAudioSpeech)
|
|
chat.POST("/audio/transcription", r.chatHandler.ChatAudioTranscription)
|
|
}
|
|
v1.POST("/openai/:chat_id/chat/completions", r.openaiChatHandler.OpenAIChatCompletions)
|
|
|
|
// Dataset routes
|
|
datasets := v1.Group("/datasets")
|
|
{
|
|
datasets.GET("", r.datasetsHandler.ListDatasets)
|
|
datasets.GET("/tags/aggregation", r.datasetsHandler.AggregateTags)
|
|
datasets.GET("/:dataset_id", r.datasetsHandler.GetDataset)
|
|
datasets.PUT("/:dataset_id", r.datasetsHandler.UpdateDataset)
|
|
datasets.GET("/:dataset_id/graph", r.datasetsHandler.GetKnowledgeGraph)
|
|
datasets.GET("/:dataset_id/tags", r.datasetsHandler.ListTags)
|
|
datasets.PUT("/:dataset_id/tags", r.datasetsHandler.RenameTag)
|
|
datasets.DELETE("/:dataset_id/tags", r.datasetsHandler.RemoveTags)
|
|
datasets.POST("/:dataset_id/embedding/check", r.datasetsHandler.CheckEmbedding)
|
|
datasets.POST("/:dataset_id/documents/batch-update-status", r.documentHandler.BatchUpdateDocumentStatus)
|
|
datasets.GET("/:dataset_id/index", r.datasetsHandler.TraceIndex)
|
|
datasets.POST("/:dataset_id/index", r.datasetsHandler.RunIndex)
|
|
datasets.DELETE("/:dataset_id/index", r.datasetsHandler.DeleteIndex)
|
|
datasets.DELETE("/:dataset_id/:index_type", r.datasetsHandler.DeleteIndex)
|
|
//datasets.DELETE("/:dataset_id/graph", r.datasetsHandler.DeleteKnowledgeGraph)
|
|
datasets.POST("", r.datasetsHandler.CreateDataset)
|
|
datasets.DELETE("", r.datasetsHandler.DeleteDatasets)
|
|
datasets.POST("/search", r.datasetsHandler.SearchDatasets)
|
|
datasets.POST("/:dataset_id/search", r.datasetsHandler.SearchDataset)
|
|
datasets.GET("/metadata/flattened", r.datasetsHandler.ListMetadataFlattened)
|
|
datasets.GET("/:dataset_id/metadata/summary", r.documentHandler.MetadataSummaryByDataset)
|
|
|
|
// Dataset ingestion logs
|
|
datasets.GET("/:dataset_id/ingestions/summary", r.datasetsHandler.GetIngestionSummary)
|
|
datasets.GET("/:dataset_id/ingestions", r.datasetsHandler.ListIngestionLogs)
|
|
datasets.GET("/:dataset_id/ingestions/:log_id", r.datasetsHandler.GetIngestionLog)
|
|
|
|
// Metadata Config
|
|
datasets.GET("/:dataset_id/metadata/config", r.datasetsHandler.GetMetadataConfig)
|
|
datasets.PUT("/:dataset_id/metadata/config", r.datasetsHandler.UpdateMetadataConfig)
|
|
|
|
// Dataset documents
|
|
datasets.GET("/:dataset_id/documents", r.documentHandler.ListDocuments)
|
|
datasets.POST("/:dataset_id/documents", r.documentHandler.UploadDocuments)
|
|
datasets.GET("/:dataset_id/documents/:document_id", r.documentHandler.DownloadDocument)
|
|
datasets.PATCH("/:dataset_id/documents/:document_id", r.documentHandler.UpdateDatasetDocument)
|
|
datasets.DELETE("/:dataset_id/documents", r.documentHandler.DeleteDocuments)
|
|
datasets.POST("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.AddChunk)
|
|
|
|
// Dataset document chunk
|
|
datasets.GET("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.ListChunks)
|
|
datasets.PATCH("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.SwitchChunks)
|
|
datasets.GET("/:dataset_id/documents/:document_id/chunks/:chunk_id", r.chunkHandler.Get)
|
|
datasets.POST("/:dataset_id/chunks", r.chunkHandler.Parse)
|
|
datasets.PATCH("/:dataset_id/documents/:document_id/chunks/:chunk_id", r.chunkHandler.UpdateChunk)
|
|
datasets.POST("/:dataset_id/documents/parse", r.documentHandler.StartIngestionTask)
|
|
datasets.GET("/ingestion/tasks", r.documentHandler.ListIngestionTasks)
|
|
datasets.PUT("/ingestion/tasks", r.documentHandler.StopIngestionTasks)
|
|
datasets.DELETE("/ingestion/tasks", r.documentHandler.RemoveIngestionTasks)
|
|
//datasets.POST("/:dataset_id/documents/parse", r.documentHandler.ParseDocuments)
|
|
//datasets.POST("/:dataset_id/documents/stop", r.documentHandler.StopParseDocuments)
|
|
datasets.DELETE("/:dataset_id/chunks", r.chunkHandler.StopParsing)
|
|
datasets.DELETE("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.RemoveChunks)
|
|
datasets.PUT("/:dataset_id/documents/:document_id/metadata/config", r.datasetsHandler.UpdateDocumentMetadataConfig)
|
|
datasets.POST("/:dataset_id/metadata/update", r.documentHandler.MetadataBatchUpdate)
|
|
datasets.PATCH("/:dataset_id/documents/metadatas", r.documentHandler.UpdateDocumentMetadatas)
|
|
}
|
|
|
|
// Search routes
|
|
searches := v1.Group("/searches")
|
|
{
|
|
searches.GET("", r.searchHandler.ListSearches)
|
|
searches.POST("", r.searchHandler.CreateSearch)
|
|
searches.GET("/:search_id", r.searchHandler.GetSearch)
|
|
searches.PUT("/:search_id", r.searchHandler.UpdateSearch)
|
|
searches.DELETE("/:search_id", r.searchHandler.DeleteSearch)
|
|
searches.POST("/:search_id/completion", r.searchHandler.Completion)
|
|
searches.POST("/:search_id/completions", r.searchHandler.Completion)
|
|
}
|
|
|
|
files := v1.Group("/files")
|
|
{
|
|
files.POST("", r.fileHandler.UploadFile)
|
|
files.GET("", r.fileHandler.ListFiles)
|
|
files.DELETE("", r.fileHandler.DeleteFiles)
|
|
files.POST("/move", r.fileHandler.MoveFiles)
|
|
files.POST("/link-to-datasets", r.fileHandler.LinkToDatasets)
|
|
files.GET("/:id/ancestors", r.fileHandler.GetFileAncestors)
|
|
files.GET("/:id/parent", r.fileHandler.GetParentFolder)
|
|
files.GET("/:id", r.fileHandler.Download)
|
|
files.GET("/:id/versions", r.fileCommitHandler.GetFileVersionHistory)
|
|
}
|
|
|
|
// File routes
|
|
file := authorized.Group("/v1/file")
|
|
{
|
|
file.GET("/root_folder", r.fileHandler.GetRootFolder)
|
|
file.GET("/parent_folder", r.fileHandler.GetParentFolder)
|
|
file.GET("/all_parent_folder", r.fileHandler.GetAllParentFolders)
|
|
}
|
|
|
|
// File commit routes — /folders/ takes folder_id directly
|
|
commitFolders := v1.Group("/folders")
|
|
{
|
|
commitFolders.POST("/:folder_id/commits", r.fileCommitHandler.CreateCommit)
|
|
commitFolders.GET("/:folder_id/commits", r.fileCommitHandler.ListCommits)
|
|
commitFolders.GET("/:folder_id/commits/diff", r.fileCommitHandler.DiffCommits)
|
|
commitFolders.GET("/:folder_id/commits/:commit_id", r.fileCommitHandler.GetCommit)
|
|
commitFolders.GET("/:folder_id/commits/:commit_id/files", r.fileCommitHandler.ListCommitFiles)
|
|
commitFolders.GET("/:folder_id/commits/:commit_id/tree", r.fileCommitHandler.GetCommitTree)
|
|
commitFolders.GET("/:folder_id/commits/:commit_id/files/:file_id/content", r.fileCommitHandler.GetCommitFileContent)
|
|
commitFolders.GET("/:folder_id/changes", r.fileCommitHandler.GetUncommittedChanges)
|
|
}
|
|
|
|
// /workspace/{workspace_id}/commits — alias for /folders/ (workspace_id == folder_id)
|
|
commitWorkspace := v1.Group("/workspace")
|
|
{
|
|
commitWorkspace.POST("/:folder_id/commits", r.fileCommitHandler.CreateCommit)
|
|
commitWorkspace.GET("/:folder_id/commits", r.fileCommitHandler.ListCommits)
|
|
commitWorkspace.GET("/:folder_id/commits/diff", r.fileCommitHandler.DiffCommits)
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id", r.fileCommitHandler.GetCommit)
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id/files", r.fileCommitHandler.ListCommitFiles)
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id/tree", r.fileCommitHandler.GetCommitTree)
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id/files/:file_id/content", r.fileCommitHandler.GetCommitFileContent)
|
|
commitWorkspace.GET("/:folder_id/changes", r.fileCommitHandler.GetUncommittedChanges)
|
|
}
|
|
|
|
// /datasets/{dataset_id}/commits — resolve dataset_id → folder_id via middleware
|
|
commitDatasets := v1.Group("/datasets/:dataset_id")
|
|
commitDatasets.Use(handler.CommitFolderResolver(r.fileCommitHandler, "datasets", "dataset_id"))
|
|
{
|
|
commitDatasets.POST("/commits", r.fileCommitHandler.CreateCommit)
|
|
commitDatasets.GET("/commits", r.fileCommitHandler.ListCommits)
|
|
commitDatasets.GET("/commits/diff", r.fileCommitHandler.DiffCommits)
|
|
commitDatasets.GET("/commits/:commit_id", r.fileCommitHandler.GetCommit)
|
|
commitDatasets.GET("/commits/:commit_id/files", r.fileCommitHandler.ListCommitFiles)
|
|
commitDatasets.GET("/commits/:commit_id/tree", r.fileCommitHandler.GetCommitTree)
|
|
commitDatasets.GET("/commits/:commit_id/files/:file_id/content", r.fileCommitHandler.GetCommitFileContent)
|
|
commitDatasets.GET("/changes", r.fileCommitHandler.GetUncommittedChanges)
|
|
}
|
|
|
|
// Memory routes
|
|
memory := v1.Group("/memories")
|
|
{
|
|
memory.POST("", r.memoryHandler.CreateMemory)
|
|
memory.PUT("/:memory_id", r.memoryHandler.UpdateMemory)
|
|
memory.DELETE("/:memory_id", r.memoryHandler.DeleteMemory)
|
|
memory.GET("", r.memoryHandler.ListMemories)
|
|
memory.GET("/:memory_id/config", r.memoryHandler.GetMemoryConfig)
|
|
memory.GET("/:memory_id", r.memoryHandler.GetMemoryMessages)
|
|
}
|
|
|
|
// Message routes
|
|
message := v1.Group("/messages")
|
|
{
|
|
message.GET("", r.memoryHandler.GetMessages)
|
|
message.POST("", r.memoryHandler.AddMessage)
|
|
message.DELETE("/:memory_message", r.memoryHandler.ForgetMessage)
|
|
message.PUT("/:memory_message", r.memoryHandler.UpdateMessage)
|
|
message.GET("/:memory_message/content", r.memoryHandler.GetMessageContent)
|
|
message.GET("/search", r.memoryHandler.SearchMessage)
|
|
}
|
|
|
|
// Skill search routes
|
|
skills := v1.Group("/skills")
|
|
{
|
|
// Skill Space management
|
|
skills.GET("/spaces", r.skillSearchHandler.ListSpaces)
|
|
skills.POST("/spaces", r.skillSearchHandler.CreateSpace)
|
|
skills.GET("/spaces/:space_id", r.skillSearchHandler.GetSpace)
|
|
skills.PUT("/spaces/:space_id", r.skillSearchHandler.UpdateSpace)
|
|
skills.DELETE("/spaces/:space_id", r.skillSearchHandler.DeleteSpace)
|
|
skills.GET("/space/by-folder", r.skillSearchHandler.GetSpaceByFolder)
|
|
|
|
// Skill search config
|
|
skills.GET("/config", r.skillSearchHandler.GetConfig)
|
|
skills.POST("/config", r.skillSearchHandler.UpdateConfig)
|
|
|
|
// Skill search and indexing
|
|
skills.POST("/search", r.skillSearchHandler.Search)
|
|
skills.POST("/index", r.skillSearchHandler.IndexSkills)
|
|
skills.DELETE("/index", r.skillSearchHandler.DeleteSkillIndex)
|
|
skills.POST("/reindex", r.skillSearchHandler.Reindex)
|
|
}
|
|
|
|
// provider pool route group
|
|
provider := v1.Group("/providers")
|
|
{
|
|
provider.GET("", r.providerHandler.ListProviders)
|
|
provider.PUT("", r.providerHandler.AddProvider)
|
|
provider.GET("/:provider_name", r.providerHandler.ShowProvider)
|
|
provider.DELETE("/:provider_name", r.providerHandler.DeleteProvider)
|
|
provider.GET("/:provider_name/models", r.providerHandler.ListModels)
|
|
provider.GET("/:provider_name/models/:model_name", r.providerHandler.ShowModel)
|
|
provider.POST("/:provider_name/instances", r.providerHandler.CreateProviderInstance)
|
|
provider.GET("/:provider_name/instances", r.providerHandler.ListProviderInstances)
|
|
provider.GET("/:provider_name/instances/:instance_name", r.providerHandler.ShowProviderInstance)
|
|
provider.GET("/:provider_name/instances/:instance_name/balance", r.providerHandler.ShowInstanceBalance)
|
|
provider.GET("/:provider_name/instances/:instance_name/connection", r.providerHandler.CheckInstanceConnection)
|
|
provider.POST("/:provider_name/connection", r.providerHandler.CheckConnection)
|
|
provider.GET("/:provider_name/instances/:instance_name/tasks", r.providerHandler.ListTasks)
|
|
provider.GET("/:provider_name/instances/:instance_name/tasks/:task_id", r.providerHandler.ShowTask)
|
|
provider.PUT("/:provider_name/instances/:instance_name", r.providerHandler.AlterProviderInstance)
|
|
provider.DELETE("/:provider_name/instances", r.providerHandler.DropProviderInstance)
|
|
provider.GET("/:provider_name/instances/:instance_name/models", r.providerHandler.ListInstanceModels)
|
|
provider.PATCH("/:provider_name/instances/:instance_name/models/*model_name", r.providerHandler.AlterModel)
|
|
provider.POST("/:provider_name/instances/:instance_name/models", r.providerHandler.AddModel)
|
|
provider.DELETE("/:provider_name/instances/:instance_name/models", r.providerHandler.DropInstanceModels)
|
|
v1.POST("/chat/to_model", r.providerHandler.ChatToModel)
|
|
v1.POST("/embeddings", r.providerHandler.EmbedText)
|
|
v1.POST("/rerank", r.providerHandler.RerankDocument)
|
|
v1.POST("/audio/transcriptions", r.providerHandler.TranscribeAudio)
|
|
v1.POST("/audio/speech", r.providerHandler.AudioSpeech)
|
|
v1.POST("/file/ocr", r.providerHandler.OCRFile)
|
|
v1.POST("/file/parse", r.providerHandler.ParseFile)
|
|
}
|
|
|
|
model := v1.Group("/models")
|
|
{
|
|
// GET /models returns the tenant's added models across
|
|
// all instances. Front-end useFetchAllAddedModels consumes this.
|
|
model.GET("", r.providerHandler.ListTenantAddedModels)
|
|
model.PATCH("", r.tenantHandler.SetModels)
|
|
// Tenant default-model selection (used by the agent page's useFetchDefaultModels hook)
|
|
model.GET("/default", r.tenantHandler.GetDefaultModels)
|
|
model.PATCH("/default", r.tenantHandler.SetDefaultModels)
|
|
}
|
|
|
|
allModels := v1.Group("/all-models")
|
|
{
|
|
allModels.GET("", r.modelHandler.ListAllModels)
|
|
allModels.GET("/:model_name", r.modelHandler.ShowModel)
|
|
}
|
|
|
|
// Agent routes
|
|
agents := v1.Group("/agents")
|
|
RegisterAgentRoutes(agents, r.agentHandler)
|
|
|
|
// Plugin routes
|
|
plugin := v1.Group("/plugin")
|
|
{
|
|
plugin.GET("/tools", r.pluginHandler.ListLLMTools)
|
|
}
|
|
|
|
// Component catalog — Phase 4 of
|
|
// port-rag-flow-pipeline-to-go.md. Optional
|
|
// ?category=ingestion,agent,shared filter; defaults to
|
|
// all categories. The data source is
|
|
// runtime.DefaultRegistry.
|
|
if r.componentsHandler != nil {
|
|
v1.GET("/components", r.componentsHandler.Get)
|
|
}
|
|
|
|
connectors := v1.Group("/connectors")
|
|
{
|
|
connectors.GET("", r.connectorHandler.ListConnectors)
|
|
connectors.POST("", r.connectorHandler.CreateConnector)
|
|
connectors.POST("/google/oauth/web/start", r.connectorHandler.StartGoogleWebOAuth)
|
|
connectors.POST("/google/oauth/web/result", r.connectorHandler.PollGoogleWebOAuthResult)
|
|
connectors.POST("/box/oauth/web/start", r.connectorHandler.StartBoxWebOAuth)
|
|
connectors.POST("/box/oauth/web/result", r.connectorHandler.PollBoxWebOAuthResult)
|
|
connectors.GET("/:connector_id", r.connectorHandler.GetConnector)
|
|
connectors.PATCH("/:connector_id", r.connectorHandler.UpdateConnector)
|
|
connectors.GET("/:connector_id/logs", r.connectorHandler.ListLogs)
|
|
connectors.DELETE("/:connector_id", r.connectorHandler.DeleteConnector)
|
|
connectors.POST("/:connector_id/rebuild", r.connectorHandler.RebuildConnector)
|
|
connectors.POST("/:connector_id/test", r.connectorHandler.TestConnector)
|
|
}
|
|
|
|
// Connector routes
|
|
connector := authorized.Group("/v1/connector")
|
|
{
|
|
connector.GET("/list", r.connectorHandler.ListConnectors)
|
|
connector.GET("/:connector_id", r.connectorHandler.GetConnector)
|
|
connector.POST("/:connector_id/rebuild", r.connectorHandler.RebuildConnector)
|
|
}
|
|
|
|
// MCP server routes.
|
|
mcp := v1.Group("/mcp")
|
|
{
|
|
mcp.POST("/servers", r.mcpHandler.CreateMCPServer)
|
|
mcp.GET("/servers", r.mcpHandler.ListMCPServers)
|
|
mcp.GET("/servers/:mcp_id", r.mcpHandler.GetMCPServer)
|
|
mcp.PUT("/servers/:mcp_id", r.mcpHandler.UpdateMCPServer)
|
|
mcp.DELETE("/servers/:mcp_id", r.mcpHandler.DeleteMCPServer)
|
|
mcp.POST("/servers/import", r.mcpHandler.ImportMCPServers)
|
|
mcp.POST("/servers/:mcp_id/test", r.mcpHandler.TestMCPServer)
|
|
}
|
|
|
|
system := v1.Group("/system")
|
|
{
|
|
system.GET("/configs", r.systemHandler.GetConfigs)
|
|
system.GET("/status", r.systemHandler.GetStatus)
|
|
system.GET("/stats", r.systemHandler.GetStats)
|
|
|
|
config := system.Group("/config")
|
|
{
|
|
config.GET("/log", r.systemHandler.GetLogLevel)
|
|
config.PUT("/log", r.systemHandler.SetLogLevel)
|
|
}
|
|
|
|
// Variables/Settings
|
|
system.GET("/variables", r.systemHandler.ListVariables)
|
|
system.PUT("/variables", r.systemHandler.SetVariable)
|
|
system.GET("/variables/:var_name", r.systemHandler.ShowVariable)
|
|
|
|
// Environments
|
|
system.GET("/environments", r.systemHandler.ListEnvironments)
|
|
|
|
tokens := system.Group("/tokens")
|
|
{
|
|
// list tokens /api/v1/system/tokens GET
|
|
tokens.GET("", r.systemHandler.ListAPIKeys)
|
|
// create token /api/v1/system/tokens POST
|
|
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)
|
|
}
|
|
}
|
|
|
|
// Document routes
|
|
doc := v1.Group("/document")
|
|
{
|
|
doc.POST("/list", r.documentHandler.ListDocuments)
|
|
doc.POST("/metadata/summary", r.documentHandler.MetadataSummary)
|
|
doc.POST("/set_meta", r.documentHandler.SetMeta)
|
|
doc.POST("/delete_meta", r.documentHandler.DeleteMeta) // Internal API only for GO
|
|
}
|
|
|
|
// Chunk routes
|
|
chunk := v1.Group("/chunk")
|
|
{
|
|
chunk.POST("/list", r.chunkHandler.List)
|
|
chunk.POST("/update", r.chunkHandler.UpdateChunk) // Internal API only for GO
|
|
}
|
|
|
|
// Chat Channel
|
|
chanChannel := v1.Group("/chat-channels")
|
|
{
|
|
chanChannel.POST("", r.chatChannelHandler.CreateChatChannel)
|
|
chanChannel.GET("", r.chatChannelHandler.ListChatChannel)
|
|
chanChannel.GET("/:channel_id", r.chatChannelHandler.GetChatChannel)
|
|
chanChannel.PATCH("/:channel_id", r.chatChannelHandler.UpdateChatChannel)
|
|
chanChannel.DELETE("/:channel_id", r.chatChannelHandler.DeleteChatChannel)
|
|
}
|
|
|
|
// Langfuse tracing keys
|
|
langfuse := v1.Group("/langfuse")
|
|
{
|
|
langfuse.POST("/api-key", r.langfuseHandler.SetAPIKey)
|
|
langfuse.PUT("/api-key", r.langfuseHandler.SetAPIKey)
|
|
langfuse.GET("/api-key", r.langfuseHandler.GetAPIKey)
|
|
langfuse.DELETE("/api-key", r.langfuseHandler.DeleteAPIKey)
|
|
}
|
|
|
|
// Dify retrieval routes
|
|
dify := v1.Group("/dify")
|
|
{
|
|
dify.POST("/retrieval", r.difyRetrievalHandler.Retrieval)
|
|
dify.GET("/retrieval", r.difyRetrievalHandler.Retrieval)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle undefined routes
|
|
engine.NoRoute(handler.HandleNoRoute)
|
|
}
|