This commit is contained in:
ryan
2026-06-08 22:14:06 +08:00
parent ddda7c44ef
commit d14d222ede
15 changed files with 234 additions and 312 deletions
+45 -2
View File
@@ -70,9 +70,19 @@ func init() {
viper.SetConfigFile(configPath)
viper.AutomaticEnv()
// 读取配置文件
// 读取配置文件(可选:找不到文件时使用空默认值 + 环境变量)
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("[Config] read config failed: %v\n", err)
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
// 文件存在但读取/解析失败
if _, statErr := os.Stat(configPath); statErr == nil {
log.Fatalf("[Config] read config failed: %v\n", err)
}
}
log.Println("[Config] no config file found, using environment variables only")
viper.SetConfigType("yaml")
if err := viper.ReadConfig(strings.NewReader("")); err != nil {
log.Fatalf("[Config] failed to init empty config: %v\n", err)
}
}
// 解析配置到结构体
@@ -81,8 +91,11 @@ func init() {
log.Fatalf("[Config] parse config failed: %v\n", err)
}
applyDefaults(&c)
// 环境变量覆盖(优先级高于 config.yaml)
applyEnvOverrides(&c)
applyDefaults(&c)
// Disable standard DB/Redis initializations during tests to prevent connection attempts.
if isTest() {
@@ -98,6 +111,12 @@ func init() {
printConfig(&c)
}
func applyDefaults(c *configModel) {
if c.App.SessionAge <= 0 {
c.App.SessionAge = 86400
}
}
// ─── 环境变量覆盖层 ────────────────────────────────────────────────────────────
// 环境变量优先级高于 config.yaml,未设置则保留 yaml 中的值。
@@ -171,11 +190,19 @@ func applyEnvOverrides(c *configModel) {
c.Database.LogLevel = envStr("DB_LOG_LEVEL", c.Database.LogLevel)
c.Database.MaxIdleConn = envInt("DB_MAX_IDLE_CONN", c.Database.MaxIdleConn)
c.Database.MaxOpenConn = envInt("DB_MAX_OPEN_CONN", c.Database.MaxOpenConn)
// 当 DB_HOST 环境变量已设置时自动启用数据库
if _, ok := os.LookupEnv("DB_HOST"); ok {
c.Database.Enabled = true
}
c.Database.Enabled = envBool("DB_ENABLED", c.Database.Enabled)
c.Database.SQLitePath = envStr("SQLITE_PATH", c.Database.SQLitePath)
// ─── Redis ───
if v, ok := os.LookupEnv("REDIS_ADDR"); ok {
c.Redis.Addrs = []string{v}
c.Redis.Enabled = true // 当 REDIS_ADDR 已设置时自动启用
}
c.Redis.Enabled = envBool("REDIS_ENABLED", c.Redis.Enabled)
c.Redis.Username = envStr("REDIS_USERNAME", c.Redis.Username)
c.Redis.Password = envStr("REDIS_PASSWORD", c.Redis.Password)
c.Redis.DB = envInt("REDIS_DB", c.Redis.DB)
@@ -185,7 +212,9 @@ func applyEnvOverrides(c *configModel) {
// ─── ClickHouse ───
if v, ok := os.LookupEnv("CLICKHOUSE_HOST"); ok {
c.ClickHouse.Hosts = []string{v}
c.ClickHouse.Enabled = true
}
c.ClickHouse.Enabled = envBool("CLICKHOUSE_ENABLED", c.ClickHouse.Enabled)
c.ClickHouse.Username = envStr("CLICKHOUSE_USERNAME", c.ClickHouse.Username)
c.ClickHouse.Password = envStr("CLICKHOUSE_PASSWORD", c.ClickHouse.Password)
c.ClickHouse.Database = envStr("CLICKHOUSE_NAME", c.ClickHouse.Database)
@@ -206,14 +235,28 @@ func applyEnvOverrides(c *configModel) {
c.S3.SecretAccessKey = envStr("S3_SECRET_ACCESS_KEY", c.S3.SecretAccessKey)
c.S3.CdnURL = envStr("S3_CDN_URL", c.S3.CdnURL)
c.S3.PathStyle = envBool("S3_PATH_STYLE", c.S3.PathStyle)
c.S3.Enabled = envBool("S3_ENABLED", c.S3.Enabled)
// ─── Worker ───
c.Worker.Concurrency = envInt("WORKER_CONCURRENCY", c.Worker.Concurrency)
c.Worker.StrictPriority = envBool("WORKER_STRICT_PRIORITY", c.Worker.StrictPriority)
// 无 yaml 且无环境变量时,使用代码级默认队列
if len(c.Worker.Queues) == 0 {
c.Worker.Queues = []QueueConfig{
{Name: "webhook", Priority: 10},
{Name: "whitelist_only", Priority: 5},
{Name: "default", Priority: 3},
}
}
// ─── Scheduler ───
c.Scheduler.CleanupUnusedUploadsTaskCron = envStr(
"SCHEDULER_CLEANUP_CRON", c.Scheduler.CleanupUnusedUploadsTaskCron,
)
if c.Scheduler.CleanupUnusedUploadsTaskCron == "" {
c.Scheduler.CleanupUnusedUploadsTaskCron = "@daily"
}
}
// printConfig 打印配置内容
+1
View File
@@ -55,6 +55,7 @@ func (a *appConfig) IsProduction() bool {
// databaseConfig 数据库配置
type databaseConfig struct {
Enabled bool `mapstructure:"enabled"`
SQLitePath string `mapstructure:"sqlite_path"` // PostgreSQL 禁用时的 SQLite 文件路径
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
+21 -17
View File
@@ -29,11 +29,15 @@ import (
"github.com/Rain-kl/Wavelet/internal/db/idgen"
)
func Migrate() {
// dbType 返回当前数据库类型名称(用于日志输出)
func dbType() string {
if !config.Config.Database.Enabled {
return
return "SQLite"
}
return "PostgreSQL"
}
func Migrate() {
if err := db.DB(context.Background()).AutoMigrate(
&model.User{},
&model.AuthSource{},
@@ -44,9 +48,9 @@ func Migrate() {
&model.TaskExecution{},
&model.Template{},
); err != nil {
log.Fatalf("[PostgreSQL] auto migrate failed: %v\n", err)
log.Fatalf("[%s] auto migrate failed: %v\n", dbType(), err)
}
log.Printf("[PostgreSQL] auto migrate success\n")
log.Printf("[%s] auto migrate success\n", dbType())
// 初始化系统配置数据
initSystemConfigs()
@@ -68,9 +72,9 @@ func ensureConfigKeyExists(key, value, configType, description string) {
Description: description,
}
if err := tx.Create(&newConfig).Error; err != nil {
log.Printf("[PostgreSQL] failed to create system config key %s: %v\n", key, err)
log.Printf("[%s] failed to create system config key %s: %v\n", dbType(), key, err)
} else {
log.Printf("[PostgreSQL] initialized system config key %s\n", key)
log.Printf("[%s] initialized system config key %s\n", dbType(), key)
}
}
}
@@ -81,7 +85,7 @@ func initSystemConfigs() {
var count int64
if err := tx.Model(&model.SystemConfig{}).Count(&count).Error; err != nil {
log.Printf("[PostgreSQL] failed to check system_config table: %v\n", err)
log.Printf("[%s] failed to check system_config table: %v\n", dbType(), err)
return
}
@@ -240,9 +244,9 @@ func initSystemConfigs() {
}
if err := tx.Create(&defaultConfigs).Error; err != nil {
log.Printf("[PostgreSQL] failed to create default system configs: %v\n", err)
log.Printf("[%s] failed to create default system configs: %v\n", dbType(), err)
} else {
log.Printf("[PostgreSQL] initialized %d default system configs\n", len(defaultConfigs))
log.Printf("[%s] initialized %d default system configs\n", dbType(), len(defaultConfigs))
}
}
@@ -252,7 +256,7 @@ func initDefaultAdmin() {
var count int64
if err := tx.Model(&model.User{}).Where("username = ?", "admin").Count(&count).Error; err != nil {
log.Printf("[PostgreSQL] failed to check default admin user: %v\n", err)
log.Printf("[%s] failed to check default admin user: %v\n", dbType(), err)
return
}
@@ -272,9 +276,9 @@ func initDefaultAdmin() {
}
if err := tx.Create(&adminUser).Error; err != nil {
log.Printf("[PostgreSQL] failed to create default admin user: %v\n", err)
log.Printf("[%s] failed to create default admin user: %v\n", dbType(), err)
} else {
log.Printf("[PostgreSQL] default admin user created successfully (username: admin, password: 12345678)\n")
log.Printf("[%s] default admin user created successfully (username: admin, password: 12345678)\n", dbType())
}
}
@@ -283,7 +287,7 @@ func initTemplates() {
tx := db.DB(context.Background())
var count int64
if err := tx.Model(&model.Template{}).Count(&count).Error; err != nil {
log.Printf("[PostgreSQL] failed to check templates table: %v\n", err)
log.Printf("[%s] failed to check templates table: %v\n", dbType(), err)
return
}
@@ -314,9 +318,9 @@ func initTemplates() {
var t model.Template
if err := tx.Where("key = ?", dt.Key).First(&t).Error; err != nil {
if err := tx.Create(&dt).Error; err != nil {
log.Printf("[PostgreSQL] failed to create template key %s: %v\n", dt.Key, err)
log.Printf("[%s] failed to create template key %s: %v\n", dbType(), dt.Key, err)
} else {
log.Printf("[PostgreSQL] initialized template key %s\n", dt.Key)
log.Printf("[%s] initialized template key %s\n", dbType(), dt.Key)
}
}
}
@@ -324,8 +328,8 @@ func initTemplates() {
}
if err := tx.Create(&defaultTemplates).Error; err != nil {
log.Printf("[PostgreSQL] failed to create default templates: %v\n", err)
log.Printf("[%s] failed to create default templates: %v\n", dbType(), err)
} else {
log.Printf("[PostgreSQL] initialized %d default templates\n", len(defaultTemplates))
log.Printf("[%s] initialized %d default templates\n", dbType(), len(defaultTemplates))
}
}
+31 -1
View File
@@ -26,6 +26,7 @@ import (
"time"
"github.com/Rain-kl/Wavelet/internal/config"
"github.com/glebarez/sqlite"
"go.opentelemetry.io/otel/attribute"
"gorm.io/driver/postgres"
"gorm.io/gorm"
@@ -39,10 +40,39 @@ var (
func init() {
if !config.Config.Database.Enabled {
log.Println("[PostgreSQL] is disabled, skipping initialization")
// PostgreSQL 禁用,使用 SQLite
initSQLite()
return
}
initPostgres()
}
// initSQLite 初始化 SQLite 数据库(PostgreSQL 禁用时的后备方案)
func initSQLite() {
sqlitePath := config.Config.Database.SQLitePath
if sqlitePath == "" {
sqlitePath = "./data/wavelet.db"
}
var err error
db, err = gorm.Open(sqlite.Open(sqlitePath), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: &gormZapLogger{
logLevel: parseLogLevel(config.Config.Database.LogLevel),
slowThreshold: config.Config.Database.SlowThreshold,
ignoreRecordNotFoundError: config.Config.App.IsProduction(),
},
})
if err != nil {
log.Fatalf("[SQLite] init connection failed: %v\n", err)
}
log.Printf("[SQLite] initialized (path: %s)\n", sqlitePath)
}
// initPostgres 初始化 PostgreSQL 数据库
func initPostgres() {
var err error
dbConfig := config.Config.Database