// // 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 admin import ( "errors" "net/http" "ragflow/internal/server" "ragflow/internal/service" "ragflow/internal/utility" "strconv" "github.com/gin-gonic/gin" ) // Common errors var ( ErrInvalidCredentials = errors.New("invalid credentials") ErrUserNotFound = errors.New("user not found") ErrInvalidToken = errors.New("invalid token") ) // Handler admin handler type Handler struct { service *Service userService *service.UserService } // NewHandler create admin handler func NewHandler(service *Service, userService *service.UserService) *Handler { return &Handler{service: service, userService: userService} } // SuccessResponse success response type SuccessResponse struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data"` } // ErrorResponse error response type ErrorResponse struct { Code int `json:"code"` Message string `json:"message"` } // success returns success response func success(c *gin.Context, data interface{}, message string) { c.JSON(200, SuccessResponse{ Code: 0, Message: message, Data: data, }) } // successNoData returns success response without data func successNoData(c *gin.Context, message string) { c.JSON(200, SuccessResponse{ Code: 0, Message: message, Data: nil, }) } // error returns error response func errorResponse(c *gin.Context, message string, code int) { c.JSON(code, ErrorResponse{ Code: code, Message: message, }) } // Health health check func (h *Handler) Health(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) } // Ping ping endpoint func (h *Handler) Ping(c *gin.Context) { successNoData(c, "PONG") } // LoginHTTPRequest login request body type LoginHTTPRequest struct { Email string `json:"email" binding:"required"` Password string `json:"password" binding:"required"` } // Login handle admin login func (h *Handler) Login(c *gin.Context) { var req service.EmailLoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": err.Error(), }) return } user, code, err := h.userService.LoginByEmail(&req) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{ "code": code, "message": err.Error(), }) return } variables := server.GetVariables() secretKey := variables.SecretKey authToken, err := utility.DumpAccessToken(*user.AccessToken, secretKey) // Set Authorization header with access_token if user.AccessToken != nil { c.Header("Authorization", authToken) } // Set CORS headers c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "*") c.Header("Access-Control-Allow-Headers", "*") c.Header("Access-Control-Expose-Headers", "Authorization") c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "Login successful", }) } // Logout handle logout func (h *Handler) Logout(c *gin.Context) { user, exists := c.Get("user") if !exists { errorResponse(c, "Not authenticated", 401) return } if err := h.service.Logout(user); err != nil { errorResponse(c, err.Error(), 500) return } successNoData(c, "Logout successful") } // AuthCheck check admin auth func (h *Handler) AuthCheck(c *gin.Context) { successNoData(c, "Admin is authorized") } // ListUsers handle list users func (h *Handler) ListUsers(c *gin.Context) { users, err := h.service.ListUsers() if err != nil { errorResponse(c, err.Error(), 500) return } success(c, users, "Get all users") } // CreateUserHTTPRequest create user request type CreateUserHTTPRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` Role string `json:"role"` } // CreateUser handle create user func (h *Handler) CreateUser(c *gin.Context) { var req CreateUserHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Username and password are required", 400) return } if req.Role == "" { req.Role = "user" } userInfo, err := h.service.CreateUser(req.Username, req.Password, req.Role) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, userInfo, "User created successfully") } // GetUser handle get user func (h *Handler) GetUser(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } userDetails, err := h.service.GetUserDetails(username) if err != nil { if errors.Is(err, ErrUserNotFound) { errorResponse(c, "User not found", 404) return } errorResponse(c, err.Error(), 500) return } success(c, userDetails, "") } // DeleteUser handle delete user func (h *Handler) DeleteUser(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } if err := h.service.DeleteUser(username); err != nil { errorResponse(c, err.Error(), 500) return } successNoData(c, "User deleted successfully") } // ChangePasswordHTTPRequest change password request type ChangePasswordHTTPRequest struct { NewPassword string `json:"new_password" binding:"required"` } // ChangePassword handle change password func (h *Handler) ChangePassword(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } var req ChangePasswordHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "New password is required", 400) return } if err := h.service.ChangePassword(username, req.NewPassword); err != nil { errorResponse(c, err.Error(), 500) return } successNoData(c, "Password updated successfully") } // UpdateActivateStatusHTTPRequest update activate status request type UpdateActivateStatusHTTPRequest struct { ActivateStatus bool `json:"activate_status" binding:"required"` } // UpdateUserActivateStatus handle update user activate status func (h *Handler) UpdateUserActivateStatus(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } var req UpdateActivateStatusHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Activation status is required", 400) return } if err := h.service.UpdateUserActivateStatus(username, req.ActivateStatus); err != nil { errorResponse(c, err.Error(), 500) return } successNoData(c, "Activation status updated") } // GrantAdmin handle grant admin role func (h *Handler) GrantAdmin(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } // Get current user from context currentUser, _ := c.Get("user") if currentUser != nil && currentUser.(string) == username { errorResponse(c, "can't grant current user: "+username, 409) return } if err := h.service.GrantAdmin(username); err != nil { errorResponse(c, err.Error(), 500) return } successNoData(c, "Admin role granted") } // RevokeAdmin handle revoke admin role func (h *Handler) RevokeAdmin(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } // Get current user from context currentUser, _ := c.Get("user") if currentUser != nil && currentUser.(string) == username { errorResponse(c, "can't revoke current user: "+username, 409) return } if err := h.service.RevokeAdmin(username); err != nil { errorResponse(c, err.Error(), 500) return } successNoData(c, "Admin role revoked") } // GetUserDatasets handle get user datasets func (h *Handler) GetUserDatasets(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } datasets, err := h.service.GetUserDatasets(username) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, datasets, "") } // GetUserAgents handle get user agents func (h *Handler) GetUserAgents(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } agents, err := h.service.GetUserAgents(username) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, agents, "") } // GetUserAPIKeys handle get user API keys func (h *Handler) GetUserAPIKeys(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } apiKeys, err := h.service.GetUserAPIKeys(username) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, apiKeys, "Get user API keys") } // GenerateUserAPIKey handle generate user API key func (h *Handler) GenerateUserAPIKey(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } apiKey, err := h.service.GenerateUserAPIKey(username) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, apiKey, "API key generated successfully") } // DeleteUserAPIKey handle delete user API key func (h *Handler) DeleteUserAPIKey(c *gin.Context) { username := c.Param("username") key := c.Param("key") if username == "" || key == "" { errorResponse(c, "Username and key are required", 400) return } if err := h.service.DeleteUserAPIKey(username, key); err != nil { errorResponse(c, err.Error(), 404) return } successNoData(c, "API key deleted successfully") } // ListRoles handle list roles func (h *Handler) ListRoles(c *gin.Context) { roles, err := h.service.ListRoles() if err != nil { errorResponse(c, err.Error(), 500) return } success(c, roles, "") } // CreateRoleHTTPRequest create role request type CreateRoleHTTPRequest struct { RoleName string `json:"role_name" binding:"required"` Description string `json:"description"` } // CreateRole handle create role func (h *Handler) CreateRole(c *gin.Context) { var req CreateRoleHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Role name is required", 400) return } role, err := h.service.CreateRole(req.RoleName, req.Description) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, role, "") } // GetRole handle get role func (h *Handler) GetRole(c *gin.Context) { roleName := c.Param("role_name") if roleName == "" { errorResponse(c, "Role name is required", 400) return } role, err := h.service.GetRole(roleName) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, role, "") } // UpdateRoleHTTPRequest update role request type UpdateRoleHTTPRequest struct { Description string `json:"description" binding:"required"` } // UpdateRole handle update role func (h *Handler) UpdateRole(c *gin.Context) { roleName := c.Param("role_name") if roleName == "" { errorResponse(c, "Role name is required", 400) return } var req UpdateRoleHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Role description is required", 400) return } role, err := h.service.UpdateRole(roleName, req.Description) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, role, "") } // DeleteRole handle delete role func (h *Handler) DeleteRole(c *gin.Context) { roleName := c.Param("role_name") if roleName == "" { errorResponse(c, "Role name is required", 400) return } if err := h.service.DeleteRole(roleName); err != nil { errorResponse(c, err.Error(), 500) return } successNoData(c, "") } // GetRolePermission handle get role permission func (h *Handler) GetRolePermission(c *gin.Context) { roleName := c.Param("role_name") if roleName == "" { errorResponse(c, "Role name is required", 400) return } permissions, err := h.service.GetRolePermission(roleName) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, permissions, "") } // GrantRolePermissionHTTPRequest grant role permission request type GrantRolePermissionHTTPRequest struct { Actions []string `json:"actions" binding:"required"` Resource string `json:"resource" binding:"required"` } // GrantRolePermission handle grant role permission func (h *Handler) GrantRolePermission(c *gin.Context) { roleName := c.Param("role_name") if roleName == "" { errorResponse(c, "Role name is required", 400) return } var req GrantRolePermissionHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Permission is required", 400) return } result, err := h.service.GrantRolePermission(roleName, req.Actions, req.Resource) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, result, "") } // RevokeRolePermissionHTTPRequest revoke role permission request type RevokeRolePermissionHTTPRequest struct { Actions []string `json:"actions" binding:"required"` Resource string `json:"resource" binding:"required"` } // RevokeRolePermission handle revoke role permission func (h *Handler) RevokeRolePermission(c *gin.Context) { roleName := c.Param("role_name") if roleName == "" { errorResponse(c, "Role name is required", 400) return } var req RevokeRolePermissionHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Permission is required", 400) return } result, err := h.service.RevokeRolePermission(roleName, req.Actions, req.Resource) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, result, "") } // UpdateUserRoleHTTPRequest update user role request type UpdateUserRoleHTTPRequest struct { RoleName string `json:"role_name" binding:"required"` } // UpdateUserRole handle update user role func (h *Handler) UpdateUserRole(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } var req UpdateUserRoleHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Role name is required", 400) return } result, err := h.service.UpdateUserRole(username, req.RoleName) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, result, "") } // GetUserPermission handle get user permission func (h *Handler) GetUserPermission(c *gin.Context) { username := c.Param("username") if username == "" { errorResponse(c, "Username is required", 400) return } permissions, err := h.service.GetUserPermission(username) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, permissions, "") } // GetServices handle get all services func (h *Handler) GetServices(c *gin.Context) { services, err := h.service.GetAllServices() if err != nil { errorResponse(c, err.Error(), 500) return } success(c, services, "Get all services") } // GetServicesByType handle get services by type func (h *Handler) GetServicesByType(c *gin.Context) { serviceType := c.Param("service_type") if serviceType == "" { errorResponse(c, "Service type is required", 400) return } services, err := h.service.GetServicesByType(serviceType) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, services, "") } // GetService handle get service details func (h *Handler) GetService(c *gin.Context) { serviceID := c.Param("service_id") if serviceID == "" { errorResponse(c, "Service ID is required", 400) return } // Get all services and find the one with matching ID allConfigs := server.GetAllConfigs() var targetService map[string]interface{} for _, config := range allConfigs { if id, ok := config["id"]; ok { if strconv.Itoa(id.(int)) == serviceID { targetService = config break } } } if targetService == nil { errorResponse(c, "Service not found", 404) return } serviceStatus, err := h.service.GetServiceDetails(targetService) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, serviceStatus, "") } // ShutdownService handle shutdown service func (h *Handler) ShutdownService(c *gin.Context) { serviceID := c.Param("service_id") if serviceID == "" { errorResponse(c, "Service ID is required", 400) return } result, err := h.service.ShutdownService(serviceID) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, result, "") } // RestartService handle restart service func (h *Handler) RestartService(c *gin.Context) { serviceID := c.Param("service_id") if serviceID == "" { errorResponse(c, "Service ID is required", 400) return } result, err := h.service.RestartService(serviceID) if err != nil { errorResponse(c, err.Error(), 500) return } success(c, result, "") } // GetVariables handle get variables func (h *Handler) GetVariables(c *gin.Context) { varName := c.Query("var_name") if varName != "" { // Get single variable variable, err := h.service.GetVariable(varName) if err != nil { errorResponse(c, err.Error(), 400) return } success(c, variable, "") return } // List all variables variables, err := h.service.GetAllVariables() if err != nil { errorResponse(c, err.Error(), 500) return } success(c, variables, "") } // SetVariableHTTPRequest set variable request type SetVariableHTTPRequest struct { VarName string `json:"var_name" binding:"required"` VarValue string `json:"var_value" binding:"required"` } // SetVariable handle set variable func (h *Handler) SetVariable(c *gin.Context) { var req SetVariableHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Var name and value are required", 400) return } if err := h.service.SetVariable(req.VarName, req.VarValue); err != nil { errorResponse(c, err.Error(), 400) return } successNoData(c, "Set variable successfully") } // GetConfigs handle get configs func (h *Handler) GetConfigs(c *gin.Context) { configs, err := h.service.GetAllConfigs() if err != nil { errorResponse(c, err.Error(), 400) return } success(c, configs, "") } // GetEnvironments handle get environments func (h *Handler) GetEnvironments(c *gin.Context) { environments, err := h.service.GetAllEnvironments() if err != nil { errorResponse(c, err.Error(), 400) return } success(c, environments, "") } // GetVersion handle get version func (h *Handler) GetVersion(c *gin.Context) { version := h.service.GetVersion() success(c, gin.H{"version": version}, "") } // ListSandboxProviders handle list sandbox providers func (h *Handler) ListSandboxProviders(c *gin.Context) { providers, err := h.service.ListSandboxProviders() if err != nil { errorResponse(c, err.Error(), 400) return } success(c, providers, "") } // GetSandboxProviderSchema handle get sandbox provider schema func (h *Handler) GetSandboxProviderSchema(c *gin.Context) { providerID := c.Param("provider_id") if providerID == "" { errorResponse(c, "Provider ID is required", 400) return } schema, err := h.service.GetSandboxProviderSchema(providerID) if err != nil { errorResponse(c, err.Error(), 400) return } success(c, schema, "") } // GetSandboxConfig handle get sandbox config func (h *Handler) GetSandboxConfig(c *gin.Context) { config, err := h.service.GetSandboxConfig() if err != nil { errorResponse(c, err.Error(), 400) return } success(c, config, "") } // SetSandboxConfigHTTPRequest set sandbox config request type SetSandboxConfigHTTPRequest struct { ProviderType string `json:"provider_type" binding:"required"` Config map[string]interface{} `json:"config"` SetActive bool `json:"set_active"` } // SetSandboxConfig handle set sandbox config func (h *Handler) SetSandboxConfig(c *gin.Context) { var req SetSandboxConfigHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Request body is required", 400) return } if req.ProviderType == "" { errorResponse(c, "provider_type is required", 400) return } // Default to true for backward compatibility _ = c.Request.Body.Close() req.SetActive = true result, err := h.service.SetSandboxConfig(req.ProviderType, req.Config, req.SetActive) if err != nil { errorResponse(c, err.Error(), 400) return } success(c, result, "Sandbox configuration updated successfully") } // TestSandboxConnectionHTTPRequest test sandbox connection request type TestSandboxConnectionHTTPRequest struct { ProviderType string `json:"provider_type" binding:"required"` Config map[string]interface{} `json:"config"` } // TestSandboxConnection handle test sandbox connection func (h *Handler) TestSandboxConnection(c *gin.Context) { var req TestSandboxConnectionHTTPRequest if err := c.ShouldBindJSON(&req); err != nil { errorResponse(c, "Request body is required", 400) return } if req.ProviderType == "" { errorResponse(c, "provider_type is required", 400) return } result, err := h.service.TestSandboxConnection(req.ProviderType, req.Config) if err != nil { errorResponse(c, err.Error(), 400) return } success(c, result, "") } // AuthMiddleware JWT auth middleware func (h *Handler) AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { token := c.GetHeader("Authorization") if token == "" { errorResponse(c, "missing authorization header", 401) c.Abort() return } // Get user by access token user, code, err := h.userService.GetUserByToken(token) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{ "code": code, "message": "Invalid access token", }) return } c.Set("user", user) c.Set("user_id", user.ID) c.Set("email", user.Email) c.Next() } } // HandleNoRoute handle undefined routes func (h *Handler) HandleNoRoute(c *gin.Context) { c.JSON(http.StatusNotFound, ErrorResponse{ Code: 404, Message: "The requested resource was not found", }) }