初始化项目:基础设施 + 用户认证 + 后台管理系统 + AGPL 3.0 许可
This commit is contained in:
119
internal/config/config.go
Normal file
119
internal/config/config.go
Normal file
@ -0,0 +1,119 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Config 应用总配置
|
||||
type Config struct {
|
||||
Server ServerConfig `mapstructure:"server"`
|
||||
Database DatabaseConfig `mapstructure:"database"`
|
||||
JWT JWTConfig `mapstructure:"jwt"`
|
||||
Bcrypt BcryptConfig `mapstructure:"bcrypt"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Port string `mapstructure:"port"`
|
||||
Mode string `mapstructure:"mode"`
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Port string `mapstructure:"port"`
|
||||
User string `mapstructure:"user"`
|
||||
Password string `mapstructure:"password"`
|
||||
DBName string `mapstructure:"dbname"`
|
||||
SSLMode string `mapstructure:"sslmode"`
|
||||
}
|
||||
|
||||
type JWTConfig struct {
|
||||
Secret string `mapstructure:"secret"`
|
||||
AccessExpire int `mapstructure:"access_expire"` // 分钟
|
||||
RefreshExpire int `mapstructure:"refresh_expire"` // 小时
|
||||
RememberExpire int `mapstructure:"remember_expire"` // 小时
|
||||
}
|
||||
|
||||
type BcryptConfig struct {
|
||||
Cost int `mapstructure:"cost"`
|
||||
}
|
||||
|
||||
// 全局配置实例(初始化后只读)
|
||||
var App *Config
|
||||
|
||||
// Load 加载配置:config.yaml → .env 覆盖
|
||||
func Load(configPath string) *Config {
|
||||
// 1. 从 .env 加载环境变量(优先于 config.yaml)
|
||||
loadEnvFile(".env")
|
||||
|
||||
v := viper.New()
|
||||
|
||||
// 2. 读取 config.yaml
|
||||
v.SetConfigFile(configPath)
|
||||
v.SetConfigType("yaml")
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
log.Fatalf("读取配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 环境变量覆盖(DATABASE_PASSWORD → database.password)
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
v.AutomaticEnv()
|
||||
bindEnvOverride(v)
|
||||
|
||||
c := &Config{}
|
||||
if err := v.Unmarshal(c); err != nil {
|
||||
log.Fatalf("解析配置失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("[Config] JWT.SecretLen=%d JWT.AccessExpire=%d min JWT.RefreshExpire=%d h JWT.RememberExpire=%d h | Server.Mode=%s",
|
||||
len(c.JWT.Secret), c.JWT.AccessExpire, c.JWT.RefreshExpire, c.JWT.RememberExpire, c.Server.Mode)
|
||||
|
||||
App = c
|
||||
return c
|
||||
}
|
||||
|
||||
// loadEnvFile 读取 .env 文件并设置环境变量(仅当环境变量未设置时)
|
||||
func loadEnvFile(path string) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return // .env 不存在,跳过
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := strings.TrimSpace(parts[1])
|
||||
if os.Getenv(key) == "" {
|
||||
os.Setenv(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bindEnvOverride 将环境变量映射到 config 的嵌套键
|
||||
func bindEnvOverride(v *viper.Viper) {
|
||||
_ = v.BindEnv("database.password", "DATABASE_PASSWORD")
|
||||
_ = v.BindEnv("jwt.secret", "JWT_SECRET")
|
||||
}
|
||||
|
||||
// DSN 返回 PostgreSQL 连接字符串
|
||||
func (d DatabaseConfig) DSN() string {
|
||||
return "host=" + d.Host +
|
||||
" port=" + d.Port +
|
||||
" user=" + d.User +
|
||||
" password=" + d.Password +
|
||||
" dbname=" + d.DBName +
|
||||
" sslmode=" + d.SSLMode
|
||||
}
|
||||
Reference in New Issue
Block a user