初始化项目:基础设施 + 用户认证 + 后台管理系统 + AGPL 3.0 许可

This commit is contained in:
2026-05-26 13:46:33 +08:00
parent 1315df6501
commit 483fdd919f
56 changed files with 5804 additions and 40 deletions

119
internal/config/config.go Normal file
View 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
}