From a1201240d431092a957e7dfa59c4f49b002da4d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:23:15 +0800 Subject: [PATCH] Add files via upload --- internal/config/config.go | 65 ++++++++++++++++++++++++++++++++++ internal/config/config_test.go | 50 ++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/internal/config/config.go b/internal/config/config.go index 03b780ea..0cb3bc3e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io/fs" "os" "path/filepath" "strconv" @@ -41,6 +42,12 @@ type Config struct { Vision VisionConfig `yaml:"vision,omitempty" json:"vision,omitempty"` } +type EnsureLocalConfigResult struct { + Created bool + GeneratedPassword string + ExamplePath string +} + const ( DefaultSummarizationUserIntentLedgerMaxRunes = 96000 DefaultSummarizationUserIntentLedgerEntryMaxRunes = 16000 @@ -1103,6 +1110,64 @@ func Load(path string) (*Config, error) { return &cfg, nil } +func EnsureLocalConfig(path string) (EnsureLocalConfigResult, error) { + path = strings.TrimSpace(path) + if path == "" { + path = "config.yaml" + } + + if _, err := os.Stat(path); err == nil { + return EnsureLocalConfigResult{}, nil + } else if err != nil && !os.IsNotExist(err) { + return EnsureLocalConfigResult{}, fmt.Errorf("检查配置文件失败: %w", err) + } + + examplePath := filepath.Join(filepath.Dir(path), "config.example.yaml") + if _, err := os.Stat(examplePath); err != nil { + if os.IsNotExist(err) { + if alt := "config.example.yaml"; examplePath != alt { + if _, altErr := os.Stat(alt); altErr == nil { + examplePath = alt + } else { + return EnsureLocalConfigResult{}, fmt.Errorf("配置文件 %s 不存在,且未找到模板 %s", path, examplePath) + } + } else { + return EnsureLocalConfigResult{}, fmt.Errorf("配置文件 %s 不存在,且未找到模板 %s", path, examplePath) + } + } else { + return EnsureLocalConfigResult{}, fmt.Errorf("检查配置模板失败: %w", err) + } + } + + data, err := os.ReadFile(examplePath) + if err != nil { + return EnsureLocalConfigResult{}, fmt.Errorf("读取配置模板失败: %w", err) + } + + if dir := filepath.Dir(path); dir != "." && dir != "" { + if err := os.MkdirAll(dir, 0700); err != nil { + return EnsureLocalConfigResult{}, fmt.Errorf("创建配置目录失败: %w", err) + } + } + if err := os.WriteFile(path, data, fs.FileMode(0600)); err != nil { + return EnsureLocalConfigResult{}, fmt.Errorf("创建配置文件失败: %w", err) + } + + password, err := generateStrongPassword(24) + if err != nil { + return EnsureLocalConfigResult{}, fmt.Errorf("生成默认密码失败: %w", err) + } + if err := PersistAuthPassword(path, password); err != nil { + return EnsureLocalConfigResult{}, fmt.Errorf("写入默认密码失败: %w", err) + } + + return EnsureLocalConfigResult{ + Created: true, + GeneratedPassword: password, + ExamplePath: examplePath, + }, nil +} + func generateStrongPassword(length int) (string, error) { if length <= 0 { length = 24 diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 2083034a..5db7f8b9 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -72,6 +72,56 @@ func TestPersistAuthPasswordDoesNotTreatQuotedHashAsComment(t *testing.T) { } } +func TestEnsureLocalConfigCreatesFromExampleWithGeneratedPassword(t *testing.T) { + dir := t.TempDir() + examplePath := filepath.Join(dir, "config.example.yaml") + configPath := filepath.Join(dir, "config.yaml") + + example := []byte(`auth: + password: "change-me-use-a-long-random-password" + session_duration_hours: 12 +server: + host: 127.0.0.1 + port: 8080 +`) + if err := os.WriteFile(examplePath, example, 0644); err != nil { + t.Fatalf("write example: %v", err) + } + + result, err := EnsureLocalConfig(configPath) + if err != nil { + t.Fatalf("EnsureLocalConfig: %v", err) + } + if !result.Created { + t.Fatal("Created = false, want true") + } + if result.GeneratedPassword == "" { + t.Fatal("GeneratedPassword is empty") + } + if result.ExamplePath != examplePath { + t.Fatalf("ExamplePath = %q, want %q", result.ExamplePath, examplePath) + } + + cfg, err := Load(configPath) + if err != nil { + t.Fatalf("Load generated config: %v", err) + } + if cfg.Auth.Password == "change-me-use-a-long-random-password" { + t.Fatal("auth.password still contains the template placeholder") + } + if cfg.Auth.Password != result.GeneratedPassword { + t.Fatalf("Auth.Password = %q, want generated password %q", cfg.Auth.Password, result.GeneratedPassword) + } + + second, err := EnsureLocalConfig(configPath) + if err != nil { + t.Fatalf("EnsureLocalConfig existing: %v", err) + } + if second.Created { + t.Fatal("Created = true for existing config, want false") + } +} + func TestHitlAuditModelEffectiveFallsBackToMainConfig(t *testing.T) { main := OpenAIConfig{ Provider: "openai",