mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-01 06:36:38 +08:00
重构获取公共参数
This commit is contained in:
@@ -34,12 +34,14 @@ type CreateSystemConfigRequest struct {
|
||||
Key string `json:"key" binding:"required,max=64"`
|
||||
Value string `json:"value" binding:"required,max=255"`
|
||||
Type string `json:"type" binding:"required,oneof=system business"`
|
||||
Visibility int `json:"visibility" binding:"oneof=0 1"`
|
||||
Description string `json:"description" binding:"max=255"`
|
||||
}
|
||||
|
||||
// UpdateSystemConfigRequest 更新系统配置请求
|
||||
type UpdateSystemConfigRequest struct {
|
||||
Value string `json:"value" binding:"required,max=255"`
|
||||
Visibility *int `json:"visibility" binding:"omitempty,oneof=0 1"`
|
||||
Description string `json:"description" binding:"max=255"`
|
||||
}
|
||||
|
||||
@@ -78,6 +80,7 @@ func CreateSystemConfig(c *gin.Context) {
|
||||
Key: req.Key,
|
||||
Value: req.Value,
|
||||
Type: req.Type,
|
||||
Visibility: req.Visibility,
|
||||
Description: req.Description,
|
||||
}
|
||||
|
||||
@@ -206,6 +209,10 @@ func UpdateSystemConfig(c *gin.Context) {
|
||||
updates := map[string]interface{}{
|
||||
"description": req.Description,
|
||||
}
|
||||
if req.Visibility != nil {
|
||||
updates["visibility"] = *req.Visibility
|
||||
config.Visibility = *req.Visibility
|
||||
}
|
||||
if key != model.ConfigKeySMTPPassword || req.Value != "******" {
|
||||
updates["value"] = req.Value
|
||||
config.Value = req.Value
|
||||
|
||||
@@ -73,6 +73,7 @@ func TestCreateSystemConfig(t *testing.T) {
|
||||
Key: "custom_key",
|
||||
Value: "custom_value",
|
||||
Type: "system",
|
||||
Visibility: model.ConfigVisibilityVisible,
|
||||
Description: "desc",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -99,7 +100,10 @@ func TestCreateSystemConfig(t *testing.T) {
|
||||
t.Fatalf("failed to find system config in Redis: %v", err)
|
||||
}
|
||||
if redisConfig.Value != "custom_value" {
|
||||
t.Errorf("expected value 'custom_value', got '%s'", redisConfig.Value)
|
||||
t.Errorf("CreateSystemConfig(custom_key).Value = %q, want %q", redisConfig.Value, "custom_value")
|
||||
}
|
||||
if redisConfig.Visibility != model.ConfigVisibilityVisible {
|
||||
t.Errorf("CreateSystemConfig(custom_key).Visibility = %d, want %d", redisConfig.Visibility, model.ConfigVisibilityVisible)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -217,8 +221,10 @@ func TestUpdateSystemConfig(t *testing.T) {
|
||||
router := setupTestRouter(adminUser)
|
||||
|
||||
t.Run("update successfully", func(t *testing.T) {
|
||||
hidden := model.ConfigVisibilityHidden
|
||||
payload := UpdateSystemConfigRequest{
|
||||
Value: "Super Site Name",
|
||||
Visibility: &hidden,
|
||||
Description: "Updated Description",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -234,7 +240,7 @@ func TestUpdateSystemConfig(t *testing.T) {
|
||||
// Verify database
|
||||
var cfg model.SystemConfig
|
||||
dbConn.Where("key = ?", model.ConfigKeySiteName).First(&cfg)
|
||||
if cfg.Value != "Super Site Name" || cfg.Description != "Updated Description" {
|
||||
if cfg.Value != "Super Site Name" || cfg.Description != "Updated Description" || cfg.Visibility != model.ConfigVisibilityHidden {
|
||||
t.Errorf("database values not updated: %+v", cfg)
|
||||
}
|
||||
|
||||
@@ -244,6 +250,9 @@ func TestUpdateSystemConfig(t *testing.T) {
|
||||
if redisConfig.Value != "Super Site Name" {
|
||||
t.Errorf("redis cache value not updated, got '%s'", redisConfig.Value)
|
||||
}
|
||||
if redisConfig.Visibility != model.ConfigVisibilityHidden {
|
||||
t.Errorf("redis cache visibility = %d, want %d", redisConfig.Visibility, model.ConfigVisibilityHidden)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("update non-existent config", func(t *testing.T) {
|
||||
|
||||
@@ -26,26 +26,9 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// PublicConfigResponse 公共配置响应
|
||||
type PublicConfigResponse struct {
|
||||
UploadAllowedExtensions string `json:"upload_allowed_extensions"` // 允许上传的图片扩展名
|
||||
SiteName string `json:"site_name"` // 站点名称
|
||||
PasswordLoginEnabled bool `json:"password_login_enabled"` // 是否允许密码登录
|
||||
RegistrationEnabled bool `json:"registration_enabled"` // 是否允许注册
|
||||
PasswordRegisterEnabled bool `json:"password_register_enabled"` // 是否允许密码注册
|
||||
OIDCLoginEnabled bool `json:"oidc_login_enabled"` // 是否允许 OIDC 登录
|
||||
MaxAPIKeysPerUser int `json:"max_api_keys_per_user"` // 每个用户最大 API Key 数量
|
||||
CapLoginEnabled bool `json:"cap_login_enabled"` // 是否启用人机验证
|
||||
CapAutoSolve bool `json:"cap_auto_solve"` // 打开页面后是否自动开始计算
|
||||
EmailLoginVerificationEnabled bool `json:"email_login_verification_enabled"` // 是否启用邮箱登录验证
|
||||
EmailRegisterVerificationEnabled bool `json:"email_register_verification_enabled"` // 是否启用邮箱注册验证
|
||||
MenuDisplayConfig map[string]bool `json:"menu_display_config"` // 目录显示配置
|
||||
SearchEngineIndexingEnabled bool `json:"search_engine_indexing_enabled"` // 是否允许搜索引擎检索
|
||||
}
|
||||
|
||||
// GetPublicConfig 获取公共配置
|
||||
// @Summary 获取公共配置
|
||||
// @Description 返回对前端公开的系统配置信息,如允许上传的文件类型、站点名称、是否开放注册等
|
||||
// @Description 返回系统配置表中 visibility 为 1 的配置键值集合
|
||||
// @Tags config
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
@@ -53,96 +36,15 @@ type PublicConfigResponse struct {
|
||||
// @Router /api/v1/config/public [get]
|
||||
func GetPublicConfig(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
var sc model.SystemConfig
|
||||
|
||||
// 1. upload_allowed_extensions
|
||||
var uploadExtensions string
|
||||
if err := sc.GetByKey(ctx, model.ConfigKeyUploadAllowedExtensions); err == nil {
|
||||
uploadExtensions = sc.Value
|
||||
}
|
||||
|
||||
// 2. site_name
|
||||
var siteName string
|
||||
if err := sc.GetByKey(ctx, model.ConfigKeySiteName); err == nil {
|
||||
siteName = sc.Value
|
||||
}
|
||||
|
||||
// 3. registration_enabled
|
||||
var registrationEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyRegistrationEnabled); err == nil {
|
||||
registrationEnabled = val
|
||||
}
|
||||
|
||||
// 3.1 password_login_enabled
|
||||
var passwordLoginEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyPasswordLoginEnabled); err == nil {
|
||||
passwordLoginEnabled = val
|
||||
}
|
||||
|
||||
// 3.2 password_register_enabled
|
||||
var passwordRegisterEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyPasswordRegisterEnabled); err == nil {
|
||||
passwordRegisterEnabled = val
|
||||
}
|
||||
|
||||
// 3.3 oidc_login_enabled
|
||||
var oidcLoginEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyOIDCLoginEnabled); err == nil {
|
||||
oidcLoginEnabled = val
|
||||
}
|
||||
|
||||
// 3.4 cap_login_enabled
|
||||
var capLoginEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyCapLoginEnabled); err == nil {
|
||||
capLoginEnabled = val
|
||||
}
|
||||
|
||||
// 3.5 cap_auto_solve
|
||||
capAutoSolve := true // 默认自动开始
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyCapAutoSolve); err == nil {
|
||||
capAutoSolve = val
|
||||
}
|
||||
|
||||
// 4. max_api_keys_per_user
|
||||
var maxAPIKeys int
|
||||
if val, err := model.GetIntByKey(ctx, model.ConfigKeyMaxAPIKeysPerUser); err == nil {
|
||||
maxAPIKeys = val
|
||||
}
|
||||
|
||||
var emailLoginVerificationEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyEmailLoginVerificationEnabled); err == nil {
|
||||
emailLoginVerificationEnabled = val
|
||||
}
|
||||
|
||||
var emailRegisterVerificationEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeyEmailRegisterVerificationEnabled); err == nil {
|
||||
emailRegisterVerificationEnabled = val
|
||||
}
|
||||
|
||||
var searchEngineIndexingEnabled bool
|
||||
if val, err := model.GetBoolByKey(ctx, model.ConfigKeySearchEngineIndexingEnabled); err == nil {
|
||||
searchEngineIndexingEnabled = val
|
||||
}
|
||||
|
||||
menuDisplayConfig, err := model.GetMenuDisplayConfig(ctx)
|
||||
configs, err := model.ListVisibleSystemConfigs(ctx)
|
||||
if err != nil {
|
||||
menuDisplayConfig = make(map[string]bool)
|
||||
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
response := PublicConfigResponse{
|
||||
UploadAllowedExtensions: uploadExtensions,
|
||||
SiteName: siteName,
|
||||
PasswordLoginEnabled: passwordLoginEnabled,
|
||||
RegistrationEnabled: registrationEnabled,
|
||||
PasswordRegisterEnabled: passwordRegisterEnabled,
|
||||
OIDCLoginEnabled: oidcLoginEnabled,
|
||||
MaxAPIKeysPerUser: maxAPIKeys,
|
||||
CapLoginEnabled: capLoginEnabled,
|
||||
CapAutoSolve: capAutoSolve,
|
||||
EmailLoginVerificationEnabled: emailLoginVerificationEnabled,
|
||||
EmailRegisterVerificationEnabled: emailRegisterVerificationEnabled,
|
||||
MenuDisplayConfig: menuDisplayConfig,
|
||||
SearchEngineIndexingEnabled: searchEngineIndexingEnabled,
|
||||
response := make(map[string]string, len(configs))
|
||||
for _, config := range configs {
|
||||
response[config.Key] = config.Value
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, util.OK(response))
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2026 linux.do
|
||||
Modified by Arctel.net, 2026
|
||||
|
||||
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 config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/testhelper"
|
||||
"github.com/Rain-kl/Wavelet/internal/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestGetPublicConfigUsesVisibility(t *testing.T) {
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
defer cleanup()
|
||||
|
||||
if err := dbConn.Create(&model.SystemConfig{
|
||||
Key: "custom_public_key",
|
||||
Value: "custom_public_value",
|
||||
Type: "system",
|
||||
Visibility: model.ConfigVisibilityVisible,
|
||||
Description: "custom public config",
|
||||
}).Error; err != nil {
|
||||
t.Fatalf("Create(custom_public_key) error = %v", err)
|
||||
}
|
||||
if err := dbConn.Model(&model.SystemConfig{}).
|
||||
Where("key = ?", model.ConfigKeySiteName).
|
||||
Update("visibility", model.ConfigVisibilityHidden).Error; err != nil {
|
||||
t.Fatalf("Update(%s.visibility) error = %v", model.ConfigKeySiteName, err)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router.GET("/api/v1/config/public", GetPublicConfig)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/config/public", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("GetPublicConfig() status = %d, want %d; body = %s", w.Code, http.StatusOK, w.Body.String())
|
||||
}
|
||||
|
||||
var resp util.ResponseAny
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("json.Unmarshal(GetPublicConfig()) error = %v", err)
|
||||
}
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal(GetPublicConfig().data) error = %v", err)
|
||||
}
|
||||
var configs map[string]string
|
||||
if err := json.Unmarshal(dataBytes, &configs); err != nil {
|
||||
t.Fatalf("json.Unmarshal(GetPublicConfig().data) error = %v", err)
|
||||
}
|
||||
|
||||
if got := configs["custom_public_key"]; got != "custom_public_value" {
|
||||
t.Errorf("GetPublicConfig()[custom_public_key] = %q, want %q", got, "custom_public_value")
|
||||
}
|
||||
if _, ok := configs[model.ConfigKeySiteName]; ok {
|
||||
t.Errorf("GetPublicConfig()[%s] is present, want hidden", model.ConfigKeySiteName)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user