新增 YAML 标签支持并优化配置文件保存逻辑

This commit is contained in:
2026-01-28 16:25:34 +08:00
parent b31711d86d
commit 617c1d0967

View File

@@ -10,38 +10,39 @@ import (
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
type Config struct {
Server ServerConfig `mapstructure:"server"`
Log LogConfig `mapstructure:"log"`
API APIConfig `mapstructure:"api"`
Cron CronConfig `mapstructure:"cron"`
Retention RetentionConfig `mapstructure:"retention"`
DB DBConfig `mapstructure:"db"`
Storage StorageConfig `mapstructure:"storage"`
Admin AdminConfig `mapstructure:"admin"`
Token TokenConfig `mapstructure:"token"`
Feature FeatureConfig `mapstructure:"feature"`
Web WebConfig `mapstructure:"web"`
Server ServerConfig `mapstructure:"server" yaml:"server"`
Log LogConfig `mapstructure:"log" yaml:"log"`
API APIConfig `mapstructure:"api" yaml:"api"`
Cron CronConfig `mapstructure:"cron" yaml:"cron"`
Retention RetentionConfig `mapstructure:"retention" yaml:"retention"`
DB DBConfig `mapstructure:"db" yaml:"db"`
Storage StorageConfig `mapstructure:"storage" yaml:"storage"`
Admin AdminConfig `mapstructure:"admin" yaml:"admin"`
Token TokenConfig `mapstructure:"token" yaml:"token"`
Feature FeatureConfig `mapstructure:"feature" yaml:"feature"`
Web WebConfig `mapstructure:"web" yaml:"web"`
}
type ServerConfig struct {
Port int `mapstructure:"port"`
BaseURL string `mapstructure:"base_url"`
Port int `mapstructure:"port" yaml:"port"`
BaseURL string `mapstructure:"base_url" yaml:"base_url"`
}
type LogConfig struct {
Level string `mapstructure:"level"`
Filename string `mapstructure:"filename"` // 业务日志文件名
DBFilename string `mapstructure:"db_filename"` // 数据库日志文件名
MaxSize int `mapstructure:"max_size"` // 每个日志文件最大大小 (MB)
MaxBackups int `mapstructure:"max_backups"` // 保留旧日志文件最大个数
MaxAge int `mapstructure:"max_age"` // 保留旧日志文件最大天数
Compress bool `mapstructure:"compress"` // 是否压缩旧日志文件
LogConsole bool `mapstructure:"log_console"` // 是否同时输出到控制台
ShowDBLog bool `mapstructure:"show_db_log"` // 是否在控制台显示数据库日志
DBLogLevel string `mapstructure:"db_log_level"` // 数据库日志级别: debug, info, warn, error
Level string `mapstructure:"level" yaml:"level"`
Filename string `mapstructure:"filename" yaml:"filename"` // 业务日志文件名
DBFilename string `mapstructure:"db_filename" yaml:"db_filename"` // 数据库日志文件名
MaxSize int `mapstructure:"max_size" yaml:"max_size"` // 每个日志文件最大大小 (MB)
MaxBackups int `mapstructure:"max_backups" yaml:"max_backups"` // 保留旧日志文件最大个数
MaxAge int `mapstructure:"max_age" yaml:"max_age"` // 保留旧日志文件最大天数
Compress bool `mapstructure:"compress" yaml:"compress"` // 是否压缩旧日志文件
LogConsole bool `mapstructure:"log_console" yaml:"log_console"` // 是否同时输出到控制台
ShowDBLog bool `mapstructure:"show_db_log" yaml:"show_db_log"` // 是否在控制台显示数据库日志
DBLogLevel string `mapstructure:"db_log_level" yaml:"db_log_level"` // 数据库日志级别: debug, info, warn, error
}
func (c LogConfig) GetLevel() string { return c.Level }
@@ -56,65 +57,65 @@ func (c LogConfig) GetShowDBLog() bool { return c.ShowDBLog }
func (c LogConfig) GetDBLogLevel() string { return c.DBLogLevel }
type APIConfig struct {
Mode string `mapstructure:"mode"` // local | redirect
Mode string `mapstructure:"mode" yaml:"mode"` // local | redirect
}
type CronConfig struct {
Enabled bool `mapstructure:"enabled"`
DailySpec string `mapstructure:"daily_spec"`
Enabled bool `mapstructure:"enabled" yaml:"enabled"`
DailySpec string `mapstructure:"daily_spec" yaml:"daily_spec"`
}
type RetentionConfig struct {
Days int `mapstructure:"days"`
Days int `mapstructure:"days" yaml:"days"`
}
type DBConfig struct {
Type string `mapstructure:"type"` // sqlite/mysql/postgres
DSN string `mapstructure:"dsn"`
Type string `mapstructure:"type" yaml:"type"` // sqlite/mysql/postgres
DSN string `mapstructure:"dsn" yaml:"dsn"`
}
type StorageConfig struct {
Type string `mapstructure:"type"` // local/s3/webdav
Local LocalConfig `mapstructure:"local"`
S3 S3Config `mapstructure:"s3"`
WebDAV WebDAVConfig `mapstructure:"webdav"`
Type string `mapstructure:"type" yaml:"type"` // local/s3/webdav
Local LocalConfig `mapstructure:"local" yaml:"local"`
S3 S3Config `mapstructure:"s3" yaml:"s3"`
WebDAV WebDAVConfig `mapstructure:"webdav" yaml:"webdav"`
}
type LocalConfig struct {
Root string `mapstructure:"root"`
Root string `mapstructure:"root" yaml:"root"`
}
type S3Config struct {
Endpoint string `mapstructure:"endpoint"`
Region string `mapstructure:"region"`
Bucket string `mapstructure:"bucket"`
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
PublicURLPrefix string `mapstructure:"public_url_prefix"`
ForcePathStyle bool `mapstructure:"force_path_style"`
Endpoint string `mapstructure:"endpoint" yaml:"endpoint"`
Region string `mapstructure:"region" yaml:"region"`
Bucket string `mapstructure:"bucket" yaml:"bucket"`
AccessKey string `mapstructure:"access_key" yaml:"access_key"`
SecretKey string `mapstructure:"secret_key" yaml:"secret_key"`
PublicURLPrefix string `mapstructure:"public_url_prefix" yaml:"public_url_prefix"`
ForcePathStyle bool `mapstructure:"force_path_style" yaml:"force_path_style"`
}
type WebDAVConfig struct {
URL string `mapstructure:"url"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
PublicURLPrefix string `mapstructure:"public_url_prefix"`
URL string `mapstructure:"url" yaml:"url"`
Username string `mapstructure:"username" yaml:"username"`
Password string `mapstructure:"password" yaml:"password"`
PublicURLPrefix string `mapstructure:"public_url_prefix" yaml:"public_url_prefix"`
}
type AdminConfig struct {
PasswordBcrypt string `mapstructure:"password_bcrypt"`
PasswordBcrypt string `mapstructure:"password_bcrypt" yaml:"password_bcrypt"`
}
type TokenConfig struct {
DefaultTTL string `mapstructure:"default_ttl"`
DefaultTTL string `mapstructure:"default_ttl" yaml:"default_ttl"`
}
type FeatureConfig struct {
WriteDailyFiles bool `mapstructure:"write_daily_files"`
WriteDailyFiles bool `mapstructure:"write_daily_files" yaml:"write_daily_files"`
}
type WebConfig struct {
Path string `mapstructure:"path"`
Path string `mapstructure:"path" yaml:"path"`
}
// Bing 默认配置 (内置)
@@ -193,8 +194,13 @@ func Init(configPath string) error {
targetConfigPath = "data/config.yaml"
}
fmt.Printf("Config file not found, creating default config at %s\n", targetConfigPath)
if err := v.SafeWriteConfigAs(targetConfigPath); err != nil {
fmt.Printf("Warning: Failed to create default config file: %v\n", err)
var defaultCfg Config
if err := v.Unmarshal(&defaultCfg); err == nil {
data, _ := yaml.Marshal(&defaultCfg)
if err := os.WriteFile(targetConfigPath, data, 0644); err != nil {
fmt.Printf("Warning: Failed to create default config file: %v\n", err)
}
}
}
@@ -237,18 +243,29 @@ func GetConfig() *Config {
}
func SaveConfig(cfg *Config) error {
v.Set("server", cfg.Server)
v.Set("log", cfg.Log)
v.Set("api", cfg.API)
v.Set("cron", cfg.Cron)
v.Set("retention", cfg.Retention)
v.Set("db", cfg.DB)
v.Set("storage", cfg.Storage)
v.Set("admin", cfg.Admin)
v.Set("token", cfg.Token)
v.Set("feature", cfg.Feature)
v.Set("web", cfg.Web)
return v.WriteConfig()
configLock.Lock()
defer configLock.Unlock()
// 1. 使用 yaml.v3 序列化,它会尊重结构体字段顺序及 yaml 标签
data, err := yaml.Marshal(cfg)
if err != nil {
return fmt.Errorf("failed to marshal config: %v", err)
}
// 2. 获取当前使用的配置文件路径
targetPath := v.ConfigFileUsed()
if targetPath == "" {
targetPath = "data/config.yaml" // 默认回退路径
}
// 3. 直接写入文件,绕过 viper 的字母序排序逻辑
if err := os.WriteFile(targetPath, data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %v", err)
}
// 4. 同步更新内存中的全局配置对象
GlobalConfig = cfg
return nil
}
func GetRawViper() *viper.Viper {