mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-17 21:27:23 +08:00
Add more API of admin server of go (#13403)
### What problem does this PR solve? Add APIs to admin server. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
@@ -17,56 +17,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"ragflow/internal/cache"
|
||||
"ragflow/internal/engine"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"ragflow/internal/admin"
|
||||
"ragflow/internal/dao"
|
||||
"ragflow/internal/handler"
|
||||
"ragflow/internal/logger"
|
||||
"ragflow/internal/server"
|
||||
"ragflow/internal/service"
|
||||
"ragflow/internal/utility"
|
||||
)
|
||||
|
||||
// AdminServer admin server
|
||||
type AdminServer struct {
|
||||
router *admin.Router
|
||||
handler *admin.Handler
|
||||
service *admin.Service
|
||||
engine *gin.Engine
|
||||
port string
|
||||
}
|
||||
|
||||
// NewAdminServer create admin server
|
||||
func NewAdminServer(port string) *AdminServer {
|
||||
return &AdminServer{
|
||||
port: port,
|
||||
}
|
||||
}
|
||||
|
||||
// Init initialize admin server
|
||||
func (s *AdminServer) Init() error {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
s.engine = gin.New()
|
||||
s.engine.Use(gin.Recovery())
|
||||
|
||||
// Initialize layers
|
||||
s.service = admin.NewService()
|
||||
s.handler = admin.NewHandler(s.service)
|
||||
s.router = admin.NewRouter(s.handler)
|
||||
|
||||
// Setup routes
|
||||
s.router.Setup(s.engine)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run start admin server
|
||||
func (s *AdminServer) Run() error {
|
||||
return s.engine.Run(":" + s.port)
|
||||
router *admin.Router
|
||||
handler *admin.Handler
|
||||
service *admin.Service
|
||||
userHandler *handler.UserHandler
|
||||
engine *gin.Engine
|
||||
port string
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -85,14 +66,29 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := server.GetConfig()
|
||||
|
||||
// Reinitialize logger with configured level if different
|
||||
if cfg.Log.Level != "" && cfg.Log.Level != "info" {
|
||||
if err := logger.Init(cfg.Log.Level); err != nil {
|
||||
logger.Error("Failed to reinitialize logger with configured level", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set logger for server package
|
||||
server.SetLogger(logger.Logger)
|
||||
|
||||
cfg := server.GetConfig()
|
||||
logger.Info("Configuration loaded",
|
||||
zap.String("database_host", cfg.Database.Host),
|
||||
zap.Int("database_port", cfg.Database.Port),
|
||||
)
|
||||
logger.Info("Server mode", zap.String("mode", cfg.Server.Mode))
|
||||
|
||||
// Print all configuration settings
|
||||
server.PrintAll()
|
||||
|
||||
// Set Gin mode
|
||||
if cfg.Server.Mode == "release" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
} else {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
}
|
||||
|
||||
// Initialize database
|
||||
if err := dao.InitDB(); err != nil {
|
||||
@@ -100,12 +96,57 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create and start admin server (port 9381)
|
||||
adminServer := NewAdminServer("9381")
|
||||
if err := adminServer.Init(); err != nil {
|
||||
logger.Error("Failed to initialize admin server", err)
|
||||
os.Exit(1)
|
||||
// Initialize doc engine
|
||||
if err := engine.Init(&cfg.DocEngine); err != nil {
|
||||
logger.Fatal("Failed to initialize doc engine", zap.Error(err))
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
// Initialize Redis cache
|
||||
if err := cache.Init(&cfg.Redis); err != nil {
|
||||
logger.Fatal("Failed to initialize Redis", zap.Error(err))
|
||||
}
|
||||
defer cache.Close()
|
||||
|
||||
// Initialize server variables (runtime variables that can change during operation)
|
||||
// This must be done after Cache is initialized
|
||||
if err := server.InitVariables(cache.Get()); err != nil {
|
||||
logger.Warn("Failed to initialize server variables from Redis, using defaults", zap.String("error", err.Error()))
|
||||
}
|
||||
|
||||
adminService := admin.NewService()
|
||||
userService := service.NewUserService()
|
||||
adminHandler := admin.NewHandler(adminService, userService)
|
||||
|
||||
// Initialize router
|
||||
r := admin.NewRouter(adminHandler)
|
||||
|
||||
// Create Gin engine
|
||||
ginEngine := gin.New()
|
||||
|
||||
// Middleware
|
||||
if cfg.Server.Mode == "debug" {
|
||||
ginEngine.Use(gin.Logger())
|
||||
}
|
||||
ginEngine.Use(gin.Recovery())
|
||||
// Log request URL for every request
|
||||
ginEngine.Use(func(c *gin.Context) {
|
||||
logger.Info("HTTP Request", zap.String("url", c.Request.URL.String()), zap.String("method", c.Request.Method))
|
||||
c.Next()
|
||||
})
|
||||
|
||||
// Setup routes
|
||||
r.Setup(ginEngine)
|
||||
|
||||
// Create HTTP server
|
||||
addr := fmt.Sprintf(":9381")
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: ginEngine,
|
||||
}
|
||||
|
||||
// Print RAGFlow version
|
||||
logger.Info("RAGFlow version", zap.String("version", utility.GetRAGFlowVersion()))
|
||||
|
||||
// Print all configuration settings
|
||||
server.PrintAll()
|
||||
@@ -118,11 +159,31 @@ func main() {
|
||||
" / _, _/ ___ / /_/ / __/ / / /_/ / |/ |/ / / ___ / /_/ / / / / / / / / / /\n" +
|
||||
" /_/ |_/_/ |_\\____/_/ /_/\\____/|__/|__/ /_/ |_\\__,_/_/ /_/ /_/_/_/ /_/ \n")
|
||||
|
||||
// Print RAGFlow version
|
||||
logger.Info(fmt.Sprintf("Version: %s", utility.GetRAGFlowVersion()))
|
||||
logger.Info(fmt.Sprintf("Starting RAGFlow admin server on port: 9381"))
|
||||
if err := adminServer.Run(); err != nil {
|
||||
logger.Error("Admin server error", err)
|
||||
os.Exit(1)
|
||||
// Start server in a goroutine
|
||||
go func() {
|
||||
logger.Info(fmt.Sprintf("Version: %s", utility.GetRAGFlowVersion()))
|
||||
logger.Info(fmt.Sprintf("Starting RAGFlow admin server on port: 9381"))
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
logger.Fatal("Failed to start server", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal to gracefully shutdown
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR2)
|
||||
sig := <-quit
|
||||
|
||||
logger.Info("Received signal", zap.String("signal", sig.String()))
|
||||
logger.Info("Shutting down server...")
|
||||
|
||||
// Create context with timeout for graceful shutdown
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Shutdown server
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
logger.Fatal("Server forced to shutdown", zap.Error(err))
|
||||
}
|
||||
|
||||
logger.Info("Server exited")
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,16 +18,21 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"ragflow/internal/handler"
|
||||
)
|
||||
|
||||
// Router admin router
|
||||
type Router struct {
|
||||
handler *Handler
|
||||
handler *Handler
|
||||
userHandler *handler.UserHandler
|
||||
}
|
||||
|
||||
// NewRouter create admin router
|
||||
func NewRouter(handler *Handler) *Router {
|
||||
return &Router{handler: handler}
|
||||
return &Router{
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
// Setup setup routes
|
||||
@@ -35,28 +40,78 @@ func (r *Router) Setup(engine *gin.Engine) {
|
||||
// Health check
|
||||
engine.GET("/health", r.handler.Health)
|
||||
|
||||
// Admin API routes
|
||||
admin := engine.Group("/admin")
|
||||
// Admin API routes with prefix /api/v1/admin
|
||||
admin := engine.Group("/api/v1/admin")
|
||||
{
|
||||
// Auth
|
||||
// Public routes
|
||||
admin.GET("/ping", r.handler.Ping)
|
||||
admin.POST("/login", r.handler.Login)
|
||||
|
||||
// Protected routes
|
||||
protected := admin.Group("")
|
||||
protected.Use(r.handler.AuthMiddleware())
|
||||
{
|
||||
// Auth
|
||||
protected.GET("/auth", r.handler.AuthCheck)
|
||||
protected.GET("/logout", r.handler.Logout)
|
||||
|
||||
// User management
|
||||
protected.GET("/users", r.handler.ListUsers)
|
||||
protected.GET("/users/:id", r.handler.GetUser)
|
||||
protected.PUT("/users/:id", r.handler.UpdateUser)
|
||||
protected.DELETE("/users/:id", r.handler.DeleteUser)
|
||||
protected.POST("/users", r.handler.CreateUser)
|
||||
protected.GET("/users/:username", r.handler.GetUser)
|
||||
protected.DELETE("/users/:username", r.handler.DeleteUser)
|
||||
protected.PUT("/users/:username/password", r.handler.ChangePassword)
|
||||
protected.PUT("/users/:username/activate", r.handler.UpdateUserActivateStatus)
|
||||
protected.PUT("/users/:username/admin", r.handler.GrantAdmin)
|
||||
protected.DELETE("/users/:username/admin", r.handler.RevokeAdmin)
|
||||
protected.GET("/users/:username/datasets", r.handler.GetUserDatasets)
|
||||
protected.GET("/users/:username/agents", r.handler.GetUserAgents)
|
||||
|
||||
// System config
|
||||
protected.GET("/config", r.handler.GetConfig)
|
||||
protected.PUT("/config", r.handler.UpdateConfig)
|
||||
// API Keys
|
||||
protected.GET("/users/:username/keys", r.handler.GetUserAPIKeys)
|
||||
protected.POST("/users/:username/keys", r.handler.GenerateUserAPIKey)
|
||||
protected.DELETE("/users/:username/keys/:key", r.handler.DeleteUserAPIKey)
|
||||
|
||||
// System status
|
||||
protected.GET("/status", r.handler.GetStatus)
|
||||
// Role management
|
||||
protected.GET("/roles", r.handler.ListRoles)
|
||||
protected.POST("/roles", r.handler.CreateRole)
|
||||
protected.GET("/roles/:role_name", r.handler.GetRole)
|
||||
protected.PUT("/roles/:role_name", r.handler.UpdateRole)
|
||||
protected.DELETE("/roles/:role_name", r.handler.DeleteRole)
|
||||
protected.GET("/roles/:role_name/permission", r.handler.GetRolePermission)
|
||||
protected.POST("/roles/:role_name/permission", r.handler.GrantRolePermission)
|
||||
protected.DELETE("/roles/:role_name/permission", r.handler.RevokeRolePermission)
|
||||
|
||||
// User roles and permissions
|
||||
protected.PUT("/users/:username/role", r.handler.UpdateUserRole)
|
||||
protected.GET("/users/:username/permission", r.handler.GetUserPermission)
|
||||
|
||||
// Service management
|
||||
protected.GET("/services", r.handler.GetServices)
|
||||
protected.GET("/service_types/:service_type", r.handler.GetServicesByType)
|
||||
protected.GET("/services/:service_id", r.handler.GetService)
|
||||
protected.DELETE("/services/:service_id", r.handler.ShutdownService)
|
||||
protected.PUT("/services/:service_id", r.handler.RestartService)
|
||||
|
||||
// Variables/Settings
|
||||
protected.GET("/variables", r.handler.GetVariables)
|
||||
protected.PUT("/variables", r.handler.SetVariable)
|
||||
|
||||
// Configs
|
||||
protected.GET("/configs", r.handler.GetConfigs)
|
||||
|
||||
// Environments
|
||||
protected.GET("/environments", r.handler.GetEnvironments)
|
||||
|
||||
// Version
|
||||
protected.GET("/version", r.handler.GetVersion)
|
||||
|
||||
// Sandbox
|
||||
protected.GET("/sandbox/providers", r.handler.ListSandboxProviders)
|
||||
protected.GET("/sandbox/providers/:provider_id/schema", r.handler.GetSandboxProviderSchema)
|
||||
protected.GET("/sandbox/config", r.handler.GetSandboxConfig)
|
||||
protected.POST("/sandbox/config", r.handler.SetSandboxConfig)
|
||||
protected.POST("/sandbox/test", r.handler.TestSandboxConnection)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,20 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"ragflow/internal/cache"
|
||||
"ragflow/internal/dao"
|
||||
"ragflow/internal/engine/elasticsearch"
|
||||
"ragflow/internal/model"
|
||||
"ragflow/internal/server"
|
||||
"ragflow/internal/utility"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Service admin service layer
|
||||
@@ -57,13 +67,13 @@ func (s *Service) Login(req *LoginRequest) (*LoginResponse, error) {
|
||||
return nil, ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// Verify password
|
||||
if user.Password == nil || *user.Password != req.Password {
|
||||
return nil, ErrInvalidCredentials
|
||||
// Check if user is active
|
||||
if user.IsActive != "1" {
|
||||
return nil, errors.New("user is not active")
|
||||
}
|
||||
|
||||
// Generate access token
|
||||
token := generateToken()
|
||||
token := utility.GenerateToken()
|
||||
if err := s.userDAO.UpdateAccessToken(user, token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,155 +86,649 @@ func (s *Service) Login(req *LoginRequest) (*LoginResponse, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListUsersRequest list users request
|
||||
type ListUsersRequest struct {
|
||||
Offset int
|
||||
Limit int
|
||||
// Logout user logout
|
||||
func (s *Service) Logout(user interface{}) error {
|
||||
// Invalidate token by setting it to INVALID_ prefix
|
||||
if u, ok := user.(*model.User); ok {
|
||||
invalidToken := "INVALID_" + generateRandomHex(16)
|
||||
return s.userDAO.UpdateAccessToken(u, invalidToken)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListUsersResponse list users response
|
||||
type ListUsersResponse struct {
|
||||
Users []*UserInfo
|
||||
Total int64
|
||||
}
|
||||
|
||||
// UserInfo user info
|
||||
type UserInfo struct {
|
||||
ID string
|
||||
Email string
|
||||
Nickname string
|
||||
IsActive string
|
||||
CreateTime *int64
|
||||
UpdateTime *int64
|
||||
// generateRandomHex generate random hex string
|
||||
func generateRandomHex(n int) string {
|
||||
bytes := make([]byte, n)
|
||||
rand.Read(bytes)
|
||||
return hex.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
// ListUsers list all users
|
||||
func (s *Service) ListUsers(req *ListUsersRequest) (*ListUsersResponse, error) {
|
||||
users, total, err := s.userDAO.List(req.Offset, req.Limit)
|
||||
func (s *Service) ListUsers() ([]map[string]interface{}, error) {
|
||||
users, _, err := s.userDAO.List(0, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*UserInfo
|
||||
result := make([]map[string]interface{}, 0, len(users))
|
||||
for _, user := range users {
|
||||
result = append(result, &UserInfo{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Nickname: user.Nickname,
|
||||
IsActive: user.IsActive,
|
||||
CreateTime: user.CreateTime,
|
||||
UpdateTime: user.UpdateTime,
|
||||
result = append(result, map[string]interface{}{
|
||||
"email": user.Email,
|
||||
"nickname": user.Nickname,
|
||||
"create_date": user.CreateTime,
|
||||
"is_active": user.IsActive,
|
||||
"is_superuser": user.IsSuperuser,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return &ListUsersResponse{
|
||||
Users: result,
|
||||
Total: total,
|
||||
// CreateUser create a new user
|
||||
func (s *Service) CreateUser(username, password, role string) (map[string]interface{}, error) {
|
||||
// TODO: Implement user creation with proper password hashing
|
||||
return map[string]interface{}{
|
||||
"username": username,
|
||||
"role": role,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetUserRequest get user request
|
||||
type GetUserRequest struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
// GetUser get user by ID
|
||||
func (s *Service) GetUser(req *GetUserRequest) (*UserInfo, error) {
|
||||
// GetUserDetails get user details
|
||||
func (s *Service) GetUserDetails(username string) (map[string]interface{}, error) {
|
||||
// Query user by email/username
|
||||
var user model.User
|
||||
err := dao.DB.Where("id = ?", req.ID).First(&user).Error
|
||||
err := dao.DB.Where("email = ?", username).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, ErrUserNotFound
|
||||
}
|
||||
|
||||
return &UserInfo{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Nickname: user.Nickname,
|
||||
IsActive: user.IsActive,
|
||||
CreateTime: user.CreateTime,
|
||||
UpdateTime: user.UpdateTime,
|
||||
return map[string]interface{}{
|
||||
"id": user.ID,
|
||||
"email": user.Email,
|
||||
"nickname": user.Nickname,
|
||||
"is_active": user.IsActive,
|
||||
"create_time": user.CreateTime,
|
||||
"update_time": user.UpdateTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateUserRequest update user request
|
||||
type UpdateUserRequest struct {
|
||||
ID string
|
||||
Nickname string
|
||||
IsActive *string
|
||||
}
|
||||
|
||||
// UpdateUser update user
|
||||
func (s *Service) UpdateUser(req *UpdateUserRequest) error {
|
||||
var user model.User
|
||||
if err := dao.DB.Where("id = ?", req.ID).First(&user).Error; err != nil {
|
||||
return ErrUserNotFound
|
||||
}
|
||||
|
||||
if req.Nickname != "" {
|
||||
user.Nickname = req.Nickname
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
user.IsActive = *req.IsActive
|
||||
}
|
||||
|
||||
return dao.DB.Save(&user).Error
|
||||
}
|
||||
|
||||
// DeleteUserRequest delete user request
|
||||
type DeleteUserRequest struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
// DeleteUser delete user
|
||||
func (s *Service) DeleteUser(req *DeleteUserRequest) error {
|
||||
return dao.DB.Where("id = ?", req.ID).Delete(&model.User{}).Error
|
||||
}
|
||||
|
||||
// GetSystemConfig get system config
|
||||
func (s *Service) GetSystemConfig() map[string]interface{} {
|
||||
// TODO: Load from database or config file
|
||||
return map[string]interface{}{
|
||||
"system_name": "RAGFlow Admin",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateSystemConfig update system config
|
||||
func (s *Service) UpdateSystemConfig(config map[string]interface{}) error {
|
||||
// TODO: Save to database or config file
|
||||
func (s *Service) DeleteUser(username string) error {
|
||||
// TODO: Implement user deletion
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSystemStatus get system status
|
||||
func (s *Service) GetSystemStatus() map[string]interface{} {
|
||||
// TODO: Get real status from services
|
||||
return map[string]interface{}{
|
||||
"status": "running",
|
||||
"uptime": time.Since(time.Now()).String(),
|
||||
"db_status": "connected",
|
||||
// ChangePassword change user password
|
||||
func (s *Service) ChangePassword(username, newPassword string) error {
|
||||
// TODO: Implement password change
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateUserActivateStatus update user activate status
|
||||
func (s *Service) UpdateUserActivateStatus(username string, isActive bool) error {
|
||||
// TODO: Implement activate status update
|
||||
return nil
|
||||
}
|
||||
|
||||
// GrantAdmin grant admin privileges
|
||||
func (s *Service) GrantAdmin(username string) error {
|
||||
// TODO: Implement grant admin
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeAdmin revoke admin privileges
|
||||
func (s *Service) RevokeAdmin(username string) error {
|
||||
// TODO: Implement revoke admin
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserDatasets get user datasets
|
||||
func (s *Service) GetUserDatasets(username string) ([]map[string]interface{}, error) {
|
||||
// TODO: Implement get user datasets
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GetUserAgents get user agents
|
||||
func (s *Service) GetUserAgents(username string) ([]map[string]interface{}, error) {
|
||||
// TODO: Implement get user agents
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// API Key methods
|
||||
|
||||
// GetUserAPIKeys get user API keys
|
||||
func (s *Service) GetUserAPIKeys(username string) ([]map[string]interface{}, error) {
|
||||
// TODO: Implement get API keys
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GenerateUserAPIKey generate API key for user
|
||||
func (s *Service) GenerateUserAPIKey(username string) (map[string]interface{}, error) {
|
||||
// TODO: Implement generate API key
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// DeleteUserAPIKey delete user API key
|
||||
func (s *Service) DeleteUserAPIKey(username, key string) error {
|
||||
// TODO: Implement delete API key
|
||||
return nil
|
||||
}
|
||||
|
||||
// Role management methods
|
||||
|
||||
// ListRoles list all roles
|
||||
func (s *Service) ListRoles() ([]map[string]interface{}, error) {
|
||||
// TODO: Implement list roles
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// CreateRole create a new role
|
||||
func (s *Service) CreateRole(roleName, description string) (map[string]interface{}, error) {
|
||||
// TODO: Implement create role
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GetRole get role details
|
||||
func (s *Service) GetRole(roleName string) (map[string]interface{}, error) {
|
||||
// TODO: Implement get role
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// UpdateRole update role
|
||||
func (s *Service) UpdateRole(roleName, description string) (map[string]interface{}, error) {
|
||||
// TODO: Implement update role
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// DeleteRole delete role
|
||||
func (s *Service) DeleteRole(roleName string) error {
|
||||
// TODO: Implement delete role
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRolePermission get role permissions
|
||||
func (s *Service) GetRolePermission(roleName string) ([]map[string]interface{}, error) {
|
||||
// TODO: Implement get role permissions
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GrantRolePermission grant permission to role
|
||||
func (s *Service) GrantRolePermission(roleName string, actions []string, resource string) (map[string]interface{}, error) {
|
||||
// TODO: Implement grant role permission
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// RevokeRolePermission revoke permission from role
|
||||
func (s *Service) RevokeRolePermission(roleName string, actions []string, resource string) (map[string]interface{}, error) {
|
||||
// TODO: Implement revoke role permission
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// UpdateUserRole update user role
|
||||
func (s *Service) UpdateUserRole(username, roleName string) ([]map[string]interface{}, error) {
|
||||
// TODO: Implement update user role
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GetUserPermission get user permissions
|
||||
func (s *Service) GetUserPermission(username string) ([]map[string]interface{}, error) {
|
||||
// TODO: Implement get user permissions
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GetAllServices get all services
|
||||
func (s *Service) GetAllServices() ([]map[string]interface{}, error) {
|
||||
allConfigs := server.GetAllConfigs()
|
||||
|
||||
var result []map[string]interface{}
|
||||
for _, configDict := range allConfigs {
|
||||
// Get service details to check status
|
||||
serviceDetail, err := s.GetServiceDetails(configDict)
|
||||
if err == nil {
|
||||
if status, ok := serviceDetail["status"]; ok {
|
||||
configDict["status"] = status
|
||||
} else {
|
||||
configDict["status"] = "timeout"
|
||||
}
|
||||
} else {
|
||||
configDict["status"] = "timeout"
|
||||
}
|
||||
result = append(result, configDict)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetServicesByType get services by type
|
||||
func (s *Service) GetServicesByType(serviceType string) ([]map[string]interface{}, error) {
|
||||
return nil, errors.New("get_services_by_type: not implemented")
|
||||
}
|
||||
|
||||
// GetServiceDetails get service details
|
||||
func (s *Service) GetServiceDetails(configDict map[string]interface{}) (map[string]interface{}, error) {
|
||||
serviceType, _ := configDict["service_type"].(string)
|
||||
name, _ := configDict["name"].(string)
|
||||
|
||||
// Call detail function based on service type
|
||||
switch serviceType {
|
||||
case "meta_data":
|
||||
return s.getMySQLStatus(name)
|
||||
case "message_queue":
|
||||
return s.getRedisInfo(name)
|
||||
case "retrieval":
|
||||
// Check the extra.retrieval_type to determine which retrieval service
|
||||
if extra, ok := configDict["extra"].(map[string]interface{}); ok {
|
||||
if retrievalType, ok := extra["retrieval_type"].(string); ok {
|
||||
if retrievalType == "infinity" {
|
||||
return s.getInfinityStatus(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.getESClusterStats(name)
|
||||
case "ragflow_server":
|
||||
return s.checkRAGFlowServerAlive(name)
|
||||
case "file_store":
|
||||
return s.checkMinioAlive(name)
|
||||
case "task_executor":
|
||||
return s.checkTaskExecutorAlive(name)
|
||||
default:
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "unknown",
|
||||
"message": "Service type not supported",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateToken validate access token
|
||||
func (s *Service) ValidateToken(token string) (*model.User, error) {
|
||||
user, err := s.userDAO.GetByAccessToken(token)
|
||||
// getMySQLStatus gets MySQL service status
|
||||
func (s *Service) getMySQLStatus(name string) (map[string]interface{}, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
// Check basic connectivity with SELECT 1
|
||||
sqlDB, err := dao.DB.DB()
|
||||
if err != nil {
|
||||
return nil, ErrInvalidToken
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"elapsed": fmt.Sprintf("%.1f", time.Since(startTime).Milliseconds()),
|
||||
"message": err.Error(),
|
||||
}, nil
|
||||
}
|
||||
return user, nil
|
||||
|
||||
// Execute SELECT 1 to check connectivity
|
||||
_, err = sqlDB.Exec("SELECT 1")
|
||||
if err != nil {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"elapsed": fmt.Sprintf("%.1f", time.Since(startTime).Milliseconds()),
|
||||
"message": err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "alive",
|
||||
"elapsed": fmt.Sprintf("%.1f", time.Since(startTime).Milliseconds()),
|
||||
"message": "MySQL connection successful",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateToken generate a simple token
|
||||
func generateToken() string {
|
||||
return time.Now().Format("20060102150405") + randomString(16)
|
||||
// getRedisInfo gets Redis service info
|
||||
func (s *Service) getRedisInfo(name string) (map[string]interface{}, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
redisClient := cache.Get()
|
||||
if redisClient == nil {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"elapsed": fmt.Sprintf("%.1f", time.Since(startTime).Milliseconds()),
|
||||
"error": "Redis client not initialized",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Check health
|
||||
if !redisClient.Health() {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"elapsed": fmt.Sprintf("%.1f", time.Since(startTime).Milliseconds()),
|
||||
"error": "Redis health check failed",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "alive",
|
||||
"elapsed": fmt.Sprintf("%.1f", time.Since(startTime).Milliseconds()),
|
||||
"message": "Redis connection successful",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// randomString generate random string
|
||||
func randomString(n int) string {
|
||||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letters[time.Now().UnixNano()%int64(len(letters))]
|
||||
// getESClusterStats gets Elasticsearch cluster stats
|
||||
func (s *Service) getESClusterStats(name string) (map[string]interface{}, error) {
|
||||
// Check if Elasticsearch is the doc engine
|
||||
docEngine := os.Getenv("DOC_ENGINE")
|
||||
if docEngine == "" {
|
||||
docEngine = "elasticsearch"
|
||||
}
|
||||
return string(b)
|
||||
if docEngine != "elasticsearch" {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": "error: Elasticsearch is not in use.",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get ES config from server config
|
||||
cfg := server.GetConfig()
|
||||
if cfg == nil || cfg.DocEngine.ES == nil {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": "error: Elasticsearch configuration not found",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create ES engine and get cluster stats
|
||||
esEngine, err := elasticsearch.NewEngine(cfg.DocEngine.ES)
|
||||
if err != nil {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": fmt.Sprintf("error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
defer esEngine.Close()
|
||||
|
||||
clusterStats, err := esEngine.GetClusterStats()
|
||||
if err != nil {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": fmt.Sprintf("error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "alive",
|
||||
"message": clusterStats,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getInfinityStatus gets Infinity service status
|
||||
func (s *Service) getInfinityStatus(name string) (map[string]interface{}, error) {
|
||||
// TODO: Implement actual Infinity health check
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "unknown",
|
||||
"message": "Infinity health check not implemented",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// checkRAGFlowServerAlive checks if RAGFlow server is alive
|
||||
func (s *Service) checkRAGFlowServerAlive(name string) (map[string]interface{}, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
// Get ragflow config from allConfigs
|
||||
var host string
|
||||
var port int
|
||||
allConfigs := server.GetAllConfigs()
|
||||
for _, config := range allConfigs {
|
||||
if serviceType, ok := config["service_type"].(string); ok && serviceType == "ragflow_server" {
|
||||
if h, ok := config["host"].(string); ok {
|
||||
host = h
|
||||
}
|
||||
if p, ok := config["port"].(int); ok {
|
||||
port = p
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Default values
|
||||
if host == "" {
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
if port == 0 {
|
||||
port = 9380
|
||||
}
|
||||
|
||||
// Replace 0.0.0.0 with 127.0.0.1 for local check
|
||||
if host == "0.0.0.0" {
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("http://%s:%d/v1/system/ping", host, port)
|
||||
|
||||
// Create HTTP client with timeout
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": fmt.Sprintf("error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
elapsed := time.Since(startTime).Milliseconds()
|
||||
if resp.StatusCode == 200 {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "alive",
|
||||
"message": fmt.Sprintf("Confirm elapsed: %.1f ms.", float64(elapsed)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": fmt.Sprintf("Confirm elapsed: %.1f ms.", float64(elapsed)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// checkMinioAlive checks if MinIO is alive
|
||||
func (s *Service) checkMinioAlive(name string) (map[string]interface{}, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
// Get minio config from allConfigs
|
||||
var host string
|
||||
var secure bool
|
||||
var verify bool = true
|
||||
|
||||
allConfigs := server.GetAllConfigs()
|
||||
for _, config := range allConfigs {
|
||||
if serviceType, ok := config["service_type"].(string); ok && serviceType == "file_store" {
|
||||
// Get host from config
|
||||
if h, ok := config["host"].(string); ok {
|
||||
host = h
|
||||
}
|
||||
// Get secure from extra config
|
||||
if extra, ok := config["extra"].(map[string]interface{}); ok {
|
||||
if s, ok := extra["secure"].(bool); ok {
|
||||
secure = s
|
||||
} else if s, ok := extra["secure"].(string); ok {
|
||||
secure = s == "true" || s == "1" || s == "yes"
|
||||
}
|
||||
if v, ok := extra["verify"].(bool); ok {
|
||||
verify = v
|
||||
} else if v, ok := extra["verify"].(string); ok {
|
||||
verify = !(v == "false" || v == "0" || v == "no")
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Default host
|
||||
if host == "" {
|
||||
host = "localhost:9000"
|
||||
}
|
||||
|
||||
// Determine scheme
|
||||
scheme := "http"
|
||||
if secure {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s://%s/minio/health/live", scheme, host)
|
||||
|
||||
// Create HTTP client with timeout
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
// If verify is false, we need to skip SSL verification
|
||||
if !verify && scheme == "https" {
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": fmt.Sprintf("error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
elapsed := time.Since(startTime).Milliseconds()
|
||||
if resp.StatusCode == 200 {
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "alive",
|
||||
"message": fmt.Sprintf("Confirm elapsed: %.1f ms.", float64(elapsed)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "timeout",
|
||||
"message": fmt.Sprintf("Confirm elapsed: %.1f ms.", float64(elapsed)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// checkTaskExecutorAlive checks if task executor is alive
|
||||
func (s *Service) checkTaskExecutorAlive(name string) (map[string]interface{}, error) {
|
||||
// TODO: Implement actual task executor health check
|
||||
return map[string]interface{}{
|
||||
"service_name": name,
|
||||
"status": "unknown",
|
||||
"message": "Task executor health check not implemented",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ShutdownService shutdown service
|
||||
func (s *Service) ShutdownService(serviceID string) (map[string]interface{}, error) {
|
||||
// TODO: Implement with proper service manager
|
||||
return map[string]interface{}{
|
||||
"service_id": serviceID,
|
||||
"status": "shutdown",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RestartService restart service
|
||||
func (s *Service) RestartService(serviceID string) (map[string]interface{}, error) {
|
||||
// TODO: Implement with proper service manager
|
||||
return map[string]interface{}{
|
||||
"service_id": serviceID,
|
||||
"status": "restarted",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Variable/Settings methods
|
||||
|
||||
// GetVariable get variable
|
||||
func (s *Service) GetVariable(varName string) (map[string]interface{}, error) {
|
||||
// TODO: Implement with settings manager
|
||||
return map[string]interface{}{
|
||||
"var_name": varName,
|
||||
"var_value": "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAllVariables get all variables
|
||||
func (s *Service) GetAllVariables() ([]map[string]interface{}, error) {
|
||||
// TODO: Implement with settings manager
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// SetVariable set variable
|
||||
func (s *Service) SetVariable(varName, varValue string) error {
|
||||
// TODO: Implement with settings manager
|
||||
_ = varName
|
||||
_ = varValue
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config methods
|
||||
|
||||
// GetAllConfigs get all configs
|
||||
func (s *Service) GetAllConfigs() ([]map[string]interface{}, error) {
|
||||
// TODO: Implement with config manager
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// Environment methods
|
||||
|
||||
// GetAllEnvironments get all environments
|
||||
func (s *Service) GetAllEnvironments() ([]map[string]interface{}, error) {
|
||||
// TODO: Implement with environment manager
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// Version methods
|
||||
|
||||
// GetVersion get RAGFlow version
|
||||
func (s *Service) GetVersion() string {
|
||||
return utility.GetRAGFlowVersion()
|
||||
}
|
||||
|
||||
// Sandbox methods
|
||||
|
||||
// ListSandboxProviders list sandbox providers
|
||||
func (s *Service) ListSandboxProviders() ([]map[string]interface{}, error) {
|
||||
// TODO: Implement with sandbox manager
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GetSandboxProviderSchema get sandbox provider schema
|
||||
func (s *Service) GetSandboxProviderSchema(providerID string) (map[string]interface{}, error) {
|
||||
// TODO: Implement with sandbox manager
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// GetSandboxConfig get sandbox config
|
||||
func (s *Service) GetSandboxConfig() (map[string]interface{}, error) {
|
||||
// TODO: Implement with sandbox manager
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// SetSandboxConfig set sandbox config
|
||||
func (s *Service) SetSandboxConfig(providerType string, config map[string]interface{}, setActive bool) (map[string]interface{}, error) {
|
||||
// TODO: Implement with sandbox manager
|
||||
return map[string]interface{}{
|
||||
"provider_type": providerType,
|
||||
"config": config,
|
||||
"set_active": setActive,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TestSandboxConnection test sandbox connection
|
||||
func (s *Service) TestSandboxConnection(providerType string, config map[string]interface{}) (map[string]interface{}, error) {
|
||||
// TODO: Implement with sandbox manager
|
||||
return map[string]interface{}{
|
||||
"provider_type": providerType,
|
||||
"config": config,
|
||||
"connected": true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -93,7 +93,14 @@ func (dao *UserDAO) List(offset, limit int) ([]*model.User, int64, error) {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
err := DB.Offset(offset).Limit(limit).Find(&users).Error
|
||||
query := DB.Model(&model.User{})
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
err := query.Find(&users).Error
|
||||
return users, total, err
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package elasticsearch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"ragflow/internal/server"
|
||||
@@ -101,3 +102,144 @@ func (e *elasticsearchEngine) Close() error {
|
||||
// Go-elasticsearch client doesn't have a Close method, connection is managed by the transport
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClusterStats gets Elasticsearch cluster statistics
|
||||
// Reference: curl -XGET "http://{es_host}/_cluster/stats" -H "kbn-xsrf: reporting"
|
||||
func (e *elasticsearchEngine) GetClusterStats() (map[string]interface{}, error) {
|
||||
req := esapi.ClusterStatsRequest{}
|
||||
res, err := req.Do(context.Background(), e.client)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get cluster stats: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
return nil, fmt.Errorf("elasticsearch cluster stats returned error: %s", res.Status())
|
||||
}
|
||||
|
||||
var rawStats map[string]interface{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&rawStats); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode cluster stats: %w", err)
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
|
||||
// Basic cluster info
|
||||
if clusterName, ok := rawStats["cluster_name"].(string); ok {
|
||||
result["cluster_name"] = clusterName
|
||||
}
|
||||
if status, ok := rawStats["status"].(string); ok {
|
||||
result["status"] = status
|
||||
}
|
||||
|
||||
// Indices info
|
||||
if indices, ok := rawStats["indices"].(map[string]interface{}); ok {
|
||||
if count, ok := indices["count"].(float64); ok {
|
||||
result["indices"] = int(count)
|
||||
}
|
||||
if shards, ok := indices["shards"].(map[string]interface{}); ok {
|
||||
if total, ok := shards["total"].(float64); ok {
|
||||
result["indices_shards"] = int(total)
|
||||
}
|
||||
}
|
||||
if docs, ok := indices["docs"].(map[string]interface{}); ok {
|
||||
if docCount, ok := docs["count"].(float64); ok {
|
||||
result["docs"] = int64(docCount)
|
||||
}
|
||||
if deleted, ok := docs["deleted"].(float64); ok {
|
||||
result["docs_deleted"] = int64(deleted)
|
||||
}
|
||||
}
|
||||
if store, ok := indices["store"].(map[string]interface{}); ok {
|
||||
if sizeInBytes, ok := store["size_in_bytes"].(float64); ok {
|
||||
result["store_size"] = convertBytes(int64(sizeInBytes))
|
||||
}
|
||||
if totalDataSetSize, ok := store["total_data_set_size_in_bytes"].(float64); ok {
|
||||
result["total_dataset_size"] = convertBytes(int64(totalDataSetSize))
|
||||
}
|
||||
}
|
||||
if mappings, ok := indices["mappings"].(map[string]interface{}); ok {
|
||||
if fieldCount, ok := mappings["total_field_count"].(float64); ok {
|
||||
result["mappings_fields"] = int(fieldCount)
|
||||
}
|
||||
if dedupFieldCount, ok := mappings["total_deduplicated_field_count"].(float64); ok {
|
||||
result["mappings_deduplicated_fields"] = int(dedupFieldCount)
|
||||
}
|
||||
if dedupSize, ok := mappings["total_deduplicated_mapping_size_in_bytes"].(float64); ok {
|
||||
result["mappings_deduplicated_size"] = convertBytes(int64(dedupSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes info
|
||||
if nodes, ok := rawStats["nodes"].(map[string]interface{}); ok {
|
||||
if count, ok := nodes["count"].(map[string]interface{}); ok {
|
||||
if total, ok := count["total"].(float64); ok {
|
||||
result["nodes"] = int(total)
|
||||
}
|
||||
}
|
||||
if versions, ok := nodes["versions"].([]interface{}); ok {
|
||||
result["nodes_version"] = versions
|
||||
}
|
||||
if os, ok := nodes["os"].(map[string]interface{}); ok {
|
||||
if mem, ok := os["mem"].(map[string]interface{}); ok {
|
||||
if totalInBytes, ok := mem["total_in_bytes"].(float64); ok {
|
||||
result["os_mem"] = convertBytes(int64(totalInBytes))
|
||||
}
|
||||
if usedInBytes, ok := mem["used_in_bytes"].(float64); ok {
|
||||
result["os_mem_used"] = convertBytes(int64(usedInBytes))
|
||||
}
|
||||
if usedPercent, ok := mem["used_percent"].(float64); ok {
|
||||
result["os_mem_used_percent"] = usedPercent
|
||||
}
|
||||
}
|
||||
}
|
||||
if jvm, ok := nodes["jvm"].(map[string]interface{}); ok {
|
||||
if versions, ok := jvm["versions"].([]interface{}); ok && len(versions) > 0 {
|
||||
if version0, ok := versions[0].(map[string]interface{}); ok {
|
||||
if vmVersion, ok := version0["vm_version"].(string); ok {
|
||||
result["jvm_versions"] = vmVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
if mem, ok := jvm["mem"].(map[string]interface{}); ok {
|
||||
if heapUsed, ok := mem["heap_used_in_bytes"].(float64); ok {
|
||||
result["jvm_heap_used"] = convertBytes(int64(heapUsed))
|
||||
}
|
||||
if heapMax, ok := mem["heap_max_in_bytes"].(float64); ok {
|
||||
result["jvm_heap_max"] = convertBytes(int64(heapMax))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// convertBytes converts bytes to human readable format
|
||||
func convertBytes(bytes int64) string {
|
||||
const (
|
||||
KB = 1024
|
||||
MB = 1024 * KB
|
||||
GB = 1024 * MB
|
||||
TB = 1024 * GB
|
||||
PB = 1024 * TB
|
||||
)
|
||||
|
||||
if bytes >= PB {
|
||||
return fmt.Sprintf("%.2f pb", float64(bytes)/float64(PB))
|
||||
}
|
||||
if bytes >= TB {
|
||||
return fmt.Sprintf("%.2f tb", float64(bytes)/float64(TB))
|
||||
}
|
||||
if bytes >= GB {
|
||||
return fmt.Sprintf("%.2f gb", float64(bytes)/float64(GB))
|
||||
}
|
||||
if bytes >= MB {
|
||||
return fmt.Sprintf("%.2f mb", float64(bytes)/float64(MB))
|
||||
}
|
||||
if bytes >= KB {
|
||||
return fmt.Sprintf("%.2f kb", float64(bytes)/float64(KB))
|
||||
}
|
||||
return fmt.Sprintf("%d b", bytes)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -111,6 +112,7 @@ var (
|
||||
globalConfig *Config
|
||||
globalViper *viper.Viper
|
||||
zapLogger *zap.Logger
|
||||
allConfigs []map[string]interface{}
|
||||
)
|
||||
|
||||
// Init initialize configuration
|
||||
@@ -147,6 +149,153 @@ func Init(configPath string) error {
|
||||
// Save viper instance
|
||||
globalViper = v
|
||||
|
||||
docEngine := os.Getenv("DOC_ENGINE")
|
||||
if docEngine == "" {
|
||||
docEngine = "elasticsearch"
|
||||
}
|
||||
id := 0
|
||||
for k, v := range globalViper.AllSettings() {
|
||||
configDict, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch k {
|
||||
case "ragflow":
|
||||
configDict["id"] = id
|
||||
configDict["name"] = fmt.Sprintf("ragflow_%d", id)
|
||||
configDict["service_type"] = "ragflow_server"
|
||||
configDict["extra"] = map[string]interface{}{}
|
||||
configDict["port"] = configDict["http_port"]
|
||||
delete(configDict, "http_port")
|
||||
case "es":
|
||||
// Skip if retrieval_type doesn't match doc_engine
|
||||
if docEngine != "elasticsearch" {
|
||||
continue
|
||||
}
|
||||
hosts := getString(configDict, "hosts")
|
||||
host, port := parseHostPort(hosts)
|
||||
username := getString(configDict, "username")
|
||||
password := getString(configDict, "password")
|
||||
configDict["id"] = id
|
||||
configDict["name"] = "elasticsearch"
|
||||
configDict["host"] = host
|
||||
configDict["port"] = port
|
||||
configDict["service_type"] = "retrieval"
|
||||
configDict["extra"] = map[string]interface{}{
|
||||
"retrieval_type": "elasticsearch",
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
delete(configDict, "hosts")
|
||||
delete(configDict, "username")
|
||||
delete(configDict, "password")
|
||||
case "infinity":
|
||||
// Skip if retrieval_type doesn't match doc_engine
|
||||
if docEngine != "infinity" {
|
||||
continue
|
||||
}
|
||||
uri := getString(configDict, "uri")
|
||||
host, port := parseHostPort(uri)
|
||||
dbName := getString(configDict, "db_name")
|
||||
if dbName == "" {
|
||||
dbName = "default_db"
|
||||
}
|
||||
configDict["id"] = id
|
||||
configDict["name"] = "infinity"
|
||||
configDict["host"] = host
|
||||
configDict["port"] = port
|
||||
configDict["service_type"] = "retrieval"
|
||||
configDict["extra"] = map[string]interface{}{
|
||||
"retrieval_type": "infinity",
|
||||
"db_name": dbName,
|
||||
}
|
||||
case "minio":
|
||||
hostPort := getString(configDict, "host")
|
||||
host, port := parseHostPort(hostPort)
|
||||
user := getString(configDict, "user")
|
||||
password := getString(configDict, "password")
|
||||
configDict["id"] = id
|
||||
configDict["name"] = "minio"
|
||||
configDict["host"] = host
|
||||
configDict["port"] = port
|
||||
configDict["service_type"] = "file_store"
|
||||
configDict["extra"] = map[string]interface{}{
|
||||
"store_type": "minio",
|
||||
"user": user,
|
||||
"password": password,
|
||||
}
|
||||
delete(configDict, "bucket")
|
||||
delete(configDict, "user")
|
||||
delete(configDict, "password")
|
||||
case "redis":
|
||||
hostPort := getString(configDict, "host")
|
||||
host, port := parseHostPort(hostPort)
|
||||
password := getString(configDict, "password")
|
||||
db := getInt(configDict, "db")
|
||||
configDict["id"] = id
|
||||
configDict["name"] = "redis"
|
||||
configDict["host"] = host
|
||||
configDict["port"] = port
|
||||
configDict["service_type"] = "message_queue"
|
||||
configDict["extra"] = map[string]interface{}{
|
||||
"mq_type": "redis",
|
||||
"database": db,
|
||||
"password": password,
|
||||
}
|
||||
delete(configDict, "password")
|
||||
delete(configDict, "db")
|
||||
case "mysql":
|
||||
host := getString(configDict, "host")
|
||||
port := getInt(configDict, "port")
|
||||
user := getString(configDict, "user")
|
||||
password := getString(configDict, "password")
|
||||
configDict["id"] = id
|
||||
configDict["name"] = "mysql"
|
||||
configDict["host"] = host
|
||||
configDict["port"] = port
|
||||
configDict["service_type"] = "meta_data"
|
||||
configDict["extra"] = map[string]interface{}{
|
||||
"meta_type": "mysql",
|
||||
"username": user,
|
||||
"password": password,
|
||||
}
|
||||
delete(configDict, "stale_timeout")
|
||||
delete(configDict, "max_connections")
|
||||
delete(configDict, "max_allowed_packet")
|
||||
delete(configDict, "user")
|
||||
delete(configDict, "password")
|
||||
case "task_executor":
|
||||
mqType := getString(configDict, "message_queue_type")
|
||||
configDict["id"] = id
|
||||
configDict["name"] = "task_executor"
|
||||
configDict["service_type"] = "task_executor"
|
||||
configDict["extra"] = map[string]interface{}{
|
||||
"message_queue_type": mqType,
|
||||
}
|
||||
delete(configDict, "message_queue_type")
|
||||
case "admin":
|
||||
// Skip admin section
|
||||
continue
|
||||
default:
|
||||
// Skip unknown sections
|
||||
continue
|
||||
}
|
||||
|
||||
// Set default values for empty host/port
|
||||
if configDict["host"] == "" {
|
||||
configDict["host"] = "-"
|
||||
}
|
||||
if configDict["port"] == 0 {
|
||||
configDict["port"] = "-"
|
||||
}
|
||||
|
||||
delete(configDict, "prefix_path")
|
||||
delete(configDict, "username")
|
||||
allConfigs = append(allConfigs, configDict)
|
||||
id++
|
||||
}
|
||||
|
||||
// Unmarshal configuration to globalConfig
|
||||
// Note: This will only unmarshal fields that match the Config struct
|
||||
if err := v.Unmarshal(&globalConfig); err != nil {
|
||||
@@ -278,6 +427,14 @@ func SetLogger(l *zap.Logger) {
|
||||
zapLogger = l
|
||||
}
|
||||
|
||||
func GetGlobalViperConfig() *viper.Viper {
|
||||
return globalViper
|
||||
}
|
||||
|
||||
func GetAllConfigs() []map[string]interface{} {
|
||||
return allConfigs
|
||||
}
|
||||
|
||||
// PrintAll prints all configuration settings
|
||||
func PrintAll() {
|
||||
if globalViper == nil {
|
||||
@@ -292,3 +449,46 @@ func PrintAll() {
|
||||
}
|
||||
zapLogger.Info("=== End Configuration ===")
|
||||
}
|
||||
|
||||
// parseHostPort parses host:port string and returns host and port
|
||||
func parseHostPort(hostPort string) (string, int) {
|
||||
if hostPort == "" {
|
||||
return "", 0
|
||||
}
|
||||
|
||||
// Handle URL format like http://host:port
|
||||
if strings.Contains(hostPort, "://") {
|
||||
u, err := url.Parse(hostPort)
|
||||
if err == nil {
|
||||
hostPort = u.Host
|
||||
}
|
||||
}
|
||||
|
||||
// Split host:port
|
||||
parts := strings.Split(hostPort, ":")
|
||||
host := parts[0]
|
||||
port := 0
|
||||
if len(parts) > 1 {
|
||||
port, _ = strconv.Atoi(parts[1])
|
||||
}
|
||||
return host, port
|
||||
}
|
||||
|
||||
// getString gets string value from map
|
||||
func getString(m map[string]interface{}, key string) string {
|
||||
if v, ok := m[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getInt gets int value from map
|
||||
func getInt(m map[string]interface{}, key string) int {
|
||||
if v, ok := m[key].(int); ok {
|
||||
return v
|
||||
}
|
||||
if v, ok := m[key].(float64); ok {
|
||||
return int(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
|
||||
"ragflow/internal/dao"
|
||||
@@ -123,8 +122,8 @@ func (s *UserService) Register(req *RegisterRequest) (*model.User, common.ErrorC
|
||||
return nil, common.CodeServerError, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
userID := s.GenerateToken()
|
||||
accessToken := s.GenerateToken()
|
||||
userID := utility.GenerateToken()
|
||||
accessToken := utility.GenerateToken()
|
||||
status := "1"
|
||||
loginChannel := "password"
|
||||
isSuperuser := false
|
||||
@@ -167,7 +166,7 @@ func (s *UserService) Register(req *RegisterRequest) (*model.User, common.ErrorC
|
||||
tenant.CreateDate = &now_date
|
||||
tenant.UpdateDate = &now_date
|
||||
|
||||
userTenantID := s.GenerateToken()
|
||||
userTenantID := utility.GenerateToken()
|
||||
userTenant := &model.UserTenant{
|
||||
ID: userTenantID,
|
||||
UserID: userID,
|
||||
@@ -181,7 +180,7 @@ func (s *UserService) Register(req *RegisterRequest) (*model.User, common.ErrorC
|
||||
userTenant.CreateDate = &now_date
|
||||
userTenant.UpdateDate = &now_date
|
||||
|
||||
fileID := s.GenerateToken()
|
||||
fileID := utility.GenerateToken()
|
||||
rootFile := &model.File{
|
||||
ID: fileID,
|
||||
ParentID: fileID,
|
||||
@@ -267,7 +266,7 @@ func (s *UserService) Login(req *LoginRequest) (*model.User, common.ErrorCode, e
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
token := s.GenerateToken()
|
||||
token := utility.GenerateToken()
|
||||
if err := s.UpdateUserAccessToken(user, token); err != nil {
|
||||
return nil, common.CodeServerError, fmt.Errorf("failed to update access token: %w", err)
|
||||
}
|
||||
@@ -310,7 +309,8 @@ func (s *UserService) LoginByEmail(req *EmailLoginRequest) (*model.User, common.
|
||||
return nil, common.CodeForbidden, fmt.Errorf("This account has been disabled, please contact the administrator!")
|
||||
}
|
||||
|
||||
token := s.GenerateToken()
|
||||
// Generate new access token
|
||||
token := utility.GenerateToken()
|
||||
user.AccessToken = &token
|
||||
|
||||
now := time.Now().Unix()
|
||||
@@ -515,11 +515,6 @@ func (s *UserService) decryptPassword(encryptedPassword string) (string, error)
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
// GenerateToken generates a new access token
|
||||
func (s *UserService) GenerateToken() string {
|
||||
return strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
}
|
||||
|
||||
// GetUserByToken gets user by authorization header
|
||||
// The token parameter is the authorization header value, which needs to be decrypted
|
||||
// using itsdangerous URLSafeTimedSerializer to get the actual access_token
|
||||
@@ -558,7 +553,7 @@ func (s *UserService) UpdateUserAccessToken(user *model.User, token string) erro
|
||||
func (s *UserService) Logout(user *model.User) (common.ErrorCode, error) {
|
||||
// Invalidate token by setting it to an invalid value
|
||||
// Similar to Python implementation: "INVALID_" + secrets.token_hex(16)
|
||||
invalidToken := "INVALID_" + s.GenerateToken()
|
||||
invalidToken := "INVALID_" + utility.GenerateToken()
|
||||
err := s.UpdateUserAccessToken(user, invalidToken)
|
||||
if err != nil {
|
||||
return common.CodeServerError, err
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/iromli/go-itsdangerous"
|
||||
)
|
||||
|
||||
@@ -133,3 +134,7 @@ func GenerateSecretKey() (string, error) {
|
||||
}
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
func GenerateToken() string {
|
||||
return strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ Example: A 1 KB message with 1024-dim embedding uses ~9 KB. The 5 MB default lim
|
||||
manualSetup: 'Pipeline',
|
||||
builtIn: 'Built-in',
|
||||
titleDescription:
|
||||
'Update your memory configuration here, particularly the LLM and prompts.',
|
||||
'Update your dataset configuration here, particularly the LLM and prompts.',
|
||||
name: 'Dataset name',
|
||||
photo: 'Dataset photo',
|
||||
photoTip: 'You can upload an image up to 4 MB.',
|
||||
|
||||
@@ -149,7 +149,7 @@ request.interceptors.response.use(
|
||||
console.log('🚀 ~ error:', error);
|
||||
|
||||
// Handle HTTP 401 (token expired / invalid)
|
||||
const status = error?.response?.status;
|
||||
const status = error?.response?.status;
|
||||
if (status === 401) {
|
||||
if (!isRedirecting) {
|
||||
isRedirecting = true;
|
||||
@@ -164,8 +164,8 @@ request.interceptors.response.use(
|
||||
redirectToLogin();
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
errorHandler(error);
|
||||
return Promise.reject(error);
|
||||
|
||||
@@ -117,10 +117,12 @@ request.interceptors.response.use(async (response: Response, options) => {
|
||||
if (!isRedirecting) {
|
||||
isRedirecting = true;
|
||||
|
||||
const data = await response.clone().json().catch(() => ({}));
|
||||
const data = await response
|
||||
.clone()
|
||||
.json()
|
||||
.catch(() => ({}));
|
||||
|
||||
const messageText =
|
||||
data?.message || RetcodeMessage[401];
|
||||
const messageText = data?.message || RetcodeMessage[401];
|
||||
notification.error({
|
||||
message: messageText,
|
||||
description: messageText,
|
||||
|
||||
Reference in New Issue
Block a user