mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-04 04:26:38 +08:00
优化,排查项目问题
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@@ -19,6 +20,12 @@ import (
|
||||
// EnvPrefix 是所有环境变量驱动的覆盖使用的前缀。
|
||||
const EnvPrefix = "MeBox"
|
||||
|
||||
// RuntimeMu 保护运行时热更新配置字段的并发读写:ApplyRuntimeSetting 在
|
||||
// HTTP goroutine 中写字段,serverManager 的证书轮询等后台协程在无锁读取
|
||||
// 同一批字段。string 是双字结构,无锁并发读写可读到撕裂的 header。
|
||||
// 写方在 ApplyRuntimeSetting 内 Lock,读方(cmd/server)在轮询处 RLock。
|
||||
var RuntimeMu sync.RWMutex
|
||||
|
||||
// Load 从默认值 / 文件 / 环境读取配置。
|
||||
//
|
||||
// 即使没有文件也始终返回可用的 Config。
|
||||
@@ -45,8 +52,13 @@ func Load() (*Config, error) {
|
||||
}
|
||||
s := viper.New()
|
||||
s.SetConfigFile(filepath.Join("config", e.Name()))
|
||||
if err := s.ReadInConfig(); err == nil {
|
||||
_ = v.MergeConfigMap(s.AllSettings())
|
||||
if err := s.ReadInConfig(); err != nil {
|
||||
// 分片解析失败不能静默吞掉:database.yaml 语法错误会让
|
||||
// database.dsn 缺失 → type=auto 静默回退 SQLite,新数据
|
||||
// 全部写进一个空库而用户无感知。
|
||||
fmt.Fprintf(os.Stderr, "warning: parse config/%s failed: %v\n", e.Name(), err)
|
||||
} else {
|
||||
v.MergeConfigMap(s.AllSettings())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,14 @@ func (c *Config) normalize() error {
|
||||
return fmt.Errorf("generate jwt secret: %w", err)
|
||||
}
|
||||
c.Secrets.JWTSecret = hex.EncodeToString(buf)
|
||||
_ = os.MkdirAll(c.App.DataDir, 0o750)
|
||||
_ = os.WriteFile(path, []byte(c.Secrets.JWTSecret), 0o600)
|
||||
// 持久化失败(DataDir 只读/权限异常)会导致每次重启重新生成
|
||||
// 密钥、全部会话静默失效、多实例各持不同 secret——必须让
|
||||
// 操作员感知。
|
||||
if mkErr := os.MkdirAll(c.App.DataDir, 0o750); mkErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: persist jwt secret failed (mkdir): %v\n", mkErr)
|
||||
} else if wErr := os.WriteFile(path, []byte(c.Secrets.JWTSecret), 0o600); wErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: persist jwt secret failed (write): %v\n", wErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -34,8 +34,15 @@ func SaveDatabaseConfig(dbType, dsn string) error {
|
||||
return fmt.Errorf("marshal config.yaml: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, out, 0644); err != nil {
|
||||
return fmt.Errorf("write config.yaml: %w", err)
|
||||
// 原子写:临时文件 + rename,避免进程崩溃/断电留下截断的 config.yaml
|
||||
// (下次启动会硬失败);DSN 含数据库密码,权限收窄到 0600。
|
||||
tmp := configPath + ".tmp"
|
||||
if err := os.WriteFile(tmp, out, 0o600); err != nil {
|
||||
return fmt.Errorf("write config.yaml.tmp: %w", err)
|
||||
}
|
||||
if err := os.Rename(tmp, configPath); err != nil {
|
||||
_ = os.Remove(tmp)
|
||||
return fmt.Errorf("replace config.yaml: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user