Feature (System Settings): Implemented system settings management functionality (#13556)

### What problem does this PR solve?

Feature (System Settings): Implemented system settings management
functionality

- Added a new SystemSettings model, including creation and update time
fields.

- Implemented SystemSettingsDAO, providing CRUD operations and
transaction support.

- Implemented management interfaces for variables, configurations, and
environment variables in the admin service.

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

Co-authored-by: Yingfeng <yingfeng.zhang@gmail.com>
This commit is contained in:
chanx
2026-03-12 19:06:20 +08:00
committed by GitHub
parent 7c79602c77
commit 1df804a14a
4 changed files with 410 additions and 39 deletions

View File

@@ -767,28 +767,46 @@ func (h *Handler) RestartService(c *gin.Context) {
}
// GetVariables handle get variables
// Python logic: if request body is empty, list all variables; otherwise get single variable by var_name from body
func (h *Handler) GetVariables(c *gin.Context) {
varName := c.Query("var_name")
if varName != "" {
// Get single variable
variable, err := h.service.GetVariable(varName)
// Check if request has body content
if c.Request.ContentLength == 0 || c.Request.ContentLength == -1 {
// List all variables
variables, err := h.service.GetAllVariables()
if err != nil {
errorResponse(c, err.Error(), 400)
errorResponse(c, err.Error(), 500)
return
}
success(c, variable, "")
success(c, variables, "")
return
}
// List all variables
variables, err := h.service.GetAllVariables()
// Get single variable by var_name from request body
var req struct {
VarName string `json:"var_name"`
}
if err := c.ShouldBindJSON(&req); err != nil {
errorResponse(c, "Invalid request body", 400)
return
}
if req.VarName == "" {
errorResponse(c, "Var name is required", 400)
return
}
variable, err := h.service.GetVariable(req.VarName)
if err != nil {
// Check if it's an AdminException
if adminErr, ok := err.(*AdminException); ok {
errorResponse(c, adminErr.Message, 400)
return
}
errorResponse(c, err.Error(), 500)
return
}
success(c, variables, "")
success(c, variable, "")
}
// SetVariableHTTPRequest set variable request
@@ -798,15 +816,31 @@ type SetVariableHTTPRequest struct {
}
// SetVariable handle set variable
// Python logic: update or create a system setting with the given name and value
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)
errorResponse(c, "Var name is required", 400)
return
}
if req.VarName == "" {
errorResponse(c, "Var name is required", 400)
return
}
if req.VarValue == "" {
errorResponse(c, "Var value is required", 400)
return
}
if err := h.service.SetVariable(req.VarName, req.VarValue); err != nil {
errorResponse(c, err.Error(), 400)
// Check if it's an AdminException
if adminErr, ok := err.(*AdminException); ok {
errorResponse(c, adminErr.Message, 400)
return
}
errorResponse(c, err.Error(), 500)
return
}
@@ -814,10 +848,16 @@ func (h *Handler) SetVariable(c *gin.Context) {
}
// GetConfigs handle get configs
// Python logic: return all service configurations
func (h *Handler) GetConfigs(c *gin.Context) {
configs, err := h.service.GetAllConfigs()
if err != nil {
errorResponse(c, err.Error(), 400)
// Check if it's an AdminException
if adminErr, ok := err.(*AdminException); ok {
errorResponse(c, adminErr.Message, 400)
return
}
errorResponse(c, err.Error(), 500)
return
}
@@ -825,10 +865,16 @@ func (h *Handler) GetConfigs(c *gin.Context) {
}
// GetEnvironments handle get environments
// Python logic: return important environment variables
func (h *Handler) GetEnvironments(c *gin.Context) {
environments, err := h.service.GetAllEnvironments()
if err != nil {
errorResponse(c, err.Error(), 400)
// Check if it's an AdminException
if adminErr, ok := err.(*AdminException); ok {
errorResponse(c, adminErr.Message, 400)
return
}
errorResponse(c, err.Error(), 500)
return
}

View File

@@ -46,15 +46,17 @@ var (
// Service admin service layer
type Service struct {
userDAO *dao.UserDAO
licenseDAO *dao.LicenseDAO
userDAO *dao.UserDAO
licenseDAO *dao.LicenseDAO
systemSettingsDAO *dao.SystemSettingsDAO
}
// NewService create admin service
func NewService() *Service {
return &Service{
userDAO: dao.NewUserDAO(),
licenseDAO: dao.NewLicenseDAO(),
userDAO: dao.NewUserDAO(),
licenseDAO: dao.NewLicenseDAO(),
systemSettingsDAO: dao.NewSystemSettingsDAO(),
}
}
@@ -888,43 +890,172 @@ func (s *Service) RestartService(serviceID string) (map[string]interface{}, erro
// 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
// AdminException admin exception error
type AdminException struct {
Message string
Code int
}
// Error implement error interface
func (e *AdminException) Error() string {
return e.Message
}
// NewAdminException create admin exception
func NewAdminException(message string) *AdminException {
return &AdminException{
Message: message,
Code: 400,
}
}
// GetVariable get variable by name
// Returns the system setting with the given name
// Returns AdminException if the setting is not found
func (s *Service) GetVariable(varName string) ([]map[string]interface{}, error) {
settings, err := s.systemSettingsDAO.GetByName(varName)
if err != nil {
return nil, err
}
if len(settings) == 0 {
return nil, NewAdminException("Can't get setting: " + varName)
}
result := make([]map[string]interface{}, 0, len(settings))
for _, setting := range settings {
result = append(result, map[string]interface{}{
"name": setting.Name,
"source": setting.Source,
"data_type": setting.DataType,
"value": setting.Value,
})
}
return result, nil
}
// GetAllVariables get all variables
// Returns all system settings from database
func (s *Service) GetAllVariables() ([]map[string]interface{}, error) {
// TODO: Implement with settings manager
return []map[string]interface{}{}, nil
settings, err := s.systemSettingsDAO.GetAll()
if err != nil {
return nil, err
}
result := make([]map[string]interface{}, 0, len(settings))
for _, setting := range settings {
result = append(result, map[string]interface{}{
"name": setting.Name,
"source": setting.Source,
"data_type": setting.DataType,
"value": setting.Value,
})
}
return result, nil
}
// SetVariable set variable
// Creates or updates a system setting
// If the setting exists, updates it; otherwise creates a new one
func (s *Service) SetVariable(varName, varValue string) error {
// TODO: Implement with settings manager
_ = varName
_ = varValue
return nil
settings, err := s.systemSettingsDAO.GetByName(varName)
if err != nil {
return err
}
if len(settings) == 1 {
setting := &settings[0]
setting.Value = varValue
return s.systemSettingsDAO.UpdateByName(varName, setting)
} else if len(settings) > 1 {
return NewAdminException("Can't update more than 1 setting: " + varName)
}
// Create new setting if it doesn't exist
// Determine data_type based on name and value
dataType := "string"
if len(varName) >= 7 && varName[:7] == "sandbox" {
dataType = "json"
} else if len(varName) >= 9 && varName[len(varName)-9:] == ".enabled" {
dataType = "boolean"
}
newSetting := &model.SystemSettings{
Name: varName,
Value: varValue,
Source: "admin",
DataType: dataType,
}
return s.systemSettingsDAO.Create(newSetting)
}
// Config methods
// GetAllConfigs get all configs
// Returns all service configurations from the config file
func (s *Service) GetAllConfigs() ([]map[string]interface{}, error) {
// TODO: Implement with config manager
return []map[string]interface{}{}, nil
result := server.GetAllConfigs()
return result, nil
}
// Environment methods
// GetAllEnvironments get all environments
// Returns important environment variables
func (s *Service) GetAllEnvironments() ([]map[string]interface{}, error) {
// TODO: Implement with environment manager
return []map[string]interface{}{}, nil
result := make([]map[string]interface{}, 0)
// DOC_ENGINE
docEngine := os.Getenv("DOC_ENGINE")
if docEngine == "" {
docEngine = "elasticsearch"
}
result = append(result, map[string]interface{}{
"env": "DOC_ENGINE",
"value": docEngine,
})
// DEFAULT_SUPERUSER_EMAIL
defaultSuperuserEmail := os.Getenv("DEFAULT_SUPERUSER_EMAIL")
if defaultSuperuserEmail == "" {
defaultSuperuserEmail = "admin@ragflow.io"
}
result = append(result, map[string]interface{}{
"env": "DEFAULT_SUPERUSER_EMAIL",
"value": defaultSuperuserEmail,
})
// DB_TYPE
dbType := os.Getenv("DB_TYPE")
if dbType == "" {
dbType = "mysql"
}
result = append(result, map[string]interface{}{
"env": "DB_TYPE",
"value": dbType,
})
// DEVICE
device := os.Getenv("DEVICE")
if device == "" {
device = "cpu"
}
result = append(result, map[string]interface{}{
"env": "DEVICE",
"value": device,
})
// STORAGE_IMPL
storageImpl := os.Getenv("STORAGE_IMPL")
if storageImpl == "" {
storageImpl = "MINIO"
}
result = append(result, map[string]interface{}{
"env": "STORAGE_IMPL",
"value": storageImpl,
})
return result, nil
}
// Version methods

View File

@@ -0,0 +1,188 @@
//
// 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 dao
import (
"errors"
"time"
"ragflow/internal/model"
"gorm.io/gorm"
)
// SystemSettingsDAO system settings data access object
type SystemSettingsDAO struct{}
// NewSystemSettingsDAO create system settings DAO instance
func NewSystemSettingsDAO() *SystemSettingsDAO {
return &SystemSettingsDAO{}
}
// GetAll get all system settings
// Returns all system settings records from database
func (d *SystemSettingsDAO) GetAll() ([]model.SystemSettings, error) {
var settings []model.SystemSettings
err := DB.Find(&settings).Error
if err != nil {
return nil, err
}
return settings, nil
}
// GetByName get system settings by name
// Returns settings records that match the given name
func (d *SystemSettingsDAO) GetByName(name string) ([]model.SystemSettings, error) {
var settings []model.SystemSettings
err := DB.Where("name = ?", name).Find(&settings).Error
if err != nil {
return nil, err
}
return settings, nil
}
// UpdateByName update system settings by name
// Updates the setting with the given name using the provided data
func (d *SystemSettingsDAO) UpdateByName(name string, setting *model.SystemSettings) error {
now := time.Now().Unix()
nowDate := time.Now()
return DB.Model(&model.SystemSettings{}).
Where("name = ?", name).
Updates(map[string]interface{}{
"value": setting.Value,
"source": setting.Source,
"data_type": setting.DataType,
"update_time": now,
"update_date": nowDate,
}).Error
}
// Create create a new system setting
// Inserts a new system setting record into database
func (d *SystemSettingsDAO) Create(setting *model.SystemSettings) error {
now := time.Now().Unix()
nowDate := time.Now()
setting.CreateTime = &now
setting.CreateDate = &nowDate
setting.UpdateTime = &now
setting.UpdateDate = &nowDate
return DB.Create(setting).Error
}
// SaveOrCreate update existing setting or create new one
// If setting exists, updates it; otherwise creates a new record
func (d *SystemSettingsDAO) SaveOrCreate(name string, value string, source string, dataType string) error {
settings, err := d.GetByName(name)
if err != nil {
return err
}
if len(settings) == 1 {
setting := &settings[0]
setting.Value = value
return d.UpdateByName(name, setting)
} else if len(settings) > 1 {
return errors.New("can't update more than 1 setting: " + name)
}
newSetting := &model.SystemSettings{
Name: name,
Value: value,
Source: source,
DataType: dataType,
}
return d.Create(newSetting)
}
// Count get total count of system settings
func (d *SystemSettingsDAO) Count() (int64, error) {
var count int64
err := DB.Model(&model.SystemSettings{}).Count(&count).Error
return count, err
}
// DeleteByName delete system setting by name
func (d *SystemSettingsDAO) DeleteByName(name string) error {
return DB.Where("name = ?", name).Delete(&model.SystemSettings{}).Error
}
// Exists check if setting exists by name
func (d *SystemSettingsDAO) Exists(name string) (bool, error) {
var count int64
err := DB.Model(&model.SystemSettings{}).Where("name = ?", name).Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
// GetBySource get system settings by source
func (d *SystemSettingsDAO) GetBySource(source string) ([]model.SystemSettings, error) {
var settings []model.SystemSettings
err := DB.Where("source = ?", source).Find(&settings).Error
if err != nil {
return nil, err
}
return settings, nil
}
// GetByDataType get system settings by data type
func (d *SystemSettingsDAO) GetByDataType(dataType string) ([]model.SystemSettings, error) {
var settings []model.SystemSettings
err := DB.Where("data_type = ?", dataType).Find(&settings).Error
if err != nil {
return nil, err
}
return settings, nil
}
// Transaction execute operations in a transaction
func (d *SystemSettingsDAO) Transaction(fn func(tx *gorm.DB) error) error {
return DB.Transaction(fn)
}
// CreateWithTx create setting within transaction
func (d *SystemSettingsDAO) CreateWithTx(tx *gorm.DB, setting *model.SystemSettings) error {
now := time.Now().Unix()
nowDate := time.Now()
setting.CreateTime = &now
setting.CreateDate = &nowDate
setting.UpdateTime = &now
setting.UpdateDate = &nowDate
return tx.Create(setting).Error
}
// UpdateByNameWithTx update setting within transaction
func (d *SystemSettingsDAO) UpdateByNameWithTx(tx *gorm.DB, name string, setting *model.SystemSettings) error {
now := time.Now().Unix()
nowDate := time.Now()
return tx.Model(&model.SystemSettings{}).
Where("name = ?", name).
Updates(map[string]interface{}{
"value": setting.Value,
"source": setting.Source,
"data_type": setting.DataType,
"update_time": now,
"update_date": nowDate,
}).Error
}

View File

@@ -16,12 +16,18 @@
package model
import "time"
// SystemSettings system settings model
type SystemSettings struct {
Name string `gorm:"column:name;primaryKey;size:128" json:"name"`
Source string `gorm:"column:source;size:32;not null" json:"source"`
DataType string `gorm:"column:data_type;size:32;not null" json:"data_type"`
Value string `gorm:"column:value;type:longtext;not null" json:"value"`
Name string `gorm:"column:name;primaryKey;size:128" json:"name"`
Source string `gorm:"column:source;size:32;not null" json:"source"`
DataType string `gorm:"column:data_type;size:32;not null" json:"data_type"`
Value string `gorm:"column:value;type:longtext;not null" json:"value"`
CreateTime *int64 `gorm:"column:create_time" json:"create_time"`
CreateDate *time.Time `gorm:"column:create_date" json:"create_date"`
UpdateTime *int64 `gorm:"column:update_time" json:"update_time"`
UpdateDate *time.Time `gorm:"column:update_date" json:"update_date"`
}
// TableName specify table name