优化结构

This commit is contained in:
Yvan
2026-08-24 13:56:06 +08:00
parent 0b6b8ddf1a
commit acaee94858
21 changed files with 249 additions and 3025 deletions
-1
View File
@@ -2,7 +2,6 @@ package data
import (
"context"
"encoding/json"
"fmt"
"net"
"os"
+6 -2
View File
@@ -6,6 +6,7 @@ import (
"time"
"github.com/fsnotify/fsnotify"
"kra/internal/config"
)
// watchConfig refreshes the in-memory config snapshot on file changes, while
@@ -79,10 +80,13 @@ func (d *Data) watchConfig() func() {
logger.Error("reload changed config", "mod", "system", "error", loadErr)
continue
}
if next.Data == nil || next.Admin == nil {
logger.Error("reload changed config: data and admin configuration are required", "mod", "system")
if next.Data == nil {
logger.Error("reload changed config: data configuration is required", "mod", "system")
continue
}
if next.Admin == nil {
next.Admin = &config.Admin{}
}
if current := d.runtime.Admin(); current != nil {
next.Admin.Storage = current.Storage
next.Admin.Email = current.Email
+4 -4
View File
@@ -13,15 +13,15 @@ import (
func TestConfigWatcherStopWaitsForDebouncedReload(t *testing.T) {
root := t.TempDir()
configPath := filepath.Join(root, "config.yaml")
config := []byte("data:\n database:\n driver: sqlite\n path: " + filepath.ToSlash(root) + "\nadmin: {}\n")
if err := os.WriteFile(configPath, config, 0o600); err != nil {
rawConfig := []byte("data:\n database:\n driver: sqlite\n path: " + filepath.ToSlash(root) + "\nadmin: {}\n")
if err := os.WriteFile(configPath, rawConfig, 0o600); err != nil {
t.Fatal(err)
}
runtime := config.NewStore(&config.Config{Data: &config.Data{}, Admin: &config.Admin{ConfigPath: configPath}})
started := make(chan struct{})
release := make(chan struct{})
var startOnce sync.Once
stopListener := runtime.Subscribe(func(*config.Data, *config.Admin) {
stopListener := runtime.Subscribe(func(*config.Config) {
startOnce.Do(func() { close(started) })
<-release
})
@@ -30,7 +30,7 @@ func TestConfigWatcherStopWaitsForDebouncedReload(t *testing.T) {
stop := data.watchConfig()
// A write event schedules the 100ms debounce reload.
if err := os.WriteFile(configPath, config, 0o600); err != nil {
if err := os.WriteFile(configPath, rawConfig, 0o600); err != nil {
t.Fatal(err)
}
select {
+1 -1
View File
@@ -78,7 +78,7 @@ extension:
if err := os.WriteFile(configPath, []byte(original), 0o600); err != nil {
t.Fatal(err)
}
runtime := config.NewStore(&config.Data{}, &config.Admin{ConfigPath: configPath})
runtime := config.NewStore(&config.Config{Data: &config.Data{}, Admin: &config.Admin{ConfigPath: configPath}})
data := &Data{runtime: runtime}
if err := data.persistDatabaseConfig(&config.Database{Driver: "mysql", Host: "db", Port: "3306", User: "root", Password: "secret", Name: "kra", Config: "parseTime=True"}, "new-key"); err != nil {
t.Fatal(err)
+9 -18
View File
@@ -12,7 +12,6 @@ import (
dataintegration "kra/internal/data/integration"
"kra/internal/integration/storage"
"google.golang.org/protobuf/encoding/protojson"
"gopkg.in/yaml.v3"
"gorm.io/gorm"
)
@@ -59,9 +58,9 @@ func TestStorageIntegrationConfigRoundTrip(t *testing.T) {
Qiniu: &config.Qiniu{
Zone: "ZoneHuadong", Bucket: "qiniu-bucket", AccessKey: "qiniu-key", SecretKey: "qiniu-secret",
},
AliyunOss: &config.ObjectStore{
AliyunOSS: &config.ObjectStore{
Endpoint: "oss-cn-hangzhou.aliyuncs.com", Region: "cn-hangzhou", Bucket: "assets",
AccessKey: "aliyun-key", SecretKey: "aliyun-secret", BaseUrl: "https://cdn.example.com", PathPrefix: "uploads",
AccessKey: "aliyun-key", SecretKey: "aliyun-secret", BaseURL: "https://cdn.example.com", PathPrefix: "uploads",
},
Minio: &config.ObjectStore{Endpoint: "127.0.0.1:9000", Bucket: "local", ForcePathStyle: true},
}
@@ -70,7 +69,7 @@ func TestStorageIntegrationConfigRoundTrip(t *testing.T) {
}
email := &config.Email{
To: "ops@example.com", From: "mailer@example.com", Host: "smtp.example.com",
Secret: "smtp-secret", Nickname: "Kra", Port: 465, IsSsl: true,
Secret: "smtp-secret", Nickname: "Kra", Port: 465, IsSSL: true,
}
if err := saveEmailIntegrationConfig(db, email); err != nil {
t.Fatal(err)
@@ -135,7 +134,7 @@ func TestResolveStorageIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if loaded.Type != "qiniu" || loaded.Qiniu.GetBucket() != "legacy" {
if loaded.Type != "qiniu" || loaded.Qiniu.Bucket != "legacy" {
t.Fatalf("migrated storage = %#v", loaded)
}
@@ -147,7 +146,7 @@ func TestResolveStorageIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if loaded.Type != "qiniu" || loaded.Qiniu.GetSecretKey() != "legacy-secret" {
if loaded.Type != "qiniu" || loaded.Qiniu.SecretKey != "legacy-secret" {
t.Fatalf("database configuration was replaced by legacy config: %#v", loaded)
}
}
@@ -155,7 +154,7 @@ func TestResolveStorageIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
func TestMaskStorageSecretsLeavesUnconfiguredProvidersEmpty(t *testing.T) {
storage := &config.Storage{
Qiniu: &config.Qiniu{},
AliyunOss: &config.ObjectStore{SecretKey: "configured-secret"},
AliyunOSS: &config.ObjectStore{SecretKey: "configured-secret"},
Minio: &config.ObjectStore{},
}
maskStorageSecrets(storage)
@@ -169,7 +168,7 @@ func TestMaskStorageSecretsLeavesUnconfiguredProvidersEmpty(t *testing.T) {
func TestResolveEmailIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
db := openIntegrationConfigTestDB(t)
legacy := &config.Email{To: "ops@example.com", From: "old@example.com", Host: "smtp.old.example.com", Secret: "old-secret", Port: 465, IsSsl: true}
legacy := &config.Email{To: "ops@example.com", From: "old@example.com", Host: "smtp.old.example.com", Secret: "old-secret", Port: 465, IsSSL: true}
loaded, err := resolveEmailIntegrationConfig(db, legacy)
if err != nil {
t.Fatal(err)
@@ -259,17 +258,9 @@ func TestPersistRuntimeConfigReplacesActiveStorage(t *testing.T) {
nextAdmin.Local = &config.Local{StorePath: newRoot, PathPrefix: "new-files"}
nextAdmin.Email = &config.Email{
To: "ops@example.com", From: "mailer@example.com", Host: "smtp.example.com",
Secret: "runtime-secret", Port: 465, IsSsl: true,
Secret: "runtime-secret", Port: 465, IsSSL: true,
}
dataRaw, err := protojson.Marshal(&config.Data{})
if err != nil {
t.Fatal(err)
}
adminRaw, err := protojson.Marshal(nextAdmin)
if err != nil {
t.Fatal(err)
}
if err = d.PersistRuntimeConfig(context.Background(), dataRaw, adminRaw); err != nil {
if err = d.PersistRuntimeConfig(context.Background(), &config.Config{Data: &config.Data{}, Admin: nextAdmin}); err != nil {
t.Fatal(err)
}